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.
+
+
+
+
+
+
+ Restart
+
+
+
+
Reveals: 0
+
Time left: 00:00
+
+
+
+
+ Level 1
+ Level 2
+ Level 3
+
+
+
+
+
+
+
+
+
+
You won!
+
+
+
+ Play again
+
+
+
+
+
+
+
+
+
Oops! Timeโs Up
+
So close!
+
+ Try again
+
+
+
+
+
+
+
+
+
+
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
-
-
-
-
-
-
-
- Froggy Pairs
-
-
-
- Can you match all the frogs?
-
-
-
-
-
- Restart
-
-
-
- You won!
-
-
-
- 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;
-}