├── .gitignore
├── server
├── .gitignore
├── models
│ └── dbModel.js
├── package.json
├── controllers
│ ├── sessionController.js
│ ├── userController.js
│ └── eventController.js
├── server.js
├── routes
│ └── api.js
└── package-lock.json
├── .DS_Store
├── docs
└── schema.jpg
├── client
├── src
│ ├── components
│ │ ├── logo.gif
│ │ ├── UserContext.js
│ │ ├── Login.jsx
│ │ ├── App.jsx
│ │ ├── MarkerCreator.jsx
│ │ ├── MarkerUpdator.jsx
│ │ └── Map.jsx
│ ├── main.jsx
│ └── stylesheets
│ │ ├── index.css
│ │ └── App.css
├── vite.config.js
├── .gitignore
├── package.json
└── index.html
├── .eslintrc.js
├── package.json
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
--------------------------------------------------------------------------------
/server/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | build/
3 | coverage/
4 | .env
--------------------------------------------------------------------------------
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/poppin-events/Poppin-Events/HEAD/.DS_Store
--------------------------------------------------------------------------------
/docs/schema.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/poppin-events/Poppin-Events/HEAD/docs/schema.jpg
--------------------------------------------------------------------------------
/client/src/components/logo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/poppin-events/Poppin-Events/HEAD/client/src/components/logo.gif
--------------------------------------------------------------------------------
/client/src/components/UserContext.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const UserContext = React.createContext(null);
4 |
5 | export { UserContext };
--------------------------------------------------------------------------------
/client/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | server: {
7 | port: 3000,
8 | proxy: {
9 | '/api': 'http://localhost:5000/',
10 | },
11 | },
12 | plugins: [react()],
13 | });
14 |
--------------------------------------------------------------------------------
/client/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './stylesheets/index.css';
4 | import App from './components/App';
5 | import { BrowserRouter } from "react-router-dom";
6 |
7 | ReactDOM.render(
8 |
9 |
10 | ,
11 | document.getElementById('root'),
12 | );
13 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | es2021: true,
5 | },
6 | extends: [
7 | 'plugin:react/recommended',
8 | 'airbnb',
9 | ],
10 | overrides: [
11 | ],
12 | parserOptions: {
13 | ecmaVersion: 'latest',
14 | sourceType: 'module',
15 | },
16 | plugins: [
17 | 'react',
18 | ],
19 | rules: {
20 | },
21 | };
22 |
--------------------------------------------------------------------------------
/client/.gitignore:
--------------------------------------------------------------------------------
1 | .env
2 |
3 | # Logs
4 | logs
5 | *.log
6 | npm-debug.log*
7 | yarn-debug.log*
8 | yarn-error.log*
9 | pnpm-debug.log*
10 | lerna-debug.log*
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 |
--------------------------------------------------------------------------------
/client/src/stylesheets/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "concurrently": "^7.6.0"
4 | },
5 | "scripts": {
6 | "start": "concurrently \"cd ./server && npm run server\" \"cd ./client && npm run dev\""
7 | },
8 | "devDependencies": {
9 | "eslint": "^8.35.0",
10 | "eslint-config-airbnb": "^19.0.4",
11 | "eslint-plugin-import": "^2.27.5",
12 | "eslint-plugin-jsx-a11y": "^6.7.1",
13 | "eslint-plugin-react": "^7.32.2",
14 | "eslint-plugin-react-hooks": "^4.6.0",
15 | "nodemon": "^2.0.21"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/server/models/dbModel.js:
--------------------------------------------------------------------------------
1 | const { Pool } = require('pg');
2 | require('dotenv').config();
3 |
4 | // put your PG_URI here
5 | const PG_URI = process.env.PG_URI;
6 | // 'postgres://leekwldv:85zQjHG1-BdoK3wOIfWOGl63DL85OW9c@mahmud.db.elephantsql.com/leekwldv'
7 | // create new pool
8 | const pool = new Pool({
9 | connectionString: PG_URI,
10 | });
11 |
12 | module.exports = {
13 | query: (text, params, callback) => {
14 | console.log('executing query', text, 'with params: ', params);
15 | return pool.query(text, params, callback);
16 | },
17 | };
18 |
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "server.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "node server.js",
9 | "server": "nodemon server.js"
10 | },
11 | "keywords": [],
12 | "author": "",
13 | "license": "ISC",
14 | "dependencies": {
15 | "cookie-parser": "^1.4.6",
16 | "express": "^4.18.2",
17 | "express-session": "^1.17.3",
18 | "google-auth-library": "^8.7.0",
19 | "jwt-decode": "^3.1.2",
20 | "pg": "^8.9.0"
21 | },
22 | "devDependencies": {
23 | "dotenv": "^16.0.3"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vite-project",
3 | "private": true,
4 | "version": "0.0.0",
5 | "scripts": {
6 | "dev": "vite",
7 | "build": "vite build",
8 | "preview": "vite preview"
9 | },
10 | "dependencies": {
11 | "@react-google-maps/api": "^2.18.1",
12 | "@react-oauth/google": "^0.8.0",
13 | "axios": "^1.3.4",
14 | "jwt-decode": "^3.1.2",
15 | "react": "^18.2.0",
16 | "react-dom": "^18.2.0",
17 | "react-dotenv": "^0.1.3",
18 | "react-google-autocomplete": "^2.7.3",
19 | "react-icons": "^4.8.0",
20 | "react-router-dom": "^6.8.2",
21 | "react-scripts": "^5.0.1"
22 | },
23 | "devDependencies": {
24 | "@vitejs/plugin-react": "^3.1.0",
25 | "vite": "^4.1.4"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/server/controllers/sessionController.js:
--------------------------------------------------------------------------------
1 | const sessionController = {};
2 |
3 | sessionController.validateSession = (req, res, next) => {
4 | console.log('validating session...');
5 | console.log('req.session is currently: ', req.session);
6 | if (req.session.loggedIn) {
7 | res.locals = {
8 | name: req.session.name,
9 | email: req.session.email,
10 | picture: req.session.picture,
11 | loggedIn: req.session.loggedIn,
12 | id: req.session.userID,
13 | };
14 | } else {
15 | res.locals.loggedIn = false;
16 | }
17 | next();
18 | };
19 | sessionController.deleteSession = (req, res, next) => {
20 | console.log('deleting session...');
21 | req.session.destroy(e => console.log('LOLLLLLLLLLLLLLLLLLLLLLL', e));
22 | next();
23 | };
24 |
25 | module.exports = sessionController;
26 |
--------------------------------------------------------------------------------
/client/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
12 |
13 | Vite App
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/server/server.js:
--------------------------------------------------------------------------------
1 | // require in dependencies
2 | const path = require('path');
3 | const express = require('express');
4 | const session = require('express-session');
5 | const apiRouter = require('./routes/api');
6 |
7 | // require in routes
8 |
9 | const PORT = 5000;
10 | const app = express();
11 |
12 | // request parsing (if needed)
13 | app.use(express.json());
14 | app.use(express.urlencoded({ extended: true }));
15 | app.use(session({
16 | secret: 'af168f987f1gh78fhg91f',
17 | name: 'ssid',
18 | saveUninitialized: false,
19 | }));
20 | // handle requests for static files
21 | app.use(express.static(path.resolve(__dirname, '../dist')));
22 | // route handlers
23 |
24 | app.use('/api', apiRouter);
25 |
26 | // catch-all 404 route handler
27 | app.use((_, res) => res.status(404).send('Page Not Found'));
28 |
29 | // error handler
30 | app.use((err, req, res, next) => {
31 | const defaultErr = {
32 | log: 'Express error handler caught unknown middleware error',
33 | status: 500,
34 | message: { err: 'An error occurred' },
35 | };
36 | const errorObj = { ...defaultErr, ...err };
37 | console.log(errorObj.log);
38 | return res.status(errorObj.status).json(errorObj.message).redirect('/');
39 | });
40 |
41 | // start server
42 | app.listen(PORT, () => console.log(`start listening on port : ${PORT}`));
43 |
44 | module.exports = app;
45 |
--------------------------------------------------------------------------------
/server/routes/api.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const userController = require('../controllers/userController');
3 | const eventController = require('../controllers/eventController');
4 | const sessionController = require('../controllers/sessionController');
5 |
6 | const router = express.Router();
7 |
8 | // Responds with user info (location + events) when passed in the correct google id
9 | // router.get(
10 | // '/user/:id',
11 | // userController.getUser,
12 | // (req, res) => res.status(200).json(res.locals.user),
13 | // );
14 |
15 | // Log in or a new user in the database
16 | router.post(
17 | '/users',
18 | userController.login,
19 | (req, res) => res.status(200).json(res.locals.id),
20 | );
21 |
22 | // Responds with all events in the database (Name, Location, Date, Description, Created By)
23 | router.get(
24 | '/events',
25 | eventController.getEvents,
26 | (req, res) => res.status(200).json(res.locals.events),
27 | );
28 |
29 | // Create an event in the database
30 | router.post(
31 | '/events',
32 | eventController.createEvent,
33 | (req, res) => res.status(200).json(res.locals.id),
34 | );
35 |
36 | // Update an event in the database
37 | router.put(
38 | '/events',
39 | eventController.updateEvent,
40 | (req, res) => res.sendStatus(200),
41 | );
42 |
43 | // Delete an event in the database
44 | router.delete(
45 | '/events',
46 | eventController.deleteEvent,
47 | (req, res) => res.sendStatus(200),
48 | );
49 |
50 | // Checks for active sessions
51 | router.get(
52 | '/sessions',
53 | sessionController.validateSession,
54 | (req, res) => res.status(200).send(res.locals),
55 | );
56 | router.delete(
57 | '/sessions',
58 | sessionController.deleteSession,
59 | (req, res) => res.sendStatus(200),
60 | );
61 |
62 | module.exports = router;
63 |
--------------------------------------------------------------------------------
/client/src/components/Login.jsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/function-component-definition */
2 | import React, {useState, useContext} from 'react';
3 | import { useNavigate } from 'react-router-dom';
4 | import { FcGoogle } from 'react-icons/fc';
5 | import { GoogleOAuthProvider } from '@react-oauth/google';
6 | import { GoogleLogin } from '@react-oauth/google';
7 | import jwt_decode from "jwt-decode";
8 | import env from 'react-dotenv';
9 | import { UserContext } from './UserContext';
10 | import axios from 'axios';
11 |
12 | const Login = (props) => {
13 | const navigate = useNavigate();
14 |
15 | const responseGoogle = async (response) => {
16 | // the google oauth (identity services) api responds with a JWT with all user info
17 | const userObject = jwt_decode(response.credential);
18 | // destructure that info for our purposes
19 | const { name, email, picture } = userObject;
20 | try {
21 | const res = await axios.post('/api/users', {
22 | name, email, picture,
23 | });
24 | // reroute to map
25 | if (res.status === 200){
26 | props.setUser({ name, email, picture, id: res.data});
27 | navigate('/map');
28 | }
29 | } catch (e) {
30 | console.log('error in post: ', e.message);
31 | }
32 | };
33 |
34 | return (
35 |
36 |
37 | (
39 |
45 | Sign in with google
46 |
47 | )}
48 | onSuccess={responseGoogle}
49 | onFailure={responseGoogle}
50 | cookiePolicy="single_host_origin"
51 | size="medium"
52 | />
53 |
54 |
55 | )
56 | }
57 |
58 | export default Login
--------------------------------------------------------------------------------
/server/controllers/userController.js:
--------------------------------------------------------------------------------
1 | // import the user model
2 | const jwt_decode = require('jwt-decode'); // ended up only using jwt_decode on front end
3 | const db = require('../models/dbModel');
4 |
5 | const userController = {};
6 |
7 | // get user info from database
8 | userController.login = async (req, res, next) => {
9 | try {
10 | // console.log('req body in userController.login', req.body);
11 | const { name, email, picture } = req.body;
12 | const query = `SELECT * FROM users WHERE email = '${email}'`;
13 | const user = await db.query(query);
14 | // if the user does not exist in the database, create them
15 | const userVals = [name, email, picture];
16 | if (user.rows.length) res.locals.id = user.rows[0].id;
17 | if (user.rows.length === 0) {
18 | const createUsr = 'INSERT INTO users (name, email, picture) VALUES ($1, $2, $3) RETURNING id';
19 | const newUser = await db.query(createUsr, userVals);
20 | res.locals.id = newUser.rows[0];
21 | } else if (user.rows[0].picture !== picture) {
22 | const updatePic = 'UPDATE users SET picture = $2 WHERE email = $1;';
23 | await db.query(updatePic, userVals.slice(1));
24 | }
25 | req.session.loggedIn = true;
26 | req.session.email = email;
27 | req.session.name = name;
28 | req.session.picture = picture;
29 | req.session.userID = res.locals.id;
30 | return next();
31 | } catch (e) {
32 | return next({
33 | log: 'Error in userController.login',
34 | status: 500,
35 | message: { error: e.message },
36 | });
37 | }
38 | };
39 |
40 | // get user info from database
41 | userController.getUser = async (req, res, next) => {
42 | const { email } = req.params;
43 | const query = `SELECT * FROM users WHERE email = ${email}`;
44 | const value = [id];
45 | const user = await db.query(query, value);
46 | // use array destructuring to get the first element of the array
47 | [res.locals.user] = user.rows;
48 | return next();
49 | };
50 |
51 | // create a new user in the database
52 | // userController.createUser = async (req, res, next) => {
53 |
54 | // }
55 |
56 | module.exports = userController;
57 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Poppin-Events
2 | An app that allows users to view events in their area, create new events, and edit/delete their own events from the map
3 |
4 | # To future archaeologists:
5 | We used Vite! Enjoy (here is the starter video for structure: https://www.youtube.com/watch?v=PPjpHaLkV7A)
6 |
7 | Note: You'll have to npm i for the root directory, the client, and the server separately
8 |
9 | Run 'npm start' in root directory to concurrently run server(5000) and client(3000)
10 |
11 | Npm install your frontend dependencies in client folder, backend dependencies in server folder
12 |
13 | If your server is running on 3001, it won't work, so shut it down and killall node before starting again
14 |
15 | Some important considerations - things you'll have to create and set up for yourself:
16 |
17 | 1 - GoogleAPI key for OAuth and Maps (make a .env file in the client directory with key value pairs that correspond with constants that are imported like import.meta.env.VITE_GOOGLE_OATH_CLIENT_ID or import.meta.env.VITE_GOOGLE_OATH_CLIENT_ID)
18 |
19 | - You will need to sign up via Google Cloud Platform to create your own application to get your own Client ID and Secret for Oauth, and same thing for Google Maps
20 |
21 | 2 - SQL server URI (make a .env file in the server directory with PG_URI='')
22 |
23 | SQL server Schema:
24 | 
25 |
26 | Must swap in your own via .evs:
27 |
28 | server/.env => swap out PG_URI (call with "process.env.PG_URI")
29 |
30 | client/.env => swap out GOOGLE_API_KEY (call with "import.meta.env.VITE_GOOGLE_OATH_CLIENT_ID")
31 |
32 | client/.env => swap out GOOGLE_OATH_CLIENT_ID (call with "import.meta.env.VITE_GOOGLE_OATH_CLIENT_ID")
33 |
34 | Unimplemented features with frameworks:
35 |
36 | 1 - display pictures next to the organizer's name in the event display: Currently, we get, store, and update user picture urls, but they are unused
37 |
38 | 2 - have an RSVP button in event display instead of organizer email that creates a new entry on the attendees table in the SQL database, linking the user to the respective event. We have a schema for using Attendees as an association table, but it is unused currently. Maybe also add an attendee list to the event info panel from there.
39 |
40 | Possible Refactors:
41 |
42 | 1 - Possibly display event boxes on map if you want, instead of to the side
43 |
44 | 2 - Refactor use of .id for both events and users to be userID / eventID on the front-end
45 |
46 | Bugs to squash:
47 |
48 | 1 - Newly created events don't have their time populate correctly on edit without a full page reload - likely, the date isn't being saved in state in a way the datetime-local input wants to receive
49 |
--------------------------------------------------------------------------------
/client/src/components/App.jsx:
--------------------------------------------------------------------------------
1 | import '../stylesheets/App.css';
2 | import axios from 'axios';
3 | import Map from './Map';
4 | import React, { useState, useEffect } from 'react';
5 | import jwt_decode from 'jwt-decode';
6 | import Login from './Login';
7 | import { UserContext } from './UserContext';
8 | import {Routes, Route, useNavigate} from 'react-router-dom';
9 |
10 |
11 | function App(props) {
12 | const navigate = useNavigate();
13 | const [user, setUser] = useState(null);
14 | console.log('in APP, user is: ', user);
15 |
16 | useEffect(() => {
17 | console.log('in useEffect, and user is: ', user);
18 | const checkSession = async () => {
19 | try {
20 | const userInfo = await axios.get('/api/sessions');
21 | console.log('user info is: ', userInfo);
22 | if (userInfo.data.loggedIn === true) {
23 | setUser({
24 | name: userInfo.data.name,
25 | email: userInfo.data.email,
26 | picture: userInfo.data.picture,
27 | // for later: refactor to be userID instead of id
28 | id: userInfo.data.id,
29 | });
30 | navigate('/map');
31 | }
32 | else navigate('login');
33 | } catch (e) {
34 | console.log('Error in checkSession: ', e.message);
35 | }
36 | };
37 | if (user === null) {
38 | console.log('user is null');
39 | checkSession();
40 | }
41 | else {
42 | console.log('user is not null');
43 | navigate('/map');
44 | }
45 | }, [user]);
46 |
47 | const logout = async () => {
48 | // make server request to logout / destroy session + cookie
49 | try {
50 | const response = await axios.delete('/api/sessions');
51 | console.log('successful logout');
52 | setUser(null);
53 | } catch (e) {
54 | console.log('error logging out: ', e.message);
55 | }
56 | }
57 |
58 |
59 | return (
60 | <>
61 |
62 |
63 | Poppin
64 |
65 |
66 | {user &&
67 |
68 | Logout
69 | {user.name}
70 |
71 | }
72 |
73 |
74 |
75 |
76 | setUser(u)}
79 | setUserJWT={(jwt) => setUserJWT(jwt)}>}
80 | />
81 | you are on path= /}
84 | />
85 | }
88 | />
89 |
90 |
91 | >
92 | );
93 | }
94 |
95 | export default App;
96 |
--------------------------------------------------------------------------------
/server/controllers/eventController.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable camelcase */
2 | // import the event model
3 | const db = require('../models/dbModel');
4 |
5 | const eventController = {};
6 |
7 | // get all events from database
8 | eventController.getEvents = async (req, res, next) => {
9 | try {
10 | // select event information, using jsonb_agg to create a json object out of lat and lng by declaring key/value pairs
11 | const query = await db.query('SELECT e.id, e.name, e.description, e.date, e.loc_name AS locName, e.address, jsonb_agg(json_build_object(\'lat\', e.lat, \'lng\', e.lng)) AS location, u.name AS organizer, u.email, u.picture FROM events e LEFT OUTER JOIN users u ON e.organizer_id = u.id group by e.id, u.name, u.email, u.picture');
12 | res.locals.events = query.rows;
13 | // query shape: {something: x, rows:[{data}, {data2}], blah: y, ....}
14 | return next();
15 | } catch (error) {
16 | return next({
17 | log: 'eventController.getEvents error',
18 | message: { err: 'Error getting events from database' },
19 | });
20 | }
21 | };
22 |
23 | // create a new event in the database
24 | eventController.createEvent = async (req, res, next) => {
25 | try {
26 | console.log('in event creator with req: ', req.body);
27 | const { name, description, date, locName, address, userID } = req.body;
28 | const { lat, lng } = req.body.location[0];
29 | // insert the event into the database using a subquery for the organizer id
30 | const addEventQuery = 'INSERT INTO events (name, description, date, loc_name, address, lat, lng, organizer_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id';
31 | const newEventVals = [name, description, date, locName, address, lat, lng, userID];
32 | const newEvent = await db.query(addEventQuery, newEventVals);
33 | // **note - that rows[0] will actually be an OBJECT containing {id: } ** !
34 | res.locals.id = newEvent.rows[0];
35 | return next();
36 | } catch (error) {
37 | return next({
38 | log: 'eventController.createEvent error',
39 | message: { err: 'Error creating event in database' },
40 | });
41 | }
42 | };
43 |
44 | // update an event in the database
45 | eventController.updateEvent = async (req, res, next) => {
46 | const {
47 | name, description, date, locName, address, userID, eventID,
48 | } = req.body;
49 | const { lat, lng } = req.body.location[0];
50 | const values = [name, description, date, locName, address, lat, lng, userID, eventID];
51 | const text = 'UPDATE events SET name = $1, description = $2, date = $3, loc_name = $4, address = $5, lat = $6, lng = $7 WHERE organizer_id = $8 AND id = $9;';
52 | try {
53 | await db.query(text, values);
54 | return next();
55 | } catch (error) {
56 | return next({
57 | log: 'eventController.updateEvent error',
58 | message: { err: 'Error updating event in database' },
59 | });
60 | }
61 | };
62 |
63 | // delete an event from the database
64 | eventController.deleteEvent = async (req, res, next) => {
65 | const { eventID, userID } = req.body.deleteReq;
66 | const values = [eventID, userID];
67 | const text = 'DELETE FROM events WHERE id = $1 AND organizer_id = $2';
68 | try {
69 | await db.query(text, values);
70 | return next();
71 | } catch (error) {
72 | return next({
73 | log: 'eventController.deleteEvent error',
74 | message: { err: 'Error deleting event from database' },
75 | });
76 | }
77 | };
78 |
79 | module.exports = eventController;
80 |
--------------------------------------------------------------------------------
/client/src/components/MarkerCreator.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useContext, useRef} from 'react';
2 | import axios from 'axios';
3 | import { useJsApiLoader } from '@react-google-maps/api';
4 | import { UserContext } from './UserContext';
5 | import Autocomplete from 'react-google-autocomplete';
6 |
7 | const libraries = ['places'];
8 | export default function MarkerCreator(props) {
9 | const { isLoaded } = useJsApiLoader({
10 | googleMapsApiKey: import.meta.env.VITE_GOOGLE_MAPS_API_KEY,
11 | libraries,
12 | });
13 |
14 | const { user } = useContext(UserContext);
15 | // state for controlled inputs
16 | const [name, setName] = useState('');
17 | const [address, setAddress] = useState('');
18 | const [date, setDate] = useState('');
19 | const [description, setDescription] = useState('');
20 | const [locName, setLocName] = useState('');
21 | let autocomplete = null;
22 |
23 | // submit handler
24 | const handleSubmit = async (e) => {
25 | e.preventDefault();
26 | try {
27 | console.log('in MARKER CREATOR user is: ', user.id);
28 | const { id, email, name: username } = user;
29 | // new event object for database
30 | const event = {
31 | name,
32 | address,
33 | locName,
34 | date,
35 | description,
36 | userID: id,
37 | };
38 | // encode the address
39 | const encoded = address.replaceAll(' ', '+');
40 | // geocode the address (https://developers.google.com/maps/documentation/geocoding/requests-geocoding)
41 | const url = `https://maps.googleapis.com/maps/api/geocode/json?address=${encoded}&key=${import.meta.env.VITE_GOOGLE_MAPS_API_KEY}`;
42 | const response = await axios.get(url);
43 | const data = response.data.results[0];
44 | event.location = [{
45 | lat: data.geometry.location.lat,
46 | lng: data.geometry.location.lng,
47 | }];
48 | // send the post request to the server
49 | const eventID = await axios.post('/api/events', event);
50 | // add other pairs to the event object for the front-end to read
51 | event.id = eventID.data.id;
52 | event.email = email;
53 | event.organizer = username;
54 | // add the new event into state (from parent component) to rerender the map + markers
55 | props.setMarkerData(prevMarkerData => {
56 | return [...prevMarkerData, event];
57 | });
58 | } catch (err) {
59 | console.log('error in post: ', err.message);
60 | }
61 | };
62 |
63 | // autocomplete onLoad
64 | function onLoad(ac) {
65 | console.log('here in ONLOAD, ac is: ', ac);
66 | autocomplete = ac;
67 | }
68 |
69 | // autocomplete change handler
70 | function handleChange() {
71 | console.log('autocomplete is currently: ', autocomplete);
72 | if(autocomplete !== null) console.log('autocomplete place is: ', autocomplete.getPlace());
73 | }
74 |
75 | // component imported from @react-google-maps/api to have autocomplete address
76 | return (
77 |
78 |
Create an Event
79 |
109 |
110 | );
111 | }
112 |
--------------------------------------------------------------------------------
/client/src/stylesheets/App.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | box-sizing: border-box;
5 | font-family: 'Poppins', sans-serif;
6 | padding-top: 80px;
7 | background-image: url('https://img.freepik.com/free-vector/hand-painted-watercolor-pastel-sky-background_23-2148902028.jpg?w=1800&t=st=1678152616~exp=1678153216~hmac=74aded69431c66906454ac1c5b72d32320d4b96356014ce1c1940c26d3ac63e0');
8 | background-size: cover;
9 | }
10 |
11 | button, input {
12 | font-family: 'Poppins', sans-serif;
13 | }
14 |
15 | button {
16 | cursor: pointer;
17 | }
18 | .edit-button, .delete-button {
19 | width: 100px;
20 | height: 30px;
21 | }
22 | .edit-button {
23 | margin-right: 10px;
24 | }
25 | #root {
26 | width: 100%;
27 | display: flex;
28 | flex-direction: column;
29 | align-items: center;
30 | }
31 |
32 | * {
33 | /* outline: 1px solid lime; */
34 | }
35 |
36 | .info-list {
37 | list-style: none;
38 | }
39 |
40 | .info-container {
41 | /* outline: 2px solid black; */
42 | display: flex;
43 | flex-direction: column;
44 | align-items: center;
45 | height: min-content;
46 | text-align: center;
47 | padding: 20px;
48 | min-width: 300px;
49 | margin-top: 20px;
50 | }
51 | .event-title {
52 | margin-top: -4px;
53 | }
54 | .event-description {
55 | font-style: italic;
56 | margin: -3px 0;
57 | }
58 | .map-container {
59 |
60 | min-width: 700px;
61 | min-height: 700px;
62 | margin: 0 30px;
63 | }
64 |
65 | .box-shadow-1 {
66 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), -1px 6px 9px rgba(0, 0, 0, 0.24);
67 | transition: all 0.3s cubic-bezier(.25, .8, .25, 1);
68 | }
69 |
70 | .box-shadow-1:hover {
71 | box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22);
72 | }
73 |
74 | .create-event-container {
75 | text-align: center;
76 | /* outline: 1px solid gray; */
77 | height: min-content;
78 | padding: 20px;
79 | padding-top: 10px;
80 | border-radius: 5px;
81 | width: 300px;
82 |
83 | }
84 |
85 | .button-primary {
86 | padding: 12px 0;
87 | width: 50%;
88 | margin-top: 5px;
89 | }
90 |
91 | .create-form {
92 | display: flex;
93 | flex-direction: column;
94 | /* width: 300px; */
95 | /* outline: 1px solid gray; */
96 | align-items: center;
97 | }
98 |
99 | .create-form input {
100 | border: none;
101 | outline: none;
102 | border-bottom: 1px solid rgb(126, 126, 126);
103 | text-align: center;
104 | margin-bottom: 18px;
105 | font-size: 15px;
106 | width: 85%;
107 | background: none;
108 | }
109 |
110 |
111 |
112 | .brand-container {
113 | display: flex;
114 | justify-content: center;
115 | align-items: center;
116 | }
117 |
118 | .brand-logo {
119 | width: 40px;
120 | }
121 |
122 | /* .autocomplete-container {
123 | display: inline-block;
124 | width: 100%;
125 | } */
126 | .autocomplete-container {
127 | /* outline: 2px solid red; */
128 | /* background: blue; */
129 | width: 100%;
130 | }
131 | .autocomplete-container .autocomplete-input {
132 | /* background: red; */
133 | /* width: 98%;
134 | display: inline-block; */
135 |
136 | }
137 |
138 |
139 |
140 | .screen-reader-text {
141 | border: 0;
142 | clip: rect(1px, 1px, 1px, 1px);
143 | clip-path: inset(50%);
144 | height: 1px;
145 | margin: -1px;
146 | width: 1px;
147 | overflow: hidden;
148 | position: absolute !important;
149 | word-wrap: normal !important;
150 | }
151 |
152 |
153 |
154 | .navbar {
155 | background: rgb(234, 248, 255);
156 | opacity: .5;
157 | position: fixed;
158 | top: 0;
159 | width: 100%;
160 | display: flex;
161 | justify-content: space-between;
162 | align-items: center;
163 | z-index: 200;
164 | height: 60px;
165 | }
166 |
167 | .nav-list {
168 | display: flex;
169 | list-style: none;
170 | }
171 | .nav-list li{
172 | margin-right: 20px;
173 | }
174 |
175 | a {
176 | cursor: pointer;
177 | }
178 |
179 | a:hover {
180 | text-decoration: underline;
181 | }
182 |
183 | .brand-heading {
184 | margin-left: 20px;
185 | display: inline-block;
186 | font-style: italic;
187 | /* text-decoration: underline; */
188 | font-weight: 800;
189 | font-size: 28px;
190 | color: rgb(136, 54, 244);
191 | }
192 |
193 | .map-section {
194 | /* outline: 2px solid gold; */
195 | display: flex;
196 | width: 80%;
197 |
198 | }
199 |
200 |
201 |
202 | .current-location-button {
203 | position: absolute;
204 | top: 9px;
205 | left: 50%;
206 | transform: translateX(-50%);
207 | padding: 10px;
208 | }
209 |
210 | button {
211 | background: none;
212 | border-radius: 3px;
213 | border: 1px solid rgb(126, 126, 126);
214 | transition: .15s ease;
215 | }
216 | button:hover {
217 | background: rgba(246, 255, 238, 0.853);
218 | }
219 |
220 | .delete-button:hover, .cancel-button:hover{
221 | background: rgba(255, 209, 220, 0.453);
222 | }
--------------------------------------------------------------------------------
/client/src/components/MarkerUpdator.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useContext } from 'react';
2 | import axios from 'axios';
3 | import { useJsApiLoader } from '@react-google-maps/api';
4 | import { UserContext } from './UserContext';
5 | import Autocomplete from 'react-google-autocomplete';
6 |
7 | const libraries = ['places'];
8 | export default function MarkerUpdator(props) {
9 | const { isLoaded } = useJsApiLoader({
10 | googleMapsApiKey: import.meta.env.VITE_GOOGLE_MAPS_API_KEY,
11 | libraries,
12 | });
13 |
14 | const { user } = useContext(UserContext);
15 |
16 | const [name, setName] = useState(props.eventData.name);
17 | const [address, setAddress] = useState(props.eventData.address);
18 | const [date, setDate] = useState(props.eventData.date.slice(0,props.eventData.date.length-8));
19 | const [description, setDescription] = useState(props.eventData.description);
20 | const [locName, setLocName] = useState(props.eventData.locName ? props.eventData.locName : props.eventData.locname);
21 | let autocomplete = null;
22 | console.log(props.eventData);
23 |
24 | // "2023-03-22T20:21:00.000Z" database format
25 | // 2023-03-22T20:21 date format
26 | // Wed, 22 Mar 2023 20:21:00 GMT
27 | // props.eventData.date.slice(0,props.eventData.date.length-8)
28 | // graveyard of broken souls
29 |
30 | // "023-03-22T20:21"
31 | // cancel handler
32 | const cancelHandler = () => {
33 | props.setUpdating(false);
34 | }
35 |
36 | // submit handler
37 | const handleSubmit = async (e) => {
38 | e.preventDefault();
39 | try {
40 | console.log('in MARKER CREATOR user is: ', user.id);
41 | const { id, email, name: username } = user;
42 | // event object for the database
43 | const event = {
44 | name,
45 | address,
46 | locName,
47 | date,
48 | description,
49 | userID: id,
50 | eventID: props.eventData.id
51 | };
52 | // encode the address and geocode it
53 | const encoded = address.replaceAll(' ', '+');
54 | const url = `https://maps.googleapis.com/maps/api/geocode/json?address=${encoded}&key=${import.meta.env.VITE_GOOGLE_MAPS_API_KEY}`;
55 | const response = await axios.get(url);
56 | const data = response.data.results[0];
57 | event.location = [{
58 | lat: data.geometry.location.lat,
59 | lng: data.geometry.location.lng,
60 | }];
61 | // send the update request to the database
62 | const eventID = await axios.put('/api/events', event);
63 | event.eventID = eventID.data;
64 | event.email = email;
65 | event.organizer = username;
66 | // replace the MarkerData in state with the updated array
67 | props.setMarkerData(prevMarkerData => {
68 | // remove the edited event
69 | // could make more performant with map instead of filter
70 | const updatedMarkers = prevMarkerData.filter(event => {
71 | return event.id !== event.eventID;
72 | });
73 | // spread in the filtered old events with the new event added in
74 | return [...updatedMarkers, event];
75 | });
76 | // update window closes and is replaced with add event
77 | props.setUpdating(false);
78 | //console.log('most recent marker is: ', markerData[markerData.length - 1]);
79 | // email from context and organizer from context
80 | // get event id to store in state
81 | } catch (err) {
82 | console.log('error in post: ', err.message);
83 | }
84 | };
85 |
86 | // autocomplete onLoad
87 | function onLoad(ac) {
88 | console.log('here in ONLOAD, ac is: ', ac);
89 | autocomplete = ac;
90 | }
91 |
92 | // autocomplete change handler
93 | function handleChange() {
94 | console.log('autocomplete is currently: ', autocomplete);
95 | if(autocomplete !== null) console.log('autocomplete place is: ', autocomplete.getPlace());
96 | }
97 |
98 | return (
99 |
100 |
Edit your Event
101 |
132 |
Cancel
133 |
134 | );
135 | }
136 |
--------------------------------------------------------------------------------
/client/src/components/Map.jsx:
--------------------------------------------------------------------------------
1 | import '../stylesheets/App.css';
2 | import React, { useState, useEffect, useContext} from 'react';
3 | import { GoogleMap, useJsApiLoader, MarkerF } from '@react-google-maps/api';
4 |
5 | import axios from 'axios';
6 | import MarkerCreator from './MarkerCreator';
7 | import MarkerUpdator from './MarkerUpdator';
8 | import { UserContext } from './UserContext';
9 |
10 | function Map() {
11 | // state for map center positioning
12 | const [mapPos, setMapPos] = useState({ lat: 0.37766641e2, lng: -0.123098308e3 });
13 |
14 | // state for the data for marker from the database
15 | const [markerData, setMarkerData] = useState([]);
16 |
17 | // state to display the event data to the page after clicking a marker
18 | const [eventData, setEventData] = useState(null);
19 |
20 | // get the userID from the context
21 | // userID is used to determine if the user is the creator of the event
22 | const { user } = useContext(UserContext);
23 | const [updating, setUpdating] = useState(false);
24 | // in-the-works refactor to clarify userID vs eventID from .id
25 | const userID = user === null ? null : user.id;
26 |
27 | // Load the script for google maps API
28 | const { isLoaded } = useJsApiLoader({
29 | googleMapsApiKey: import.meta.env.VITE_GOOGLE_MAPS_API_KEY,
30 | // we don't think this is actually used, but removing it breaks EVERYTHING?!
31 | libraries: ['places'],
32 | });
33 |
34 | // get all marker data from database on mount
35 | useEffect(() => {
36 | try {
37 | const getEvents = async () => {
38 | const response = await axios.get('/api/events');
39 | const { data } = response;
40 | setMarkerData(data);
41 | };
42 | getEvents();
43 | // get current user location and set the center of the map to that location
44 | if (navigator.geolocation) { // native browser geolocation functionality
45 | navigator.geolocation.getCurrentPosition(
46 | (position) => {
47 | const pos = {
48 | lat: position.coords.latitude,
49 | lng: position.coords.longitude,
50 | };
51 | // change map center positioning state
52 | setMapPos(pos);
53 | },
54 | );
55 | }
56 | } catch (e) {
57 | console.log('error in getEvents: ', e.message);
58 | }
59 | }, []);
60 |
61 | // change google map position to current user location on button click
62 | const currPosition = () => {
63 | if (navigator.geolocation) {
64 | navigator.geolocation.getCurrentPosition(
65 | (position) => {
66 | const pos = {
67 | lat: position.coords.latitude,
68 | lng: position.coords.longitude,
69 | };
70 | // change map center positioning state
71 | setMapPos(pos);
72 | },
73 | );
74 | }
75 | };
76 |
77 | // handle click on update button
78 | const handleUpdate = () => {
79 | setUpdating(true);
80 | };
81 |
82 | // handle click on delete button
83 | const handleDelete = async (eID, uID) => {
84 | // create the object for the db query on backend
85 | const deleteReq = {
86 | eventID: eID,
87 | userID: uID,
88 | };
89 | // send object to the server to delete the event
90 | const response = await axios.delete('/api/events/', { // yeah, not restful, oh well sowee >.<
91 | data: { deleteReq } });
92 | // filter the removed event from the marker data array
93 | setMarkerData(prevMarkerData => {
94 | return prevMarkerData.filter(event => {
95 | return event.id !== eID;
96 | });
97 | });
98 | setEventData(null);
99 | };
100 |
101 | // ensures that a div exists for the map even when the map API key is not loaded successfully. DO NOT DELETE
102 | if (!isLoaded) return Loading... 🥺
;
103 | // component imported from @react-google-maps/api used to render google maps
104 | // https://react-google-maps-api-docs.netlify.app/#googlemap
105 |
106 |
107 | // yes ... we know that this could be refactored into multiple components but .... time
108 | return (
109 |
110 |
115 | currPosition()}>Go to current location
116 | {/* If markerData is changed, places corresponding Markers in the map */}
117 | {/* component imported from @react-google-maps/api renders markers on the map */}
118 | {markerData.length > 0 && markerData.map((event) => (
119 | setEventData(event)}
124 | />
125 | ))}
126 |
127 | {/* If a Marker is being added, call MarkerCreator and if updated, call MarkerUpdator */}
128 |
129 | {!updating &&
}
130 | {updating
131 | && (
132 |
138 | )}
139 | {/* If eventData and user are not null, display the event data */}
140 | {
141 | eventData && user &&
142 | (
143 |
144 |
{eventData.name}
145 |
{eventData.description}
146 |
147 | Organizer: {eventData.organizer}
148 | Location: {eventData.address}
149 | Date: {(new Date(eventData.date)).toLocaleString()}
150 | RSVP: {eventData.email}
151 |
152 | {/* If the user is the creator of the event, display the edit and delete buttons */}
153 | {
154 | eventData.email === user.email && (
155 |
156 | Edit
157 | handleDelete(eventData.id, user.id)}> Delete
158 |
159 | )
160 | }
161 |
162 | )
163 | }
164 |
165 |
166 | );
167 | }
168 |
169 | export default Map;
170 |
--------------------------------------------------------------------------------
/server/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server",
3 | "version": "1.0.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "server",
9 | "version": "1.0.0",
10 | "license": "ISC",
11 | "dependencies": {
12 | "cookie-parser": "^1.4.6",
13 | "express": "^4.18.2",
14 | "express-session": "^1.17.3",
15 | "google-auth-library": "^8.7.0",
16 | "jwt-decode": "^3.1.2",
17 | "pg": "^8.9.0"
18 | },
19 | "devDependencies": {
20 | "dotenv": "^16.0.3"
21 | }
22 | },
23 | "node_modules/accepts": {
24 | "version": "1.3.8",
25 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
26 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
27 | "dependencies": {
28 | "mime-types": "~2.1.34",
29 | "negotiator": "0.6.3"
30 | },
31 | "engines": {
32 | "node": ">= 0.6"
33 | }
34 | },
35 | "node_modules/agent-base": {
36 | "version": "6.0.2",
37 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
38 | "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
39 | "dependencies": {
40 | "debug": "4"
41 | },
42 | "engines": {
43 | "node": ">= 6.0.0"
44 | }
45 | },
46 | "node_modules/agent-base/node_modules/debug": {
47 | "version": "4.3.4",
48 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
49 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
50 | "dependencies": {
51 | "ms": "2.1.2"
52 | },
53 | "engines": {
54 | "node": ">=6.0"
55 | },
56 | "peerDependenciesMeta": {
57 | "supports-color": {
58 | "optional": true
59 | }
60 | }
61 | },
62 | "node_modules/agent-base/node_modules/ms": {
63 | "version": "2.1.2",
64 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
65 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
66 | },
67 | "node_modules/array-flatten": {
68 | "version": "1.1.1",
69 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
70 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
71 | },
72 | "node_modules/arrify": {
73 | "version": "2.0.1",
74 | "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
75 | "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
76 | "engines": {
77 | "node": ">=8"
78 | }
79 | },
80 | "node_modules/base64-js": {
81 | "version": "1.5.1",
82 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
83 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
84 | "funding": [
85 | {
86 | "type": "github",
87 | "url": "https://github.com/sponsors/feross"
88 | },
89 | {
90 | "type": "patreon",
91 | "url": "https://www.patreon.com/feross"
92 | },
93 | {
94 | "type": "consulting",
95 | "url": "https://feross.org/support"
96 | }
97 | ]
98 | },
99 | "node_modules/bignumber.js": {
100 | "version": "9.1.1",
101 | "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz",
102 | "integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==",
103 | "engines": {
104 | "node": "*"
105 | }
106 | },
107 | "node_modules/body-parser": {
108 | "version": "1.20.1",
109 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
110 | "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
111 | "dependencies": {
112 | "bytes": "3.1.2",
113 | "content-type": "~1.0.4",
114 | "debug": "2.6.9",
115 | "depd": "2.0.0",
116 | "destroy": "1.2.0",
117 | "http-errors": "2.0.0",
118 | "iconv-lite": "0.4.24",
119 | "on-finished": "2.4.1",
120 | "qs": "6.11.0",
121 | "raw-body": "2.5.1",
122 | "type-is": "~1.6.18",
123 | "unpipe": "1.0.0"
124 | },
125 | "engines": {
126 | "node": ">= 0.8",
127 | "npm": "1.2.8000 || >= 1.4.16"
128 | }
129 | },
130 | "node_modules/buffer-equal-constant-time": {
131 | "version": "1.0.1",
132 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
133 | "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
134 | },
135 | "node_modules/buffer-writer": {
136 | "version": "2.0.0",
137 | "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
138 | "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==",
139 | "engines": {
140 | "node": ">=4"
141 | }
142 | },
143 | "node_modules/bytes": {
144 | "version": "3.1.2",
145 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
146 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
147 | "engines": {
148 | "node": ">= 0.8"
149 | }
150 | },
151 | "node_modules/call-bind": {
152 | "version": "1.0.2",
153 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
154 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
155 | "dependencies": {
156 | "function-bind": "^1.1.1",
157 | "get-intrinsic": "^1.0.2"
158 | },
159 | "funding": {
160 | "url": "https://github.com/sponsors/ljharb"
161 | }
162 | },
163 | "node_modules/content-disposition": {
164 | "version": "0.5.4",
165 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
166 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
167 | "dependencies": {
168 | "safe-buffer": "5.2.1"
169 | },
170 | "engines": {
171 | "node": ">= 0.6"
172 | }
173 | },
174 | "node_modules/content-type": {
175 | "version": "1.0.5",
176 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
177 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
178 | "engines": {
179 | "node": ">= 0.6"
180 | }
181 | },
182 | "node_modules/cookie": {
183 | "version": "0.5.0",
184 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
185 | "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
186 | "engines": {
187 | "node": ">= 0.6"
188 | }
189 | },
190 | "node_modules/cookie-parser": {
191 | "version": "1.4.6",
192 | "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz",
193 | "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==",
194 | "dependencies": {
195 | "cookie": "0.4.1",
196 | "cookie-signature": "1.0.6"
197 | },
198 | "engines": {
199 | "node": ">= 0.8.0"
200 | }
201 | },
202 | "node_modules/cookie-parser/node_modules/cookie": {
203 | "version": "0.4.1",
204 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
205 | "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==",
206 | "engines": {
207 | "node": ">= 0.6"
208 | }
209 | },
210 | "node_modules/cookie-signature": {
211 | "version": "1.0.6",
212 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
213 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
214 | },
215 | "node_modules/debug": {
216 | "version": "2.6.9",
217 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
218 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
219 | "dependencies": {
220 | "ms": "2.0.0"
221 | }
222 | },
223 | "node_modules/depd": {
224 | "version": "2.0.0",
225 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
226 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
227 | "engines": {
228 | "node": ">= 0.8"
229 | }
230 | },
231 | "node_modules/destroy": {
232 | "version": "1.2.0",
233 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
234 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
235 | "engines": {
236 | "node": ">= 0.8",
237 | "npm": "1.2.8000 || >= 1.4.16"
238 | }
239 | },
240 | "node_modules/dotenv": {
241 | "version": "16.0.3",
242 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
243 | "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==",
244 | "dev": true,
245 | "engines": {
246 | "node": ">=12"
247 | }
248 | },
249 | "node_modules/ecdsa-sig-formatter": {
250 | "version": "1.0.11",
251 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
252 | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
253 | "dependencies": {
254 | "safe-buffer": "^5.0.1"
255 | }
256 | },
257 | "node_modules/ee-first": {
258 | "version": "1.1.1",
259 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
260 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
261 | },
262 | "node_modules/encodeurl": {
263 | "version": "1.0.2",
264 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
265 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
266 | "engines": {
267 | "node": ">= 0.8"
268 | }
269 | },
270 | "node_modules/escape-html": {
271 | "version": "1.0.3",
272 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
273 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
274 | },
275 | "node_modules/etag": {
276 | "version": "1.8.1",
277 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
278 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
279 | "engines": {
280 | "node": ">= 0.6"
281 | }
282 | },
283 | "node_modules/express": {
284 | "version": "4.18.2",
285 | "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
286 | "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
287 | "dependencies": {
288 | "accepts": "~1.3.8",
289 | "array-flatten": "1.1.1",
290 | "body-parser": "1.20.1",
291 | "content-disposition": "0.5.4",
292 | "content-type": "~1.0.4",
293 | "cookie": "0.5.0",
294 | "cookie-signature": "1.0.6",
295 | "debug": "2.6.9",
296 | "depd": "2.0.0",
297 | "encodeurl": "~1.0.2",
298 | "escape-html": "~1.0.3",
299 | "etag": "~1.8.1",
300 | "finalhandler": "1.2.0",
301 | "fresh": "0.5.2",
302 | "http-errors": "2.0.0",
303 | "merge-descriptors": "1.0.1",
304 | "methods": "~1.1.2",
305 | "on-finished": "2.4.1",
306 | "parseurl": "~1.3.3",
307 | "path-to-regexp": "0.1.7",
308 | "proxy-addr": "~2.0.7",
309 | "qs": "6.11.0",
310 | "range-parser": "~1.2.1",
311 | "safe-buffer": "5.2.1",
312 | "send": "0.18.0",
313 | "serve-static": "1.15.0",
314 | "setprototypeof": "1.2.0",
315 | "statuses": "2.0.1",
316 | "type-is": "~1.6.18",
317 | "utils-merge": "1.0.1",
318 | "vary": "~1.1.2"
319 | },
320 | "engines": {
321 | "node": ">= 0.10.0"
322 | }
323 | },
324 | "node_modules/express-session": {
325 | "version": "1.17.3",
326 | "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz",
327 | "integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==",
328 | "dependencies": {
329 | "cookie": "0.4.2",
330 | "cookie-signature": "1.0.6",
331 | "debug": "2.6.9",
332 | "depd": "~2.0.0",
333 | "on-headers": "~1.0.2",
334 | "parseurl": "~1.3.3",
335 | "safe-buffer": "5.2.1",
336 | "uid-safe": "~2.1.5"
337 | },
338 | "engines": {
339 | "node": ">= 0.8.0"
340 | }
341 | },
342 | "node_modules/express-session/node_modules/cookie": {
343 | "version": "0.4.2",
344 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
345 | "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
346 | "engines": {
347 | "node": ">= 0.6"
348 | }
349 | },
350 | "node_modules/extend": {
351 | "version": "3.0.2",
352 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
353 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
354 | },
355 | "node_modules/fast-text-encoding": {
356 | "version": "1.0.6",
357 | "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz",
358 | "integrity": "sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w=="
359 | },
360 | "node_modules/finalhandler": {
361 | "version": "1.2.0",
362 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
363 | "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
364 | "dependencies": {
365 | "debug": "2.6.9",
366 | "encodeurl": "~1.0.2",
367 | "escape-html": "~1.0.3",
368 | "on-finished": "2.4.1",
369 | "parseurl": "~1.3.3",
370 | "statuses": "2.0.1",
371 | "unpipe": "~1.0.0"
372 | },
373 | "engines": {
374 | "node": ">= 0.8"
375 | }
376 | },
377 | "node_modules/forwarded": {
378 | "version": "0.2.0",
379 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
380 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
381 | "engines": {
382 | "node": ">= 0.6"
383 | }
384 | },
385 | "node_modules/fresh": {
386 | "version": "0.5.2",
387 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
388 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
389 | "engines": {
390 | "node": ">= 0.6"
391 | }
392 | },
393 | "node_modules/function-bind": {
394 | "version": "1.1.1",
395 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
396 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
397 | },
398 | "node_modules/gaxios": {
399 | "version": "5.0.2",
400 | "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.0.2.tgz",
401 | "integrity": "sha512-TjtV2AJOZoMQqRYoy5eM8cCQogYwazWNYLQ72QB0kwa6vHHruYkGmhhyrlzbmgNHK1dNnuP2WSH81urfzyN2Og==",
402 | "dependencies": {
403 | "extend": "^3.0.2",
404 | "https-proxy-agent": "^5.0.0",
405 | "is-stream": "^2.0.0",
406 | "node-fetch": "^2.6.7"
407 | },
408 | "engines": {
409 | "node": ">=12"
410 | }
411 | },
412 | "node_modules/gcp-metadata": {
413 | "version": "5.2.0",
414 | "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.2.0.tgz",
415 | "integrity": "sha512-aFhhvvNycky2QyhG+dcfEdHBF0FRbYcf39s6WNHUDysKSrbJ5vuFbjydxBcmewtXeV248GP8dWT3ByPNxsyHCw==",
416 | "dependencies": {
417 | "gaxios": "^5.0.0",
418 | "json-bigint": "^1.0.0"
419 | },
420 | "engines": {
421 | "node": ">=12"
422 | }
423 | },
424 | "node_modules/get-intrinsic": {
425 | "version": "1.2.0",
426 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
427 | "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
428 | "dependencies": {
429 | "function-bind": "^1.1.1",
430 | "has": "^1.0.3",
431 | "has-symbols": "^1.0.3"
432 | },
433 | "funding": {
434 | "url": "https://github.com/sponsors/ljharb"
435 | }
436 | },
437 | "node_modules/google-auth-library": {
438 | "version": "8.7.0",
439 | "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.7.0.tgz",
440 | "integrity": "sha512-1M0NG5VDIvJZEnstHbRdckLZESoJwguinwN8Dhae0j2ZKIQFIV63zxm6Fo6nM4xkgqUr2bbMtV5Dgo+Hy6oo0Q==",
441 | "dependencies": {
442 | "arrify": "^2.0.0",
443 | "base64-js": "^1.3.0",
444 | "ecdsa-sig-formatter": "^1.0.11",
445 | "fast-text-encoding": "^1.0.0",
446 | "gaxios": "^5.0.0",
447 | "gcp-metadata": "^5.0.0",
448 | "gtoken": "^6.1.0",
449 | "jws": "^4.0.0",
450 | "lru-cache": "^6.0.0"
451 | },
452 | "engines": {
453 | "node": ">=12"
454 | }
455 | },
456 | "node_modules/google-p12-pem": {
457 | "version": "4.0.1",
458 | "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-4.0.1.tgz",
459 | "integrity": "sha512-WPkN4yGtz05WZ5EhtlxNDWPhC4JIic6G8ePitwUWy4l+XPVYec+a0j0Ts47PDtW59y3RwAhUd9/h9ZZ63px6RQ==",
460 | "dependencies": {
461 | "node-forge": "^1.3.1"
462 | },
463 | "bin": {
464 | "gp12-pem": "build/src/bin/gp12-pem.js"
465 | },
466 | "engines": {
467 | "node": ">=12.0.0"
468 | }
469 | },
470 | "node_modules/gtoken": {
471 | "version": "6.1.2",
472 | "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-6.1.2.tgz",
473 | "integrity": "sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ==",
474 | "dependencies": {
475 | "gaxios": "^5.0.1",
476 | "google-p12-pem": "^4.0.0",
477 | "jws": "^4.0.0"
478 | },
479 | "engines": {
480 | "node": ">=12.0.0"
481 | }
482 | },
483 | "node_modules/has": {
484 | "version": "1.0.3",
485 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
486 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
487 | "dependencies": {
488 | "function-bind": "^1.1.1"
489 | },
490 | "engines": {
491 | "node": ">= 0.4.0"
492 | }
493 | },
494 | "node_modules/has-symbols": {
495 | "version": "1.0.3",
496 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
497 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
498 | "engines": {
499 | "node": ">= 0.4"
500 | },
501 | "funding": {
502 | "url": "https://github.com/sponsors/ljharb"
503 | }
504 | },
505 | "node_modules/http-errors": {
506 | "version": "2.0.0",
507 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
508 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
509 | "dependencies": {
510 | "depd": "2.0.0",
511 | "inherits": "2.0.4",
512 | "setprototypeof": "1.2.0",
513 | "statuses": "2.0.1",
514 | "toidentifier": "1.0.1"
515 | },
516 | "engines": {
517 | "node": ">= 0.8"
518 | }
519 | },
520 | "node_modules/https-proxy-agent": {
521 | "version": "5.0.1",
522 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
523 | "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
524 | "dependencies": {
525 | "agent-base": "6",
526 | "debug": "4"
527 | },
528 | "engines": {
529 | "node": ">= 6"
530 | }
531 | },
532 | "node_modules/https-proxy-agent/node_modules/debug": {
533 | "version": "4.3.4",
534 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
535 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
536 | "dependencies": {
537 | "ms": "2.1.2"
538 | },
539 | "engines": {
540 | "node": ">=6.0"
541 | },
542 | "peerDependenciesMeta": {
543 | "supports-color": {
544 | "optional": true
545 | }
546 | }
547 | },
548 | "node_modules/https-proxy-agent/node_modules/ms": {
549 | "version": "2.1.2",
550 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
551 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
552 | },
553 | "node_modules/iconv-lite": {
554 | "version": "0.4.24",
555 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
556 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
557 | "dependencies": {
558 | "safer-buffer": ">= 2.1.2 < 3"
559 | },
560 | "engines": {
561 | "node": ">=0.10.0"
562 | }
563 | },
564 | "node_modules/inherits": {
565 | "version": "2.0.4",
566 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
567 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
568 | },
569 | "node_modules/ipaddr.js": {
570 | "version": "1.9.1",
571 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
572 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
573 | "engines": {
574 | "node": ">= 0.10"
575 | }
576 | },
577 | "node_modules/is-stream": {
578 | "version": "2.0.1",
579 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
580 | "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
581 | "engines": {
582 | "node": ">=8"
583 | },
584 | "funding": {
585 | "url": "https://github.com/sponsors/sindresorhus"
586 | }
587 | },
588 | "node_modules/json-bigint": {
589 | "version": "1.0.0",
590 | "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
591 | "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
592 | "dependencies": {
593 | "bignumber.js": "^9.0.0"
594 | }
595 | },
596 | "node_modules/jwa": {
597 | "version": "2.0.0",
598 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
599 | "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
600 | "dependencies": {
601 | "buffer-equal-constant-time": "1.0.1",
602 | "ecdsa-sig-formatter": "1.0.11",
603 | "safe-buffer": "^5.0.1"
604 | }
605 | },
606 | "node_modules/jws": {
607 | "version": "4.0.0",
608 | "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
609 | "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
610 | "dependencies": {
611 | "jwa": "^2.0.0",
612 | "safe-buffer": "^5.0.1"
613 | }
614 | },
615 | "node_modules/jwt-decode": {
616 | "version": "3.1.2",
617 | "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz",
618 | "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A=="
619 | },
620 | "node_modules/lru-cache": {
621 | "version": "6.0.0",
622 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
623 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
624 | "dependencies": {
625 | "yallist": "^4.0.0"
626 | },
627 | "engines": {
628 | "node": ">=10"
629 | }
630 | },
631 | "node_modules/media-typer": {
632 | "version": "0.3.0",
633 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
634 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
635 | "engines": {
636 | "node": ">= 0.6"
637 | }
638 | },
639 | "node_modules/merge-descriptors": {
640 | "version": "1.0.1",
641 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
642 | "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
643 | },
644 | "node_modules/methods": {
645 | "version": "1.1.2",
646 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
647 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
648 | "engines": {
649 | "node": ">= 0.6"
650 | }
651 | },
652 | "node_modules/mime": {
653 | "version": "1.6.0",
654 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
655 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
656 | "bin": {
657 | "mime": "cli.js"
658 | },
659 | "engines": {
660 | "node": ">=4"
661 | }
662 | },
663 | "node_modules/mime-db": {
664 | "version": "1.52.0",
665 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
666 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
667 | "engines": {
668 | "node": ">= 0.6"
669 | }
670 | },
671 | "node_modules/mime-types": {
672 | "version": "2.1.35",
673 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
674 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
675 | "dependencies": {
676 | "mime-db": "1.52.0"
677 | },
678 | "engines": {
679 | "node": ">= 0.6"
680 | }
681 | },
682 | "node_modules/ms": {
683 | "version": "2.0.0",
684 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
685 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
686 | },
687 | "node_modules/negotiator": {
688 | "version": "0.6.3",
689 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
690 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
691 | "engines": {
692 | "node": ">= 0.6"
693 | }
694 | },
695 | "node_modules/node-fetch": {
696 | "version": "2.6.9",
697 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz",
698 | "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==",
699 | "dependencies": {
700 | "whatwg-url": "^5.0.0"
701 | },
702 | "engines": {
703 | "node": "4.x || >=6.0.0"
704 | },
705 | "peerDependencies": {
706 | "encoding": "^0.1.0"
707 | },
708 | "peerDependenciesMeta": {
709 | "encoding": {
710 | "optional": true
711 | }
712 | }
713 | },
714 | "node_modules/node-forge": {
715 | "version": "1.3.1",
716 | "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
717 | "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
718 | "engines": {
719 | "node": ">= 6.13.0"
720 | }
721 | },
722 | "node_modules/object-inspect": {
723 | "version": "1.12.3",
724 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
725 | "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
726 | "funding": {
727 | "url": "https://github.com/sponsors/ljharb"
728 | }
729 | },
730 | "node_modules/on-finished": {
731 | "version": "2.4.1",
732 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
733 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
734 | "dependencies": {
735 | "ee-first": "1.1.1"
736 | },
737 | "engines": {
738 | "node": ">= 0.8"
739 | }
740 | },
741 | "node_modules/on-headers": {
742 | "version": "1.0.2",
743 | "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
744 | "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
745 | "engines": {
746 | "node": ">= 0.8"
747 | }
748 | },
749 | "node_modules/packet-reader": {
750 | "version": "1.0.0",
751 | "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
752 | "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ=="
753 | },
754 | "node_modules/parseurl": {
755 | "version": "1.3.3",
756 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
757 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
758 | "engines": {
759 | "node": ">= 0.8"
760 | }
761 | },
762 | "node_modules/path-to-regexp": {
763 | "version": "0.1.7",
764 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
765 | "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
766 | },
767 | "node_modules/pg": {
768 | "version": "8.9.0",
769 | "resolved": "https://registry.npmjs.org/pg/-/pg-8.9.0.tgz",
770 | "integrity": "sha512-ZJM+qkEbtOHRuXjmvBtOgNOXOtLSbxiMiUVMgE4rV6Zwocy03RicCVvDXgx8l4Biwo8/qORUnEqn2fdQzV7KCg==",
771 | "dependencies": {
772 | "buffer-writer": "2.0.0",
773 | "packet-reader": "1.0.0",
774 | "pg-connection-string": "^2.5.0",
775 | "pg-pool": "^3.5.2",
776 | "pg-protocol": "^1.6.0",
777 | "pg-types": "^2.1.0",
778 | "pgpass": "1.x"
779 | },
780 | "engines": {
781 | "node": ">= 8.0.0"
782 | },
783 | "peerDependencies": {
784 | "pg-native": ">=3.0.1"
785 | },
786 | "peerDependenciesMeta": {
787 | "pg-native": {
788 | "optional": true
789 | }
790 | }
791 | },
792 | "node_modules/pg-connection-string": {
793 | "version": "2.5.0",
794 | "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz",
795 | "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ=="
796 | },
797 | "node_modules/pg-int8": {
798 | "version": "1.0.1",
799 | "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
800 | "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
801 | "engines": {
802 | "node": ">=4.0.0"
803 | }
804 | },
805 | "node_modules/pg-pool": {
806 | "version": "3.5.2",
807 | "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.2.tgz",
808 | "integrity": "sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==",
809 | "peerDependencies": {
810 | "pg": ">=8.0"
811 | }
812 | },
813 | "node_modules/pg-protocol": {
814 | "version": "1.6.0",
815 | "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz",
816 | "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q=="
817 | },
818 | "node_modules/pg-types": {
819 | "version": "2.2.0",
820 | "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
821 | "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
822 | "dependencies": {
823 | "pg-int8": "1.0.1",
824 | "postgres-array": "~2.0.0",
825 | "postgres-bytea": "~1.0.0",
826 | "postgres-date": "~1.0.4",
827 | "postgres-interval": "^1.1.0"
828 | },
829 | "engines": {
830 | "node": ">=4"
831 | }
832 | },
833 | "node_modules/pgpass": {
834 | "version": "1.0.5",
835 | "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
836 | "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
837 | "dependencies": {
838 | "split2": "^4.1.0"
839 | }
840 | },
841 | "node_modules/postgres-array": {
842 | "version": "2.0.0",
843 | "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
844 | "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
845 | "engines": {
846 | "node": ">=4"
847 | }
848 | },
849 | "node_modules/postgres-bytea": {
850 | "version": "1.0.0",
851 | "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
852 | "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
853 | "engines": {
854 | "node": ">=0.10.0"
855 | }
856 | },
857 | "node_modules/postgres-date": {
858 | "version": "1.0.7",
859 | "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
860 | "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
861 | "engines": {
862 | "node": ">=0.10.0"
863 | }
864 | },
865 | "node_modules/postgres-interval": {
866 | "version": "1.2.0",
867 | "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
868 | "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
869 | "dependencies": {
870 | "xtend": "^4.0.0"
871 | },
872 | "engines": {
873 | "node": ">=0.10.0"
874 | }
875 | },
876 | "node_modules/proxy-addr": {
877 | "version": "2.0.7",
878 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
879 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
880 | "dependencies": {
881 | "forwarded": "0.2.0",
882 | "ipaddr.js": "1.9.1"
883 | },
884 | "engines": {
885 | "node": ">= 0.10"
886 | }
887 | },
888 | "node_modules/qs": {
889 | "version": "6.11.0",
890 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
891 | "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
892 | "dependencies": {
893 | "side-channel": "^1.0.4"
894 | },
895 | "engines": {
896 | "node": ">=0.6"
897 | },
898 | "funding": {
899 | "url": "https://github.com/sponsors/ljharb"
900 | }
901 | },
902 | "node_modules/random-bytes": {
903 | "version": "1.0.0",
904 | "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
905 | "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==",
906 | "engines": {
907 | "node": ">= 0.8"
908 | }
909 | },
910 | "node_modules/range-parser": {
911 | "version": "1.2.1",
912 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
913 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
914 | "engines": {
915 | "node": ">= 0.6"
916 | }
917 | },
918 | "node_modules/raw-body": {
919 | "version": "2.5.1",
920 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
921 | "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
922 | "dependencies": {
923 | "bytes": "3.1.2",
924 | "http-errors": "2.0.0",
925 | "iconv-lite": "0.4.24",
926 | "unpipe": "1.0.0"
927 | },
928 | "engines": {
929 | "node": ">= 0.8"
930 | }
931 | },
932 | "node_modules/safe-buffer": {
933 | "version": "5.2.1",
934 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
935 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
936 | "funding": [
937 | {
938 | "type": "github",
939 | "url": "https://github.com/sponsors/feross"
940 | },
941 | {
942 | "type": "patreon",
943 | "url": "https://www.patreon.com/feross"
944 | },
945 | {
946 | "type": "consulting",
947 | "url": "https://feross.org/support"
948 | }
949 | ]
950 | },
951 | "node_modules/safer-buffer": {
952 | "version": "2.1.2",
953 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
954 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
955 | },
956 | "node_modules/send": {
957 | "version": "0.18.0",
958 | "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
959 | "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
960 | "dependencies": {
961 | "debug": "2.6.9",
962 | "depd": "2.0.0",
963 | "destroy": "1.2.0",
964 | "encodeurl": "~1.0.2",
965 | "escape-html": "~1.0.3",
966 | "etag": "~1.8.1",
967 | "fresh": "0.5.2",
968 | "http-errors": "2.0.0",
969 | "mime": "1.6.0",
970 | "ms": "2.1.3",
971 | "on-finished": "2.4.1",
972 | "range-parser": "~1.2.1",
973 | "statuses": "2.0.1"
974 | },
975 | "engines": {
976 | "node": ">= 0.8.0"
977 | }
978 | },
979 | "node_modules/send/node_modules/ms": {
980 | "version": "2.1.3",
981 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
982 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
983 | },
984 | "node_modules/serve-static": {
985 | "version": "1.15.0",
986 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
987 | "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
988 | "dependencies": {
989 | "encodeurl": "~1.0.2",
990 | "escape-html": "~1.0.3",
991 | "parseurl": "~1.3.3",
992 | "send": "0.18.0"
993 | },
994 | "engines": {
995 | "node": ">= 0.8.0"
996 | }
997 | },
998 | "node_modules/setprototypeof": {
999 | "version": "1.2.0",
1000 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
1001 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
1002 | },
1003 | "node_modules/side-channel": {
1004 | "version": "1.0.4",
1005 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
1006 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
1007 | "dependencies": {
1008 | "call-bind": "^1.0.0",
1009 | "get-intrinsic": "^1.0.2",
1010 | "object-inspect": "^1.9.0"
1011 | },
1012 | "funding": {
1013 | "url": "https://github.com/sponsors/ljharb"
1014 | }
1015 | },
1016 | "node_modules/split2": {
1017 | "version": "4.1.0",
1018 | "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz",
1019 | "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==",
1020 | "engines": {
1021 | "node": ">= 10.x"
1022 | }
1023 | },
1024 | "node_modules/statuses": {
1025 | "version": "2.0.1",
1026 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
1027 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
1028 | "engines": {
1029 | "node": ">= 0.8"
1030 | }
1031 | },
1032 | "node_modules/toidentifier": {
1033 | "version": "1.0.1",
1034 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
1035 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
1036 | "engines": {
1037 | "node": ">=0.6"
1038 | }
1039 | },
1040 | "node_modules/tr46": {
1041 | "version": "0.0.3",
1042 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
1043 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
1044 | },
1045 | "node_modules/type-is": {
1046 | "version": "1.6.18",
1047 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
1048 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
1049 | "dependencies": {
1050 | "media-typer": "0.3.0",
1051 | "mime-types": "~2.1.24"
1052 | },
1053 | "engines": {
1054 | "node": ">= 0.6"
1055 | }
1056 | },
1057 | "node_modules/uid-safe": {
1058 | "version": "2.1.5",
1059 | "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
1060 | "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
1061 | "dependencies": {
1062 | "random-bytes": "~1.0.0"
1063 | },
1064 | "engines": {
1065 | "node": ">= 0.8"
1066 | }
1067 | },
1068 | "node_modules/unpipe": {
1069 | "version": "1.0.0",
1070 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
1071 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
1072 | "engines": {
1073 | "node": ">= 0.8"
1074 | }
1075 | },
1076 | "node_modules/utils-merge": {
1077 | "version": "1.0.1",
1078 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
1079 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
1080 | "engines": {
1081 | "node": ">= 0.4.0"
1082 | }
1083 | },
1084 | "node_modules/vary": {
1085 | "version": "1.1.2",
1086 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
1087 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
1088 | "engines": {
1089 | "node": ">= 0.8"
1090 | }
1091 | },
1092 | "node_modules/webidl-conversions": {
1093 | "version": "3.0.1",
1094 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
1095 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
1096 | },
1097 | "node_modules/whatwg-url": {
1098 | "version": "5.0.0",
1099 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
1100 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
1101 | "dependencies": {
1102 | "tr46": "~0.0.3",
1103 | "webidl-conversions": "^3.0.0"
1104 | }
1105 | },
1106 | "node_modules/xtend": {
1107 | "version": "4.0.2",
1108 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
1109 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
1110 | "engines": {
1111 | "node": ">=0.4"
1112 | }
1113 | },
1114 | "node_modules/yallist": {
1115 | "version": "4.0.0",
1116 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
1117 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
1118 | }
1119 | }
1120 | }
1121 |
--------------------------------------------------------------------------------