├── .gitignore
├── README.md
├── backend
├── config
│ └── db.js
├── controllers
│ └── productController.js
├── lib
│ └── arcjet.js
├── routes
│ └── productRoutes.js
├── seeds
│ └── products.js
└── server.js
├── frontend
├── README.md
├── eslint.config.js
├── index.html
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
│ ├── screenshot-for-readme.png
│ └── vite.svg
├── src
│ ├── App.jsx
│ ├── components
│ │ ├── AddProductModal.jsx
│ │ ├── Navbar.jsx
│ │ ├── ProductCard.jsx
│ │ └── ThemeSelector.jsx
│ ├── constants
│ │ └── index.js
│ ├── index.css
│ ├── main.jsx
│ ├── pages
│ │ ├── HomePage.jsx
│ │ └── ProductPage.jsx
│ └── store
│ │ ├── useProductStore.js
│ │ └── useThemeStore.js
├── tailwind.config.js
└── vite.config.js
├── package-lock.json
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | .env
11 |
12 | node_modules
13 | dist
14 | dist-ssr
15 | *.local
16 |
17 | # Editor directories and files
18 | .vscode/*
19 | !.vscode/extensions.json
20 | .idea
21 | .DS_Store
22 | *.suo
23 | *.ntvs*
24 | *.njsproj
25 | *.sln
26 | *.sw?
27 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
✨ PERN Stack Tutorial ✨
2 |
3 | 
4 |
5 | Highlights:
6 |
7 | - 🌟 Tech stack: PERN + TailwindCSS + Daisy UI
8 | - 🚀 Rate Limiting & Bot Detection
9 | - 👌 Global state management with Zustand
10 | - 🐞 Error handling both on the server and client
11 | - ⭐ Deployment for FREE!
12 | - ⏳ And much more!
13 |
14 | ### Setup .env file
15 |
16 | ```js
17 | PORT=3000
18 |
19 | PGUSER=...
20 | PGPASSWORD=...
21 | PGHOST=...
22 | PGDATABASE=...
23 |
24 | ARCJET_KEY=...
25 | ARCJET_ENV=development
26 | ```
27 |
28 | ### Run the API
29 |
30 | ```shell
31 | npm run dev
32 | ```
33 |
34 | ### Run the frontend
35 |
36 | ```shell
37 | cd frontend
38 | npm run dev
39 | ```
40 |
--------------------------------------------------------------------------------
/backend/config/db.js:
--------------------------------------------------------------------------------
1 | import { neon } from "@neondatabase/serverless";
2 | import dotenv from "dotenv";
3 |
4 | dotenv.config();
5 |
6 | const { PGHOST, PGDATABASE, PGUSER, PGPASSWORD } = process.env;
7 |
8 | // creates a SQL connection using our env variables
9 | export const sql = neon(
10 | `postgresql://${PGUSER}:${PGPASSWORD}@${PGHOST}/${PGDATABASE}?sslmode=require`
11 | );
12 | // this sql function we export is used as a tagged template literal, which allows us to write SQL queries safely
13 |
14 | // postgresql://neondb_owner:npg_NW8olSOGfx5E@ep-dawn-meadow-a83i0d05-pooler.eastus2.azure.neon.tech/neondb?sslmode=require
15 |
--------------------------------------------------------------------------------
/backend/controllers/productController.js:
--------------------------------------------------------------------------------
1 | import { sql } from "../config/db.js";
2 |
3 | export const getProducts = async (req, res) => {
4 | try {
5 | const products = await sql`
6 | SELECT * FROM products
7 | ORDER BY created_at DESC
8 | `;
9 |
10 | console.log("fetched products", products);
11 | res.status(200).json({ success: true, data: products });
12 | } catch (error) {
13 | console.log("Error in getProducts function", error);
14 | res.status(500).json({ success: false, message: "Internal Server Error" });
15 | }
16 | };
17 |
18 | export const createProduct = async (req, res) => {
19 | const { name, price, image } = req.body;
20 |
21 | if (!name || !price || !image) {
22 | return res.status(400).json({ success: false, message: "All fields are required" });
23 | }
24 |
25 | try {
26 | const newProduct = await sql`
27 | INSERT INTO products (name,price,image)
28 | VALUES (${name},${price},${image})
29 | RETURNING *
30 | `;
31 |
32 | res.status(201).json({ success: true, data: newProduct[0] });
33 | } catch (error) {
34 | console.log("Error in createProduct function", error);
35 | res.status(500).json({ success: false, message: "Internal Server Error" });
36 | }
37 | };
38 |
39 | export const getProduct = async (req, res) => {
40 | const { id } = req.params;
41 |
42 | try {
43 | const product = await sql`
44 | SELECT * FROM products WHERE id=${id}
45 | `;
46 |
47 | res.status(200).json({ success: true, data: product[0] });
48 | } catch (error) {
49 | console.log("Error in getProduct function", error);
50 | res.status(500).json({ success: false, message: "Internal Server Error" });
51 | }
52 | };
53 |
54 | export const updateProduct = async (req, res) => {
55 | const { id } = req.params;
56 | const { name, price, image } = req.body;
57 |
58 | try {
59 | const updateProduct = await sql`
60 | UPDATE products
61 | SET name=${name}, price=${price}, image=${image}
62 | WHERE id=${id}
63 | RETURNING *
64 | `;
65 |
66 | if (updateProduct.length === 0) {
67 | return res.status(404).json({
68 | success: false,
69 | message: "Product not found",
70 | });
71 | }
72 |
73 | res.status(200).json({ success: true, data: updateProduct[0] });
74 | } catch (error) {
75 | console.log("Error in updateProduct function", error);
76 | res.status(500).json({ success: false, message: "Internal Server Error" });
77 | }
78 | };
79 |
80 | export const deleteProduct = async (req, res) => {
81 | const { id } = req.params;
82 |
83 | try {
84 | const deletedProduct = await sql`
85 | DELETE FROM products WHERE id=${id} RETURNING *
86 | `;
87 |
88 | if (deletedProduct.length === 0) {
89 | return res.status(404).json({
90 | success: false,
91 | message: "Product not found",
92 | });
93 | }
94 |
95 | res.status(200).json({ success: true, data: deletedProduct[0] });
96 | } catch (error) {
97 | console.log("Error in deleteProduct function", error);
98 | res.status(500).json({ success: false, message: "Internal Server Error" });
99 | }
100 | };
101 |
--------------------------------------------------------------------------------
/backend/lib/arcjet.js:
--------------------------------------------------------------------------------
1 | import arcjet, { tokenBucket, shield, detectBot, slidingWindow } from "@arcjet/node";
2 |
3 | import "dotenv/config";
4 |
5 | // init arcjet
6 | export const aj = arcjet({
7 | key: process.env.ARCJET_KEY,
8 | characteristics: ["ip.src"],
9 | rules: [
10 | // shield protects your app from common attacks e.g. SQL injection, XSS, CSRF attacks
11 | shield({ mode: "LIVE" }),
12 | detectBot({
13 | mode: "LIVE",
14 | // block all bots except search engines
15 | allow: [
16 | "CATEGORY:SEARCH_ENGINE",
17 | // see the full list at https://arcjet.com/bot-list
18 | ],
19 | }),
20 | // rate limiting
21 | tokenBucket({
22 | mode: "LIVE",
23 | refillRate: 30,
24 | interval: 5,
25 | capacity: 20,
26 | }),
27 | ],
28 | });
29 |
--------------------------------------------------------------------------------
/backend/routes/productRoutes.js:
--------------------------------------------------------------------------------
1 | import expres from "express";
2 | import {
3 | createProduct,
4 | deleteProduct,
5 | getProduct,
6 | getProducts,
7 | updateProduct,
8 | } from "../controllers/productController.js";
9 |
10 | const router = expres.Router();
11 |
12 | router.get("/", getProducts);
13 | router.get("/:id", getProduct);
14 | router.post("/", createProduct);
15 | router.put("/:id", updateProduct);
16 | router.delete("/:id", deleteProduct);
17 |
18 | export default router;
19 |
--------------------------------------------------------------------------------
/backend/seeds/products.js:
--------------------------------------------------------------------------------
1 | import { sql } from "../config/db.js";
2 |
3 | const SAMPLE_PRODUCTS = [
4 | {
5 | name: "Premium Wireless Headphones",
6 | price: 299.99,
7 | image:
8 | "https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=800&auto=format&fit=crop&q=60",
9 | },
10 | {
11 | name: "Mechanical Gaming Keyboard",
12 | price: 159.99,
13 | image:
14 | "https://images.unsplash.com/photo-1511467687858-23d96c32e4ae?w=800&auto=format&fit=crop&q=60",
15 | },
16 | {
17 | name: "Smart Watch Pro",
18 | price: 249.99,
19 | image:
20 | "https://images.unsplash.com/photo-1579586337278-3befd40fd17a?w=800&auto=format&fit=crop&q=60",
21 | },
22 | {
23 | name: "4K Ultra HD Camera",
24 | price: 899.99,
25 | image:
26 | "https://images.unsplash.com/photo-1516035069371-29a1b244cc32?w=800&auto=format&fit=crop&q=60",
27 | },
28 | {
29 | name: "Minimalist Backpack",
30 | price: 79.99,
31 | image:
32 | "https://images.unsplash.com/photo-1553062407-98eeb64c6a62?w=800&auto=format&fit=crop&q=60",
33 | },
34 | {
35 | name: "Wireless Gaming Mouse",
36 | price: 89.99,
37 | image:
38 | "https://images.unsplash.com/photo-1527814050087-3793815479db?w=800&auto=format&fit=crop&q=60",
39 | },
40 | {
41 | name: "Smart Home Speaker",
42 | price: 159.99,
43 | image:
44 | "https://images.unsplash.com/photo-1589492477829-5e65395b66cc?w=800&auto=format&fit=crop&q=60",
45 | },
46 | {
47 | name: "LED Gaming Monitor",
48 | price: 449.99,
49 | image:
50 | "https://images.unsplash.com/photo-1527443224154-c4a3942d3acf?w=800&auto=format&fit=crop&q=60",
51 | },
52 | ];
53 |
54 | async function seedDatabase() {
55 | try {
56 | // first, clear existing data
57 | await sql`TRUNCATE TABLE products RESTART IDENTITY`;
58 |
59 | // insert all products
60 | for (const product of SAMPLE_PRODUCTS) {
61 | await sql`
62 | INSERT INTO products (name, price, image)
63 | VALUES (${product.name}, ${product.price}, ${product.image})
64 | `;
65 | }
66 |
67 | console.log("Database seeded successfully");
68 | process.exit(0); // success code
69 | } catch (error) {
70 | console.error("Error seeding database:", error);
71 | process.exit(1); // failure code
72 | }
73 | }
74 |
75 | seedDatabase();
76 |
--------------------------------------------------------------------------------
/backend/server.js:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import helmet from "helmet";
3 | import morgan from "morgan";
4 | import cors from "cors";
5 | import dotenv from "dotenv";
6 | import path from "path";
7 |
8 | import productRoutes from "./routes/productRoutes.js";
9 | import { sql } from "./config/db.js";
10 | import { aj } from "./lib/arcjet.js";
11 |
12 | dotenv.config();
13 |
14 | const app = express();
15 | const PORT = process.env.PORT || 3000;
16 | const __dirname = path.resolve();
17 |
18 | app.use(express.json());
19 | app.use(cors());
20 | app.use(
21 | helmet({
22 | contentSecurityPolicy: false,
23 | })
24 | ); // helmet is a security middleware that helps you protect your app by setting various HTTP headers
25 | app.use(morgan("dev")); // log the requests
26 |
27 | // apply arcjet rate-limit to all routes
28 | app.use(async (req, res, next) => {
29 | try {
30 | const decision = await aj.protect(req, {
31 | requested: 1, // specifies that each request consumes 1 token
32 | });
33 |
34 | if (decision.isDenied()) {
35 | if (decision.reason.isRateLimit()) {
36 | res.status(429).json({ error: "Too Many Requests" });
37 | } else if (decision.reason.isBot()) {
38 | res.status(403).json({ error: "Bot access denied" });
39 | } else {
40 | res.status(403).json({ error: "Forbidden" });
41 | }
42 | return;
43 | }
44 |
45 | // check for spoofed bots
46 | if (decision.results.some((result) => result.reason.isBot() && result.reason.isSpoofed())) {
47 | res.status(403).json({ error: "Spoofed bot detected" });
48 | return;
49 | }
50 |
51 | next();
52 | } catch (error) {
53 | console.log("Arcjet error", error);
54 | next(error);
55 | }
56 | });
57 |
58 | app.use("/api/products", productRoutes);
59 |
60 | if (process.env.NODE_ENV === "production") {
61 | // server our react app
62 | app.use(express.static(path.join(__dirname, "/frontend/dist")));
63 |
64 | app.get("*", (req, res) => {
65 | res.sendFile(path.resolve(__dirname, "frontend", "dist", "index.html"));
66 | });
67 | }
68 |
69 | async function initDB() {
70 | try {
71 | await sql`
72 | CREATE TABLE IF NOT EXISTS products (
73 | id SERIAL PRIMARY KEY,
74 | name VARCHAR(255) NOT NULL,
75 | image VARCHAR(255) NOT NULL,
76 | price DECIMAL(10, 2) NOT NULL,
77 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
78 | )
79 | `;
80 |
81 | console.log("Database initialized successfully");
82 | } catch (error) {
83 | console.log("Error initDB", error);
84 | }
85 | }
86 |
87 | initDB().then(() => {
88 | app.listen(PORT, () => {
89 | console.log("Server is running on port " + PORT);
90 | });
91 | });
92 |
--------------------------------------------------------------------------------
/frontend/README.md:
--------------------------------------------------------------------------------
1 | # React + Vite
2 |
3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4 |
5 | Currently, two official plugins are available:
6 |
7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9 |
--------------------------------------------------------------------------------
/frontend/eslint.config.js:
--------------------------------------------------------------------------------
1 | import js from "@eslint/js";
2 | import globals from "globals";
3 | import react from "eslint-plugin-react";
4 | import reactHooks from "eslint-plugin-react-hooks";
5 | import reactRefresh from "eslint-plugin-react-refresh";
6 |
7 | export default [
8 | { ignores: ["dist"] },
9 | {
10 | files: ["**/*.{js,jsx}"],
11 | languageOptions: {
12 | ecmaVersion: 2020,
13 | globals: globals.browser,
14 | parserOptions: {
15 | ecmaVersion: "latest",
16 | ecmaFeatures: { jsx: true },
17 | sourceType: "module",
18 | },
19 | },
20 | settings: { react: { version: "18.3" } },
21 | plugins: {
22 | react,
23 | "react-hooks": reactHooks,
24 | "react-refresh": reactRefresh,
25 | },
26 | rules: {
27 | ...js.configs.recommended.rules,
28 | ...react.configs.recommended.rules,
29 | ...react.configs["jsx-runtime"].rules,
30 | ...reactHooks.configs.recommended.rules,
31 | "react/jsx-no-target-blank": "off",
32 | "react-refresh/only-export-components": ["warn", { allowConstantExport: true }],
33 | "react/prop-types": "off",
34 | },
35 | },
36 | ];
37 |
--------------------------------------------------------------------------------
/frontend/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint .",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "axios": "^1.7.9",
14 | "lucide-react": "^0.474.0",
15 | "react": "^18.3.1",
16 | "react-dom": "^18.3.1",
17 | "react-hot-toast": "^2.5.1",
18 | "react-router-dom": "^7.1.5",
19 | "zustand": "^5.0.3"
20 | },
21 | "devDependencies": {
22 | "@eslint/js": "^9.17.0",
23 | "@types/react": "^18.3.18",
24 | "@types/react-dom": "^18.3.5",
25 | "@vitejs/plugin-react": "^4.3.4",
26 | "autoprefixer": "^10.4.20",
27 | "daisyui": "^4.12.23",
28 | "eslint": "^9.17.0",
29 | "eslint-plugin-react": "^7.37.2",
30 | "eslint-plugin-react-hooks": "^5.0.0",
31 | "eslint-plugin-react-refresh": "^0.4.16",
32 | "globals": "^15.14.0",
33 | "postcss": "^8.5.1",
34 | "tailwindcss": "^3.4.17",
35 | "vite": "^6.0.5"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/frontend/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/frontend/public/screenshot-for-readme.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/burakorkmez/pern-crash-course/92358e33d10eeb7b2a8e35f35074a3181039765b/frontend/public/screenshot-for-readme.png
--------------------------------------------------------------------------------
/frontend/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/App.jsx:
--------------------------------------------------------------------------------
1 | import Navbar from "./components/Navbar";
2 |
3 | import HomePage from "./pages/HomePage";
4 | import ProductPage from "./pages/ProductPage";
5 |
6 | import { Routes, Route } from "react-router-dom";
7 | import { useThemeStore } from "./store/useThemeStore";
8 |
9 | import { Toaster } from "react-hot-toast";
10 |
11 | function App() {
12 | const { theme } = useThemeStore();
13 |
14 | return (
15 |
16 |
17 |
18 |
19 | } />
20 | } />
21 |
22 |
23 |
24 |
25 | );
26 | }
27 |
28 | export default App;
29 |
--------------------------------------------------------------------------------
/frontend/src/components/AddProductModal.jsx:
--------------------------------------------------------------------------------
1 | import { DollarSignIcon, ImageIcon, Package2Icon, PlusCircleIcon } from "lucide-react";
2 | import { useProductStore } from "../store/useProductStore";
3 |
4 | function AddProductModal() {
5 | const { addProduct, formData, setFormData, loading } = useProductStore();
6 |
7 | return (
8 |
108 | );
109 | }
110 | export default AddProductModal;
111 |
--------------------------------------------------------------------------------
/frontend/src/components/Navbar.jsx:
--------------------------------------------------------------------------------
1 | import { Link, useResolvedPath } from "react-router-dom";
2 | import { ShoppingBagIcon, ShoppingCartIcon } from "lucide-react";
3 | import ThemeSelector from "./ThemeSelector";
4 | import { useProductStore } from "../store/useProductStore";
5 |
6 | function Navbar() {
7 | const { pathname } = useResolvedPath();
8 | const isHomePage = pathname === "/";
9 |
10 | const { products } = useProductStore();
11 |
12 | return (
13 |
14 |
15 |
16 | {/* LOGO */}
17 |
18 |
19 |
20 |
21 |
25 | POSGRESTORE
26 |
27 |
28 |
29 |
30 |
31 | {/* RIGHT SECTION */}
32 |
33 |
34 |
35 | {isHomePage && (
36 |
37 |
38 |
39 |
40 | {products.length}
41 |
42 |
43 |
44 | )}
45 |
46 |
47 |
48 |
49 | );
50 | }
51 | export default Navbar;
52 |
--------------------------------------------------------------------------------
/frontend/src/components/ProductCard.jsx:
--------------------------------------------------------------------------------
1 | import { EditIcon, Trash2Icon } from "lucide-react";
2 | import { Link } from "react-router-dom";
3 | import { useProductStore } from "../store/useProductStore";
4 |
5 | function ProductCard({ product }) {
6 | const { deleteProduct } = useProductStore();
7 | return (
8 |
9 | {/* PRODUCT IMAGE */}
10 |
11 |
16 |
17 |
18 |
19 | {/* PRODUCT INFO */}
20 |
{product.name}
21 |
${Number(product.price).toFixed(2)}
22 |
23 | {/* CARD ACTIONS */}
24 |
25 |
26 |
27 |
28 |
29 |
35 |
36 |
37 |
38 | );
39 | }
40 | export default ProductCard;
41 |
--------------------------------------------------------------------------------
/frontend/src/components/ThemeSelector.jsx:
--------------------------------------------------------------------------------
1 | import { PaletteIcon } from "lucide-react";
2 | import { THEMES } from "../constants";
3 | import { useThemeStore } from "../store/useThemeStore";
4 |
5 | function ThemeSelector() {
6 | const { theme, setTheme } = useThemeStore();
7 |
8 | return (
9 |
10 | {/* DROPDOWN TRIGGER */}
11 |
12 |
15 |
16 |
22 | {THEMES.map((themeOption) => (
23 |
45 | ))}
46 |
47 |
48 | );
49 | }
50 | export default ThemeSelector;
51 |
--------------------------------------------------------------------------------
/frontend/src/constants/index.js:
--------------------------------------------------------------------------------
1 | export const THEMES = [
2 | {
3 | name: "pastel",
4 | label: "Pastel",
5 | colors: ["#ffd8d8", "#b7e4c7", "#d8b4fe"],
6 | },
7 | {
8 | name: "retro",
9 | label: "Retro",
10 | colors: ["#e2d5bc", "#ef9995", "#a4cbb4"],
11 | },
12 | {
13 | name: "coffee",
14 | label: "Coffee",
15 | colors: ["#20161F", "#A67C58", "#807666"],
16 | },
17 | {
18 | name: "forest",
19 | label: "Forest",
20 | colors: ["#171212", "#2B4C3F", "#6BAA75"],
21 | },
22 | {
23 | name: "cyberpunk",
24 | label: "Cyberpunk",
25 | colors: ["#FF00FF", "#00FFFF", "#FF7598"],
26 | },
27 | {
28 | name: "synthwave",
29 | label: "Synthwave",
30 | colors: ["#2D1B69", "#FF1E9E", "#1EDBFF"],
31 | },
32 | {
33 | name: "luxury",
34 | label: "Luxury",
35 | colors: ["#171618", "#B6862D", "#E2C697"],
36 | },
37 | {
38 | name: "autumn",
39 | label: "Autumn",
40 | colors: ["#D8B4A0", "#D27548", "#BA4A00"],
41 | },
42 | {
43 | name: "valentine",
44 | label: "Valentine",
45 | colors: ["#E96D7B", "#FF8FAB", "#FFB3C6"],
46 | },
47 | {
48 | name: "aqua",
49 | label: "Aqua",
50 | colors: ["#2DD4BF", "#06B6D4", "#0EA5E9"],
51 | },
52 | {
53 | name: "business",
54 | label: "Business",
55 | colors: ["#1C4E80", "#0091D5", "#7DB9DE"],
56 | },
57 | {
58 | name: "night",
59 | label: "Night",
60 | colors: ["#0F172A", "#334155", "#64748B"],
61 | },
62 | {
63 | name: "dracula",
64 | label: "Dracula",
65 | colors: ["#282A36", "#BD93F9", "#FF79C6"],
66 | },
67 | ];
68 |
--------------------------------------------------------------------------------
/frontend/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/frontend/src/main.jsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from "react";
2 | import { createRoot } from "react-dom/client";
3 | import "./index.css";
4 | import App from "./App.jsx";
5 | import { BrowserRouter } from "react-router-dom";
6 |
7 | createRoot(document.getElementById("root")).render(
8 |
9 |
10 |
11 |
12 |
13 | );
14 |
--------------------------------------------------------------------------------
/frontend/src/pages/HomePage.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import { useProductStore } from "../store/useProductStore";
3 | import { PackageIcon, PlusCircleIcon, RefreshCwIcon } from "lucide-react";
4 | import ProductCard from "../components/ProductCard";
5 | import AddProductModal from "../components/AddProductModal";
6 |
7 | function HomePage() {
8 | const { products, loading, error, fetchProducts } = useProductStore();
9 |
10 | useEffect(() => {
11 | fetchProducts();
12 | }, [fetchProducts]);
13 |
14 | return (
15 |
16 |
17 |
24 |
27 |
28 |
29 |
30 |
31 | {error && {error}
}
32 |
33 | {products.length === 0 && !loading && (
34 |
35 |
38 |
39 |
No products found
40 |
41 | Get started by adding your first product to the inventory
42 |
43 |
44 |
45 | )}
46 |
47 | {loading ? (
48 |
51 | ) : (
52 |
53 | {products.map((product) => (
54 |
55 | ))}
56 |
57 | )}
58 |
59 | );
60 | }
61 | export default HomePage;
62 |
--------------------------------------------------------------------------------
/frontend/src/pages/ProductPage.jsx:
--------------------------------------------------------------------------------
1 | import { useNavigate, useParams } from "react-router-dom";
2 | import { useProductStore } from "../store/useProductStore";
3 | import { useEffect } from "react";
4 | import { ArrowLeftIcon, SaveIcon, Trash2Icon } from "lucide-react";
5 |
6 | function ProductPage() {
7 | const {
8 | currentProduct,
9 | formData,
10 | setFormData,
11 | loading,
12 | error,
13 | fetchProduct,
14 | updateProduct,
15 | deleteProduct,
16 | } = useProductStore();
17 | const navigate = useNavigate();
18 | const { id } = useParams();
19 |
20 | useEffect(() => {
21 | fetchProduct(id);
22 | }, [fetchProduct, id]);
23 |
24 | const handleDelete = async () => {
25 | if (window.confirm("Are you sure you want to delete this product?")) {
26 | await deleteProduct(id);
27 | navigate("/");
28 | }
29 | };
30 |
31 | if (loading) {
32 | return (
33 |
36 | );
37 | }
38 |
39 | if (error) {
40 | return (
41 |
44 | );
45 | }
46 |
47 | return (
48 |
49 |
53 |
54 |
55 | {/* PRODUCT IMAGE */}
56 |
57 |

62 |
63 |
64 | {/* PRODUCT FORM */}
65 |
145 |
146 |
147 | );
148 | }
149 | export default ProductPage;
150 |
--------------------------------------------------------------------------------
/frontend/src/store/useProductStore.js:
--------------------------------------------------------------------------------
1 | import { create } from "zustand";
2 | import axios from "axios";
3 | import toast from "react-hot-toast";
4 |
5 | // base url will be dynamic depending on the environment
6 | const BASE_URL = import.meta.env.MODE === "development" ? "http://localhost:3000" : "";
7 |
8 | export const useProductStore = create((set, get) => ({
9 | // products state
10 | products: [],
11 | loading: false,
12 | error: null,
13 | currentProduct: null,
14 |
15 | // form state
16 | formData: {
17 | name: "",
18 | price: "",
19 | image: "",
20 | },
21 |
22 | setFormData: (formData) => set({ formData }),
23 | resetForm: () => set({ formData: { name: "", price: "", image: "" } }),
24 |
25 | addProduct: async (e) => {
26 | e.preventDefault();
27 | set({ loading: true });
28 |
29 | try {
30 | const { formData } = get();
31 | await axios.post(`${BASE_URL}/api/products`, formData);
32 | await get().fetchProducts();
33 | get().resetForm();
34 | toast.success("Product added successfully");
35 | document.getElementById("add_product_modal").close();
36 | } catch (error) {
37 | console.log("Error in addProduct function", error);
38 | toast.error("Something went wrong");
39 | } finally {
40 | set({ loading: false });
41 | }
42 | },
43 |
44 | fetchProducts: async () => {
45 | set({ loading: true });
46 | try {
47 | const response = await axios.get(`${BASE_URL}/api/products`);
48 | set({ products: response.data.data, error: null });
49 | } catch (err) {
50 | if (err.status == 429) set({ error: "Rate limit exceeded", products: [] });
51 | else set({ error: "Something went wrong", products: [] });
52 | } finally {
53 | set({ loading: false });
54 | }
55 | },
56 |
57 | deleteProduct: async (id) => {
58 | console.log("deleteProduct function called", id);
59 | set({ loading: true });
60 | try {
61 | await axios.delete(`${BASE_URL}/api/products/${id}`);
62 | set((prev) => ({ products: prev.products.filter((product) => product.id !== id) }));
63 | toast.success("Product deleted successfully");
64 | } catch (error) {
65 | console.log("Error in deleteProduct function", error);
66 | toast.error("Something went wrong");
67 | } finally {
68 | set({ loading: false });
69 | }
70 | },
71 |
72 | fetchProduct: async (id) => {
73 | set({ loading: true });
74 | try {
75 | const response = await axios.get(`${BASE_URL}/api/products/${id}`);
76 | set({
77 | currentProduct: response.data.data,
78 | formData: response.data.data, // pre-fill form with current product data
79 | error: null,
80 | });
81 | } catch (error) {
82 | console.log("Error in fetchProduct function", error);
83 | set({ error: "Something went wrong", currentProduct: null });
84 | } finally {
85 | set({ loading: false });
86 | }
87 | },
88 | updateProduct: async (id) => {
89 | set({ loading: true });
90 | try {
91 | const { formData } = get();
92 | const response = await axios.put(`${BASE_URL}/api/products/${id}`, formData);
93 | set({ currentProduct: response.data.data });
94 | toast.success("Product updated successfully");
95 | } catch (error) {
96 | toast.error("Something went wrong");
97 | console.log("Error in updateProduct function", error);
98 | } finally {
99 | set({ loading: false });
100 | }
101 | },
102 | }));
103 |
--------------------------------------------------------------------------------
/frontend/src/store/useThemeStore.js:
--------------------------------------------------------------------------------
1 | import { create } from "zustand";
2 |
3 | export const useThemeStore = create((set) => ({
4 | theme: localStorage.getItem("preferred-theme") || "forest",
5 | setTheme: (theme) => {
6 | localStorage.setItem("preferred-theme", theme);
7 | set({ theme });
8 | },
9 | }));
10 |
--------------------------------------------------------------------------------
/frontend/tailwind.config.js:
--------------------------------------------------------------------------------
1 | import daisyui from "daisyui";
2 |
3 | /** @type {import('tailwindcss').Config} */
4 | export default {
5 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
6 | theme: {
7 | extend: {},
8 | },
9 | plugins: [daisyui],
10 | daisyui: {
11 | themes: [
12 | "pastel",
13 | "retro",
14 | "coffee",
15 | "forest",
16 | "cyberpunk",
17 | "synthwave",
18 | "luxury",
19 | "autumn",
20 | "valentine",
21 | "aqua",
22 | "business",
23 | "night",
24 | "dracula",
25 | ],
26 | },
27 | };
28 |
--------------------------------------------------------------------------------
/frontend/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vite.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pern-stack-tutorial",
3 | "version": "1.0.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "pern-stack-tutorial",
9 | "version": "1.0.0",
10 | "license": "ISC",
11 | "dependencies": {
12 | "@arcjet/node": "^1.0.0-beta.1",
13 | "@neondatabase/serverless": "^0.10.4",
14 | "cors": "^2.8.5",
15 | "dotenv": "^16.4.7",
16 | "express": "^4.21.2",
17 | "helmet": "^8.0.0",
18 | "morgan": "^1.10.0"
19 | },
20 | "devDependencies": {
21 | "nodemon": "^3.1.9"
22 | }
23 | },
24 | "node_modules/@arcjet/analyze": {
25 | "version": "1.0.0-beta.1",
26 | "resolved": "https://registry.npmjs.org/@arcjet/analyze/-/analyze-1.0.0-beta.1.tgz",
27 | "integrity": "sha512-k/rNH8g48iR8B8QOyA+0474jgp3NPqezGJERbfJLyPEzOBP4/5ZgKED7J9NUVtzoKB9Bf4eVLXksTjvf8J8IHQ==",
28 | "license": "Apache-2.0",
29 | "dependencies": {
30 | "@arcjet/analyze-wasm": "1.0.0-beta.1",
31 | "@arcjet/protocol": "1.0.0-beta.1"
32 | },
33 | "engines": {
34 | "node": ">=18"
35 | }
36 | },
37 | "node_modules/@arcjet/analyze-wasm": {
38 | "version": "1.0.0-beta.1",
39 | "resolved": "https://registry.npmjs.org/@arcjet/analyze-wasm/-/analyze-wasm-1.0.0-beta.1.tgz",
40 | "integrity": "sha512-TRSDM2g+knkoDXDrmfEiAqXOctQ+fXha3JnpLgodKjFB3DRje+jnCxobkdxypWkyPj6V/2n0IftUaVOCWT8nmg==",
41 | "license": "Apache-2.0",
42 | "engines": {
43 | "node": ">=18"
44 | }
45 | },
46 | "node_modules/@arcjet/body": {
47 | "version": "1.0.0-beta.1",
48 | "resolved": "https://registry.npmjs.org/@arcjet/body/-/body-1.0.0-beta.1.tgz",
49 | "integrity": "sha512-0cRif9ylkSBbr341HB9MRGtm6SdPHjtvG/Wk1zQiWP0Ad3KYkSQ+zXtfhK7G7fcdKP1qA10gmTaSDJ0Vg+Ppxg==",
50 | "license": "Apache-2.0",
51 | "engines": {
52 | "node": ">=18"
53 | }
54 | },
55 | "node_modules/@arcjet/duration": {
56 | "version": "1.0.0-beta.1",
57 | "resolved": "https://registry.npmjs.org/@arcjet/duration/-/duration-1.0.0-beta.1.tgz",
58 | "integrity": "sha512-w+0apQrrs7g84rF8nfSeKdH2cbDxCmkJLYWNYmTOYkELyqDO1dn6kKKCxaXjyqLvX6gJdHdrX/YrxADuax1v4Q==",
59 | "license": "Apache-2.0",
60 | "engines": {
61 | "node": ">=18"
62 | }
63 | },
64 | "node_modules/@arcjet/env": {
65 | "version": "1.0.0-beta.1",
66 | "resolved": "https://registry.npmjs.org/@arcjet/env/-/env-1.0.0-beta.1.tgz",
67 | "integrity": "sha512-o+YgWWjt96m5tyxHvhxY0farNQvOKplxnnyLVw1kLduDfKX+0e7Quq5O2l1heHtRep310uquoldG46SqSkORFA==",
68 | "license": "Apache-2.0",
69 | "engines": {
70 | "node": ">=18"
71 | }
72 | },
73 | "node_modules/@arcjet/headers": {
74 | "version": "1.0.0-beta.1",
75 | "resolved": "https://registry.npmjs.org/@arcjet/headers/-/headers-1.0.0-beta.1.tgz",
76 | "integrity": "sha512-bw/4nQE1NRLzf1rFn2pOJE/LOxpdPPDjAPv4SbunMBUFja+gMWA9RqmfYdRdkngnJyPdJnGW7kkREngMqcauqA==",
77 | "license": "Apache-2.0",
78 | "engines": {
79 | "node": ">=18"
80 | }
81 | },
82 | "node_modules/@arcjet/ip": {
83 | "version": "1.0.0-beta.1",
84 | "resolved": "https://registry.npmjs.org/@arcjet/ip/-/ip-1.0.0-beta.1.tgz",
85 | "integrity": "sha512-hMR4ss/zodmmWfHNRvJTOekbJxcErGTZjQYq9W/bUsqEvjNCK4lK11GRp5r2OI3NaA8nXoZgkZKMG1EhNFf3uQ==",
86 | "license": "Apache-2.0",
87 | "engines": {
88 | "node": ">=18"
89 | }
90 | },
91 | "node_modules/@arcjet/logger": {
92 | "version": "1.0.0-beta.1",
93 | "resolved": "https://registry.npmjs.org/@arcjet/logger/-/logger-1.0.0-beta.1.tgz",
94 | "integrity": "sha512-5LREJwsAGIHWTiGDqMv/rWF9A0aQ94h9fznRbiv+uM3jaHR1mUrN3BF5PM27IgYmcxNeRsVEqmZx2F19hzbJrQ==",
95 | "license": "Apache-2.0",
96 | "dependencies": {
97 | "@arcjet/sprintf": "1.0.0-beta.1"
98 | },
99 | "engines": {
100 | "node": ">=18"
101 | }
102 | },
103 | "node_modules/@arcjet/node": {
104 | "version": "1.0.0-beta.1",
105 | "resolved": "https://registry.npmjs.org/@arcjet/node/-/node-1.0.0-beta.1.tgz",
106 | "integrity": "sha512-tcY8flZwjLBVMy5lIFRIQX2SmFIwM/jE/h3AAe/pMEy5imMDPijI3B5sDl2QwgGFF/4xgYgNAO2qpSVNEL6kOw==",
107 | "license": "Apache-2.0",
108 | "dependencies": {
109 | "@arcjet/body": "1.0.0-beta.1",
110 | "@arcjet/env": "1.0.0-beta.1",
111 | "@arcjet/headers": "1.0.0-beta.1",
112 | "@arcjet/ip": "1.0.0-beta.1",
113 | "@arcjet/logger": "1.0.0-beta.1",
114 | "@arcjet/protocol": "1.0.0-beta.1",
115 | "@arcjet/transport": "1.0.0-beta.1",
116 | "arcjet": "1.0.0-beta.1"
117 | },
118 | "engines": {
119 | "node": ">=18"
120 | }
121 | },
122 | "node_modules/@arcjet/protocol": {
123 | "version": "1.0.0-beta.1",
124 | "resolved": "https://registry.npmjs.org/@arcjet/protocol/-/protocol-1.0.0-beta.1.tgz",
125 | "integrity": "sha512-e1P0Dqt9Rhm4bsnVp1vmReKB7n4zi7BHuSC3t4mj181FWqZP2LxBN5l5vJGyav9Aq1ts6SGkEBHjGXLc4Bq7Yg==",
126 | "license": "Apache-2.0",
127 | "dependencies": {
128 | "@bufbuild/protobuf": "1.10.0",
129 | "@connectrpc/connect": "1.6.1",
130 | "typeid-js": "1.1.0"
131 | },
132 | "engines": {
133 | "node": ">=18"
134 | }
135 | },
136 | "node_modules/@arcjet/runtime": {
137 | "version": "1.0.0-beta.1",
138 | "resolved": "https://registry.npmjs.org/@arcjet/runtime/-/runtime-1.0.0-beta.1.tgz",
139 | "integrity": "sha512-SqFKJx+KZTEbRDxUqo8YI6Ye4DhnQkpaaj1o48q+GbtRsLKVTPxRxW9VOSBMHi4VgMj3+lGy6cSMsB8Y02AzZg==",
140 | "license": "Apache-2.0",
141 | "engines": {
142 | "node": ">=18"
143 | }
144 | },
145 | "node_modules/@arcjet/sprintf": {
146 | "version": "1.0.0-beta.1",
147 | "resolved": "https://registry.npmjs.org/@arcjet/sprintf/-/sprintf-1.0.0-beta.1.tgz",
148 | "integrity": "sha512-Ysik/z1Jf1UNyVyWbFbmFU2gXKSVz2ZoPOT6vx11/zvgRBfpeX1uHJfABzjzimZ4pIW43mSwm7Qnr259tG3gzQ==",
149 | "license": "Apache-2.0",
150 | "engines": {
151 | "node": ">=18"
152 | }
153 | },
154 | "node_modules/@arcjet/transport": {
155 | "version": "1.0.0-beta.1",
156 | "resolved": "https://registry.npmjs.org/@arcjet/transport/-/transport-1.0.0-beta.1.tgz",
157 | "integrity": "sha512-07nsucGLItomFjVgrSe4P5Y1fFvfsPPpu93zat4VwTAH6rhSard8YU/HShGf/24TN/ZhAkYCgYtMrXWDi11jDg==",
158 | "license": "Apache-2.0",
159 | "dependencies": {
160 | "@connectrpc/connect-node": "1.6.1",
161 | "@connectrpc/connect-web": "1.6.1"
162 | },
163 | "engines": {
164 | "node": ">=18"
165 | }
166 | },
167 | "node_modules/@bufbuild/protobuf": {
168 | "version": "1.10.0",
169 | "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-1.10.0.tgz",
170 | "integrity": "sha512-QDdVFLoN93Zjg36NoQPZfsVH9tZew7wKDKyV5qRdj8ntT4wQCOradQjRaTdwMhWUYsgKsvCINKKm87FdEk96Ag==",
171 | "license": "(Apache-2.0 AND BSD-3-Clause)"
172 | },
173 | "node_modules/@connectrpc/connect": {
174 | "version": "1.6.1",
175 | "resolved": "https://registry.npmjs.org/@connectrpc/connect/-/connect-1.6.1.tgz",
176 | "integrity": "sha512-KchMDNtU4CDTdkyf0qG7ugJ6qHTOR/aI7XebYn3OTCNagaDYWiZUVKgRgwH79yeMkpNgvEUaXSK7wKjaBK9b/Q==",
177 | "license": "Apache-2.0",
178 | "peerDependencies": {
179 | "@bufbuild/protobuf": "^1.10.0"
180 | }
181 | },
182 | "node_modules/@connectrpc/connect-node": {
183 | "version": "1.6.1",
184 | "resolved": "https://registry.npmjs.org/@connectrpc/connect-node/-/connect-node-1.6.1.tgz",
185 | "integrity": "sha512-DxcD1wsF/aX9GegjAtl7VbpiZNjVJozy87VbaFoN6AF0Ln1Q757r5dgV59Gz0wmlk5f17txUsrEr1f2inlnnAg==",
186 | "license": "Apache-2.0",
187 | "dependencies": {
188 | "undici": "^5.28.4"
189 | },
190 | "engines": {
191 | "node": ">=16.0.0"
192 | },
193 | "peerDependencies": {
194 | "@bufbuild/protobuf": "^1.10.0",
195 | "@connectrpc/connect": "1.6.1"
196 | }
197 | },
198 | "node_modules/@connectrpc/connect-web": {
199 | "version": "1.6.1",
200 | "resolved": "https://registry.npmjs.org/@connectrpc/connect-web/-/connect-web-1.6.1.tgz",
201 | "integrity": "sha512-GVfxQOmt3TtgTaKeXLS/EA2IHa3nHxwe2BCHT7X0Q/0hohM+nP5DDnIItGEjGrGdt3LTTqWqE4s70N4h+qIMlQ==",
202 | "license": "Apache-2.0",
203 | "peerDependencies": {
204 | "@bufbuild/protobuf": "^1.10.0",
205 | "@connectrpc/connect": "1.6.1"
206 | }
207 | },
208 | "node_modules/@fastify/busboy": {
209 | "version": "2.1.1",
210 | "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz",
211 | "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==",
212 | "license": "MIT",
213 | "engines": {
214 | "node": ">=14"
215 | }
216 | },
217 | "node_modules/@neondatabase/serverless": {
218 | "version": "0.10.4",
219 | "resolved": "https://registry.npmjs.org/@neondatabase/serverless/-/serverless-0.10.4.tgz",
220 | "integrity": "sha512-2nZuh3VUO9voBauuh+IGYRhGU/MskWHt1IuZvHcJw6GLjDgtqj/KViKo7SIrLdGLdot7vFbiRRw+BgEy3wT9HA==",
221 | "license": "MIT",
222 | "dependencies": {
223 | "@types/pg": "8.11.6"
224 | }
225 | },
226 | "node_modules/@types/node": {
227 | "version": "22.13.0",
228 | "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.0.tgz",
229 | "integrity": "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA==",
230 | "license": "MIT",
231 | "dependencies": {
232 | "undici-types": "~6.20.0"
233 | }
234 | },
235 | "node_modules/@types/pg": {
236 | "version": "8.11.6",
237 | "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.6.tgz",
238 | "integrity": "sha512-/2WmmBXHLsfRqzfHW7BNZ8SbYzE8OSk7i3WjFYvfgRHj7S1xj+16Je5fUKv3lVdVzk/zn9TXOqf+avFCFIE0yQ==",
239 | "license": "MIT",
240 | "dependencies": {
241 | "@types/node": "*",
242 | "pg-protocol": "*",
243 | "pg-types": "^4.0.1"
244 | }
245 | },
246 | "node_modules/accepts": {
247 | "version": "1.3.8",
248 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
249 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
250 | "license": "MIT",
251 | "dependencies": {
252 | "mime-types": "~2.1.34",
253 | "negotiator": "0.6.3"
254 | },
255 | "engines": {
256 | "node": ">= 0.6"
257 | }
258 | },
259 | "node_modules/anymatch": {
260 | "version": "3.1.3",
261 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
262 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
263 | "dev": true,
264 | "license": "ISC",
265 | "dependencies": {
266 | "normalize-path": "^3.0.0",
267 | "picomatch": "^2.0.4"
268 | },
269 | "engines": {
270 | "node": ">= 8"
271 | }
272 | },
273 | "node_modules/arcjet": {
274 | "version": "1.0.0-beta.1",
275 | "resolved": "https://registry.npmjs.org/arcjet/-/arcjet-1.0.0-beta.1.tgz",
276 | "integrity": "sha512-tpade4RiuM4xlqqsgTTwCzGT/f2qojsOFfAh0vpFl31AF85UIazx6id3IE6oHOjSuFE4wVGoIL0JIIvQ0jr5Yg==",
277 | "license": "Apache-2.0",
278 | "dependencies": {
279 | "@arcjet/analyze": "1.0.0-beta.1",
280 | "@arcjet/duration": "1.0.0-beta.1",
281 | "@arcjet/headers": "1.0.0-beta.1",
282 | "@arcjet/protocol": "1.0.0-beta.1",
283 | "@arcjet/runtime": "1.0.0-beta.1"
284 | },
285 | "engines": {
286 | "node": ">=18"
287 | }
288 | },
289 | "node_modules/array-flatten": {
290 | "version": "1.1.1",
291 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
292 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
293 | "license": "MIT"
294 | },
295 | "node_modules/balanced-match": {
296 | "version": "1.0.2",
297 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
298 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
299 | "dev": true,
300 | "license": "MIT"
301 | },
302 | "node_modules/basic-auth": {
303 | "version": "2.0.1",
304 | "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
305 | "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
306 | "license": "MIT",
307 | "dependencies": {
308 | "safe-buffer": "5.1.2"
309 | },
310 | "engines": {
311 | "node": ">= 0.8"
312 | }
313 | },
314 | "node_modules/basic-auth/node_modules/safe-buffer": {
315 | "version": "5.1.2",
316 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
317 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
318 | "license": "MIT"
319 | },
320 | "node_modules/binary-extensions": {
321 | "version": "2.3.0",
322 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
323 | "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
324 | "dev": true,
325 | "license": "MIT",
326 | "engines": {
327 | "node": ">=8"
328 | },
329 | "funding": {
330 | "url": "https://github.com/sponsors/sindresorhus"
331 | }
332 | },
333 | "node_modules/body-parser": {
334 | "version": "1.20.3",
335 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
336 | "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
337 | "license": "MIT",
338 | "dependencies": {
339 | "bytes": "3.1.2",
340 | "content-type": "~1.0.5",
341 | "debug": "2.6.9",
342 | "depd": "2.0.0",
343 | "destroy": "1.2.0",
344 | "http-errors": "2.0.0",
345 | "iconv-lite": "0.4.24",
346 | "on-finished": "2.4.1",
347 | "qs": "6.13.0",
348 | "raw-body": "2.5.2",
349 | "type-is": "~1.6.18",
350 | "unpipe": "1.0.0"
351 | },
352 | "engines": {
353 | "node": ">= 0.8",
354 | "npm": "1.2.8000 || >= 1.4.16"
355 | }
356 | },
357 | "node_modules/brace-expansion": {
358 | "version": "1.1.11",
359 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
360 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
361 | "dev": true,
362 | "license": "MIT",
363 | "dependencies": {
364 | "balanced-match": "^1.0.0",
365 | "concat-map": "0.0.1"
366 | }
367 | },
368 | "node_modules/braces": {
369 | "version": "3.0.3",
370 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
371 | "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
372 | "dev": true,
373 | "license": "MIT",
374 | "dependencies": {
375 | "fill-range": "^7.1.1"
376 | },
377 | "engines": {
378 | "node": ">=8"
379 | }
380 | },
381 | "node_modules/bytes": {
382 | "version": "3.1.2",
383 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
384 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
385 | "license": "MIT",
386 | "engines": {
387 | "node": ">= 0.8"
388 | }
389 | },
390 | "node_modules/call-bind-apply-helpers": {
391 | "version": "1.0.1",
392 | "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz",
393 | "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==",
394 | "license": "MIT",
395 | "dependencies": {
396 | "es-errors": "^1.3.0",
397 | "function-bind": "^1.1.2"
398 | },
399 | "engines": {
400 | "node": ">= 0.4"
401 | }
402 | },
403 | "node_modules/call-bound": {
404 | "version": "1.0.3",
405 | "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz",
406 | "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==",
407 | "license": "MIT",
408 | "dependencies": {
409 | "call-bind-apply-helpers": "^1.0.1",
410 | "get-intrinsic": "^1.2.6"
411 | },
412 | "engines": {
413 | "node": ">= 0.4"
414 | },
415 | "funding": {
416 | "url": "https://github.com/sponsors/ljharb"
417 | }
418 | },
419 | "node_modules/chokidar": {
420 | "version": "3.6.0",
421 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
422 | "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
423 | "dev": true,
424 | "license": "MIT",
425 | "dependencies": {
426 | "anymatch": "~3.1.2",
427 | "braces": "~3.0.2",
428 | "glob-parent": "~5.1.2",
429 | "is-binary-path": "~2.1.0",
430 | "is-glob": "~4.0.1",
431 | "normalize-path": "~3.0.0",
432 | "readdirp": "~3.6.0"
433 | },
434 | "engines": {
435 | "node": ">= 8.10.0"
436 | },
437 | "funding": {
438 | "url": "https://paulmillr.com/funding/"
439 | },
440 | "optionalDependencies": {
441 | "fsevents": "~2.3.2"
442 | }
443 | },
444 | "node_modules/concat-map": {
445 | "version": "0.0.1",
446 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
447 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
448 | "dev": true,
449 | "license": "MIT"
450 | },
451 | "node_modules/content-disposition": {
452 | "version": "0.5.4",
453 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
454 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
455 | "license": "MIT",
456 | "dependencies": {
457 | "safe-buffer": "5.2.1"
458 | },
459 | "engines": {
460 | "node": ">= 0.6"
461 | }
462 | },
463 | "node_modules/content-type": {
464 | "version": "1.0.5",
465 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
466 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
467 | "license": "MIT",
468 | "engines": {
469 | "node": ">= 0.6"
470 | }
471 | },
472 | "node_modules/cookie": {
473 | "version": "0.7.1",
474 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
475 | "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
476 | "license": "MIT",
477 | "engines": {
478 | "node": ">= 0.6"
479 | }
480 | },
481 | "node_modules/cookie-signature": {
482 | "version": "1.0.6",
483 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
484 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
485 | "license": "MIT"
486 | },
487 | "node_modules/cors": {
488 | "version": "2.8.5",
489 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
490 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
491 | "license": "MIT",
492 | "dependencies": {
493 | "object-assign": "^4",
494 | "vary": "^1"
495 | },
496 | "engines": {
497 | "node": ">= 0.10"
498 | }
499 | },
500 | "node_modules/debug": {
501 | "version": "2.6.9",
502 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
503 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
504 | "license": "MIT",
505 | "dependencies": {
506 | "ms": "2.0.0"
507 | }
508 | },
509 | "node_modules/depd": {
510 | "version": "2.0.0",
511 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
512 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
513 | "license": "MIT",
514 | "engines": {
515 | "node": ">= 0.8"
516 | }
517 | },
518 | "node_modules/destroy": {
519 | "version": "1.2.0",
520 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
521 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
522 | "license": "MIT",
523 | "engines": {
524 | "node": ">= 0.8",
525 | "npm": "1.2.8000 || >= 1.4.16"
526 | }
527 | },
528 | "node_modules/dotenv": {
529 | "version": "16.4.7",
530 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
531 | "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
532 | "license": "BSD-2-Clause",
533 | "engines": {
534 | "node": ">=12"
535 | },
536 | "funding": {
537 | "url": "https://dotenvx.com"
538 | }
539 | },
540 | "node_modules/dunder-proto": {
541 | "version": "1.0.1",
542 | "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
543 | "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
544 | "license": "MIT",
545 | "dependencies": {
546 | "call-bind-apply-helpers": "^1.0.1",
547 | "es-errors": "^1.3.0",
548 | "gopd": "^1.2.0"
549 | },
550 | "engines": {
551 | "node": ">= 0.4"
552 | }
553 | },
554 | "node_modules/ee-first": {
555 | "version": "1.1.1",
556 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
557 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
558 | "license": "MIT"
559 | },
560 | "node_modules/encodeurl": {
561 | "version": "2.0.0",
562 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
563 | "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
564 | "license": "MIT",
565 | "engines": {
566 | "node": ">= 0.8"
567 | }
568 | },
569 | "node_modules/es-define-property": {
570 | "version": "1.0.1",
571 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
572 | "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
573 | "license": "MIT",
574 | "engines": {
575 | "node": ">= 0.4"
576 | }
577 | },
578 | "node_modules/es-errors": {
579 | "version": "1.3.0",
580 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
581 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
582 | "license": "MIT",
583 | "engines": {
584 | "node": ">= 0.4"
585 | }
586 | },
587 | "node_modules/es-object-atoms": {
588 | "version": "1.1.1",
589 | "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
590 | "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
591 | "license": "MIT",
592 | "dependencies": {
593 | "es-errors": "^1.3.0"
594 | },
595 | "engines": {
596 | "node": ">= 0.4"
597 | }
598 | },
599 | "node_modules/escape-html": {
600 | "version": "1.0.3",
601 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
602 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
603 | "license": "MIT"
604 | },
605 | "node_modules/etag": {
606 | "version": "1.8.1",
607 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
608 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
609 | "license": "MIT",
610 | "engines": {
611 | "node": ">= 0.6"
612 | }
613 | },
614 | "node_modules/express": {
615 | "version": "4.21.2",
616 | "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
617 | "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
618 | "license": "MIT",
619 | "dependencies": {
620 | "accepts": "~1.3.8",
621 | "array-flatten": "1.1.1",
622 | "body-parser": "1.20.3",
623 | "content-disposition": "0.5.4",
624 | "content-type": "~1.0.4",
625 | "cookie": "0.7.1",
626 | "cookie-signature": "1.0.6",
627 | "debug": "2.6.9",
628 | "depd": "2.0.0",
629 | "encodeurl": "~2.0.0",
630 | "escape-html": "~1.0.3",
631 | "etag": "~1.8.1",
632 | "finalhandler": "1.3.1",
633 | "fresh": "0.5.2",
634 | "http-errors": "2.0.0",
635 | "merge-descriptors": "1.0.3",
636 | "methods": "~1.1.2",
637 | "on-finished": "2.4.1",
638 | "parseurl": "~1.3.3",
639 | "path-to-regexp": "0.1.12",
640 | "proxy-addr": "~2.0.7",
641 | "qs": "6.13.0",
642 | "range-parser": "~1.2.1",
643 | "safe-buffer": "5.2.1",
644 | "send": "0.19.0",
645 | "serve-static": "1.16.2",
646 | "setprototypeof": "1.2.0",
647 | "statuses": "2.0.1",
648 | "type-is": "~1.6.18",
649 | "utils-merge": "1.0.1",
650 | "vary": "~1.1.2"
651 | },
652 | "engines": {
653 | "node": ">= 0.10.0"
654 | },
655 | "funding": {
656 | "type": "opencollective",
657 | "url": "https://opencollective.com/express"
658 | }
659 | },
660 | "node_modules/fill-range": {
661 | "version": "7.1.1",
662 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
663 | "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
664 | "dev": true,
665 | "license": "MIT",
666 | "dependencies": {
667 | "to-regex-range": "^5.0.1"
668 | },
669 | "engines": {
670 | "node": ">=8"
671 | }
672 | },
673 | "node_modules/finalhandler": {
674 | "version": "1.3.1",
675 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
676 | "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
677 | "license": "MIT",
678 | "dependencies": {
679 | "debug": "2.6.9",
680 | "encodeurl": "~2.0.0",
681 | "escape-html": "~1.0.3",
682 | "on-finished": "2.4.1",
683 | "parseurl": "~1.3.3",
684 | "statuses": "2.0.1",
685 | "unpipe": "~1.0.0"
686 | },
687 | "engines": {
688 | "node": ">= 0.8"
689 | }
690 | },
691 | "node_modules/forwarded": {
692 | "version": "0.2.0",
693 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
694 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
695 | "license": "MIT",
696 | "engines": {
697 | "node": ">= 0.6"
698 | }
699 | },
700 | "node_modules/fresh": {
701 | "version": "0.5.2",
702 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
703 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
704 | "license": "MIT",
705 | "engines": {
706 | "node": ">= 0.6"
707 | }
708 | },
709 | "node_modules/fsevents": {
710 | "version": "2.3.3",
711 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
712 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
713 | "dev": true,
714 | "hasInstallScript": true,
715 | "license": "MIT",
716 | "optional": true,
717 | "os": [
718 | "darwin"
719 | ],
720 | "engines": {
721 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
722 | }
723 | },
724 | "node_modules/function-bind": {
725 | "version": "1.1.2",
726 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
727 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
728 | "license": "MIT",
729 | "funding": {
730 | "url": "https://github.com/sponsors/ljharb"
731 | }
732 | },
733 | "node_modules/get-intrinsic": {
734 | "version": "1.2.7",
735 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz",
736 | "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==",
737 | "license": "MIT",
738 | "dependencies": {
739 | "call-bind-apply-helpers": "^1.0.1",
740 | "es-define-property": "^1.0.1",
741 | "es-errors": "^1.3.0",
742 | "es-object-atoms": "^1.0.0",
743 | "function-bind": "^1.1.2",
744 | "get-proto": "^1.0.0",
745 | "gopd": "^1.2.0",
746 | "has-symbols": "^1.1.0",
747 | "hasown": "^2.0.2",
748 | "math-intrinsics": "^1.1.0"
749 | },
750 | "engines": {
751 | "node": ">= 0.4"
752 | },
753 | "funding": {
754 | "url": "https://github.com/sponsors/ljharb"
755 | }
756 | },
757 | "node_modules/get-proto": {
758 | "version": "1.0.1",
759 | "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
760 | "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
761 | "license": "MIT",
762 | "dependencies": {
763 | "dunder-proto": "^1.0.1",
764 | "es-object-atoms": "^1.0.0"
765 | },
766 | "engines": {
767 | "node": ">= 0.4"
768 | }
769 | },
770 | "node_modules/glob-parent": {
771 | "version": "5.1.2",
772 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
773 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
774 | "dev": true,
775 | "license": "ISC",
776 | "dependencies": {
777 | "is-glob": "^4.0.1"
778 | },
779 | "engines": {
780 | "node": ">= 6"
781 | }
782 | },
783 | "node_modules/gopd": {
784 | "version": "1.2.0",
785 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
786 | "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
787 | "license": "MIT",
788 | "engines": {
789 | "node": ">= 0.4"
790 | },
791 | "funding": {
792 | "url": "https://github.com/sponsors/ljharb"
793 | }
794 | },
795 | "node_modules/has-flag": {
796 | "version": "3.0.0",
797 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
798 | "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
799 | "dev": true,
800 | "license": "MIT",
801 | "engines": {
802 | "node": ">=4"
803 | }
804 | },
805 | "node_modules/has-symbols": {
806 | "version": "1.1.0",
807 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
808 | "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
809 | "license": "MIT",
810 | "engines": {
811 | "node": ">= 0.4"
812 | },
813 | "funding": {
814 | "url": "https://github.com/sponsors/ljharb"
815 | }
816 | },
817 | "node_modules/hasown": {
818 | "version": "2.0.2",
819 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
820 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
821 | "license": "MIT",
822 | "dependencies": {
823 | "function-bind": "^1.1.2"
824 | },
825 | "engines": {
826 | "node": ">= 0.4"
827 | }
828 | },
829 | "node_modules/helmet": {
830 | "version": "8.0.0",
831 | "resolved": "https://registry.npmjs.org/helmet/-/helmet-8.0.0.tgz",
832 | "integrity": "sha512-VyusHLEIIO5mjQPUI1wpOAEu+wl6Q0998jzTxqUYGE45xCIcAxy3MsbEK/yyJUJ3ADeMoB6MornPH6GMWAf+Pw==",
833 | "license": "MIT",
834 | "engines": {
835 | "node": ">=18.0.0"
836 | }
837 | },
838 | "node_modules/http-errors": {
839 | "version": "2.0.0",
840 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
841 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
842 | "license": "MIT",
843 | "dependencies": {
844 | "depd": "2.0.0",
845 | "inherits": "2.0.4",
846 | "setprototypeof": "1.2.0",
847 | "statuses": "2.0.1",
848 | "toidentifier": "1.0.1"
849 | },
850 | "engines": {
851 | "node": ">= 0.8"
852 | }
853 | },
854 | "node_modules/iconv-lite": {
855 | "version": "0.4.24",
856 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
857 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
858 | "license": "MIT",
859 | "dependencies": {
860 | "safer-buffer": ">= 2.1.2 < 3"
861 | },
862 | "engines": {
863 | "node": ">=0.10.0"
864 | }
865 | },
866 | "node_modules/ignore-by-default": {
867 | "version": "1.0.1",
868 | "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
869 | "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
870 | "dev": true,
871 | "license": "ISC"
872 | },
873 | "node_modules/inherits": {
874 | "version": "2.0.4",
875 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
876 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
877 | "license": "ISC"
878 | },
879 | "node_modules/ipaddr.js": {
880 | "version": "1.9.1",
881 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
882 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
883 | "license": "MIT",
884 | "engines": {
885 | "node": ">= 0.10"
886 | }
887 | },
888 | "node_modules/is-binary-path": {
889 | "version": "2.1.0",
890 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
891 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
892 | "dev": true,
893 | "license": "MIT",
894 | "dependencies": {
895 | "binary-extensions": "^2.0.0"
896 | },
897 | "engines": {
898 | "node": ">=8"
899 | }
900 | },
901 | "node_modules/is-extglob": {
902 | "version": "2.1.1",
903 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
904 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
905 | "dev": true,
906 | "license": "MIT",
907 | "engines": {
908 | "node": ">=0.10.0"
909 | }
910 | },
911 | "node_modules/is-glob": {
912 | "version": "4.0.3",
913 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
914 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
915 | "dev": true,
916 | "license": "MIT",
917 | "dependencies": {
918 | "is-extglob": "^2.1.1"
919 | },
920 | "engines": {
921 | "node": ">=0.10.0"
922 | }
923 | },
924 | "node_modules/is-number": {
925 | "version": "7.0.0",
926 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
927 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
928 | "dev": true,
929 | "license": "MIT",
930 | "engines": {
931 | "node": ">=0.12.0"
932 | }
933 | },
934 | "node_modules/math-intrinsics": {
935 | "version": "1.1.0",
936 | "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
937 | "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
938 | "license": "MIT",
939 | "engines": {
940 | "node": ">= 0.4"
941 | }
942 | },
943 | "node_modules/media-typer": {
944 | "version": "0.3.0",
945 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
946 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
947 | "license": "MIT",
948 | "engines": {
949 | "node": ">= 0.6"
950 | }
951 | },
952 | "node_modules/merge-descriptors": {
953 | "version": "1.0.3",
954 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
955 | "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
956 | "license": "MIT",
957 | "funding": {
958 | "url": "https://github.com/sponsors/sindresorhus"
959 | }
960 | },
961 | "node_modules/methods": {
962 | "version": "1.1.2",
963 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
964 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
965 | "license": "MIT",
966 | "engines": {
967 | "node": ">= 0.6"
968 | }
969 | },
970 | "node_modules/mime": {
971 | "version": "1.6.0",
972 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
973 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
974 | "license": "MIT",
975 | "bin": {
976 | "mime": "cli.js"
977 | },
978 | "engines": {
979 | "node": ">=4"
980 | }
981 | },
982 | "node_modules/mime-db": {
983 | "version": "1.52.0",
984 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
985 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
986 | "license": "MIT",
987 | "engines": {
988 | "node": ">= 0.6"
989 | }
990 | },
991 | "node_modules/mime-types": {
992 | "version": "2.1.35",
993 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
994 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
995 | "license": "MIT",
996 | "dependencies": {
997 | "mime-db": "1.52.0"
998 | },
999 | "engines": {
1000 | "node": ">= 0.6"
1001 | }
1002 | },
1003 | "node_modules/minimatch": {
1004 | "version": "3.1.2",
1005 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
1006 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
1007 | "dev": true,
1008 | "license": "ISC",
1009 | "dependencies": {
1010 | "brace-expansion": "^1.1.7"
1011 | },
1012 | "engines": {
1013 | "node": "*"
1014 | }
1015 | },
1016 | "node_modules/morgan": {
1017 | "version": "1.10.0",
1018 | "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz",
1019 | "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==",
1020 | "license": "MIT",
1021 | "dependencies": {
1022 | "basic-auth": "~2.0.1",
1023 | "debug": "2.6.9",
1024 | "depd": "~2.0.0",
1025 | "on-finished": "~2.3.0",
1026 | "on-headers": "~1.0.2"
1027 | },
1028 | "engines": {
1029 | "node": ">= 0.8.0"
1030 | }
1031 | },
1032 | "node_modules/morgan/node_modules/on-finished": {
1033 | "version": "2.3.0",
1034 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
1035 | "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==",
1036 | "license": "MIT",
1037 | "dependencies": {
1038 | "ee-first": "1.1.1"
1039 | },
1040 | "engines": {
1041 | "node": ">= 0.8"
1042 | }
1043 | },
1044 | "node_modules/ms": {
1045 | "version": "2.0.0",
1046 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
1047 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
1048 | "license": "MIT"
1049 | },
1050 | "node_modules/negotiator": {
1051 | "version": "0.6.3",
1052 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
1053 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
1054 | "license": "MIT",
1055 | "engines": {
1056 | "node": ">= 0.6"
1057 | }
1058 | },
1059 | "node_modules/nodemon": {
1060 | "version": "3.1.9",
1061 | "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz",
1062 | "integrity": "sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==",
1063 | "dev": true,
1064 | "license": "MIT",
1065 | "dependencies": {
1066 | "chokidar": "^3.5.2",
1067 | "debug": "^4",
1068 | "ignore-by-default": "^1.0.1",
1069 | "minimatch": "^3.1.2",
1070 | "pstree.remy": "^1.1.8",
1071 | "semver": "^7.5.3",
1072 | "simple-update-notifier": "^2.0.0",
1073 | "supports-color": "^5.5.0",
1074 | "touch": "^3.1.0",
1075 | "undefsafe": "^2.0.5"
1076 | },
1077 | "bin": {
1078 | "nodemon": "bin/nodemon.js"
1079 | },
1080 | "engines": {
1081 | "node": ">=10"
1082 | },
1083 | "funding": {
1084 | "type": "opencollective",
1085 | "url": "https://opencollective.com/nodemon"
1086 | }
1087 | },
1088 | "node_modules/nodemon/node_modules/debug": {
1089 | "version": "4.4.0",
1090 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
1091 | "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
1092 | "dev": true,
1093 | "license": "MIT",
1094 | "dependencies": {
1095 | "ms": "^2.1.3"
1096 | },
1097 | "engines": {
1098 | "node": ">=6.0"
1099 | },
1100 | "peerDependenciesMeta": {
1101 | "supports-color": {
1102 | "optional": true
1103 | }
1104 | }
1105 | },
1106 | "node_modules/nodemon/node_modules/ms": {
1107 | "version": "2.1.3",
1108 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1109 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
1110 | "dev": true,
1111 | "license": "MIT"
1112 | },
1113 | "node_modules/normalize-path": {
1114 | "version": "3.0.0",
1115 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
1116 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
1117 | "dev": true,
1118 | "license": "MIT",
1119 | "engines": {
1120 | "node": ">=0.10.0"
1121 | }
1122 | },
1123 | "node_modules/object-assign": {
1124 | "version": "4.1.1",
1125 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
1126 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
1127 | "license": "MIT",
1128 | "engines": {
1129 | "node": ">=0.10.0"
1130 | }
1131 | },
1132 | "node_modules/object-inspect": {
1133 | "version": "1.13.3",
1134 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz",
1135 | "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==",
1136 | "license": "MIT",
1137 | "engines": {
1138 | "node": ">= 0.4"
1139 | },
1140 | "funding": {
1141 | "url": "https://github.com/sponsors/ljharb"
1142 | }
1143 | },
1144 | "node_modules/obuf": {
1145 | "version": "1.1.2",
1146 | "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
1147 | "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
1148 | "license": "MIT"
1149 | },
1150 | "node_modules/on-finished": {
1151 | "version": "2.4.1",
1152 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
1153 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
1154 | "license": "MIT",
1155 | "dependencies": {
1156 | "ee-first": "1.1.1"
1157 | },
1158 | "engines": {
1159 | "node": ">= 0.8"
1160 | }
1161 | },
1162 | "node_modules/on-headers": {
1163 | "version": "1.0.2",
1164 | "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
1165 | "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
1166 | "license": "MIT",
1167 | "engines": {
1168 | "node": ">= 0.8"
1169 | }
1170 | },
1171 | "node_modules/parseurl": {
1172 | "version": "1.3.3",
1173 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
1174 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
1175 | "license": "MIT",
1176 | "engines": {
1177 | "node": ">= 0.8"
1178 | }
1179 | },
1180 | "node_modules/path-to-regexp": {
1181 | "version": "0.1.12",
1182 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
1183 | "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
1184 | "license": "MIT"
1185 | },
1186 | "node_modules/pg-int8": {
1187 | "version": "1.0.1",
1188 | "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
1189 | "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
1190 | "license": "ISC",
1191 | "engines": {
1192 | "node": ">=4.0.0"
1193 | }
1194 | },
1195 | "node_modules/pg-numeric": {
1196 | "version": "1.0.2",
1197 | "resolved": "https://registry.npmjs.org/pg-numeric/-/pg-numeric-1.0.2.tgz",
1198 | "integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==",
1199 | "license": "ISC",
1200 | "engines": {
1201 | "node": ">=4"
1202 | }
1203 | },
1204 | "node_modules/pg-protocol": {
1205 | "version": "1.7.0",
1206 | "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.7.0.tgz",
1207 | "integrity": "sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==",
1208 | "license": "MIT"
1209 | },
1210 | "node_modules/pg-types": {
1211 | "version": "4.0.2",
1212 | "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.0.2.tgz",
1213 | "integrity": "sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==",
1214 | "license": "MIT",
1215 | "dependencies": {
1216 | "pg-int8": "1.0.1",
1217 | "pg-numeric": "1.0.2",
1218 | "postgres-array": "~3.0.1",
1219 | "postgres-bytea": "~3.0.0",
1220 | "postgres-date": "~2.1.0",
1221 | "postgres-interval": "^3.0.0",
1222 | "postgres-range": "^1.1.1"
1223 | },
1224 | "engines": {
1225 | "node": ">=10"
1226 | }
1227 | },
1228 | "node_modules/picomatch": {
1229 | "version": "2.3.1",
1230 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
1231 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
1232 | "dev": true,
1233 | "license": "MIT",
1234 | "engines": {
1235 | "node": ">=8.6"
1236 | },
1237 | "funding": {
1238 | "url": "https://github.com/sponsors/jonschlinkert"
1239 | }
1240 | },
1241 | "node_modules/postgres-array": {
1242 | "version": "3.0.2",
1243 | "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.2.tgz",
1244 | "integrity": "sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==",
1245 | "license": "MIT",
1246 | "engines": {
1247 | "node": ">=12"
1248 | }
1249 | },
1250 | "node_modules/postgres-bytea": {
1251 | "version": "3.0.0",
1252 | "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz",
1253 | "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==",
1254 | "license": "MIT",
1255 | "dependencies": {
1256 | "obuf": "~1.1.2"
1257 | },
1258 | "engines": {
1259 | "node": ">= 6"
1260 | }
1261 | },
1262 | "node_modules/postgres-date": {
1263 | "version": "2.1.0",
1264 | "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.1.0.tgz",
1265 | "integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==",
1266 | "license": "MIT",
1267 | "engines": {
1268 | "node": ">=12"
1269 | }
1270 | },
1271 | "node_modules/postgres-interval": {
1272 | "version": "3.0.0",
1273 | "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz",
1274 | "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==",
1275 | "license": "MIT",
1276 | "engines": {
1277 | "node": ">=12"
1278 | }
1279 | },
1280 | "node_modules/postgres-range": {
1281 | "version": "1.1.4",
1282 | "resolved": "https://registry.npmjs.org/postgres-range/-/postgres-range-1.1.4.tgz",
1283 | "integrity": "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==",
1284 | "license": "MIT"
1285 | },
1286 | "node_modules/proxy-addr": {
1287 | "version": "2.0.7",
1288 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
1289 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
1290 | "license": "MIT",
1291 | "dependencies": {
1292 | "forwarded": "0.2.0",
1293 | "ipaddr.js": "1.9.1"
1294 | },
1295 | "engines": {
1296 | "node": ">= 0.10"
1297 | }
1298 | },
1299 | "node_modules/pstree.remy": {
1300 | "version": "1.1.8",
1301 | "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
1302 | "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
1303 | "dev": true,
1304 | "license": "MIT"
1305 | },
1306 | "node_modules/qs": {
1307 | "version": "6.13.0",
1308 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
1309 | "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
1310 | "license": "BSD-3-Clause",
1311 | "dependencies": {
1312 | "side-channel": "^1.0.6"
1313 | },
1314 | "engines": {
1315 | "node": ">=0.6"
1316 | },
1317 | "funding": {
1318 | "url": "https://github.com/sponsors/ljharb"
1319 | }
1320 | },
1321 | "node_modules/range-parser": {
1322 | "version": "1.2.1",
1323 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
1324 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
1325 | "license": "MIT",
1326 | "engines": {
1327 | "node": ">= 0.6"
1328 | }
1329 | },
1330 | "node_modules/raw-body": {
1331 | "version": "2.5.2",
1332 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
1333 | "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
1334 | "license": "MIT",
1335 | "dependencies": {
1336 | "bytes": "3.1.2",
1337 | "http-errors": "2.0.0",
1338 | "iconv-lite": "0.4.24",
1339 | "unpipe": "1.0.0"
1340 | },
1341 | "engines": {
1342 | "node": ">= 0.8"
1343 | }
1344 | },
1345 | "node_modules/readdirp": {
1346 | "version": "3.6.0",
1347 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
1348 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
1349 | "dev": true,
1350 | "license": "MIT",
1351 | "dependencies": {
1352 | "picomatch": "^2.2.1"
1353 | },
1354 | "engines": {
1355 | "node": ">=8.10.0"
1356 | }
1357 | },
1358 | "node_modules/safe-buffer": {
1359 | "version": "5.2.1",
1360 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
1361 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
1362 | "funding": [
1363 | {
1364 | "type": "github",
1365 | "url": "https://github.com/sponsors/feross"
1366 | },
1367 | {
1368 | "type": "patreon",
1369 | "url": "https://www.patreon.com/feross"
1370 | },
1371 | {
1372 | "type": "consulting",
1373 | "url": "https://feross.org/support"
1374 | }
1375 | ],
1376 | "license": "MIT"
1377 | },
1378 | "node_modules/safer-buffer": {
1379 | "version": "2.1.2",
1380 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
1381 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
1382 | "license": "MIT"
1383 | },
1384 | "node_modules/semver": {
1385 | "version": "7.7.0",
1386 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz",
1387 | "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==",
1388 | "dev": true,
1389 | "license": "ISC",
1390 | "bin": {
1391 | "semver": "bin/semver.js"
1392 | },
1393 | "engines": {
1394 | "node": ">=10"
1395 | }
1396 | },
1397 | "node_modules/send": {
1398 | "version": "0.19.0",
1399 | "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
1400 | "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
1401 | "license": "MIT",
1402 | "dependencies": {
1403 | "debug": "2.6.9",
1404 | "depd": "2.0.0",
1405 | "destroy": "1.2.0",
1406 | "encodeurl": "~1.0.2",
1407 | "escape-html": "~1.0.3",
1408 | "etag": "~1.8.1",
1409 | "fresh": "0.5.2",
1410 | "http-errors": "2.0.0",
1411 | "mime": "1.6.0",
1412 | "ms": "2.1.3",
1413 | "on-finished": "2.4.1",
1414 | "range-parser": "~1.2.1",
1415 | "statuses": "2.0.1"
1416 | },
1417 | "engines": {
1418 | "node": ">= 0.8.0"
1419 | }
1420 | },
1421 | "node_modules/send/node_modules/encodeurl": {
1422 | "version": "1.0.2",
1423 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
1424 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
1425 | "license": "MIT",
1426 | "engines": {
1427 | "node": ">= 0.8"
1428 | }
1429 | },
1430 | "node_modules/send/node_modules/ms": {
1431 | "version": "2.1.3",
1432 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1433 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
1434 | "license": "MIT"
1435 | },
1436 | "node_modules/serve-static": {
1437 | "version": "1.16.2",
1438 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
1439 | "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
1440 | "license": "MIT",
1441 | "dependencies": {
1442 | "encodeurl": "~2.0.0",
1443 | "escape-html": "~1.0.3",
1444 | "parseurl": "~1.3.3",
1445 | "send": "0.19.0"
1446 | },
1447 | "engines": {
1448 | "node": ">= 0.8.0"
1449 | }
1450 | },
1451 | "node_modules/setprototypeof": {
1452 | "version": "1.2.0",
1453 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
1454 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
1455 | "license": "ISC"
1456 | },
1457 | "node_modules/side-channel": {
1458 | "version": "1.1.0",
1459 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
1460 | "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
1461 | "license": "MIT",
1462 | "dependencies": {
1463 | "es-errors": "^1.3.0",
1464 | "object-inspect": "^1.13.3",
1465 | "side-channel-list": "^1.0.0",
1466 | "side-channel-map": "^1.0.1",
1467 | "side-channel-weakmap": "^1.0.2"
1468 | },
1469 | "engines": {
1470 | "node": ">= 0.4"
1471 | },
1472 | "funding": {
1473 | "url": "https://github.com/sponsors/ljharb"
1474 | }
1475 | },
1476 | "node_modules/side-channel-list": {
1477 | "version": "1.0.0",
1478 | "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
1479 | "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
1480 | "license": "MIT",
1481 | "dependencies": {
1482 | "es-errors": "^1.3.0",
1483 | "object-inspect": "^1.13.3"
1484 | },
1485 | "engines": {
1486 | "node": ">= 0.4"
1487 | },
1488 | "funding": {
1489 | "url": "https://github.com/sponsors/ljharb"
1490 | }
1491 | },
1492 | "node_modules/side-channel-map": {
1493 | "version": "1.0.1",
1494 | "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
1495 | "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
1496 | "license": "MIT",
1497 | "dependencies": {
1498 | "call-bound": "^1.0.2",
1499 | "es-errors": "^1.3.0",
1500 | "get-intrinsic": "^1.2.5",
1501 | "object-inspect": "^1.13.3"
1502 | },
1503 | "engines": {
1504 | "node": ">= 0.4"
1505 | },
1506 | "funding": {
1507 | "url": "https://github.com/sponsors/ljharb"
1508 | }
1509 | },
1510 | "node_modules/side-channel-weakmap": {
1511 | "version": "1.0.2",
1512 | "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
1513 | "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
1514 | "license": "MIT",
1515 | "dependencies": {
1516 | "call-bound": "^1.0.2",
1517 | "es-errors": "^1.3.0",
1518 | "get-intrinsic": "^1.2.5",
1519 | "object-inspect": "^1.13.3",
1520 | "side-channel-map": "^1.0.1"
1521 | },
1522 | "engines": {
1523 | "node": ">= 0.4"
1524 | },
1525 | "funding": {
1526 | "url": "https://github.com/sponsors/ljharb"
1527 | }
1528 | },
1529 | "node_modules/simple-update-notifier": {
1530 | "version": "2.0.0",
1531 | "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
1532 | "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
1533 | "dev": true,
1534 | "license": "MIT",
1535 | "dependencies": {
1536 | "semver": "^7.5.3"
1537 | },
1538 | "engines": {
1539 | "node": ">=10"
1540 | }
1541 | },
1542 | "node_modules/statuses": {
1543 | "version": "2.0.1",
1544 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
1545 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
1546 | "license": "MIT",
1547 | "engines": {
1548 | "node": ">= 0.8"
1549 | }
1550 | },
1551 | "node_modules/supports-color": {
1552 | "version": "5.5.0",
1553 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
1554 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
1555 | "dev": true,
1556 | "license": "MIT",
1557 | "dependencies": {
1558 | "has-flag": "^3.0.0"
1559 | },
1560 | "engines": {
1561 | "node": ">=4"
1562 | }
1563 | },
1564 | "node_modules/to-regex-range": {
1565 | "version": "5.0.1",
1566 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
1567 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
1568 | "dev": true,
1569 | "license": "MIT",
1570 | "dependencies": {
1571 | "is-number": "^7.0.0"
1572 | },
1573 | "engines": {
1574 | "node": ">=8.0"
1575 | }
1576 | },
1577 | "node_modules/toidentifier": {
1578 | "version": "1.0.1",
1579 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
1580 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
1581 | "license": "MIT",
1582 | "engines": {
1583 | "node": ">=0.6"
1584 | }
1585 | },
1586 | "node_modules/touch": {
1587 | "version": "3.1.1",
1588 | "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz",
1589 | "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==",
1590 | "dev": true,
1591 | "license": "ISC",
1592 | "bin": {
1593 | "nodetouch": "bin/nodetouch.js"
1594 | }
1595 | },
1596 | "node_modules/type-is": {
1597 | "version": "1.6.18",
1598 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
1599 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
1600 | "license": "MIT",
1601 | "dependencies": {
1602 | "media-typer": "0.3.0",
1603 | "mime-types": "~2.1.24"
1604 | },
1605 | "engines": {
1606 | "node": ">= 0.6"
1607 | }
1608 | },
1609 | "node_modules/typeid-js": {
1610 | "version": "1.1.0",
1611 | "resolved": "https://registry.npmjs.org/typeid-js/-/typeid-js-1.1.0.tgz",
1612 | "integrity": "sha512-SS0GEC0/0OJ06IvGEbqc6dVa5WwQN4mDq2UG9x2D7f+CD2X/b+p2xfEPNli0DO23lmeVl45bFt/bAOQa0GB4fg==",
1613 | "license": "Apache-2.0",
1614 | "dependencies": {
1615 | "uuid": "^10.0.0"
1616 | }
1617 | },
1618 | "node_modules/undefsafe": {
1619 | "version": "2.0.5",
1620 | "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
1621 | "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
1622 | "dev": true,
1623 | "license": "MIT"
1624 | },
1625 | "node_modules/undici": {
1626 | "version": "5.28.5",
1627 | "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.5.tgz",
1628 | "integrity": "sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA==",
1629 | "license": "MIT",
1630 | "dependencies": {
1631 | "@fastify/busboy": "^2.0.0"
1632 | },
1633 | "engines": {
1634 | "node": ">=14.0"
1635 | }
1636 | },
1637 | "node_modules/undici-types": {
1638 | "version": "6.20.0",
1639 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
1640 | "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
1641 | "license": "MIT"
1642 | },
1643 | "node_modules/unpipe": {
1644 | "version": "1.0.0",
1645 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
1646 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
1647 | "license": "MIT",
1648 | "engines": {
1649 | "node": ">= 0.8"
1650 | }
1651 | },
1652 | "node_modules/utils-merge": {
1653 | "version": "1.0.1",
1654 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
1655 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
1656 | "license": "MIT",
1657 | "engines": {
1658 | "node": ">= 0.4.0"
1659 | }
1660 | },
1661 | "node_modules/uuid": {
1662 | "version": "10.0.0",
1663 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
1664 | "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
1665 | "funding": [
1666 | "https://github.com/sponsors/broofa",
1667 | "https://github.com/sponsors/ctavan"
1668 | ],
1669 | "license": "MIT",
1670 | "bin": {
1671 | "uuid": "dist/bin/uuid"
1672 | }
1673 | },
1674 | "node_modules/vary": {
1675 | "version": "1.1.2",
1676 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
1677 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
1678 | "license": "MIT",
1679 | "engines": {
1680 | "node": ">= 0.8"
1681 | }
1682 | }
1683 | }
1684 | }
1685 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pern-stack-tutorial",
3 | "version": "1.0.0",
4 | "main": "backend/server.js",
5 | "scripts": {
6 | "dev": "nodemon backend/server.js",
7 | "build": "npm install && npm install --prefix frontend && npm run build --prefix frontend",
8 | "start": "node backend/server.js"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "type": "module",
13 | "license": "ISC",
14 | "description": "",
15 | "dependencies": {
16 | "@arcjet/node": "^1.0.0-beta.1",
17 | "@neondatabase/serverless": "^0.10.4",
18 | "cors": "^2.8.5",
19 | "dotenv": "^16.4.7",
20 | "express": "^4.21.2",
21 | "helmet": "^8.0.0",
22 | "morgan": "^1.10.0"
23 | },
24 | "devDependencies": {
25 | "nodemon": "^3.1.9"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------