diff --git a/Images/frog-hunter.png b/Images/frog-hunter.png deleted file mode 100644 index 9e08e3b..0000000 Binary files a/Images/frog-hunter.png and /dev/null differ diff --git a/Images/frog.gif b/Images/frog.gif deleted file mode 100644 index 56516a6..0000000 Binary files a/Images/frog.gif and /dev/null differ diff --git a/Images/froggy.png b/Images/froggy.png deleted file mode 100644 index ce600e6..0000000 Binary files a/Images/froggy.png and /dev/null differ diff --git a/README.md b/README.md index 0120b42..95b8fd1 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,188 @@ # ๐Ÿธ Frog Hunter โ€“ Memory Game Frog Hunter is a browser-based memory game developed as part of the **Foundation Project at Hack Your Future**. +The project explores how a simple, familiar game can be built incrementally while introducing core frontend logic and a basic backend architecture. -The game is inspired by the classic Memory Game and challenges players to find all matching pairs of cards by flipping them two at a time. -Our focus is on making the game work and building a smooth, intuitive, and enjoyable experience that encourages players to keep playing. +The game is inspired by the classic Memory game: players reveal cards two at a time and try to find all matching pairs. +Over the course of the project, the implementation evolves from a purely frontend solution to a full-stack version where card data is served from a backend API. -This project is developed incrementally, following a sprint-based approach, where functionality, structure, and complexity grow step by step. +This repository reflects that progression and the decisions made along the way. --- ## ๐ŸŽฎ About the Game -In Frog Hunter, players interact with a grid of cards showing frog-themed images. +In Frog Hunter, players interact with a grid of frog-themed cards. + +Core mechanics: - Cards start face down -- Players flip cards to reveal the images -- Only two cards can be revealed at the same time -- Matching cards remain revealed -- Non-matching cards flip back after a short delay -- The game ends once all matching pairs have been found -- The timer starts from when the player clicks their first card -- The counter shows how many times the player has revealed a card +- Players can reveal two cards at a time +- Revealed cards show their image +- Matching cards disappear from the board and are no longer interactive +- Not matching cards flip back after a short delay +- The game ends when all matching pairs are found +- A timer starts when the first card is revealed +- A counter tracks how many cards reveals the player has made (a reveal is not the same as a click) + +The game logic is intentionally kept clear and predictable, focusing on state management, timing, and user feedback rather than complex visual effects. + +--- + +## ๐Ÿง  Development Approach + +The project follows a sprint-based, incremental approach: + +### 1. Initial version + +- Game logic implemented entirely in the frontend +- Cards hardcoded in JavaScript +- Focus on DOM manipulation and game state + +### 2. Refactored version -Display a timer should start from when the player clicks their first card. +- Card data moved to a backend API +- SQLite database introduced for persistence +- Frontend refactored to fetch card data dynamically +- Game logic remains fully client-side -The game logic and interactions are designed to be clear, responsive, and easy to understand. +This separation keeps responsibilities clear: + +- **Frontend** โ†’ game logic, UI, state, timing +- **Backend** โ†’ data storage and retrieval only --- -## ๐Ÿ› ๏ธ Technologies & Approach +## ๐Ÿ› ๏ธ Technologies Used + +### Frontend -This project is built using: +- **HTML** โ€” structure and semantic markup +- **CSS** โ€” layout, visual styling, and basic responsiveness +- **JavaScript** โ€” game logic, state handling, and DOM updates -- **HTML** for structure -- **CSS** for layout and animations -- **JavaScript** for game logic and DOM manipulation -- **Git & GitHub** for version control and collaboration +### Backend -As part of the project milestones, the game evolves from a fully frontend-based implementation to a version that includes: +- **Node.js** โ€” server runtime +- **Express.js** โ€” REST API +- **SQLite** โ€” lightweight relational database for card data -- Fetching card data from a backend API -- Storing card information in a database +### Tooling -These steps are introduced progressively as the project advances through the sprints. +- **Git & GitHub** โ€” version control and collaboration +- **npm** โ€” dependency management and scripts +- **Postman** โ€” API testing during development +- **VS Code** โ€” development environment + +--- + +## ๐Ÿ”Œ API Overview + +The frontend fetches card data from a backend API. + +`GET /cards` + +Returns all available cards in JSON format. +Each card includes: + +`- id` +`- name` +`- image` +`- alt` +`- color` + +Example response: + +```json +[ + { + "id": 1, + "name": "frog1", + "image": "Images/frog1.png", + "alt": "Frog smiling", + "color": "#43bef7" + } +] +``` + +The API was manually tested using Postman. +Valid endpoints return `200 OK`; invalid routes return `404 Not Found`. + +--- + +## โ–ถ๏ธ How to Run the Project + +### Backend + +```bash +cd server +npm install +npm start +``` + +The server runs at: + +```arduino +http://localhost:3000 +``` + +### Frontend + +Open `app/index.html` in your browser. +The game will fetch card data from the running backend server. --- ## ๐Ÿ“‚ Project Structure -/frogHunter -โ”‚โ”€โ”€ index.html -โ”‚โ”€โ”€ styles.css -โ”‚โ”€โ”€ script.js +``` +frog hunter/ +โ”œโ”€โ”€ postman/ # Postman tests, collection and API evidence +| โ”œโ”€โ”€ collection/ # Exported Postman collection +โ”‚ โ”œโ”€โ”€ screenshots/ # Evidence of API responses and test results +โ”‚ โ”‚ โ”œโ”€โ”€ body-preview.png +โ”‚ โ”‚ โ””โ”€โ”€ test-results.png +โ”‚ โ””โ”€โ”€ postman-tests.md # Documentation of Postman test scripts +| +โ”œโ”€โ”€ app/ # Frontend application +| |โ”€โ”€ Images/ # Card images and visual assets +| โ”‚โ”€โ”€ index.html # Main HTML file +| โ”‚โ”€โ”€ styles.css # Application styles +| โ””โ”€โ”€ script.js # Frontend logic +| +โ”œโ”€โ”€ server/ # Backend +| โ”œโ”€โ”€ package.json # Backend dependencies +| โ”œโ”€โ”€ package-lock.json # Dependency lock file +| โ”œโ”€โ”€ index.js # Express server and API routes +| โ”œโ”€โ”€ database.db # SQLite database +| โ””โ”€โ”€ script-2.sql # Database schema and seed data +| +โ””โ”€โ”€ README.md # Project overview and setup instructions +``` + +The structure is intentionally simple to keep the focus on: + +- game logic +- state management +- frontendโ€“backend communication + +--- + +## ๐Ÿ“ Notes & Decisions -The structure is intentionally simple, allowing us to focus on core concepts such as game logic, state handling, and user interaction. +- Card data is no longer hardcoded in the frontend. +- New cards can be added by updating the database without changing game logic. +- The backend remains minimal by design. +- The frontend grid and cards scale with screen size; minor UI refinements are possible for smaller devices. +- Image assets are credited in the UI. +- Matched cards are now visually hidden with a smooth animation, providing clear feedback when a pair is found. +- The 'matched' state ensures cards are both non-interactive and visually removed without affecting game logic or grid layout. --- ## ๐Ÿ‘ฉ๐Ÿปโ€๐Ÿ’ป๐Ÿ‘ฉ๐Ÿผโ€๐Ÿ’ป Team -This project is developed as a pair-programming exercise, emphasizing collaboration, communication, and shared ownership of the codebase: +This project was developed as a pair-programming exercise, emphasizing collaboration, communication, and shared ownership of the codebase. - **Paloma Cardozo** - **Iryna Lopatina** diff --git a/app/Images/celebration.png b/app/Images/celebration.png new file mode 100644 index 0000000..898230b Binary files /dev/null and b/app/Images/celebration.png differ diff --git a/app/Images/favicon.ico b/app/Images/favicon.ico new file mode 100644 index 0000000..93e5c45 Binary files /dev/null and b/app/Images/favicon.ico differ diff --git a/Images/frog1.png b/app/Images/frog1.png similarity index 100% rename from Images/frog1.png rename to app/Images/frog1.png diff --git a/Images/frog10.png b/app/Images/frog10.png similarity index 100% rename from Images/frog10.png rename to app/Images/frog10.png diff --git a/Images/frog11.png b/app/Images/frog11.png similarity index 100% rename from Images/frog11.png rename to app/Images/frog11.png diff --git a/Images/frog12.png b/app/Images/frog12.png similarity index 100% rename from Images/frog12.png rename to app/Images/frog12.png diff --git a/Images/frog13.png b/app/Images/frog13.png similarity index 100% rename from Images/frog13.png rename to app/Images/frog13.png diff --git a/Images/frog14.png b/app/Images/frog14.png similarity index 100% rename from Images/frog14.png rename to app/Images/frog14.png diff --git a/Images/frog15.png b/app/Images/frog15.png similarity index 100% rename from Images/frog15.png rename to app/Images/frog15.png diff --git a/Images/frog16.png b/app/Images/frog16.png similarity index 100% rename from Images/frog16.png rename to app/Images/frog16.png diff --git a/Images/frog17.png b/app/Images/frog17.png similarity index 100% rename from Images/frog17.png rename to app/Images/frog17.png diff --git a/Images/frog18.png b/app/Images/frog18.png similarity index 100% rename from Images/frog18.png rename to app/Images/frog18.png diff --git a/Images/frog19.png b/app/Images/frog19.png similarity index 100% rename from Images/frog19.png rename to app/Images/frog19.png diff --git a/Images/frog2.png b/app/Images/frog2.png similarity index 100% rename from Images/frog2.png rename to app/Images/frog2.png diff --git a/Images/frog20.png b/app/Images/frog20.png similarity index 100% rename from Images/frog20.png rename to app/Images/frog20.png diff --git a/Images/frog3.png b/app/Images/frog3.png similarity index 100% rename from Images/frog3.png rename to app/Images/frog3.png diff --git a/Images/frog4.png b/app/Images/frog4.png similarity index 100% rename from Images/frog4.png rename to app/Images/frog4.png diff --git a/Images/frog5.png b/app/Images/frog5.png similarity index 100% rename from Images/frog5.png rename to app/Images/frog5.png diff --git a/Images/frog6.png b/app/Images/frog6.png similarity index 100% rename from Images/frog6.png rename to app/Images/frog6.png diff --git a/Images/frog7.png b/app/Images/frog7.png similarity index 100% rename from Images/frog7.png rename to app/Images/frog7.png diff --git a/Images/frog8.png b/app/Images/frog8.png similarity index 100% rename from Images/frog8.png rename to app/Images/frog8.png diff --git a/Images/frog9.png b/app/Images/frog9.png similarity index 100% rename from Images/frog9.png rename to app/Images/frog9.png diff --git a/Images/lotus-flower.png b/app/Images/lotus-flower.png similarity index 100% rename from Images/lotus-flower.png rename to app/Images/lotus-flower.png diff --git a/app/Images/mind-blown.png b/app/Images/mind-blown.png new file mode 100644 index 0000000..cc79ff9 Binary files /dev/null and b/app/Images/mind-blown.png differ diff --git a/app/index.html b/app/index.html new file mode 100644 index 0000000..ec6c9ee --- /dev/null +++ b/app/index.html @@ -0,0 +1,90 @@ + + + + + + Frog Hunter - memory game + + + + + + +
+

