From cca30756e52230fb0ffb136bc47398e3458655e8 Mon Sep 17 00:00:00 2001 From: Ishaan Upadhyay <33141569+ishaan-upadhyay@users.noreply.github.com> Date: Thu, 10 Aug 2023 12:15:07 -0400 Subject: [PATCH] [CIT-28] Allow users to message one another (#51) * [CIT-28] Make messaging API related changes * [CIT-28] Get Ably demo working in prod * [CIT-28] Begin adding code to persist messages * [CIT-28] Group creation, message persistence * [CIT-28] API rewrite into library functions * [CIT-28] Intermediate commit * [CIT-28] Cleanup, make groups secure * [CIT-28] Cleanup * [CIT-28] Cleanup, let users follow each other * CIT-28: add link for chats --------- Co-authored-by: Daniel --- .gitignore | 5 +- .../groups/[group]/JSXStyleComponent.jsx | 99 ++ .../groups/[group]/messagehistory/page.tsx | 3 + citrus/app/(users)/groups/[group]/page.tsx | 25 + citrus/app/(users)/groups/page.tsx | 29 + citrus/app/(users)/layout.tsx | 4 +- citrus/app/api/followers/route.ts | 44 + citrus/app/api/messages/ably/route.ts | 12 + .../messages/groups/[id]/messages/route.ts | 88 ++ citrus/app/api/messages/groups/[id]/route.ts | 108 ++ citrus/app/api/messages/groups/route.ts | 67 ++ citrus/components/AblyChatComponent.jsx | 84 ++ .../components/AblyChatComponent.module.css | 55 + citrus/components/ConnectWithPeople.tsx | 21 +- citrus/components/GroupChatHolder.tsx | 17 + citrus/components/UninterestButton.tsx | 6 +- citrus/effects/AblyReactEffect.js | 27 + citrus/lib/messages.ts | 86 ++ citrus/package-lock.json | 955 +++++++++++++++++- citrus/package.json | 15 +- .../20230725163850_add_messages/migration.sql | 48 + .../migration.sql | 13 + .../migration.sql | 2 + citrus/prisma/schema.prisma | 95 +- citrus/tsconfig.json | 4 +- 25 files changed, 1855 insertions(+), 57 deletions(-) create mode 100644 citrus/app/(users)/groups/[group]/JSXStyleComponent.jsx create mode 100644 citrus/app/(users)/groups/[group]/messagehistory/page.tsx create mode 100644 citrus/app/(users)/groups/[group]/page.tsx create mode 100644 citrus/app/(users)/groups/page.tsx create mode 100644 citrus/app/api/followers/route.ts create mode 100644 citrus/app/api/messages/ably/route.ts create mode 100644 citrus/app/api/messages/groups/[id]/messages/route.ts create mode 100644 citrus/app/api/messages/groups/[id]/route.ts create mode 100644 citrus/app/api/messages/groups/route.ts create mode 100644 citrus/components/AblyChatComponent.jsx create mode 100644 citrus/components/AblyChatComponent.module.css create mode 100644 citrus/components/GroupChatHolder.tsx create mode 100644 citrus/effects/AblyReactEffect.js create mode 100644 citrus/lib/messages.ts create mode 100644 citrus/prisma/migrations/20230725163850_add_messages/migration.sql create mode 100644 citrus/prisma/migrations/20230728011707_add_following/migration.sql create mode 100644 citrus/prisma/migrations/20230731150517_user_indicate_prepaid_tickets/migration.sql diff --git a/.gitignore b/.gitignore index 8b73356..820e38a 100644 --- a/.gitignore +++ b/.gitignore @@ -38,4 +38,7 @@ citrus/next-env.d.ts #data cockroach-data/ -node_modules/ \ No newline at end of file +node_modules/ + +#VS Code files +*.code-workspace \ No newline at end of file diff --git a/citrus/app/(users)/groups/[group]/JSXStyleComponent.jsx b/citrus/app/(users)/groups/[group]/JSXStyleComponent.jsx new file mode 100644 index 0000000..ea2c152 --- /dev/null +++ b/citrus/app/(users)/groups/[group]/JSXStyleComponent.jsx @@ -0,0 +1,99 @@ +"use client" +import AblyChatComponent from "@/components/AblyChatComponent" + +export default function ChatContainer(params) { + return ( +
+ +
+

{params.groupName}

+ +
+ + + + +
+ ) +} \ No newline at end of file diff --git a/citrus/app/(users)/groups/[group]/messagehistory/page.tsx b/citrus/app/(users)/groups/[group]/messagehistory/page.tsx new file mode 100644 index 0000000..341eda2 --- /dev/null +++ b/citrus/app/(users)/groups/[group]/messagehistory/page.tsx @@ -0,0 +1,3 @@ +export default function Page() { + +} \ No newline at end of file diff --git a/citrus/app/(users)/groups/[group]/page.tsx b/citrus/app/(users)/groups/[group]/page.tsx new file mode 100644 index 0000000..8d861bc --- /dev/null +++ b/citrus/app/(users)/groups/[group]/page.tsx @@ -0,0 +1,25 @@ +import ChatContainer from './JSXStyleComponent'; +import { getServerSession } from 'next-auth'; +import { authOptions } from '@/app/api/auth/[...nextauth]/route'; +import { isUserInGroup, getGroup } from '@/lib/messages'; +export const dynamic = "force-dynamic" + +export default async function Home({ params }: { params: { group: string } }) { + const groupID = params.group; + const session = await getServerSession(authOptions); + + if (!session || !session.user) { + return
loading...
+ } + + const inGroup = await isUserInGroup(session.user.name as string, groupID) + if (!inGroup) { + return
loading...
+ } + + const group = await getGroup(groupID); + + return ( + + ) + } \ No newline at end of file diff --git a/citrus/app/(users)/groups/page.tsx b/citrus/app/(users)/groups/page.tsx new file mode 100644 index 0000000..0bd5b03 --- /dev/null +++ b/citrus/app/(users)/groups/page.tsx @@ -0,0 +1,29 @@ +import GroupChatHolder from "@/components/GroupChatHolder" +import { use } from "react"; +import { getGroupsForUser } from "@/lib/messages" +export const dynamic = "force-dynamic" + +function GroupCard({ group }: { group: any }) { + return ( +
  • + {group.name} +
  • + ) +} + +export default function Page() { + const groups = use(getGroupsForUser()) + if (groups === "Unauthorized") { + return
    loading...
    + } + return ( +
    +

    Groups

    +
    +
      + {groups.map((group: any) => )} +
    +
    +
    + ) +} \ No newline at end of file diff --git a/citrus/app/(users)/layout.tsx b/citrus/app/(users)/layout.tsx index b0be4a0..985ca54 100644 --- a/citrus/app/(users)/layout.tsx +++ b/citrus/app/(users)/layout.tsx @@ -21,7 +21,6 @@ export default async function RootLayout({ children: React.ReactNode }) { const session = await getServerSession(authOptions); - const correctUserType = (!session) || (session.user && session.user.userType != 'organizer'); return ( @@ -46,6 +45,9 @@ export default async function RootLayout({
  • About
  • +
  • + Chats +
  • Organizer portal diff --git a/citrus/app/api/followers/route.ts b/citrus/app/api/followers/route.ts new file mode 100644 index 0000000..45c3f15 --- /dev/null +++ b/citrus/app/api/followers/route.ts @@ -0,0 +1,44 @@ +import Ably from "ably/promises"; +import { NextResponse } from "next/server"; +import * as db from "@/lib/db" +import "@/lib/patch" +import { getServerSession } from "next-auth"; +import { authOptions } from "@/app/api/auth/[...nextauth]/route"; + +const prisma = db.getClient(); + +export async function POST(request: Request) { + const session = await getServerSession(authOptions); + const body = await request.json(); + + if (!session || !session.user) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + } + + try { + const follows = await prisma.Follows.create({ + data: { + follower_username: session.user.name, + following_username: body.username + } + }) + + const group = await prisma.Group.create({ + data: { + name: `${session.user.name} & ${body.username}`, + users: { + connect: [ + { username: session.user.name }, + { username: body.username } + ] + } + } + }) + return NextResponse.json( + { follows, group, success: true }, + { status: 200 } + ) + } catch (e) { + return db.handleError(e); + } +} diff --git a/citrus/app/api/messages/ably/route.ts b/citrus/app/api/messages/ably/route.ts new file mode 100644 index 0000000..f37d638 --- /dev/null +++ b/citrus/app/api/messages/ably/route.ts @@ -0,0 +1,12 @@ +import Ably from "ably/promises"; +import { NextResponse } from "next/server"; + +export async function GET(request: Request) { + const { searchParams } = new URL(request.url); + const clientId = searchParams.get("clientId"); + if (!clientId) return NextResponse.json({ error: "clientId is required" }, { status: 400 }) + + const client = new Ably.Realtime(process.env.ABLY_API_KEY as string); + const tokenRequestData = await client.auth.createTokenRequest({ clientId: clientId}); + return NextResponse.json(tokenRequestData); +}; \ No newline at end of file diff --git a/citrus/app/api/messages/groups/[id]/messages/route.ts b/citrus/app/api/messages/groups/[id]/messages/route.ts new file mode 100644 index 0000000..e60b48c --- /dev/null +++ b/citrus/app/api/messages/groups/[id]/messages/route.ts @@ -0,0 +1,88 @@ +import * as db from "@/lib/db" +import { getServerSession } from "next-auth"; +import { NextResponse } from "next/server"; +import { authOptions } from "@/app/api/auth/[...nextauth]/route"; + +const prisma = db.getClient(); + +// Return messages in a group 200 at a time, starting from the most recent +export async function GET(request:Request, { params }: { params: { id: string } }) { + const session = await getServerSession(authOptions); + const { searchParams } = new URL(request.url) + const prevCursor = searchParams.get("cursor") + const nextCursor = searchParams.get("cursor") + var take = 200 + var cursor = undefined + var skip = 1 + + if (prevCursor) { + cursor = { + id: prevCursor + } + take = -200 + } else if (nextCursor) { + cursor = { + id: nextCursor + } + } else { + skip = 0 + } + + if (!session || !session.user) { + return NextResponse.json("Unauthorized", { status: 401 }) + } + + try { + const messages = await prisma.Message.findMany({ + where: { + group_id: params.id + }, + orderBy: { + created_at: "desc" + }, + take: take, + skip: skip, + cursor: cursor + }) + + const nextCursor = messages[messages.length - 1]?.id + const prevCursor = messages[0]?.id + + return NextResponse.json( + { messages: messages, + nextCursor: nextCursor, + prevCursor: prevCursor }, + { status: 200 } + ) + } catch(e) { + return db.handleError(e) + } +} + +export async function POST(request: Request, + { params }: { params: { id: string } }) { + const body = await request.json(); + const { text } = body; + const session = await getServerSession(authOptions); + + if (!session || !session.user) { + return NextResponse.json("Unauthorized", { status: 401 }) + } + + try { + await prisma.Message.create({ + data: { + text: text, + user_id: session.user?.name, + group_id: params.id + } + }) + } catch (e) { + return db.handleError(e) + } + + return NextResponse.json( + { message: "Message sent" }, + { status: 200 } + ) +} diff --git a/citrus/app/api/messages/groups/[id]/route.ts b/citrus/app/api/messages/groups/[id]/route.ts new file mode 100644 index 0000000..47a2c9b --- /dev/null +++ b/citrus/app/api/messages/groups/[id]/route.ts @@ -0,0 +1,108 @@ +import { NextResponse } from "next/server"; +import { getServerSession } from "next-auth"; +import { authOptions } from "@/app/api/auth/[...nextauth]/route"; +import * as db from "@/lib/db"; + +const prisma = db.getClient(); + +export async function GET(request: Request, { params }: { params: { id: string } }) { + const session = await getServerSession(authOptions); + + if (!session || !session.user) { + return NextResponse.json("Unauthorized", { status: 401 }) + } + + try { + const group = await prisma.Group.findUnique({ + where: { + id: params.id + }, + select: { + users: { + select: { + username: true + } + } + }, + }) + + const users = group.users.map((user: any) => { + return user.username + }) + + if (users.includes(session.user.name)) { + return NextResponse.json( + group, + { status: 200 } + ) + } else { + return NextResponse.json("Unauthorized", { status: 401 }) + } + } catch (e) { + return db.handleError(e) + } +} + +export async function PUT(request: Request, + { params }: { params: { id: string } }) { + + const session = await getServerSession(authOptions); + if (!session || !session.user) { + return new Response("Unauthorized", { status: 401 }) + } + + const body = await request.json(); + const { toAdd, name } = body; + + try { + const group = await prisma.groups.update({ + where: { + id: params.id + }, + data: { + name: name, + users: { + connect: toAdd.map((username: string) => { + return { username: username } + } + ) + } + } + }) + + return NextResponse.json( + group, + { status: 200 } + ) + } catch (e) { + return db.handleError(e) + } +} + +export async function DELETE(request: Request, + { params }: { params: { id: string } }) { + + const session = await getServerSession(authOptions); + if (!session || !session.user) { + return new Response("Unauthorized", { status: 401 }) + } + + try { + const group = await prisma.groups.delete({ + where: { + id: params.id, + users: { + some: { + username: session.user.name + } + } + } + }) + return NextResponse.json( + group, + { status: 200 } + ) + } catch (e) { + return db.handleError(e) + } +} \ No newline at end of file diff --git a/citrus/app/api/messages/groups/route.ts b/citrus/app/api/messages/groups/route.ts new file mode 100644 index 0000000..b73f533 --- /dev/null +++ b/citrus/app/api/messages/groups/route.ts @@ -0,0 +1,67 @@ +import { NextResponse } from "next/server" +import { getServerSession } from "next-auth" +import { authOptions } from "@/app/api/auth/[...nextauth]/route" +import * as db from "@/lib/db" +import "@/lib/patch" + +const prisma = db.getClient() +/* + GET /api/messages/groups + @desc Get all groups that the user is in + @access Private +*/ +export async function GET() { + const session = await getServerSession(authOptions) + + if (!session || !session.user) { + return NextResponse.json("Unauthorized", { status: 401 }) + } + + const user = await prisma.users.findUnique({ + where: { + username: session.user.name + }, + include: { + groups: true + } + }) + + const groups = user.groups + + return NextResponse.json( + groups, + { status: 200 } + ) +} + +/* +* +*/ +export async function POST(request: Request) { + const { searchParams } = new URL(request.url) + const toAdd = searchParams.get("toAdd")?.split(",") || [] + const name = searchParams.get("name") || "New Group" + + const session = await getServerSession(authOptions) + if (!session || !session.user) { + return new Response("Unauthorized", { status: 401 }) + } + + const group = await prisma.groups.create( + { + data: { + name: name, + users: { + connect: toAdd.map((username) => { + return { username: username } + }) + } + } + } + ) + + return NextResponse.json( + group, + { status: 200 } + ) +} \ No newline at end of file diff --git a/citrus/components/AblyChatComponent.jsx b/citrus/components/AblyChatComponent.jsx new file mode 100644 index 0000000..590b8ba --- /dev/null +++ b/citrus/components/AblyChatComponent.jsx @@ -0,0 +1,84 @@ +"use client" +import React, { useEffect, useState } from 'react'; +import { useChannel } from "@/effects/AblyReactEffect"; +import styles from './AblyChatComponent.module.css'; +import { useSession } from 'next-auth/react' + +export default function AblyChatComponent(channelID) { + const { data: session } = useSession() + let inputBox = null; + let messageEnd = null; + + const [messageText, setMessageText] = useState(""); + const [receivedMessages, setMessages] = useState([]); + const messageTextIsEmpty = messageText.trim().length === 0; + + const [channel, ably] = useChannel(channelID.channelID, (message) => { + const history = receivedMessages.slice(-199); + setMessages([...history, message]); + }, session.user.name); + + function sendChatMessage(messageText) { + channel.publish({ name: "chat-message", data: messageText }); + fetch('/api/messages/groups/' + channelID.channelID + '/messages', + { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + text: messageText, + }) + }).then((response) => { + if (response.ok) { + } + } + ) + setMessageText(""); + inputBox.focus(); +} + +const handleFormSubmission = (event) => { + event.preventDefault(); + sendChatMessage(messageText); +} + +const handleKeyPress = (event) => { + if (event.code !== "Enter" || messageTextIsEmpty) { + return; + } + sendChatMessage(messageText); + event.preventDefault(); +} + +const messages = receivedMessages.map((message, index) => { + const author = message.connectionId === ably.connection.id ? "me" : "other"; + return {message.data}; +}); + +useEffect(() => { + if (messageEnd !== null) { + messageEnd.scrollIntoView({ behaviour: "smooth" }); + } +}); + +return ( +
    +
    + {messages} +
    { messageEnd = element; }}>
    +
    +
    + + +
    +
    +) +} \ No newline at end of file diff --git a/citrus/components/AblyChatComponent.module.css b/citrus/components/AblyChatComponent.module.css new file mode 100644 index 0000000..1312ebe --- /dev/null +++ b/citrus/components/AblyChatComponent.module.css @@ -0,0 +1,55 @@ +.chatHolder { + display: grid; + grid-template-rows: 1fr 100px; +} + +.chatText { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 1em; + padding: 1em; + height: calc(100vh - 40px - 100px - 100px - 100px); + overflow-y: auto; +} + +.form { + display: grid; + grid-template-columns: 1fr 100px; + border-top: 1px solid #eee; +} + +.textarea { + padding: 1em; + border: 0; + font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto, Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue, sans-serif; + font-size: 1.2em; + color: black; +} + +.button { + border: 0; + color: white; + font-weight: bold; + font-size: 1.4em; + background: linear-gradient(to right, #363795, #005C97); +} + +.button:hover { + background: linear-gradient(90deg, rgba(54,55,149,1) 0%, rgba(0,92,151,1) 62%, rgba(0,125,205,1) 100%); +} + +.button:disabled, +.button:hover:disabled { + background: linear-gradient(to right, #363795, #005C97); + opacity: 0.5; +} + +.message { + background-color: #eef5f8; + padding: 1em; + border-radius: 10px; + flex-grow: 0; + border-bottom-left-radius: 0; + color: black; +} \ No newline at end of file diff --git a/citrus/components/ConnectWithPeople.tsx b/citrus/components/ConnectWithPeople.tsx index 420daa8..a17a4eb 100644 --- a/citrus/components/ConnectWithPeople.tsx +++ b/citrus/components/ConnectWithPeople.tsx @@ -4,8 +4,6 @@ import { ReadonlyURLSearchParams, usePathname, useSearchParams } from 'next/navi import type { users } from '@prisma/client'; import { useSession } from 'next-auth/react'; import InfiniteScroll from 'react-infinite-scroll-component'; -import "react-datepicker/dist/react-datepicker.css"; -import "react-datepicker/dist/react-datepicker-cssmodules.css"; function buildAPISearchParams(searchParams: ReadonlyURLSearchParams, basePathName: string) { var apiPathName = basePathName @@ -18,6 +16,24 @@ function buildAPISearchParams(searchParams: ReadonlyURLSearchParams, basePathNam return apiPathName; } +function followUser(username: string) { + fetch('/api/followers', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ username }), + }) + .then((res) => res.json()) + .then((data) => { + if (data.success) { + alert('You are now following ' + username); + } else { + alert('There was an error following ' + username); + } + }); +} + export default function ConnectWithPeople() { const { data: session } = useSession(); @@ -102,6 +118,7 @@ export default function ConnectWithPeople() { + ))} diff --git a/citrus/components/GroupChatHolder.tsx b/citrus/components/GroupChatHolder.tsx new file mode 100644 index 0000000..ac63bbd --- /dev/null +++ b/citrus/components/GroupChatHolder.tsx @@ -0,0 +1,17 @@ +export default async function GroupChatHolder() { + const res = await fetch('http://localhost:3000/api/messages/groups') + const body = await res.json() + const groups = body.groups + return ( +
    +

    Groups

    +
      + {groups.map((group: any) => ( +
    • + {group.name} +
    • + ))} +
    = +
    + ) +} \ No newline at end of file diff --git a/citrus/components/UninterestButton.tsx b/citrus/components/UninterestButton.tsx index f42b9a1..1492c9b 100644 --- a/citrus/components/UninterestButton.tsx +++ b/citrus/components/UninterestButton.tsx @@ -3,7 +3,6 @@ import { useRouter } from "next/navigation"; async function onUninterestHandler(username: string | null | undefined, eventID: string){ const res = await fetch(`/api/experiences/${eventID}`); - console.log(res); const data = await res.json(); const attendees = data.attendees; const index = attendees.indexOf(username); @@ -13,14 +12,11 @@ async function onUninterestHandler(username: string | null | undefined, eventID: method: 'PUT', body: JSON.stringify({"attendees": attendees}), }); - console.log(res2); } const res3 = await fetch(`/api/statuses?event_id=${eventID}`, { method: 'DELETE' - }); - console.log(res3); - + }); } export default function UninterestButton({username, eventID}: {username: string | null | undefined, eventID: string}){ diff --git a/citrus/effects/AblyReactEffect.js b/citrus/effects/AblyReactEffect.js new file mode 100644 index 0000000..dbc5535 --- /dev/null +++ b/citrus/effects/AblyReactEffect.js @@ -0,0 +1,27 @@ +"use client" +import Ably from "ably/promises"; +import { getSession } from 'next-auth/react' +import { useEffect } from 'react' + +const ably = new Ably.Realtime.Promise({ authUrl: '/api/messages/ably?clientId=nextjs' }); + +export function useChannel(channelName, callbackOnMessage) { + const channel = ably.channels.get(channelName); + + const onMount = () => { + channel.subscribe(msg => { callbackOnMessage(msg); }); + } + + const onUnmount = () => { + channel.unsubscribe(); + } + + const useEffectHook = () => { + onMount(); + return () => { onUnmount(); }; + }; + + useEffect(useEffectHook); + + return [channel, ably]; +} \ No newline at end of file diff --git a/citrus/lib/messages.ts b/citrus/lib/messages.ts new file mode 100644 index 0000000..28b2779 --- /dev/null +++ b/citrus/lib/messages.ts @@ -0,0 +1,86 @@ +import { getServerSession } from "next-auth"; +import { authOptions } from "@/app/api/auth/[...nextauth]/route"; +import * as db from "@/lib/db" +import "@/lib/patch" + +const prisma = db.getClient() + +export async function getGroupsForUser() { + const session = await getServerSession(authOptions); + if (!session || !session.user) { + return "Unauthorized" + } + + const user = await prisma.users.findUnique({ + where: { + username: session.user.name + }, + include: { + groups: true + } + }) + + const groups = user.groups + + return groups +} + +export async function getGroup(groupID: string) { + const session = await getServerSession(authOptions); + if (!session || !session.user) { + return "Unauthorized" + } + + const group = await prisma.Group.findUnique({ + where: { + id: groupID + }, + }) + + return group +} + +export async function getUsersForGroup(groupID: string) { + const session = await getServerSession(authOptions); + if (!session || !session.user) { + return "Unauthorized" + } + + const group = await prisma.groups.findUnique({ + where: { + id: groupID + }, + include: { + users: true + } + }) + + const users = group.users + return users +} + +export async function isUserInGroup(username: string, groupID: string) { + try { + const group = await prisma.Group.findUnique({ + where: { + id: groupID, + }, + select: { + users: { + select: { + username: true + } + } + } + }) + + const usernames = group.users.map((user: any) => user.username) + + if (usernames.includes(username)) { + return true + } + return false + } catch (e) { + return false + } +} \ No newline at end of file diff --git a/citrus/package-lock.json b/citrus/package-lock.json index ba15d66..4b5231f 100644 --- a/citrus/package-lock.json +++ b/citrus/package-lock.json @@ -9,23 +9,29 @@ "version": "0.1.0", "hasInstallScript": true, "dependencies": { + "@ably-labs/react-hooks": "^2.1.1", "@auth/prisma-adapter": "^1.0.0", "@chakra-ui/react": "^2.7.0", "@fortawesome/fontawesome-svg-core": "^6.4.0", "@fortawesome/free-regular-svg-icons": "^6.4.0", "@fortawesome/free-solid-svg-icons": "^6.4.0", "@fortawesome/react-fontawesome": "^0.2.0", + "@keyvhq/core": "^2.0.0", "@next-auth/prisma-adapter": "^1.0.7", "@prisma/client": "^4.16.2", "@types/node": "20.2.5", "@types/react": "18.2.7", "@types/react-datepicker": "^4.11.2", "@types/react-dom": "18.2.4", + "ably": "^1.2.42", "autoprefixer": "10.4.14", "bcrypt": "^5.1.0", + "bufferutil": "^4.0.7", "eslint": "8.41.0", "eslint-config-next": "13.4.4", "fill-range": "^7.0.1", + "got": "^13.0.0", + "keyv": "^4.5.3", "next": "^13.4.11", "next-auth": "^4.22.1", "password-validator": "^5.3.0", @@ -37,9 +43,31 @@ "react-dom": "18.2.0", "react-icons": "^4.9.0", "react-infinite-scroll-component": "^6.1.0", + "request": "^2.88.2", "streamsearch": "^1.1.0", "tailwindcss": "3.3.2", - "typescript": "5.0.4" + "typescript": "5.0.4", + "utf-8-validate": "^6.0.3" + } + }, + "node_modules/@ably-labs/react-hooks": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@ably-labs/react-hooks/-/react-hooks-2.1.1.tgz", + "integrity": "sha512-Bunqu9GDFInZLpFMfWhUboU1g4W5UXzDfeAqI9ueNIF3p9KIMS7LfgjKBfXsC0DtAWkBgCjL22PvNToiHP92Ig==", + "dependencies": { + "ably": "^1.2.27" + }, + "peerDependencies": { + "react": ">=18.1.0", + "react-dom": ">=18.1.0" + } + }, + "node_modules/@ably/msgpack-js": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@ably/msgpack-js/-/msgpack-js-0.4.0.tgz", + "integrity": "sha512-IPt/BoiQwCWubqoNik1aw/6M/DleMdrxJOUpSja6xmMRbT2p1TA8oqKWgfZabqzrq8emRNeSl/+4XABPNnW5pQ==", + "dependencies": { + "bops": "^1.0.1" } }, "node_modules/@alloc/quick-lru": { @@ -1740,6 +1768,17 @@ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, + "node_modules/@keyvhq/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@keyvhq/core/-/core-2.0.0.tgz", + "integrity": "sha512-fi3+F7GNImn1j4r6UFhsHRwN8a05uhUlrbNWZgnkX0h1NzcBEPNNqqMOE4KSASJwH2e9Eh/jm+bEfto58csNgg==", + "dependencies": { + "json-buffer": "~3.0.1" + }, + "engines": { + "node": ">= 16" + } + }, "node_modules/@mapbox/node-pre-gyp": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz", @@ -2039,6 +2078,17 @@ "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.0.tgz", "integrity": "sha512-IthPJsJR85GhOkp3Hvp8zFOPK5ynKn6STyHa/WZpioK7E1aYDiBzpqQPrngc14DszIUkIrdd3k9Iu0XSzlP/1w==" }, + "node_modules/@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, "node_modules/@swc/helpers": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz", @@ -2047,11 +2097,46 @@ "tslib": "^2.4.0" } }, + "node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "dependencies": { + "defer-to-connect": "^2.0.1" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", + "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/lodash": { "version": "4.14.195", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.195.tgz", @@ -2110,6 +2195,14 @@ "@types/react": "*" } }, + "node_modules/@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/scheduler": { "version": "0.16.3", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", @@ -2253,6 +2346,166 @@ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, + "node_modules/ably": { + "version": "1.2.42", + "resolved": "https://registry.npmjs.org/ably/-/ably-1.2.42.tgz", + "integrity": "sha512-dUnza7cERLWaDa/2pLVXtU2PJoU5k/t6g9sQZI1dgWC5Vok39nE6tf/xH2Rat7PJs7pXl9hLpkg1AhS4Xwd2/w==", + "dependencies": { + "@ably/msgpack-js": "^0.4.0", + "got": "^11.8.5", + "ws": "^5.1" + }, + "engines": { + "node": ">=5.10.x" + } + }, + "node_modules/ably/node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/ably/node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ably/node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/ably/node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ably/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ably/node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/ably/node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/ably/node_modules/keyv": { + "name": "@keyvhq/core", + "version": "1.6.26", + "resolved": "https://registry.npmjs.org/@keyvhq/core/-/core-1.6.26.tgz", + "integrity": "sha512-0jy/fOVCYdo3MImM3yjdHuFZNkycPmEAq0v+UGt0dRltnXccgQ+7vCdCNbLpgP3688g5Mw8L+0vcRt5MK53UPA==", + "dependencies": { + "json-buffer": "~3.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ably/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ably/node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ably/node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ably/node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/acorn": { "version": "8.8.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", @@ -2467,11 +2720,37 @@ "get-intrinsic": "^1.1.3" } }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/ast-types-flow": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==" }, + "node_modules/async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/autoprefixer": { "version": "10.4.14", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", @@ -2515,6 +2794,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", + "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" + }, "node_modules/axe-core": { "version": "4.7.2", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.2.tgz", @@ -2551,6 +2843,14 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/base64-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.0.2.tgz", + "integrity": "sha512-ZXBDPMt/v/8fsIqn+Z5VwrhdR6jVka0bYobHdGia0Nxi7BJ9i/Uvml3AocHIBtIIBhZjBw5MR0aR4ROs/8+SNg==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/bcrypt": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.0.tgz", @@ -2564,6 +2864,14 @@ "node": ">= 10.0.0" } }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, "node_modules/big-integer": { "version": "1.6.51", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", @@ -2580,6 +2888,15 @@ "node": ">=8" } }, + "node_modules/bops": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bops/-/bops-1.0.1.tgz", + "integrity": "sha512-qCMBuZKP36tELrrgXpAfM+gHzqa0nLsWZ+L37ncsb8txYlnAoxOPpVp+g7fK0sGkMXfA0wl8uQkESqw3v4HNag==", + "dependencies": { + "base64-js": "1.0.2", + "to-utf8": "0.0.1" + } + }, "node_modules/bplist-parser": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", @@ -2650,6 +2967,18 @@ "node": ">=4" } }, + "node_modules/bufferutil": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.7.tgz", + "integrity": "sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/bundle-name": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", @@ -2675,6 +3004,43 @@ "node": ">=10.16.0" } }, + "node_modules/cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/cacheable-request": { + "version": "10.2.13", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.13.tgz", + "integrity": "sha512-3SD4rrMu1msNGEtNSt8Od6enwdo//U9s4ykmXfA2TD58kcLkCobtCDiby7kNyj7a/Q7lz/mAesAFI54rTdnvBA==", + "dependencies": { + "@types/http-cache-semantics": "^4.0.1", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/cacheable-request/node_modules/keyv": { + "name": "@keyvhq/core", + "version": "1.6.26", + "resolved": "https://registry.npmjs.org/@keyvhq/core/-/core-1.6.26.tgz", + "integrity": "sha512-0jy/fOVCYdo3MImM3yjdHuFZNkycPmEAq0v+UGt0dRltnXccgQ+7vCdCNbLpgP3688g5Mw8L+0vcRt5MK53UPA==", + "dependencies": { + "json-buffer": "~3.0.1" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -2722,6 +3088,11 @@ } ] }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2792,6 +3163,25 @@ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clone-response/node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "engines": { + "node": ">=4" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2821,6 +3211,17 @@ "resolved": "https://registry.npmjs.org/color2k/-/color2k-2.0.2.tgz", "integrity": "sha512-kJhwH5nAwb34tmyuqq/lgjEKzlFXn1U99NlnB6Ws4qVaERcRUYeYP1cBw6BJ4vxaWStAUEef4WMr7WjOCnBt8w==" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -2866,6 +3267,11 @@ "toggle-selection": "^1.0.6" } }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" + }, "node_modules/cosmiconfig": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", @@ -2933,6 +3339,17 @@ "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==" }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/date-fns": { "version": "2.30.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", @@ -2964,6 +3381,31 @@ } } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/deep-equal": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.1.tgz", @@ -3029,6 +3471,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "engines": { + "node": ">=10" + } + }, "node_modules/define-lazy-prop": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", @@ -3055,6 +3505,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -3105,6 +3563,15 @@ "node": ">=6.0.0" } }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.432", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.432.tgz", @@ -3115,6 +3582,14 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.15.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", @@ -3453,9 +3928,9 @@ } }, "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -3490,9 +3965,9 @@ } }, "node_modules/eslint-plugin-jsx-a11y/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -3564,9 +4039,9 @@ } }, "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -3673,6 +4148,19 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "engines": [ + "node >=0.6.0" + ] + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3801,6 +4289,35 @@ "is-callable": "^1.1.3" } }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/form-data-encoder": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", + "engines": { + "node": ">= 14.17" + } + }, "node_modules/fraction.js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", @@ -4015,6 +4532,14 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -4107,6 +4632,30 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/got": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/got/-/got-13.0.0.tgz", + "integrity": "sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==", + "dependencies": { + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -4117,6 +4666,27 @@ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -4205,6 +4775,37 @@ "react-is": "^16.7.0" } }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/http2-wrapper": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.0.tgz", + "integrity": "sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ==", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -4596,6 +5197,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, "node_modules/is-weakmap": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", @@ -4662,6 +5268,11 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" + }, "node_modules/jiti": { "version": "1.18.2", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz", @@ -4694,12 +5305,27 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "peer": true }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -4710,6 +5336,11 @@ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, "node_modules/json5": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", @@ -4721,6 +5352,20 @@ "json5": "lib/cli.js" } }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", @@ -4733,6 +5378,14 @@ "node": ">=4.0" } }, + "node_modules/keyv": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", + "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/language-subtag-registry": { "version": "0.3.22", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", @@ -4806,6 +5459,17 @@ "loose-envify": "cli.js" } }, + "node_modules/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -4832,9 +5496,9 @@ } }, "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -4864,6 +5528,25 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mimic-fn": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", @@ -4875,6 +5558,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mimic-response": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -5097,6 +5791,16 @@ } } }, + "node_modules/node-gyp-build": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", + "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/node-releases": { "version": "2.0.12", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", @@ -5132,6 +5836,17 @@ "node": ">=0.10.0" } }, + "node_modules/normalize-url": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz", + "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/npm-run-path": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", @@ -5173,6 +5888,14 @@ "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "engines": { + "node": "*" + } + }, "node_modules/oauth4webapi": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-2.3.0.tgz", @@ -5387,6 +6110,14 @@ "node": ">= 0.8.0" } }, + "node_modules/p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", + "engines": { + "node": ">=12.20" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -5494,6 +6225,11 @@ "node": ">=8" } }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, "node_modules/pg": { "version": "8.11.0", "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.0.tgz", @@ -5827,6 +6563,20 @@ "react-is": "^16.13.1" } }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", @@ -5835,6 +6585,14 @@ "node": ">=6" } }, + "node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "engines": { + "node": ">=0.6" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5854,6 +6612,17 @@ } ] }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", @@ -6103,6 +6872,46 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, "node_modules/resolve": { "version": "1.22.2", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", @@ -6119,6 +6928,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -6135,6 +6949,20 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/responselike": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", + "dependencies": { + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -6308,6 +7136,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, "node_modules/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -6408,6 +7241,30 @@ "node": ">= 10.x" } }, + "node_modules/sshpk": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", + "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/stop-iteration-iterator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", @@ -6788,11 +7645,28 @@ "node": ">=8.0" } }, + "node_modules/to-utf8": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/to-utf8/-/to-utf8-0.0.1.tgz", + "integrity": "sha512-zks18/TWT1iHO3v0vFp5qLKOG27m67ycq/Y7a7cTiRuUNlc4gf3HGnkRgMv0NyhnfTamtkYBJl+YeD1/j07gBQ==" + }, "node_modules/toggle-selection": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -6838,6 +7712,22 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -6985,6 +7875,18 @@ } } }, + "node_modules/utf-8-validate": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-6.0.3.tgz", + "integrity": "sha512-uIuGf9TWQ/y+0Lp+KGZCMuJWc3N9BHA+l/UmHd/oUHwJJDeysyTRxNQVkbzsIWfGFbRe3OcgML/i0mvVRPOyDA==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -6998,6 +7900,19 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, "node_modules/warning": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", @@ -7103,9 +8018,9 @@ } }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "engines": { "node": ">=0.10.0" } @@ -7115,6 +8030,14 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, + "node_modules/ws": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.3.tgz", + "integrity": "sha512-jZArVERrMsKUatIdnLzqvcfydI85dvd/Fp1u/VOpfdDWQ4c9qWXe+VIeAbQ5FrDwciAkr+lzofXLz3Kuf26AOA==", + "dependencies": { + "async-limiter": "~1.0.0" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/citrus/package.json b/citrus/package.json index 0abe0be..b84d773 100644 --- a/citrus/package.json +++ b/citrus/package.json @@ -10,23 +10,29 @@ "postinstall": "prisma generate" }, "dependencies": { + "@ably-labs/react-hooks": "^2.1.1", "@auth/prisma-adapter": "^1.0.0", "@chakra-ui/react": "^2.7.0", "@fortawesome/fontawesome-svg-core": "^6.4.0", "@fortawesome/free-regular-svg-icons": "^6.4.0", "@fortawesome/free-solid-svg-icons": "^6.4.0", "@fortawesome/react-fontawesome": "^0.2.0", + "@keyvhq/core": "^2.0.0", "@next-auth/prisma-adapter": "^1.0.7", "@prisma/client": "^4.16.2", "@types/node": "20.2.5", "@types/react": "18.2.7", "@types/react-datepicker": "^4.11.2", "@types/react-dom": "18.2.4", + "ably": "^1.2.42", "autoprefixer": "10.4.14", "bcrypt": "^5.1.0", + "bufferutil": "^4.0.7", "eslint": "8.41.0", "eslint-config-next": "13.4.4", "fill-range": "^7.0.1", + "got": "^13.0.0", + "keyv": "^4.5.3", "next": "^13.4.11", "next-auth": "^4.22.1", "password-validator": "^5.3.0", @@ -38,8 +44,15 @@ "react-dom": "18.2.0", "react-icons": "^4.9.0", "react-infinite-scroll-component": "^6.1.0", + "request": "^2.88.2", "streamsearch": "^1.1.0", "tailwindcss": "3.3.2", - "typescript": "5.0.4" + "typescript": "5.0.4", + "utf-8-validate": "^6.0.3" + }, + "overrides": { + "cacheable-request": { + "keyv": "npm:@keyvhq/core@~1.6.6" + } } } diff --git a/citrus/prisma/migrations/20230725163850_add_messages/migration.sql b/citrus/prisma/migrations/20230725163850_add_messages/migration.sql new file mode 100644 index 0000000..bfa5e2e --- /dev/null +++ b/citrus/prisma/migrations/20230725163850_add_messages/migration.sql @@ -0,0 +1,48 @@ +-- CreateSchema +CREATE SCHEMA IF NOT EXISTS "messages"; + +-- CreateTable +CREATE TABLE "messages"."Message" ( + "id" STRING NOT NULL, + "text" STRING(1000) NOT NULL, + "user_id" STRING(32) NOT NULL, + "group_id" STRING(32) NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Message_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "messages"."Group" ( + "id" STRING NOT NULL, + "name" STRING(255) NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Group_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "messages"."_GroupTousers" ( + "A" STRING NOT NULL, + "B" STRING(32) NOT NULL +); + +-- CreateIndex +CREATE UNIQUE INDEX "_GroupTousers_AB_unique" ON "messages"."_GroupTousers"("A", "B"); + +-- CreateIndex +CREATE INDEX "_GroupTousers_B_index" ON "messages"."_GroupTousers"("B"); + +-- AddForeignKey +ALTER TABLE "messages"."Message" ADD CONSTRAINT "Message_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"."users"("username") ON DELETE NO ACTION ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "messages"."Message" ADD CONSTRAINT "Message_group_id_fkey" FOREIGN KEY ("group_id") REFERENCES "messages"."Group"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "messages"."_GroupTousers" ADD CONSTRAINT "_GroupTousers_A_fkey" FOREIGN KEY ("A") REFERENCES "messages"."Group"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "messages"."_GroupTousers" ADD CONSTRAINT "_GroupTousers_B_fkey" FOREIGN KEY ("B") REFERENCES "users"."users"("username") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/citrus/prisma/migrations/20230728011707_add_following/migration.sql b/citrus/prisma/migrations/20230728011707_add_following/migration.sql new file mode 100644 index 0000000..506fa41 --- /dev/null +++ b/citrus/prisma/migrations/20230728011707_add_following/migration.sql @@ -0,0 +1,13 @@ +-- CreateTable +CREATE TABLE "users"."Follows" ( + "follower_username" STRING(32) NOT NULL, + "following_username" STRING(32) NOT NULL, + + CONSTRAINT "Follows_pkey" PRIMARY KEY ("follower_username","following_username") +); + +-- AddForeignKey +ALTER TABLE "users"."Follows" ADD CONSTRAINT "Follows_follower_username_fkey" FOREIGN KEY ("follower_username") REFERENCES "users"."users"("username") ON DELETE NO ACTION ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "users"."Follows" ADD CONSTRAINT "Follows_following_username_fkey" FOREIGN KEY ("following_username") REFERENCES "users"."users"("username") ON DELETE NO ACTION ON UPDATE NO ACTION; diff --git a/citrus/prisma/migrations/20230731150517_user_indicate_prepaid_tickets/migration.sql b/citrus/prisma/migrations/20230731150517_user_indicate_prepaid_tickets/migration.sql new file mode 100644 index 0000000..9faed57 --- /dev/null +++ b/citrus/prisma/migrations/20230731150517_user_indicate_prepaid_tickets/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "users"."user_attending_status" ADD COLUMN "prepaid_tickets" INT8 NOT NULL DEFAULT 0; diff --git a/citrus/prisma/schema.prisma b/citrus/prisma/schema.prisma index 04d001b..3efac2d 100644 --- a/citrus/prisma/schema.prisma +++ b/citrus/prisma/schema.prisma @@ -6,7 +6,7 @@ generator client { datasource db { provider = "cockroachdb" url = env("DATABASE_URL") - schemas = ["experiences", "organizers", "users"] + schemas = ["experiences", "messages", "organizers", "users"] } /// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info. @@ -49,39 +49,45 @@ model organizers { } model user_attending_status { - username String @db.String(32) - event_id String @db.Uuid - attending attending_status_type - experiences experiences @relation(fields: [event_id], references: [id], onDelete: NoAction, onUpdate: NoAction) - users users @relation(fields: [username], references: [username], onDelete: NoAction, onUpdate: NoAction) - + username String @db.String(32) + event_id String @db.Uuid + attending attending_status_type + prepaid_tickets BigInt @default(0) + experiences experiences @relation(fields: [event_id], references: [id], onDelete: NoAction, onUpdate: NoAction) + users users @relation(fields: [username], references: [username], onDelete: NoAction, onUpdate: NoAction) @@id([username, event_id]) @@schema("users") } model users { - username String @id @db.String(32) - pass String @db.String(60) - email String @unique @db.String(255) - phone_number String? @unique @db.String(15) - instagram String? - facebook String? - premium Boolean? @default(false) + username String @id @db.String(32) + pass String @db.String(60) + email String @unique @db.String(255) + phone_number String? @unique @db.String(15) + premium Boolean? @default(false) interests String[] + facebook String? + instagram String? experiences experiences[] - user_attending_status user_attending_status[] + messages Message[] accounts Account[] + followedBy Follows[] @relation("following") + following Follows[] @relation("follower") sessions Session[] + user_attending_status user_attending_status[] + groups Group[] @relation("GroupTousers") @@schema("users") } -enum attending_status_type { - attending - attended - interested +model Follows { + follower_username String @db.String(32) + following_username String @db.String(32) + follower users @relation("following", fields: [follower_username], references: [username], onDelete: NoAction, onUpdate: NoAction) + following users @relation("follower", fields: [following_username], references: [username], onDelete: NoAction, onUpdate: NoAction) + @@id([follower_username, following_username]) @@schema("users") } @@ -90,31 +96,62 @@ model Session { sessionToken String @unique userId String expires DateTime - user users @relation(fields: [userId], references: [username], onDelete: Cascade) - organizers organizers? @relation(fields: [organizersOrg_id], references: [org_id]) organizersOrg_id String? @db.String(32) + organizers organizers? @relation(fields: [organizersOrg_id], references: [org_id]) + user users @relation(fields: [userId], references: [username], onDelete: Cascade) @@schema("users") } model Account { - id String @id @default(cuid()) + id String @id @default(cuid()) userId String type String provider String providerAccountId String - refresh_token String? @db.String - access_token String? @db.String + refresh_token String? + access_token String? expires_at Int? token_type String? scope String? - id_token String? @db.String + id_token String? session_state String? - - user users @relation(fields: [userId], references: [username], onDelete: Cascade) - organizers organizers? @relation(fields: [organizersOrg_id], references: [org_id]) - organizersOrg_id String? @db.String(32) + organizersOrg_id String? @db.String(32) + organizers organizers? @relation(fields: [organizersOrg_id], references: [org_id]) + user users @relation(fields: [userId], references: [username], onDelete: Cascade) @@unique([provider, providerAccountId]) @@schema("users") } + +model Message { + id String @id @default(cuid()) + text String @db.String(1000) + user_id String @db.String(32) + group_id String @db.String(32) + created_at DateTime @default(now()) @db.Timestamptz(6) + updated_at DateTime @updatedAt + group Group @relation(fields: [group_id], references: [id], onDelete: NoAction, onUpdate: NoAction) + user users @relation(fields: [user_id], references: [username], onDelete: NoAction, onUpdate: NoAction) + + @@schema("messages") +} + +model Group { + id String @id @default(cuid()) + name String @db.String(255) + created_at DateTime @default(now()) @db.Timestamptz(6) + updated_at DateTime @updatedAt + messages Message[] + users users[] @relation("GroupTousers") + + @@schema("messages") +} + +enum attending_status_type { + attending + attended + interested + + @@schema("users") +} diff --git a/citrus/tsconfig.json b/citrus/tsconfig.json index a076c45..492d280 100644 --- a/citrus/tsconfig.json +++ b/citrus/tsconfig.json @@ -17,14 +17,14 @@ "downlevelIteration": true, "plugins": [ { - "name": "next" + "name": "next", } ], "paths": { "@/*": ["./*"] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "effects/AblyReactEffect.js"], "exclude": ["node_modules"], "typeRoots": ["./node_modules/@types", "./types"], }