diff --git a/packages/db/prisma/migrations/20250120201905_add_role/migration.sql b/packages/db/prisma/migrations/20250120201905_add_role/migration.sql new file mode 100644 index 00000000..7636d14c --- /dev/null +++ b/packages/db/prisma/migrations/20250120201905_add_role/migration.sql @@ -0,0 +1,18 @@ +-- RedefineTables +PRAGMA defer_foreign_keys=ON; +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_UserToOrg" ( + "joinedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "orgId" INTEGER NOT NULL, + "userId" TEXT NOT NULL, + "role" TEXT NOT NULL DEFAULT 'MEMBER', + + PRIMARY KEY ("orgId", "userId"), + CONSTRAINT "UserToOrg_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "Org" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT "UserToOrg_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); +INSERT INTO "new_UserToOrg" ("joinedAt", "orgId", "userId") SELECT "joinedAt", "orgId", "userId" FROM "UserToOrg"; +DROP TABLE "UserToOrg"; +ALTER TABLE "new_UserToOrg" RENAME TO "UserToOrg"; +PRAGMA foreign_keys=ON; +PRAGMA defer_foreign_keys=OFF; diff --git a/packages/db/prisma/migrations/20250120222015_add_active_org_id/migration.sql b/packages/db/prisma/migrations/20250120222015_add_active_org_id/migration.sql new file mode 100644 index 00000000..2113e34a --- /dev/null +++ b/packages/db/prisma/migrations/20250120222015_add_active_org_id/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "User" ADD COLUMN "activeOrgId" INTEGER; diff --git a/packages/db/prisma/migrations/20250120230752_relate_repo_to_org/migration.sql b/packages/db/prisma/migrations/20250120230752_relate_repo_to_org/migration.sql new file mode 100644 index 00000000..7ef2794f --- /dev/null +++ b/packages/db/prisma/migrations/20250120230752_relate_repo_to_org/migration.sql @@ -0,0 +1,27 @@ +-- RedefineTables +PRAGMA defer_foreign_keys=ON; +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Repo" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "name" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + "indexedAt" DATETIME, + "isFork" BOOLEAN NOT NULL, + "isArchived" BOOLEAN NOT NULL, + "metadata" JSONB NOT NULL, + "cloneUrl" TEXT NOT NULL, + "tenantId" INTEGER NOT NULL, + "repoIndexingStatus" TEXT NOT NULL DEFAULT 'NEW', + "external_id" TEXT NOT NULL, + "external_codeHostType" TEXT NOT NULL, + "external_codeHostUrl" TEXT NOT NULL, + "orgId" INTEGER, + CONSTRAINT "Repo_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "Org" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); +INSERT INTO "new_Repo" ("cloneUrl", "createdAt", "external_codeHostType", "external_codeHostUrl", "external_id", "id", "indexedAt", "isArchived", "isFork", "metadata", "name", "repoIndexingStatus", "tenantId", "updatedAt") SELECT "cloneUrl", "createdAt", "external_codeHostType", "external_codeHostUrl", "external_id", "id", "indexedAt", "isArchived", "isFork", "metadata", "name", "repoIndexingStatus", "tenantId", "updatedAt" FROM "Repo"; +DROP TABLE "Repo"; +ALTER TABLE "new_Repo" RENAME TO "Repo"; +CREATE UNIQUE INDEX "Repo_external_id_external_codeHostUrl_key" ON "Repo"("external_id", "external_codeHostUrl"); +PRAGMA foreign_keys=ON; +PRAGMA defer_foreign_keys=OFF; diff --git a/packages/db/prisma/schema.prisma b/packages/db/prisma/schema.prisma index ea785ee3..92661bcb 100644 --- a/packages/db/prisma/schema.prisma +++ b/packages/db/prisma/schema.prisma @@ -47,6 +47,9 @@ model Repo { // The base url of the external service (e.g., https://github.com) external_codeHostUrl String + org Org? @relation(fields: [orgId], references: [id], onDelete: Cascade) + orgId Int? + @@unique([external_id, external_codeHostUrl]) } @@ -71,6 +74,12 @@ model Org { updatedAt DateTime @updatedAt members UserToOrg[] configs Config[] + repos Repo[] +} + +enum OrgRole { + OWNER + MEMBER } model UserToOrg { @@ -84,18 +93,21 @@ model UserToOrg { user User @relation(fields: [userId], references: [id], onDelete: Cascade) userId String + role OrgRole @default(MEMBER) + @@id([orgId, userId]) } // @see : https://authjs.dev/concepts/database-models#user model User { - id String @id @default(cuid()) + id String @id @default(cuid()) name String? - email String? @unique + email String? @unique emailVerified DateTime? image String? accounts Account[] orgs UserToOrg[] + activeOrgId Int? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt diff --git a/packages/web/package.json b/packages/web/package.json index b5079051..9a877dfb 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -41,6 +41,7 @@ "@iconify/react": "^5.1.0", "@iizukak/codemirror-lang-wgsl": "^0.3.0", "@radix-ui/react-avatar": "^1.1.2", + "@radix-ui/react-dialog": "^1.1.4", "@radix-ui/react-dropdown-menu": "^2.1.1", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.1.0", @@ -69,6 +70,7 @@ "client-only": "^0.0.1", "clsx": "^2.1.1", "cm6-graphql": "^0.2.0", + "cmdk": "1.0.0", "codemirror": "^5.65.3", "codemirror-lang-brainfuck": "^0.1.0", "codemirror-lang-elixir": "^4.0.0", @@ -110,6 +112,7 @@ "zod": "^3.23.8" }, "devDependencies": { + "@sourcebot/db": "^0.1.0", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", @@ -122,10 +125,9 @@ "jsdom": "^25.0.1", "npm-run-all": "^4.1.5", "postcss": "^8", - "@sourcebot/db": "^0.1.0", "tailwindcss": "^3.4.1", "typescript": "^5", "vite-tsconfig-paths": "^5.1.3", "vitest": "^2.1.5" } -} \ No newline at end of file +} diff --git a/packages/web/public/placeholder_avatar.png b/packages/web/public/placeholder_avatar.png new file mode 100644 index 00000000..6ebb0058 Binary files /dev/null and b/packages/web/public/placeholder_avatar.png differ diff --git a/packages/web/src/actions.ts b/packages/web/src/actions.ts new file mode 100644 index 00000000..858b7a8f --- /dev/null +++ b/packages/web/src/actions.ts @@ -0,0 +1,64 @@ +'use server'; + +import { auth } from "./auth"; +import { notAuthenticated, notFound } from "./lib/serviceError"; +import { prisma } from "@/prisma"; + + +export const createOrg = async (name: string) => { + const session = await auth(); + if (!session) { + return notAuthenticated(); + } + + // Create the org + const org = await prisma.org.create({ + data: { + name, + members: { + create: { + userId: session.user.id, + role: "OWNER", + }, + }, + } + }); + + return { + id: org.id, + } +} + +export const switchActiveOrg = async (orgId: number) => { + const session = await auth(); + if (!session) { + return notAuthenticated(); + } + + // Check to see if the user is a member of the org + const membership = await prisma.userToOrg.findUnique({ + where: { + orgId_userId: { + userId: session.user.id, + orgId, + } + }, + }); + if (!membership) { + return notFound(); + } + + // Update the user's active org + await prisma.user.update({ + where: { + id: session.user.id, + }, + data: { + activeOrgId: orgId, + } + }); + + return { + id: orgId, + } +} \ No newline at end of file diff --git a/packages/web/src/app/components/navigationMenu.tsx b/packages/web/src/app/components/navigationMenu.tsx index 9aebba2e..871259ab 100644 --- a/packages/web/src/app/components/navigationMenu.tsx +++ b/packages/web/src/app/components/navigationMenu.tsx @@ -8,6 +8,7 @@ import logoLight from "../../../public/sb_logo_light_small.png"; import { SettingsDropdown } from "./settingsDropdown"; import { GitHubLogoIcon, DiscordLogoIcon } from "@radix-ui/react-icons"; import { redirect } from "next/navigation"; +import { OrgSelector } from "./orgSelector"; const SOURCEBOT_DISCORD_URL = "https://discord.gg/6Fhp27x7Pb"; const SOURCEBOT_GITHUB_URL = "https://github.com/sourcebot-dev/sourcebot"; @@ -36,6 +37,9 @@ export const NavigationMenu = async () => { /> + + + diff --git a/packages/web/src/app/components/orgSelector/index.tsx b/packages/web/src/app/components/orgSelector/index.tsx new file mode 100644 index 00000000..922cb388 --- /dev/null +++ b/packages/web/src/app/components/orgSelector/index.tsx @@ -0,0 +1,31 @@ +import { auth } from "@/auth"; +import { getUser, getUserOrgs } from "../../data/user"; +import { OrgSelectorDropdown } from "./orgSelectorDropdown"; + +export const OrgSelector = async () => { + const session = await auth(); + if (!session) { + return null; + } + + const user = await getUser(session.user.id); + if (!user) { + return null; + } + + const orgs = await getUserOrgs(session.user.id); + const activeOrg = orgs.find((org) => org.id === user.activeOrgId); + if (!activeOrg) { + return null; + } + + return ( + ({ + name: org.name, + id: org.id, + }))} + activeOrgId={activeOrg.id} + /> + ) +} \ No newline at end of file diff --git a/packages/web/src/app/components/orgSelector/orgCreationDialog.tsx b/packages/web/src/app/components/orgSelector/orgCreationDialog.tsx new file mode 100644 index 00000000..fd8ae538 --- /dev/null +++ b/packages/web/src/app/components/orgSelector/orgCreationDialog.tsx @@ -0,0 +1,80 @@ +'use client'; +import { Button } from "@/components/ui/button"; +import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; +import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; +import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { PlusCircledIcon } from "@radix-ui/react-icons"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; + +const formSchema = z.object({ + name: z.string().min(2).max(40), +}); + + +interface OrgCreationDialogProps { + onSubmit: (data: z.infer) => void; + isOpen: boolean; + onOpenChange: (isOpen: boolean) => void; +} + +export const OrgCreationDialog = ({ + onSubmit, + isOpen, + onOpenChange, +}: OrgCreationDialogProps) => { + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + name: "", + }, + }); + + return ( + + + e.preventDefault()} + > + + + Create organization + + + + + + Create an organization + Organizations allow you to collaborate with team members. + + + + ( + + Organization name + + + + + + )} + /> + Submit + + + + + ) +} diff --git a/packages/web/src/app/components/orgSelector/orgIcon.tsx b/packages/web/src/app/components/orgSelector/orgIcon.tsx new file mode 100644 index 00000000..2be5bedc --- /dev/null +++ b/packages/web/src/app/components/orgSelector/orgIcon.tsx @@ -0,0 +1,36 @@ +import { cn } from "@/lib/utils"; +import placeholderAvatar from "@/public/placeholder_avatar.png"; +import { cva } from "class-variance-authority"; +import Image from "next/image"; + +interface OrgIconProps { + className?: string; + size?: "default"; +} + +const iconVariants = cva( + "rounded-full", + { + variants: { + size: { + default: "w-5 h-5" + } + }, + defaultVariants: { + size: "default" + } + }, +) + +export const OrgIcon = ({ + className, + size, +}: OrgIconProps) => { + return ( + + ) +} \ No newline at end of file diff --git a/packages/web/src/app/components/orgSelector/orgSelectorDropdown.tsx b/packages/web/src/app/components/orgSelector/orgSelectorDropdown.tsx new file mode 100644 index 00000000..b1a1d9c1 --- /dev/null +++ b/packages/web/src/app/components/orgSelector/orgSelectorDropdown.tsx @@ -0,0 +1,176 @@ +'use client'; +import { createOrg, switchActiveOrg } from "@/actions"; +import { useToast } from "@/components/hooks/use-toast"; +import { Button } from "@/components/ui/button"; +import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command"; +import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"; +import { isServiceError } from "@/lib/utils"; +import { CaretSortIcon, CheckIcon } from "@radix-ui/react-icons"; +import { useRouter } from "next/navigation"; +import { useCallback, useMemo, useState } from "react"; +import { OrgCreationDialog } from "./orgCreationDialog"; +import { OrgIcon } from "./orgIcon"; + + +interface OrgSelectorDropdownProps { + orgs: { + name: string, + id: number, + }[], + activeOrgId: number, +} + +export const OrgSelectorDropdown = ({ + orgs: _orgs, + activeOrgId +}: OrgSelectorDropdownProps) => { + const [searchFilter, setSearchFilter] = useState(""); + const [isDropdownOpen, setIsDropdownOpen] = useState(false); + const [isCreateOrgDialogOpen, setIsCreateOrgDialogOpen] = useState(false); + const { toast } = useToast(); + const router = useRouter(); + + const activeOrg = _orgs.find((org) => org.id === activeOrgId)!; + const orgs = useMemo(() => { + // always place the active org at the top + return [ + activeOrg, + ..._orgs.filter(org => org.id !== activeOrgId), + ]; + }, [_orgs, activeOrg, activeOrgId]); + + const onSwitchOrg = useCallback((orgId: number, orgName: string) => { + switchActiveOrg(orgId) + .then((response) => { + if (isServiceError(response)) { + toast({ + description: `❌ Failed to switch organization. Reason: ${response.message}`, + }); + } else { + toast({ + description: `✅ Switched to ${orgName}`, + }); + } + + + setIsDropdownOpen(false); + // Necessary to refresh the server component. + router.refresh(); + }); + }, [router, toast]); + + const onCreateOrg = useCallback((name: string) => { + createOrg(name) + .then((response) => { + if (isServiceError(response)) { + throw response; + } + + return switchActiveOrg(response.id); + }) + .then((response) => { + if (isServiceError(response)) { + throw response; + } + + toast({ + description: `✅ Organization '${name}' created successfully.`, + }); + + setIsDropdownOpen(false); + // Necessary to refresh the server component. + router.refresh(); + }) + .catch((error) => { + if (isServiceError(error)) { + toast({ + description: `❌ Failed to create organization. Reason: ${error.message}`, + }); + } + }) + .finally(() => { + setIsCreateOrgDialogOpen(false); + }); + }, [router, toast]); + + + return ( + /* + We need to set `modal=false` to fix a issue with having a dialog menu inside of + a dropdown menu. + @see : https://github.com/radix-ui/primitives/issues/1836#issuecomment-1547607143 + */ + + + + + {activeOrg.name} + + + + + + + setSearchFilter(value)} + autoFocus={true} + /> + + + No organization found + {`Your search term "${searchFilter}" did not match any organizations.`} + setSearchFilter("")} + > + Clear search + + + + {orgs.map((org, index) => ( + onSwitchOrg(org.id, org.name)} + > + + + {org.name} + + {org.id === activeOrgId && ( + + )} + + ))} + + + + + + {searchFilter.length === 0 && ( + + + onCreateOrg(name)} + /> + + )} + + + ); +} diff --git a/packages/web/src/app/data/user.ts b/packages/web/src/app/data/user.ts new file mode 100644 index 00000000..4047d11f --- /dev/null +++ b/packages/web/src/app/data/user.ts @@ -0,0 +1,26 @@ +import 'server-only'; +import { prisma } from "@/prisma"; + +export const getUser = async (userId: string) => { + const user = await prisma.user.findUnique({ + where: { + id: userId, + }, + }); + + return user; +} + +export const getUserOrgs = async (userId: string) => { + const orgs = await prisma.org.findMany({ + where: { + members: { + some: { + userId: userId, + }, + }, + }, + }); + + return orgs; +} \ No newline at end of file diff --git a/packages/web/src/auth.ts b/packages/web/src/auth.ts index 96d6560c..128822a9 100644 --- a/packages/web/src/auth.ts +++ b/packages/web/src/auth.ts @@ -1,9 +1,25 @@ -import NextAuth from "next-auth" +import 'next-auth/jwt'; +import NextAuth, { User as AuthJsUser, DefaultSession } from "next-auth" import GitHub from "next-auth/providers/github" import { PrismaAdapter } from "@auth/prisma-adapter" import { prisma } from "@/prisma"; import type { Provider } from "next-auth/providers" import { AUTH_GITHUB_CLIENT_ID, AUTH_GITHUB_CLIENT_SECRET, AUTH_SECRET } from "./lib/environment"; +import { User } from '@sourcebot/db'; + +declare module 'next-auth' { + interface Session { + user: { + id: string; + } & DefaultSession['user']; + } +} + +declare module 'next-auth/jwt' { + interface JWT { + userId: string + } + } const providers: Provider[] = [ GitHub({ @@ -24,6 +40,46 @@ export const providerMap = providers }) .filter((provider) => provider.id !== "credentials"); +const onCreateUser = async ({ user }: { user: AuthJsUser }) => { + if (!user.id) { + throw new Error("User ID is required."); + } + + const orgName = (() => { + if (user.name) { + return `${user.name}'s Org`; + } else { + return `Default Org`; + } + })(); + + await prisma.$transaction((async (tx) => { + const org = await tx.org.create({ + data: { + name: orgName, + members: { + create: { + role: "OWNER", + user: { + connect: { + id: user.id, + } + } + } + } + } + }); + + await tx.user.update({ + where: { + id: user.id, + }, + data: { + activeOrgId: org.id, + } + }); + })); +} export const { handlers, signIn, signOut, auth } = NextAuth({ secret: AUTH_SECRET, @@ -31,8 +87,32 @@ export const { handlers, signIn, signOut, auth } = NextAuth({ session: { strategy: "jwt", }, + events: { + createUser: onCreateUser, + }, + callbacks: { + async jwt({ token, user: _user }) { + const user = _user as User | undefined; + // @note: `user` will be available on signUp or signIn triggers. + // Cache the userId in the JWT for later use. + if (user) { + token.userId = user.id; + } + return token; + }, + async session({ session, token }) { + // @WARNING: Anything stored in the session will be sent over + // to the client. + session.user = { + ...session.user, + // Propogate the userId to the session. + id: token.userId, + } + return session; + } + }, providers: providers, pages: { signIn: "/login" } -}) +}); diff --git a/packages/web/src/components/ui/command.tsx b/packages/web/src/components/ui/command.tsx new file mode 100644 index 00000000..59a26452 --- /dev/null +++ b/packages/web/src/components/ui/command.tsx @@ -0,0 +1,153 @@ +"use client" + +import * as React from "react" +import { type DialogProps } from "@radix-ui/react-dialog" +import { Command as CommandPrimitive } from "cmdk" +import { Search } from "lucide-react" + +import { cn } from "@/lib/utils" +import { Dialog, DialogContent } from "@/components/ui/dialog" + +const Command = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Command.displayName = CommandPrimitive.displayName + +const CommandDialog = ({ children, ...props }: DialogProps) => { + return ( + + + + {children} + + + + ) +} + +const CommandInput = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + +)) + +CommandInput.displayName = CommandPrimitive.Input.displayName + +const CommandList = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) + +CommandList.displayName = CommandPrimitive.List.displayName + +const CommandEmpty = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>((props, ref) => ( + +)) + +CommandEmpty.displayName = CommandPrimitive.Empty.displayName + +const CommandGroup = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) + +CommandGroup.displayName = CommandPrimitive.Group.displayName + +const CommandSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +CommandSeparator.displayName = CommandPrimitive.Separator.displayName + +const CommandItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) + +CommandItem.displayName = CommandPrimitive.Item.displayName + +const CommandShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ) +} +CommandShortcut.displayName = "CommandShortcut" + +export { + Command, + CommandDialog, + CommandInput, + CommandList, + CommandEmpty, + CommandGroup, + CommandItem, + CommandShortcut, + CommandSeparator, +} diff --git a/packages/web/src/components/ui/dialog.tsx b/packages/web/src/components/ui/dialog.tsx new file mode 100644 index 00000000..01ff19c7 --- /dev/null +++ b/packages/web/src/components/ui/dialog.tsx @@ -0,0 +1,122 @@ +"use client" + +import * as React from "react" +import * as DialogPrimitive from "@radix-ui/react-dialog" +import { X } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Dialog = DialogPrimitive.Root + +const DialogTrigger = DialogPrimitive.Trigger + +const DialogPortal = DialogPrimitive.Portal + +const DialogClose = DialogPrimitive.Close + +const DialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogOverlay.displayName = DialogPrimitive.Overlay.displayName + +const DialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + {children} + + + Close + + + +)) +DialogContent.displayName = DialogPrimitive.Content.displayName + +const DialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( + +) +DialogHeader.displayName = "DialogHeader" + +const DialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( + +) +DialogFooter.displayName = "DialogFooter" + +const DialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogTitle.displayName = DialogPrimitive.Title.displayName + +const DialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogDescription.displayName = DialogPrimitive.Description.displayName + +export { + Dialog, + DialogPortal, + DialogOverlay, + DialogClose, + DialogTrigger, + DialogContent, + DialogHeader, + DialogFooter, + DialogTitle, + DialogDescription, +} diff --git a/packages/web/src/lib/errorCodes.ts b/packages/web/src/lib/errorCodes.ts index 2f5677f5..ba89333b 100644 --- a/packages/web/src/lib/errorCodes.ts +++ b/packages/web/src/lib/errorCodes.ts @@ -6,4 +6,5 @@ export enum ErrorCode { FILE_NOT_FOUND = 'FILE_NOT_FOUND', INVALID_REQUEST_BODY = 'INVALID_REQUEST_BODY', NOT_AUTHENTICATED = 'NOT_AUTHENTICATED', + NOT_FOUND = 'NOT_FOUND', } diff --git a/packages/web/src/lib/serviceError.ts b/packages/web/src/lib/serviceError.ts index 0d97e537..d8e94cfc 100644 --- a/packages/web/src/lib/serviceError.ts +++ b/packages/web/src/lib/serviceError.ts @@ -75,4 +75,12 @@ export const notAuthenticated = (): ServiceError => { errorCode: ErrorCode.NOT_AUTHENTICATED, message: "Not authenticated", } +} + +export const notFound = (): ServiceError => { + return { + statusCode: StatusCodes.NOT_FOUND, + errorCode: ErrorCode.NOT_FOUND, + message: "Not found", + } } \ No newline at end of file diff --git a/packages/web/src/middleware.ts b/packages/web/src/middleware.ts index febc8966..d92b192f 100644 --- a/packages/web/src/middleware.ts +++ b/packages/web/src/middleware.ts @@ -26,6 +26,9 @@ const defaultMiddleware = (req: NextAuthRequest) => { if (!req.auth && req.nextUrl.pathname !== "/login") { const newUrl = new URL("/login", req.nextUrl.origin); return NextResponse.redirect(newUrl); + } else if (req.auth && req.nextUrl.pathname === "/login") { + const newUrl = new URL("/", req.nextUrl.origin); + return NextResponse.redirect(newUrl); } return NextResponse.next(); diff --git a/packages/web/src/prisma.ts b/packages/web/src/prisma.ts index 5f5b674e..bbd1bc46 100644 --- a/packages/web/src/prisma.ts +++ b/packages/web/src/prisma.ts @@ -1,3 +1,4 @@ +import 'server-only'; import { PrismaClient } from "@sourcebot/db"; // @see: https://authjs.dev/getting-started/adapters/prisma diff --git a/yarn.lock b/yarn.lock index 8314f3d2..fa37ae17 100644 --- a/yarn.lock +++ b/yarn.lock @@ -47,6 +47,13 @@ dependencies: "@auth/core" "0.37.4" +"@babel/runtime@^7.13.10": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1" + integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/runtime@^7.18.6": version "7.25.7" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.25.7.tgz#7ffb53c37a8f247c8c4d335e89cdf16a2e0d0fb6" @@ -1357,11 +1364,23 @@ resolved "https://registry.yarnpkg.com/@radix-ui/number/-/number-1.1.0.tgz#1e95610461a09cdf8bb05c152e76ca1278d5da46" integrity sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ== +"@radix-ui/primitive@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.0.1.tgz#e46f9958b35d10e9f6dc71c497305c22e3e55dbd" + integrity sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.0.tgz#42ef83b3b56dccad5d703ae8c42919a68798bbe2" integrity sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA== +"@radix-ui/primitive@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.1.tgz#fc169732d755c7fbad33ba8d0cd7fd10c90dc8e3" + integrity sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA== + "@radix-ui/react-arrow@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz#744f388182d360b86285217e43b6c63633f39e7a" @@ -1389,6 +1408,13 @@ "@radix-ui/react-primitive" "2.0.0" "@radix-ui/react-slot" "1.1.0" +"@radix-ui/react-compose-refs@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz#7ed868b66946aa6030e580b1ffca386dd4d21989" + integrity sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-compose-refs@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz#656432461fc8283d7b591dcf0d79152fae9ecc74" @@ -1399,6 +1425,13 @@ resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz#6f766faa975f8738269ebb8a23bad4f5a8d2faec" integrity sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw== +"@radix-ui/react-context@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.0.1.tgz#fe46e67c96b240de59187dcb7a1a50ce3e2ec00c" + integrity sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-context@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.0.tgz#6df8d983546cfd1999c8512f3a8ad85a6e7fcee8" @@ -1409,11 +1442,64 @@ resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.1.tgz#82074aa83a472353bb22e86f11bcbd1c61c4c71a" integrity sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q== +"@radix-ui/react-dialog@1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz#71657b1b116de6c7a0b03242d7d43e01062c7300" + integrity sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive" "1.0.1" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-context" "1.0.1" + "@radix-ui/react-dismissable-layer" "1.0.5" + "@radix-ui/react-focus-guards" "1.0.1" + "@radix-ui/react-focus-scope" "1.0.4" + "@radix-ui/react-id" "1.0.1" + "@radix-ui/react-portal" "1.0.4" + "@radix-ui/react-presence" "1.0.1" + "@radix-ui/react-primitive" "1.0.3" + "@radix-ui/react-slot" "1.0.2" + "@radix-ui/react-use-controllable-state" "1.0.1" + aria-hidden "^1.1.1" + react-remove-scroll "2.5.5" + +"@radix-ui/react-dialog@^1.1.4": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.1.4.tgz#d68e977acfcc0d044b9dab47b6dd2c179d2b3191" + integrity sha512-Ur7EV1IwQGCyaAuyDRiOLA5JIUZxELJljF+MbM/2NC0BYwfuRrbpS30BiQBJrVruscgUkieKkqXYDOoByaxIoA== + dependencies: + "@radix-ui/primitive" "1.1.1" + "@radix-ui/react-compose-refs" "1.1.1" + "@radix-ui/react-context" "1.1.1" + "@radix-ui/react-dismissable-layer" "1.1.3" + "@radix-ui/react-focus-guards" "1.1.1" + "@radix-ui/react-focus-scope" "1.1.1" + "@radix-ui/react-id" "1.1.0" + "@radix-ui/react-portal" "1.1.3" + "@radix-ui/react-presence" "1.1.2" + "@radix-ui/react-primitive" "2.0.1" + "@radix-ui/react-slot" "1.1.1" + "@radix-ui/react-use-controllable-state" "1.1.0" + aria-hidden "^1.1.1" + react-remove-scroll "^2.6.1" + "@radix-ui/react-direction@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.1.0.tgz#a7d39855f4d077adc2a1922f9c353c5977a09cdc" integrity sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg== +"@radix-ui/react-dismissable-layer@1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz#3f98425b82b9068dfbab5db5fff3df6ebf48b9d4" + integrity sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive" "1.0.1" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-primitive" "1.0.3" + "@radix-ui/react-use-callback-ref" "1.0.1" + "@radix-ui/react-use-escape-keydown" "1.0.3" + "@radix-ui/react-dismissable-layer@1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.1.tgz#cbdcb739c5403382bdde5f9243042ba643883396" @@ -1425,6 +1511,17 @@ "@radix-ui/react-use-callback-ref" "1.1.0" "@radix-ui/react-use-escape-keydown" "1.1.0" +"@radix-ui/react-dismissable-layer@1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.3.tgz#4ee0f0f82d53bf5bd9db21665799bb0d1bad5ed8" + integrity sha512-onrWn/72lQoEucDmJnr8uczSNTujT0vJnA/X5+3AkChVPowr8n1yvIKIabhWyMQeMvvmdpsvcyDqx3X1LEXCPg== + dependencies: + "@radix-ui/primitive" "1.1.1" + "@radix-ui/react-compose-refs" "1.1.1" + "@radix-ui/react-primitive" "2.0.1" + "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-use-escape-keydown" "1.1.0" + "@radix-ui/react-dropdown-menu@^2.1.1": version "2.1.2" resolved "https://registry.yarnpkg.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.2.tgz#acc49577130e3c875ef0133bd1e271ea3392d924" @@ -1438,11 +1535,28 @@ "@radix-ui/react-primitive" "2.0.0" "@radix-ui/react-use-controllable-state" "1.1.0" +"@radix-ui/react-focus-guards@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz#1ea7e32092216b946397866199d892f71f7f98ad" + integrity sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-focus-guards@1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz#8635edd346304f8b42cae86b05912b61aef27afe" integrity sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg== +"@radix-ui/react-focus-scope@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz#2ac45fce8c5bb33eb18419cdc1905ef4f1906525" + integrity sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-primitive" "1.0.3" + "@radix-ui/react-use-callback-ref" "1.0.1" + "@radix-ui/react-focus-scope@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.0.tgz#ebe2891a298e0a33ad34daab2aad8dea31caf0b2" @@ -1452,11 +1566,28 @@ "@radix-ui/react-primitive" "2.0.0" "@radix-ui/react-use-callback-ref" "1.1.0" +"@radix-ui/react-focus-scope@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.1.tgz#5c602115d1db1c4fcfa0fae4c3b09bb8919853cb" + integrity sha512-01omzJAYRxXdG2/he/+xy+c8a8gCydoQ1yOxnWNcRhrrBW5W+RQJ22EK1SaO8tb3WoUsuEw7mJjBozPzihDFjA== + dependencies: + "@radix-ui/react-compose-refs" "1.1.1" + "@radix-ui/react-primitive" "2.0.1" + "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-icons@^1.3.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-icons/-/react-icons-1.3.0.tgz#c61af8f323d87682c5ca76b856d60c2312dbcb69" integrity sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw== +"@radix-ui/react-id@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.0.1.tgz#73cdc181f650e4df24f0b6a5b7aa426b912c88c0" + integrity sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-use-layout-effect" "1.0.1" + "@radix-ui/react-id@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.1.0.tgz#de47339656594ad722eb87f94a6b25f9cffae0ed" @@ -1531,6 +1662,14 @@ "@radix-ui/react-use-size" "1.1.0" "@radix-ui/rect" "1.1.0" +"@radix-ui/react-portal@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.0.4.tgz#df4bfd353db3b1e84e639e9c63a5f2565fb00e15" + integrity sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-primitive" "1.0.3" + "@radix-ui/react-portal@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.1.2.tgz#51eb46dae7505074b306ebcb985bf65cc547d74e" @@ -1539,6 +1678,23 @@ "@radix-ui/react-primitive" "2.0.0" "@radix-ui/react-use-layout-effect" "1.1.0" +"@radix-ui/react-portal@1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.1.3.tgz#b0ea5141103a1671b715481b13440763d2ac4440" + integrity sha512-NciRqhXnGojhT93RPyDaMPfLH3ZSl4jjIFbZQ1b/vxvZEdHsBZ49wP9w8L3HzUQwep01LcWtkUvm0OVB5JAHTw== + dependencies: + "@radix-ui/react-primitive" "2.0.1" + "@radix-ui/react-use-layout-effect" "1.1.0" + +"@radix-ui/react-presence@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.0.1.tgz#491990ba913b8e2a5db1b06b203cb24b5cdef9ba" + integrity sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-use-layout-effect" "1.0.1" + "@radix-ui/react-presence@1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.1.1.tgz#98aba423dba5e0c687a782c0669dcd99de17f9b1" @@ -1547,6 +1703,22 @@ "@radix-ui/react-compose-refs" "1.1.0" "@radix-ui/react-use-layout-effect" "1.1.0" +"@radix-ui/react-presence@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.1.2.tgz#bb764ed8a9118b7ec4512da5ece306ded8703cdc" + integrity sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg== + dependencies: + "@radix-ui/react-compose-refs" "1.1.1" + "@radix-ui/react-use-layout-effect" "1.1.0" + +"@radix-ui/react-primitive@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz#d49ea0f3f0b2fe3ab1cb5667eb03e8b843b914d0" + integrity sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-slot" "1.0.2" + "@radix-ui/react-primitive@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz#fe05715faa9203a223ccc0be15dc44b9f9822884" @@ -1598,6 +1770,14 @@ dependencies: "@radix-ui/react-primitive" "2.0.0" +"@radix-ui/react-slot@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.0.2.tgz#a9ff4423eade67f501ffb32ec22064bc9d3099ab" + integrity sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-slot@1.1.0", "@radix-ui/react-slot@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.1.0.tgz#7c5e48c36ef5496d97b08f1357bb26ed7c714b84" @@ -1657,11 +1837,26 @@ "@radix-ui/react-use-controllable-state" "1.1.0" "@radix-ui/react-visually-hidden" "1.1.0" +"@radix-ui/react-use-callback-ref@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz#f4bb1f27f2023c984e6534317ebc411fc181107a" + integrity sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-use-callback-ref@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz#bce938ca413675bc937944b0d01ef6f4a6dc5bf1" integrity sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw== +"@radix-ui/react-use-controllable-state@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz#ecd2ced34e6330caf89a82854aa2f77e07440286" + integrity sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-use-callback-ref" "1.0.1" + "@radix-ui/react-use-controllable-state@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz#1321446857bb786917df54c0d4d084877aab04b0" @@ -1669,6 +1864,14 @@ dependencies: "@radix-ui/react-use-callback-ref" "1.1.0" +"@radix-ui/react-use-escape-keydown@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz#217b840c250541609c66f67ed7bab2b733620755" + integrity sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-use-callback-ref" "1.0.1" + "@radix-ui/react-use-escape-keydown@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz#31a5b87c3b726504b74e05dac1edce7437b98754" @@ -1676,6 +1879,13 @@ dependencies: "@radix-ui/react-use-callback-ref" "1.1.0" +"@radix-ui/react-use-layout-effect@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz#be8c7bc809b0c8934acf6657b577daf948a75399" + integrity sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-use-layout-effect@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz#3c2c8ce04827b26a39e442ff4888d9212268bd27" @@ -2715,6 +2925,14 @@ cm6-graphql@^0.2.0: dependencies: graphql-language-service "^5.3.0" +cmdk@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/cmdk/-/cmdk-1.0.0.tgz#0a095fdafca3dfabed82d1db78a6262fb163ded9" + integrity sha512-gDzVf0a09TvoJ5jnuPvygTB77+XdOSwEmJ88L6XPFPlv7T3RxbP9jgenfylrAMD0+Le1aO0nVjQUzl2g+vjz5Q== + dependencies: + "@radix-ui/react-dialog" "1.0.5" + "@radix-ui/react-primitive" "1.0.3" + codemirror-lang-brainfuck@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/codemirror-lang-brainfuck/-/codemirror-lang-brainfuck-0.1.0.tgz#528d8a4dd4c7c1f57151f6f1c8141719e7313c18" @@ -5374,6 +5592,14 @@ react-is@^16.13.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +react-remove-scroll-bar@^2.3.3, react-remove-scroll-bar@^2.3.7: + version "2.3.8" + resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz#99c20f908ee467b385b68a3469b4a3e750012223" + integrity sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q== + dependencies: + react-style-singleton "^2.2.2" + tslib "^2.0.0" + react-remove-scroll-bar@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz#3e585e9d163be84a010180b18721e851ac81a29c" @@ -5382,6 +5608,17 @@ react-remove-scroll-bar@^2.3.6: react-style-singleton "^2.2.1" tslib "^2.0.0" +react-remove-scroll@2.5.5: + version "2.5.5" + resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz#1e31a1260df08887a8a0e46d09271b52b3a37e77" + integrity sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw== + dependencies: + react-remove-scroll-bar "^2.3.3" + react-style-singleton "^2.2.1" + tslib "^2.1.0" + use-callback-ref "^1.3.0" + use-sidecar "^1.1.2" + react-remove-scroll@2.6.0: version "2.6.0" resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.6.0.tgz#fb03a0845d7768a4f1519a99fdb84983b793dc07" @@ -5393,6 +5630,17 @@ react-remove-scroll@2.6.0: use-callback-ref "^1.3.0" use-sidecar "^1.1.2" +react-remove-scroll@^2.6.1: + version "2.6.2" + resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.6.2.tgz#2518d2c5112e71ea8928f1082a58459b5c7a2a97" + integrity sha512-KmONPx5fnlXYJQqC62Q+lwIeAk64ws/cUw6omIumRzMRPqgnYqhSSti99nbj0Ry13bv7dF+BKn7NB+OqkdZGTw== + dependencies: + react-remove-scroll-bar "^2.3.7" + react-style-singleton "^2.2.1" + tslib "^2.1.0" + use-callback-ref "^1.3.3" + use-sidecar "^1.1.2" + react-resizable-panels@^2.1.1: version "2.1.4" resolved "https://registry.yarnpkg.com/react-resizable-panels/-/react-resizable-panels-2.1.4.tgz#ae1803a916ba759e483336c7bd49830f1b0cd16f" @@ -5407,6 +5655,14 @@ react-style-singleton@^2.2.1: invariant "^2.2.4" tslib "^2.0.0" +react-style-singleton@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.2.3.tgz#4265608be69a4d70cfe3047f2c6c88b2c3ace388" + integrity sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ== + dependencies: + get-nonce "^1.0.0" + tslib "^2.0.0" + react@^18: version "18.3.1" resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" @@ -6348,6 +6604,13 @@ use-callback-ref@^1.3.0: dependencies: tslib "^2.0.0" +use-callback-ref@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.3.3.tgz#98d9fab067075841c5b2c6852090d5d0feabe2bf" + integrity sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg== + dependencies: + tslib "^2.0.0" + use-sidecar@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.1.2.tgz#2f43126ba2d7d7e117aa5855e5d8f0276dfe73c2"
{activeOrg.name}
No organization found
{`Your search term "${searchFilter}" did not match any organizations.`}