Frog Hunter

+

Test Your Memory. Hunt the Frogs.

+
+ +
+
+
+ + +
+
Reveals: 0
+
Time left: 00:00
+
+
+ +
+ + + +
+
+ +
+ +
+
+ Frog celebration + +

You won!

+

+

+ +
+
+ +
+
+ Frog mind blown + +

Oops! Timeโ€™s Up

+

So close!

+ +
+
+
+ + + + + + diff --git a/app/script.js b/app/script.js new file mode 100644 index 0000000..e817e49 --- /dev/null +++ b/app/script.js @@ -0,0 +1,304 @@ +const gameBoard = document.querySelector(".container"); +const moves = document.querySelector(".moves"); +const timer = document.querySelector(".timer"); +const winner = document.querySelector(".winner"); +const timeout = document.querySelector(".timeout"); +const buttons = document.querySelectorAll(".button"); +const levelButtons = document.querySelectorAll(".level-btn"); + +const defaultNumberOfPairs = 6; +let currentPairs = defaultNumberOfPairs; + +const cardFrontImageSrc = "Images/lotus-flower.png"; + +const levelSettings = { + 6: 60, + 8: 90, + 10: 120, +}; + +let gameCards = []; +let timeLeft = levelSettings[defaultNumberOfPairs]; +let endTime = null; +let moveCounter = 0; +let timerInterval = null; + +let hasFlippedCard = false; +let lockBoard = false; +let firstCard = null; +let secondCard = null; + +async function fetchCards() { + try { + const response = await fetch("/cards"); + if (!response.ok) throw new Error("Failed to fetch cards"); + + return await response.json(); + } catch (error) { + console.error("API error:", error); + + return []; + } +} + +function shuffleArray(array) { + let currentIndex = array.length; + + while (currentIndex !== 0) { + const randomIndex = Math.floor(Math.random() * currentIndex); + currentIndex--; + + [array[currentIndex], array[randomIndex]] = [ + array[randomIndex], + array[currentIndex], + ]; + } + + return array; +} + +function createElement(tag, className, attributes = {}) { + const element = document.createElement(tag); + if (className) element.className = className; + + Object.entries(attributes).forEach(([key, value]) => { + element[key] = value; + }); + + return element; +} + +function formatTime(totalSeconds) { + const mins = Math.floor(totalSeconds / 60) + .toString() + .padStart(2, "0"); + const secs = (totalSeconds % 60).toString().padStart(2, "0"); + + return `Time left: ${mins}:${secs}`; +} + +function stopTimer() { + clearInterval(timerInterval); + timerInterval = null; +} + +function resetBoard() { + hasFlippedCard = false; + lockBoard = false; + firstCard = null; + secondCard = null; +} + +function showTimeout() { + lockBoard = true; + resetBoard(); + timeout.style.display = "flex"; +} + +function startTimer() { + if (timerInterval) return; + + endTime = Date.now() + timeLeft * 1000; + + timerInterval = setInterval(() => { + const remaining = Math.max(0, Math.ceil((endTime - Date.now()) / 1000)); + + timer.textContent = formatTime(remaining); + + if (remaining === 0) { + stopTimer(); + showTimeout(); + } + }, 250); +} + +function incrementMoves() { + moveCounter++; + moves.textContent = `Reveals: ${moveCounter}`; +} + +function revealCard(card) { + card.classList.add("flipped"); + incrementMoves(); +} + +function checkForMatch() { + const isMatch = firstCard.dataset.name === secondCard.dataset.name; + + if (isMatch) { + disableCards(); + } else { + unflipCards(); + } +} + +function disableCards() { + lockBoard = true; + + const handleTransitionEnd = (e) => { + if (e.propertyName !== "transform") return; + + firstCard.classList.add("matched"); + secondCard.classList.add("matched"); + + secondCard.removeEventListener("transitionend", handleTransitionEnd); + + const matchedCards = document.querySelectorAll( + ".flip-card-inner.matched", + ).length; + + if (matchedCards === gameCards.length) { + stopTimer(); + showWinner(); + } + + resetBoard(); + }; + + secondCard.addEventListener("transitionend", handleTransitionEnd); +} + +function showWinner() { + lockBoard = true; + + const winnerMoves = winner.querySelector(".winner-moves"); + const winnerTime = winner.querySelector(".winner-time"); + + if (winnerMoves) winnerMoves.textContent = moves.textContent; + if (winnerTime) winnerTime.textContent = timer.textContent; + + winner.style.display = "flex"; +} + +function flipCard(card) { + if ( + lockBoard || + card === firstCard || + card.classList.contains("flipped") || + card.classList.contains("matched") + ) + if ( + lockBoard || + card === firstCard || + card.classList.contains("flipped") || + card.classList.contains("matched") + ) + return; + + revealCard(card); + + if (!timerInterval) startTimer(); + + if (!hasFlippedCard) { + hasFlippedCard = true; + firstCard = card; + + return; + } + + secondCard = card; + checkForMatch(); +} + +function unflipCards() { + lockBoard = true; + + setTimeout(() => { + firstCard.classList.remove("flipped"); + secondCard.classList.remove("flipped"); + + resetBoard(); + }, 1200); +} + +function setGridColumns(numberOfPairs = defaultNumberOfPairs) { + let columns = 4; + if (numberOfPairs === 10) columns = 5; + + gameBoard.style.setProperty("--columns", columns); +} + +function renderGameBoard() { + gameCards.forEach((card) => { + const cardElement = createElement("div", "flip-card"); + const cardInner = createElement("div", "flip-card-inner"); + const cardFront = createElement("div", "flip-card-front"); + const cardBack = createElement("div", "flip-card-back"); + + cardInner.dataset.name = card.name; + cardBack.style.backgroundColor = card.color; + + const cardFrontImage = createElement("img", null, { + src: cardFrontImageSrc, + alt: "Lotus Flower", + }); + + const cardBackImage = createElement("img", null, { + src: card.image, + alt: card.alt, + }); + + cardFront.appendChild(cardFrontImage); + cardBack.appendChild(cardBackImage); + cardInner.appendChild(cardFront); + cardInner.appendChild(cardBack); + cardElement.appendChild(cardInner); + gameBoard.appendChild(cardElement); + + cardInner.addEventListener("click", () => flipCard(cardInner)); + }); +} + +async function createGame(numberOfPairs) { + gameBoard.replaceChildren(); + + winner.style.display = "none"; + timeout.style.display = "none"; + + stopTimer(); + resetBoard(); + + moveCounter = 0; + moves.textContent = `Reveals: 0`; + + gameCards = []; + + if (levelSettings[numberOfPairs] !== undefined) { + timeLeft = levelSettings[numberOfPairs]; + } else { + timeLeft = 60; + } + + timer.textContent = formatTime(timeLeft); + + setGridColumns(numberOfPairs); + + const cards = await fetchCards(); + if (cards.length === 0) return; + const shuffleCards = shuffleArray([...cards]); + const selectedCards = shuffleCards.slice(0, numberOfPairs); + gameCards = shuffleArray([...selectedCards, ...selectedCards]); + + renderGameBoard(); +} + +document.addEventListener("DOMContentLoaded", () => { + levelButtons.forEach((btn) => { + btn.addEventListener("click", () => { + currentPairs = parseInt(btn.dataset.pairs, 10); + + levelButtons.forEach((level) => level.classList.remove("active")); + btn.classList.add("active"); + + createGame(currentPairs); + }); + }); + + buttons.forEach((button) => { + button.addEventListener("click", () => { + createGame(currentPairs); + }); + }); + + createGame(currentPairs); +}); diff --git a/app/styles.css b/app/styles.css new file mode 100644 index 0000000..1f0bc6e --- /dev/null +++ b/app/styles.css @@ -0,0 +1,330 @@ +:root { + --background: #0e4b43; + --primary-color: #3a9377; + --secondary-color: #54b999; + --hover: #76e7c3; + --cover: #10574d; + --button: #cb6812; + --btn-cover: #fa8e35; + --card: #19996b; + --white: #ffffff; + --font: "Noto Sans", sans-serif; +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + background: var(--background); + font-family: var(--font); + display: flex; + flex-direction: column; + min-height: 100vh; + color: var(--primary-color); +} + +header { + padding-top: 1em; + text-align: center; +} + +.wrap { + width: 95%; + max-width: 600px; + margin: 0 auto; +} + +h1 { + font-size: 2em; +} + +h2 { + font-size: 0.9em; + font-weight: normal; +} + +.game { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + margin-top: 2em; +} + +.text, +footer { + text-align: center; + margin: 10px; +} + +.text { + font-size: 20px; + color: var(--primary-color); +} + +a { + color: var(--secondary-color); + text-decoration: none; +} + +a:hover { + color: var(--hover); +} + +.controls { + display: flex; + flex-direction: column; + align-items: flex-start; + width: 100%; + gap: 15px; + margin-bottom: 2em; +} + +.controls-main { + display: flex; + align-items: center; + justify-content: flex-start; + width: 100%; + gap: 15px; +} + +.levels { + display: flex; + width: 100%; + justify-content: flex-start; + gap: 8px; +} + +.level-btn { + background: var(--cover); + border: 2px solid var(--primary-color); + color: var(--secondary-color); + padding: 0.4rem 0.8rem; + border-radius: 5px; + cursor: pointer; + font-weight: 600; + font-family: inherit; + transition: all 0.2s; +} + +.level-btn:hover { + border-color: var(--secondary-color); + color: var(--white); + transform: scale(1.05); +} + +.level-btn.active { + background: var(--primary-color); + border-color: var(--primary-color); + color: var(--white); +} + +.button { + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; + padding: 0.6rem 1.2rem; + background-color: var(--button); + color: var(--white); + border: none; + border-radius: 5px; + cursor: pointer; + font-size: 1.1rem; + font-weight: 600; + text-transform: uppercase; + justify-content: center; + font-family: var(--font); + transition: all 0.3s ease; + box-shadow: 0 6px 10px rgba(0, 0, 0, 0.15); +} + +.button:hover { + background-color: var(--btn-cover); + transform: scale(1.05); +} + +.state { + font-size: 18px; + color: var(--secondary-color); +} + +.container { + display: grid; + grid-template-columns: repeat(4, 1fr); + width: 100%; + gap: 0.5em; + margin: 0; + transition: all 0.5s ease; +} + +@media (min-width: 600px) { + .container { + grid-template-columns: repeat(var(--columns, 4), 1fr); + } +} + +.flip-card { + width: 100%; + aspect-ratio: 1 / 1; + perspective: 1000px; + cursor: pointer; + transition: all 0.5s ease; +} + +.flip-card-inner { + position: relative; + width: 100%; + height: 100%; + transition: + transform 0.6s ease, + opacity 0.8s ease; + transform-style: preserve-3d; + border-radius: 5px; + box-shadow: 0 6px 10px rgba(0, 0, 0, 0.15); +} + +.flip-card:hover .flip-card-inner:not(.flipped) { + transform: scale(1.05); +} + +.flip-card:active .flip-card-inner:not(.flipped) { + transform: scale(0.95); +} + +.flip-card-inner.flipped { + transform: rotateY(180deg); +} + +.flip-card-inner.matched { + pointer-events: none; + opacity: 0; + transform: scale(0.5) rotateY(180deg); +} + +.flip-card-front, +.flip-card-back { + position: absolute; + width: 100%; + height: 100%; + backface-visibility: hidden; + display: flex; + align-items: center; + justify-content: center; + border-radius: 5px; +} + +.flip-card-front { + background-color: var(--card); +} + +.flip-card-back { + transform: rotateY(180deg); +} + +.flip-card-front img { + width: 50%; + height: auto; +} + +.flip-card-back img { + width: 80%; + height: 80%; + object-fit: cover; +} + +.frog-icon { + width: 120px; + height: auto; +} + +footer { + padding: 1em 0; +} + +.winner, +.timeout { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + backdrop-filter: blur(4px); + display: none; + justify-content: center; + align-items: center; + flex-direction: column; + text-align: center; + font-size: 1.2rem; + z-index: 1000; +} + +@keyframes floatIn { + 0% { + opacity: 0; + transform: scale(0.7) translateY(100px); + } + 100% { + opacity: 1; + transform: scale(1) translateY(0); + } +} + +.winner-container, +.timeout-container { + padding: 3rem; + border-radius: 5px; + border: 10px solid var(--cover); + max-width: 600px; + margin: 10%; + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.3); + background: var(--background); + animation: floatIn 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards; +} + +.winner-title, +.timeout-title { + font-weight: 700; + font-size: 2rem; + color: var(--secondary-color); + margin-bottom: 20px; +} + +.winner-btn, +.timeout-btn { + justify-self: center; + margin-top: 25px; +} + +@media (min-width: 601px) { + .controls { + flex-direction: row; + justify-content: space-between; + align-items: center; + gap: 30px; + } + + .controls-main { + width: auto; + gap: 30px; + } + + .levels { + width: auto; + justify-content: flex-end; + } +} + +@media (min-width: 1000px) { + .container { + gap: 1em; + } + + .winner-container, + .timeout-container { + margin: 10% auto; + } +} diff --git a/index.html b/index.html deleted file mode 100644 index 575c875..0000000 --- a/index.html +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - Frog Hunter - - - - - -

- Person dressed as a frog - Froggy Pairs - Person dressed as a frog -

- -

Can you match all the frogs?

- -
-
- -
- Frog animation - You won! - Frog animation -
-
- Reveals: 0 - Time: 0 s -
-
-
-
- - - - - - diff --git a/postman/collection/11 Feb/Final project.postman_test_run.json b/postman/collection/11 Feb/Final project.postman_test_run.json new file mode 100644 index 0000000..40c3c7b --- /dev/null +++ b/postman/collection/11 Feb/Final project.postman_test_run.json @@ -0,0 +1,88 @@ +{ + "id": "338af7a0-f7ac-443c-8467-bd08da62647f", + "name": "Final project", + "timestamp": "2026-02-11T09:17:37.565Z", + "collection_id": "a697a91d-89c8-4bdc-8ecd-a77567afd725", + "folder_id": 0, + "environment_id": "0", + "totalPass": 0, + "delay": 0, + "persist": true, + "status": "finished", + "startedAt": "2026-02-11T09:17:36.938Z", + "totalFail": 0, + "results": [ + { + "id": "1d4b3333-68f1-40dd-9bd7-f85011c16276", + "name": "http://localhost:3000/", + "url": "http://localhost:3000/", + "time": 37, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": {}, + "testPassFailCounts": {}, + "times": [ + 37 + ], + "allTests": [ + {} + ] + }, + { + "id": "7f62cc82-ee77-4fa2-b880-6c4f7f9118b7", + "name": "http://localhost:3000/cards", + "url": "http://localhost:3000/cards", + "time": 12, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": {}, + "testPassFailCounts": {}, + "times": [ + 12 + ], + "allTests": [ + {} + ] + }, + { + "id": "11c00cf9-e8f2-4400-afdd-26fc75329495", + "name": "http://localhost:3000/card", + "url": "http://localhost:3000/card", + "time": 5, + "responseCode": { + "code": 404, + "name": "Not Found" + }, + "tests": {}, + "testPassFailCounts": {}, + "times": [ + 5 + ], + "allTests": [ + {} + ] + } + ], + "count": 1, + "totalTime": 54, + "collection": { + "requests": [ + { + "id": "1d4b3333-68f1-40dd-9bd7-f85011c16276", + "method": "GET" + }, + { + "id": "7f62cc82-ee77-4fa2-b880-6c4f7f9118b7", + "method": "GET" + }, + { + "id": "11c00cf9-e8f2-4400-afdd-26fc75329495", + "method": "GET" + } + ] + } +} \ No newline at end of file diff --git a/postman/collection/9 Feb/Final project.postman_test_run.json b/postman/collection/9 Feb/Final project.postman_test_run.json new file mode 100644 index 0000000..ce086da --- /dev/null +++ b/postman/collection/9 Feb/Final project.postman_test_run.json @@ -0,0 +1,88 @@ +{ + "id": "ad9be582-469d-421c-96bf-a3f80906ed14", + "name": "Final project", + "timestamp": "2026-02-09T19:30:37.426Z", + "collection_id": "a697a91d-89c8-4bdc-8ecd-a77567afd725", + "folder_id": 0, + "environment_id": "0", + "totalPass": 0, + "delay": 0, + "persist": true, + "status": "finished", + "startedAt": "2026-02-09T19:30:36.800Z", + "totalFail": 0, + "results": [ + { + "id": "1d4b3333-68f1-40dd-9bd7-f85011c16276", + "name": "http://localhost:3000/", + "url": "http://localhost:3000/", + "time": 10, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": {}, + "testPassFailCounts": {}, + "times": [ + 10 + ], + "allTests": [ + {} + ] + }, + { + "id": "7f62cc82-ee77-4fa2-b880-6c4f7f9118b7", + "name": "http://localhost:3000/cards", + "url": "http://localhost:3000/cards", + "time": 5, + "responseCode": { + "code": 200, + "name": "OK" + }, + "tests": {}, + "testPassFailCounts": {}, + "times": [ + 5 + ], + "allTests": [ + {} + ] + }, + { + "id": "11c00cf9-e8f2-4400-afdd-26fc75329495", + "name": "http://localhost:3000/card", + "url": "http://localhost:3000/card", + "time": 5, + "responseCode": { + "code": 404, + "name": "Not Found" + }, + "tests": {}, + "testPassFailCounts": {}, + "times": [ + 5 + ], + "allTests": [ + {} + ] + } + ], + "count": 1, + "totalTime": 20, + "collection": { + "requests": [ + { + "id": "1d4b3333-68f1-40dd-9bd7-f85011c16276", + "method": "GET" + }, + { + "id": "7f62cc82-ee77-4fa2-b880-6c4f7f9118b7", + "method": "GET" + }, + { + "id": "11c00cf9-e8f2-4400-afdd-26fc75329495", + "method": "GET" + } + ] + } +} \ No newline at end of file diff --git a/postman/postman-tests.md b/postman/postman-tests.md new file mode 100644 index 0000000..abc8c16 --- /dev/null +++ b/postman/postman-tests.md @@ -0,0 +1,94 @@ +# Postman โ€“ Test Scripts + +## Endpoint tested + +GET /cards + +## Purpose + +Validate that the API returns a valid JSON array of cards with the expected structure and data quality. + +--- + +## Test scripts used in Postman + +```javascript +pm.test("Status code is 200", function () { + pm.response.to.have.status(200); +}); + +pm.test("Response is valid JSON and is an array", function () { + const jsonData = pm.response.json(); + pm.expect(jsonData).to.be.an("array"); +}); + +pm.test("Each item has required properties", function () { + const jsonData = pm.response.json(); + jsonData.forEach(function (item, index) { + pm.expect(item).to.have.property("id"); + pm.expect(item).to.have.property("name"); + pm.expect(item).to.have.property("image"); + pm.expect(item).to.have.property("alt"); + pm.expect(item).to.have.property("color"); + }); +}); + +pm.test("Response time is under 1000ms", function () { + pm.expect(pm.response.responseTime).to.be.below(1000); +}); + +pm.test("Content-Type header is application/json", function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); +}); + +pm.test("At least one card exists in the response", function () { + const jsonData = pm.response.json(); + pm.expect(jsonData.length).to.be.at.least(1); +}); + +pm.test( + "Each card has required properties (id, name, image, alt, color)", + function () { + const jsonData = pm.response.json(); + jsonData.forEach((card, index) => { + pm.expect(card).to.have.property("id"); + pm.expect(card).to.have.property("name"); + pm.expect(card).to.have.property("image"); + pm.expect(card).to.have.property("alt"); + pm.expect(card).to.have.property("color"); + }); + }, +); + +pm.test("Each card has correct property types", function () { + const jsonData = pm.response.json(); + jsonData.forEach((card, index) => { + pm.expect(card.id).to.be.a("number"); + pm.expect(card.name).to.be.a("string"); + pm.expect(card.image).to.be.a("string"); + pm.expect(card.alt).to.be.a("string"); + pm.expect(card.color).to.be.a("string"); + }); +}); + +pm.test("Each card's color is a valid hex color format", function () { + const jsonData = pm.response.json(); + const hexColorRegex = /^#[0-9A-Fa-f]{6}$/; + jsonData.forEach((card, index) => { + pm.expect(card.color).to.match(hexColorRegex); + }); +}); +``` + +--- + +## Test results + +All tests passed successfully. + +See screenshots: + +- screenshots/body-preview.png +- screenshots/test-results.png diff --git a/postman/screenshots/body-preview.png b/postman/screenshots/body-preview.png new file mode 100644 index 0000000..750e410 Binary files /dev/null and b/postman/screenshots/body-preview.png differ diff --git a/postman/screenshots/test-results.png b/postman/screenshots/test-results.png new file mode 100644 index 0000000..9245b11 Binary files /dev/null and b/postman/screenshots/test-results.png differ diff --git a/script.js b/script.js deleted file mode 100644 index 59faf70..0000000 --- a/script.js +++ /dev/null @@ -1,334 +0,0 @@ -const gameBoard = document.querySelector(".container"); -const moves = document.querySelector(".moves"); -const timer = document.querySelector(".timer"); -const winner = document.querySelector(".winner"); -const restartButton = document.querySelector(".button"); -const defaultNumberOfPairs = 6; - -let gameCards = []; - -const game = { - gameStarted: false, - totalReveals: 0, - totalTime: 0, - timerInterval: null, - firstCard: null, - secondCard: null, - lockBoard: false, - matchedPairs: 0, -}; - -const cardFrontImageSrc = "Images/lotus-flower.png"; - -const availableCards = [ - { - name: "frog1", - image: "Images/frog1.png", - alt: "Smiling frog", - color: "#ee76f8", - matched: false, - }, - { - name: "frog2", - image: "Images/frog2.png", - alt: "Frog playing guitar", - color: "#3a93f9", - matched: false, - }, - { - name: "frog3", - image: "Images/frog3.png", - alt: "Frog with umbrella", - color: "#f2ba49", - matched: false, - }, - { - name: "frog4", - image: "Images/frog4.png", - alt: "Frog putting on makeup", - color: "#5dcd5d", - matched: false, - }, - { - name: "frog5", - image: "Images/frog5.png", - alt: "Dancing frog", - color: "#db7866", - matched: false, - }, - { - name: "frog6", - image: "Images/frog6.png", - alt: "Frog with a microphone", - color: "#9566db", - matched: false, - }, - { - name: "frog7", - image: "Images/frog7.png", - alt: "Frog with a walking stick", - color: "#76f887", - matched: false, - }, - { - name: "frog8", - image: "Images/frog8.png", - alt: "Frog eating", - color: "#a476f8", - matched: false, - }, - { - name: "frog9", - image: "Images/frog9.png", - alt: "Frog waving", - color: "#fc6c56", - matched: false, - }, - { - name: "frog10", - image: "Images/frog10.png", - alt: "Frog lying down", - color: "#3d5fe3", - matched: false, - }, - { - name: "frog11", - image: "Images/frog11.png", - alt: "Frog swimming", - color: "#f876a6", - matched: false, - }, - { - name: "frog12", - image: "Images/frog12.png", - alt: "Frog jumping", - color: "#f2f876", - matched: false, - }, - { - name: "frog13", - image: "Images/frog13.png", - alt: "Frog drinking a martini", - color: "#768cf8", - matched: false, - }, - { - name: "frog14", - image: "Images/frog14.png", - alt: "Singing frog", - color: "#942e9e", - matched: false, - }, - { - name: "frog15", - image: "Images/frog15.png", - alt: "Happy frog", - color: "#3fe1f3", - matched: false, - }, - { - name: "frog16", - image: "Images/frog16.png", - alt: "Frog saying goodbye", - color: "#f87676", - matched: false, - }, - { - name: "frog17", - image: "Images/frog17.png", - alt: "Frog sunbathing", - color: "#f8e276", - matched: false, - }, - { - name: "frog18", - image: "Images/frog18.png", - alt: "Frog catching a fish", - color: "#8afd7b", - matched: false, - }, - { - name: "frog19", - image: "Images/frog19.png", - alt: "Frog smelling a flower", - color: "#43bef7", - matched: false, - }, - { - name: "frog20", - image: "Images/frog20.png", - alt: "Frog playing a harmonica", - color: "#fe67ae", - matched: false, - }, -]; - -function shuffleArray(array) { - let currentIndex = array.length; - let randomIndex; - - while (currentIndex !== 0) { - randomIndex = Math.floor(Math.random() * currentIndex); - currentIndex--; - - [array[currentIndex], array[randomIndex]] = [ - array[randomIndex], - array[currentIndex], - ]; - } - - return array; -} - -function createElement(tag, className, attributes = {}) { - const element = document.createElement(tag); - - if (className) { - element.className = className; - } - - Object.entries(attributes).forEach(([key, value]) => { - element[key] = value; - }); - - return element; -} - -function revealCard(card) { - card.classList.add("flipped"); - game.totalReveals++; - moves.textContent = `Reveals: ${game.totalReveals}`; -} - -function startGame() { - game.gameStarted = true; - game.timerInterval = setInterval(() => { - game.totalTime++; - - timer.textContent = `Time: ${game.totalTime} s`; - }, 1000); -} - -function flipCard(card) { - if (game.lockBoard) return; - if (card === game.firstCard) return; - if (card.classList.contains("flipped")) return; - - revealCard(card); - - if (!game.gameStarted) { - startGame(); - } - - if (!game.firstCard) { - game.firstCard = card; - return; - } - - game.secondCard = card; - checkForMatch(); -} - -function endGame() { - clearInterval(game.timerInterval); - winner.style.display = "block"; -} - -function resetBoard() { - game.lockBoard = false; - game.firstCard = null; - game.secondCard = null; -} - -function disableCards() { - game.firstCard.removeEventListener("click", flipCard); - game.secondCard.removeEventListener("click", flipCard); - - game.matchedPairs++; - - if (game.matchedPairs === gameCards.length / 2) { - endGame(); - } - - resetBoard(); -} - -function renderGameBoard() { - gameCards.forEach((card) => { - const cardElement = createElement("div", "flip-card"); - const cardInner = createElement("div", "flip-card-inner"); - const cardFront = createElement("div", "flip-card-front"); - const cardBack = createElement("div", "flip-card-back"); - - cardInner.dataset.name = card.name; - cardBack.style.backgroundColor = card.color; - - const cardFrontImage = createElement("img", null, { - src: cardFrontImageSrc, - alt: "Lotus Flower", - }); - - const cardBackImage = createElement("img", null, { - src: card.image, - alt: card.alt, - }); - - cardFront.appendChild(cardFrontImage); - cardBack.appendChild(cardBackImage); - cardInner.appendChild(cardFront); - cardInner.appendChild(cardBack); - cardElement.appendChild(cardInner); - gameBoard.appendChild(cardElement); - - cardInner.addEventListener("click", () => flipCard(cardInner)); - }); -} - -function unflipCards() { - game.lockBoard = true; - - setTimeout(function () { - game.firstCard.classList.remove("flipped"); - game.secondCard.classList.remove("flipped"); - resetBoard(); - }, 1500); -} - -function checkForMatch() { - const isMatch = game.firstCard.dataset.name === game.secondCard.dataset.name; - - if (isMatch) { - disableCards(); - } else { - unflipCards(); - } -} - -function createGame(numberOfPairs) { - gameBoard.replaceChildren(); - - const shuffleCards = shuffleArray([...availableCards]); - const selectedCards = shuffleCards.slice(0, numberOfPairs); - gameCards = shuffleArray([...selectedCards, ...selectedCards]); - - renderGameBoard(); -} - -function resetGame() { - clearInterval(game.timerInterval); - - game.gameStarted = false; - game.totalReveals = 0; - game.totalTime = 0; - game.matchedPairs = 0; - - moves.textContent = `Reveals: 0`; - timer.textContent = `Time: 0 s`; - winner.style.display = "none"; - - resetBoard(); - createGame(defaultNumberOfPairs); -} - -restartButton.addEventListener("click", resetGame); -createGame(defaultNumberOfPairs); diff --git a/server/.gitignore b/server/.gitignore new file mode 100644 index 0000000..a56a7ef --- /dev/null +++ b/server/.gitignore @@ -0,0 +1,2 @@ +node_modules + diff --git a/server/Script-2.sql b/server/Script-2.sql new file mode 100644 index 0000000..a581e71 --- /dev/null +++ b/server/Script-2.sql @@ -0,0 +1,28 @@ +CREATE TABLE cards ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL UNIQUE, + image TEXT NOT NULL, + alt TEXT NOT NULL, + color TEXT NOT NULL); + +INSERT INTO cards (name, image, alt, color) VALUES +('frog1', 'Images/frog1.png', 'Smiling frog', '#ee76f8'), +('frog2', 'Images/frog2.png', 'Frog playing guitar', '#3a93f9'), +('frog3', 'Images/frog3.png', 'Frog with umbrella', '#f2ba49'), +('frog4', 'Images/frog4.png', 'Frog putting on makeup', '#5dcd5d'), +('frog5', 'Images/frog5.png', 'Dancing frog', '#db7866'), +('frog6', 'Images/frog6.png', 'Frog with a microphone', '#9566db'), +('frog7', 'Images/frog7.png', 'Frog with a walking stick', '#76f887'), +('frog8', 'Images/frog8.png', 'Frog eating', '#a476f8'), +('frog9', 'Images/frog9.png', 'Frog waving', '#fc6c56'), +('frog10', 'Images/frog10.png', 'Frog lying down', '#3d5fe3'), +('frog11', 'Images/frog11.png', 'Frog swimming', '#f876a6'), +('frog12', 'Images/frog12.png', 'Frog jumping', '#f2f876'), +('frog13', 'Images/frog13.png', 'Frog drinking a martini', '#768cf8'), +('frog14', 'Images/frog14.png', 'Singing frog', '#942e9e'), +('frog15', 'Images/frog15.png', 'Happy frog', '#3fe1f3'), +('frog16', 'Images/frog16.png', 'Frog saying goodbye', '#f87676'), +('frog17', 'Images/frog17.png', 'Frog sunbathing', '#f8e276'), +('frog18', 'Images/frog18.png', 'Frog catching a fish', '#8afd7b'), +('frog19', 'Images/frog19.png', 'Frog smelling a flower', '#43bef7'), +('frog20', 'Images/frog20.png', 'Frog playing a harmonica', '#fe67ae'); diff --git a/server/database.db b/server/database.db new file mode 100644 index 0000000..886039b Binary files /dev/null and b/server/database.db differ diff --git a/server/index.js b/server/index.js new file mode 100644 index 0000000..7137566 --- /dev/null +++ b/server/index.js @@ -0,0 +1,45 @@ +import express from "express"; +import knex from "knex"; +import path from "path"; +import { fileURLToPath } from "url"; + +const app = express(); +app.use(express.json()); + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +app.use(express.static(path.join(__dirname, "../app"))); + +const db = knex({ + client: "sqlite3", + connection: { filename: path.join(__dirname, "database.db") }, + useNullAsDefault: true, +}); + +app.get("/cards", async (request, response) => { + try { + const cards = await db("cards").select("*"); + response.status(200).json(cards); + } catch (error) { + console.error("Database error:", error.message); + response + .status(500) + .json({ error: "Failed to retrieve cards", details: error.message }); + } +}); + +app.use((request, response) => { + response.status(404).json({ + error: "Endpoint not found", + }); +}); + +const PORT = process.env.PORT || 3000; +const server = app.listen(PORT, () => { + console.log(`App running on http://localhost:${PORT}`); +}); + +server.on("error", (error) => { + console.error("Server error:", error.message); +}); diff --git a/server/package-lock.json b/server/package-lock.json new file mode 100644 index 0000000..e6c844b --- /dev/null +++ b/server/package-lock.json @@ -0,0 +1,2503 @@ +{ + "name": "memory-game", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "memory-game", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "express": "^4.21.2", + "knex": "^3.1.0", + "sqlite3": "^5.1.7" + }, + "devDependencies": { + "nodemon": "^3.1.11" + } + }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "license": "MIT", + "optional": true + }, + "node_modules/@npmcli/fs": { + "version": "1.1.1", + "license": "ISC", + "optional": true, + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/@npmcli/move-file": { + "version": "1.1.2", + "license": "MIT", + "optional": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/abbrev": { + "version": "1.1.1", + "license": "ISC", + "optional": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "license": "MIT", + "optional": true, + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "license": "MIT", + "optional": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "2.1.0", + "license": "ISC", + "optional": true + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "license": "ISC", + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "devOptional": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "devOptional": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "15.3.0", + "license": "ISC", + "optional": true, + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/color-support": { + "version": "1.1.3", + "license": "ISC", + "optional": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/colorette": { + "version": "2.0.19", + "license": "MIT" + }, + "node_modules/commander": { + "version": "10.0.1", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "devOptional": true, + "license": "MIT" + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "license": "ISC", + "optional": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "license": "MIT", + "optional": true + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "license": "MIT", + "optional": true + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding": { + "version": "0.1.13", + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "license": "MIT", + "optional": true + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/esm": { + "version": "3.2.25", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/express": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.14.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/forwarded": { + "version": "0.2.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "license": "ISC", + "optional": true + }, + "node_modules/fsevents": { + "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, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "4.0.4", + "license": "ISC", + "optional": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/getopts": { + "version": "2.3.0", + "license": "MIT" + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "license": "MIT" + }, + "node_modules/glob": { + "version": "7.2.3", + "license": "ISC", + "optional": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "license": "ISC", + "optional": true + }, + "node_modules/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, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "license": "ISC", + "optional": true + }, + "node_modules/hasown": { + "version": "2.0.2", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "license": "BSD-2-Clause", + "optional": true + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "license": "MIT", + "optional": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "license": "ISC", + "optional": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "license": "ISC", + "optional": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "license": "ISC" + }, + "node_modules/interpret": { + "version": "2.2.0", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/ip-address": { + "version": "10.1.0", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "license": "MIT", + "optional": true + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "license": "ISC", + "optional": true + }, + "node_modules/knex": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/knex/-/knex-3.1.0.tgz", + "integrity": "sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw==", + "license": "MIT", + "dependencies": { + "colorette": "2.0.19", + "commander": "^10.0.0", + "debug": "4.3.4", + "escalade": "^3.1.1", + "esm": "^3.2.25", + "get-package-type": "^0.1.0", + "getopts": "2.3.0", + "interpret": "^2.2.0", + "lodash": "^4.17.21", + "pg-connection-string": "2.6.2", + "rechoir": "^0.8.0", + "resolve-from": "^5.0.0", + "tarn": "^3.0.2", + "tildify": "2.0.0" + }, + "bin": { + "knex": "bin/cli.js" + }, + "engines": { + "node": ">=16" + }, + "peerDependenciesMeta": { + "better-sqlite3": { + "optional": true + }, + "mysql": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-native": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } + } + }, + "node_modules/knex/node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/knex/node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/lodash": { + "version": "4.17.23", + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-fetch-happen": { + "version": "9.1.0", + "license": "ISC", + "optional": true, + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "devOptional": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "3.3.6", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-fetch": { + "version": "1.4.1", + "license": "MIT", + "optional": true, + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-abi": { + "version": "3.87.0", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "license": "MIT" + }, + "node_modules/node-gyp": { + "version": "8.4.1", + "license": "MIT", + "optional": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/nodemon": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.11.tgz", + "integrity": "sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nopt": { + "version": "5.0.0", + "license": "ISC", + "optional": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npmlog": { + "version": "6.0.2", + "license": "ISC", + "optional": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "license": "MIT", + "optional": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "license": "MIT" + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" + }, + "node_modules/pg-connection-string": { + "version": "2.6.2", + "license": "MIT" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "license": "ISC", + "optional": true + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "license": "MIT", + "optional": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.3", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/qs": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.8.0", + "license": "MIT", + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "license": "ISC", + "optional": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.3", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "license": "ISC", + "optional": true + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "license": "ISC", + "optional": true + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.7", + "license": "MIT", + "optional": true, + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "6.2.1", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/sqlite3": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz", + "integrity": "sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^7.0.0", + "prebuild-install": "^7.1.1", + "tar": "^6.1.11" + }, + "optionalDependencies": { + "node-gyp": "8.x" + }, + "peerDependencies": { + "node-gyp": "8.x" + }, + "peerDependenciesMeta": { + "node-gyp": { + "optional": true + } + } + }, + "node_modules/ssri": { + "version": "8.0.1", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "license": "MIT", + "optional": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar-fs": { + "version": "2.1.4", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-fs/node_modules/chownr": { + "version": "1.1.4", + "license": "ISC" + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/tarn": { + "version": "3.0.2", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/tildify": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "license": "ISC", + "optional": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "license": "ISC", + "optional": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "license": "ISC", + "optional": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "license": "ISC", + "optional": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "license": "ISC" + }, + "node_modules/yallist": { + "version": "4.0.0", + "license": "ISC" + } + } +} diff --git a/server/package.json b/server/package.json new file mode 100644 index 0000000..881b391 --- /dev/null +++ b/server/package.json @@ -0,0 +1,21 @@ +{ + "name": "memory-game", + "version": "1.0.0", + "description": "Full-stack memory game", + "license": "ISC", + "author": "Paloma Cardozo", + "type": "module", + "main": "index.js", + "scripts": { + "start": "node index.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "dependencies": { + "express": "^4.21.2", + "knex": "^3.1.0", + "sqlite3": "^5.1.7" + }, + "devDependencies": { + "nodemon": "^3.1.11" + } +} diff --git a/styles.css b/styles.css deleted file mode 100644 index 2976472..0000000 --- a/styles.css +++ /dev/null @@ -1,162 +0,0 @@ -* { - box-sizing: border-box; - margin: 0; - padding: 0; -} - -body { - background: #ffffff; - font-family: "Noto Sans", sans-serif; - display: flex; - flex-direction: column; -} - -.game { - max-height: 90vh; - display: flex; - flex-direction: column; -} - -.title, -.text { - text-align: center; - margin: 10px; -} - -.title { - font-size: 35px; - color: #127439; - text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3); -} - -.text { - font-size: 20px; - color: #095327; -} - -.text a { - color: #095327; - text-decoration: none; -} - -.text a:hover { - text-decoration: underline; - cursor: pointer; -} - -.controls { - display: flex; - justify-content: space-between; - align-items: center; - margin: 0 20px; -} - -.button { - display: flex; - align-items: center; - gap: 10px; - padding: 10px 20px; - background-color: #e1f7b5; - color: #84b131; - border: none; - border-radius: 20px; - cursor: pointer; - font-size: 22px; - font-weight: 600; - font-family: "Noto Sans", sans-serif; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.288); -} - -.button:hover { - background-color: #335840; -} - -.state { - font-size: 22px; - font-weight: 600; - color: #095327; -} - -.container { - display: grid; - gap: 2vh; - grid-template-columns: repeat(3, minmax(120px, 18vh)); - justify-content: center; -} - -.flip-card { - width: 18vh; - height: 18vh; - max-width: 160px; - max-height: 160px; - perspective: 1000px; - cursor: pointer; -} - -.flip-card-inner { - position: relative; - width: 100%; - height: 100%; - transition: transform 1s; - transform-style: preserve-3d; - box-shadow: - 0 14px 28px rgba(0, 0, 0, 0.25), - 0 10px 10px rgba(0, 0, 0, 0.22); - border-radius: 15px; -} - -.flip-card:hover .flip-card-inner:not(.flipped) { - transform: scale(1.05); -} - -.flip-card:active .flip-card-inner:not(.flipped) { - transform: scale(0.95); -} - -.flip-card-inner.flipped { - transform: rotateY(180deg); -} - -.flip-card-front, -.flip-card-back { - position: absolute; - width: 100%; - height: 100%; - backface-visibility: hidden; - display: flex; - align-items: center; - justify-content: center; - border-radius: 15px; -} - -.flip-card-front { - background-color: #f8ffc9; -} - -.flip-card-back { - transform: rotateY(180deg); -} - -.flip-card-front img { - width: 50%; - height: auto; -} - -.flip-card-back img { - width: 100%; - height: 100%; - object-fit: cover; -} - -.frog-icon { - width: 50px; - height: 50px; -} - -.winner { - text-align: center; - font-size: 40px; - font-weight: 700; - color: #69ba2e; - display: none; -}