clerk #1

Merged
afiqzudinhadi merged 7 commits from clerk into main 2025-07-08 21:45:30 +08:00
23 changed files with 560 additions and 35 deletions

View file

@ -21,6 +21,7 @@ This Turborepo includes the following packages and apps:
- `@repo/ui`: shared UI components for frontend use. - `@repo/ui`: shared UI components for frontend use.
- A dummy React UI library (which contains `<CounterButton>` and `<Link>` components) - A dummy React UI library (which contains `<CounterButton>` and `<Link>` components)
- And shadcn/ui components (which contains `<Button>` and `<Card>` components) - And shadcn/ui components (which contains `<Button>` and `<Card>` components)
- `@repo/auth`: shared auth helpers using [Clerk](https://clerk.com/) as auth provider
Each package and app is 100% [TypeScript](https://www.typescriptlang.org/). Each package and app is 100% [TypeScript](https://www.typescriptlang.org/).

View file

@ -0,0 +1,2 @@
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=
CLERK_SECRET_KEY=

3
apps/next-template/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
# clerk configuration (can include secrets)
/.clerk/

View file

@ -12,6 +12,7 @@
"clean": "rm -rf .next" "clean": "rm -rf .next"
}, },
"dependencies": { "dependencies": {
"@clerk/nextjs": "^6.22.0",
"@repo/logger": "*", "@repo/logger": "*",
"@repo/ui": "*", "@repo/ui": "*",
"next": "^15.3.0", "next": "^15.3.0",

View file

@ -0,0 +1,23 @@
import {
Card,
CardHeader,
CardTitle,
CardContent,
} from "@repo/ui/components/card";
export default function ClerkTestPage() {
return (
<div className="flex items-center justify-center min-h-screen bg-muted">
<Card className="w-full max-w-md shadow-lg">
<CardHeader>
<CardTitle>Welcome, you are logged in!</CardTitle>
</CardHeader>
<CardContent>
<p className="text-muted-foreground">
This is a protected test page.
</p>
</CardContent>
</Card>
</div>
);
}

View file

@ -1,5 +1,14 @@
import "@repo/ui/styles/styles.css"; import "@repo/ui/styles/styles.css";
import "@repo/ui/styles/global.css"; import "@repo/ui/styles/global.css";
import {
ClerkProvider,
SignInButton,
SignUpButton,
SignedIn,
SignedOut,
UserButton,
} from "@clerk/nextjs";
import { Button } from "@repo/ui/components/button";
export default function RootLayout({ export default function RootLayout({
children, children,
@ -7,8 +16,24 @@ export default function RootLayout({
children: React.ReactNode; children: React.ReactNode;
}) { }) {
return ( return (
<html lang="en"> <ClerkProvider>
<body>{children}</body> <html lang="en">
</html> <body>
<header className="flex items-center justify-end py-4 px-6 bg-white shadow-sm mb-6">
<div>
<SignedOut>
<SignInButton mode="modal">
<Button variant="outline">Sign In</Button>
</SignInButton>
</SignedOut>
<SignedIn>
<UserButton afterSignOutUrl="/" />
</SignedIn>
</div>
</header>
{children}
</body>
</html>
</ClerkProvider>
); );
} }

View file

@ -0,0 +1,28 @@
import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";
const isProtectedRoute = createRouteMatcher(["/clerk(.*)"]);
export default clerkMiddleware(async (auth, req) => {
// Uncomment the following line to enable Clerk's default behavior | Redirect to sign-in page if the user is not authenticated
// if (isProtectedRoute(req)) await auth.protect();
// More control over authentication flow
const { userId, redirectToSignIn } = await auth();
if (!userId && isProtectedRoute(req)) {
// Add custom logic to run before redirecting
console.log(
"User is not authenticated, redirecting to sign-in page..."
);
return redirectToSignIn();
}
});
export const config = {
matcher: [
// Skip Next.js internals and all static files, unless found in search params
"/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)",
// Always run for API routes
"/(api|trpc)(.*)",
],
};

View file

@ -0,0 +1,2 @@
CLERK_PUBLISHABLE_KEY=
CLERK_SECRET_KEY=

View file

@ -6,9 +6,15 @@ import {
ScrollRestoration, ScrollRestoration,
} from "@remix-run/react"; } from "@remix-run/react";
import { Analytics } from "@vercel/analytics/react"; import { Analytics } from "@vercel/analytics/react";
import "@repo/ui/styles/styles.css"; import "@repo/ui/styles/styles.css";
import "@repo/ui/styles/global.css"; import "@repo/ui/styles/global.css";
import type { LoaderFunction } from "@remix-run/node";
import { rootAuthLoader } from "@clerk/remix/ssr.server";
import { ClerkApp } from "@clerk/remix";
export const loader: LoaderFunction = (args) => rootAuthLoader(args);
export function Layout({ children }: { children: React.ReactNode }) { export function Layout({ children }: { children: React.ReactNode }) {
return ( return (
<html lang="en"> <html lang="en">
@ -31,6 +37,8 @@ export function Layout({ children }: { children: React.ReactNode }) {
); );
} }
export default function App() { export function App() {
return <Outlet />; return <Outlet />;
} }
export default ClerkApp(App);

View file

@ -0,0 +1,40 @@
import { SignInButton, UserButton, SignedIn, SignedOut } from "@clerk/remix";
import {
Card,
CardContent,
CardHeader,
CardTitle,
} from "@repo/ui/components/card";
import { Button } from "@repo/ui/components/button";
export default function Index() {
return (
<div className="flex min-h-screen items-center justify-center">
<Card className="w-full max-w-md shadow-lg">
<CardHeader>
<CardTitle className="text-center">Welcome</CardTitle>
</CardHeader>
<CardContent>
<SignedIn>
<div className="flex flex-col items-center gap-4">
<p className="text-green-700 font-medium">
You are signed in!
</p>
<UserButton />
</div>
</SignedIn>
<SignedOut>
<div className="flex flex-col items-center gap-4">
<p className="text-red-700 font-medium">
You are signed out
</p>
<SignInButton mode="modal">
<Button variant="default">Sign In</Button>
</SignInButton>
</div>
</SignedOut>
</CardContent>
</Card>
</div>
);
}

View file

@ -0,0 +1,24 @@
import { SignInButton, UserButton, SignedIn, SignedOut } from "@clerk/remix";
import { Button } from "@repo/ui/components/button";
import { Outlet } from "@remix-run/react";
export default function Index() {
return (
<div>
<header className="flex items-center justify-end py-4 px-6 bg-white shadow-sm mb-6">
<div>
<SignedOut>
<SignInButton mode="modal">
<Button variant="outline">Sign In</Button>
</SignInButton>
</SignedOut>
<SignedIn>
<UserButton />
</SignedIn>
</div>
</header>
<Outlet />
</div>
);
}

View file

@ -11,10 +11,12 @@
"lint": "eslint app/ --max-warnings 0" "lint": "eslint app/ --max-warnings 0"
}, },
"dependencies": { "dependencies": {
"@clerk/remix": "^4.8.1",
"@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-slot": "^1.2.3",
"@remix-run/node": "^2.15.2", "@remix-run/node": "^2.15.2",
"@remix-run/react": "^2.15.2", "@remix-run/react": "^2.15.2",
"@remix-run/server-runtime": "^2.15.2", "@remix-run/server-runtime": "^2.15.2",
"@repo/auth": "*",
"@repo/ui": "*", "@repo/ui": "*",
"@vercel/analytics": "^1.5.0", "@vercel/analytics": "^1.5.0",
"@vercel/remix": "2.16.6", "@vercel/remix": "2.16.6",

View file

@ -0,0 +1 @@
VITE_CLERK_PUBLISHABLE_KEY=

View file

@ -10,6 +10,7 @@
"lint": "eslint src/ --max-warnings 0" "lint": "eslint src/ --max-warnings 0"
}, },
"dependencies": { "dependencies": {
"@clerk/clerk-react": "^5.32.0",
"@repo/ui": "*", "@repo/ui": "*",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1" "react-dom": "^18.3.1"

View file

@ -10,33 +10,66 @@ import {
CardHeader, CardHeader,
CardTitle, CardTitle,
} from "@repo/ui/components/card"; } from "@repo/ui/components/card";
import {
SignedIn,
SignedOut,
SignInButton,
UserButton,
} from "@clerk/clerk-react";
function App() { function App() {
return ( return (
<div className="container"> <div>
<h1 className="title"> <header className="flex items-center justify-end py-4 px-6 bg-white shadow-sm mb-6">
Admin <br /> <div>
<span>Kitchen Sink</span> <SignedOut>
</h1> <SignInButton mode="modal">
<CounterButton /> <Button variant="outline">Sign In</Button>
<Card className="my-6"> </SignInButton>
<CardHeader> </SignedOut>
<CardTitle>shadcn/ui Card Example</CardTitle> <SignedIn>
</CardHeader> <UserButton />
<CardContent> </SignedIn>
<Button>shadcn/ui Button</Button> </div>
</CardContent> </header>
</Card>
<p className="description"> <div className="container">
Built With{" "} <h1 className="title">
<Link href="https://turborepo.com" newTab> Admin <br />
Turborepo <span>Kitchen Sink</span>
</Link> </h1>
{" & "} <CounterButton />
<Link href="https://vitejs.dev/" newTab>
Vite <Card className="my-6">
</Link> <CardHeader>
</p> <CardTitle>shadcn/ui Card Example</CardTitle>
</CardHeader>
<CardContent>
<Button>shadcn/ui Button</Button>
</CardContent>
</Card>
<Card className="my-6">
<CardHeader>
<CardTitle>Clerk Authentication Example</CardTitle>
</CardHeader>
<CardContent>
<SignedOut>You are signed out!</SignedOut>
<SignedIn>You are signed in!</SignedIn>
</CardContent>
</Card>
<p className="description">
Built With{" "}
<Link href="https://turborepo.com" newTab>
Turborepo
</Link>
{" & "}
<Link href="https://vitejs.dev/" newTab>
Vite
</Link>
</p>
</div>
</div> </div>
); );
} }

View file

@ -2,15 +2,26 @@ import React from "react";
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import App from "./app"; import App from "./app";
import "./index.css"; import "./index.css";
import { ClerkProvider } from "@clerk/clerk-react";
const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY;
if (!PUBLISHABLE_KEY) {
throw new Error(
"VITE_PUBLISHABLE_KEY is not defined. Please set it in your environment variables."
);
}
const el = document.getElementById("root"); const el = document.getElementById("root");
if (el) { if (el) {
const root = createRoot(el); const root = createRoot(el);
root.render( root.render(
<React.StrictMode> <React.StrictMode>
<App /> <ClerkProvider publishableKey={PUBLISHABLE_KEY} afterSignOutUrl="/">
</React.StrictMode> <App />
); </ClerkProvider>
</React.StrictMode>
);
} else { } else {
throw new Error("Could not find root element"); throw new Error("Could not find root element");
} }

279
package-lock.json generated
View file

@ -125,6 +125,7 @@
"apps/next-template": { "apps/next-template": {
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"@clerk/nextjs": "^6.22.0",
"@repo/logger": "*", "@repo/logger": "*",
"@repo/ui": "*", "@repo/ui": "*",
"next": "^15.3.0", "next": "^15.3.0",
@ -166,10 +167,12 @@
"apps/remix-template": { "apps/remix-template": {
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"@clerk/remix": "^4.8.1",
"@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-slot": "^1.2.3",
"@remix-run/node": "^2.15.2", "@remix-run/node": "^2.15.2",
"@remix-run/react": "^2.15.2", "@remix-run/react": "^2.15.2",
"@remix-run/server-runtime": "^2.15.2", "@remix-run/server-runtime": "^2.15.2",
"@repo/auth": "*",
"@repo/ui": "*", "@repo/ui": "*",
"@vercel/analytics": "^1.5.0", "@vercel/analytics": "^1.5.0",
"@vercel/remix": "2.16.6", "@vercel/remix": "2.16.6",
@ -274,6 +277,7 @@
"apps/vite-template": { "apps/vite-template": {
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"@clerk/clerk-react": "^5.32.0",
"@repo/ui": "*", "@repo/ui": "*",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1" "react-dom": "^18.3.1"
@ -1034,6 +1038,146 @@
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@clerk/backend": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@clerk/backend/-/backend-2.1.0.tgz",
"integrity": "sha512-Ltb3IzdGeahM1Jr+u0UAVnVjEIYXUsq9/mTb1adolsOc9oR2LCKHE7rTRnmkwY8y+IwCzXVuywIYW+zLTk47+Q==",
"license": "MIT",
"dependencies": {
"@clerk/shared": "^3.9.7",
"@clerk/types": "^4.60.1",
"cookie": "1.0.2",
"snakecase-keys": "8.0.1",
"tslib": "2.8.1"
},
"engines": {
"node": ">=18.17.0"
}
},
"node_modules/@clerk/backend/node_modules/cookie": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
"integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/@clerk/clerk-react": {
"version": "5.32.0",
"resolved": "https://registry.npmjs.org/@clerk/clerk-react/-/clerk-react-5.32.0.tgz",
"integrity": "sha512-RdipOVBdbUbFaDb8kF3EzhJw0eh5LPxSWWRTu4k+pOdRB1gzETiToLchj5nDWFkl8qW/uL4gQbZH+Pfc7izO0A==",
"license": "MIT",
"dependencies": {
"@clerk/shared": "^3.9.7",
"@clerk/types": "^4.60.1",
"tslib": "2.8.1"
},
"engines": {
"node": ">=18.17.0"
},
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0 || ^19.0.0-0",
"react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-0"
}
},
"node_modules/@clerk/nextjs": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/@clerk/nextjs/-/nextjs-6.22.0.tgz",
"integrity": "sha512-Lh+hq4iuvuSgiWSDHDM9BAM8+YZU/wtADGo2TAU7FBVEZ6I3SwyXo5JEY4F7s30TpH3kS9qC/Ye3YA6VIOOu7Q==",
"license": "MIT",
"dependencies": {
"@clerk/backend": "^2.1.0",
"@clerk/clerk-react": "^5.32.0",
"@clerk/shared": "^3.9.7",
"@clerk/types": "^4.60.1",
"server-only": "0.0.1",
"tslib": "2.8.1"
},
"engines": {
"node": ">=18.17.0"
},
"peerDependencies": {
"next": "^13.5.7 || ^14.2.25 || ^15.2.3",
"react": "^18.0.0 || ^19.0.0 || ^19.0.0-0",
"react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-0"
}
},
"node_modules/@clerk/remix": {
"version": "4.8.1",
"resolved": "https://registry.npmjs.org/@clerk/remix/-/remix-4.8.1.tgz",
"integrity": "sha512-QOK9tuijOrAzRDo8Cy+d4PqQfVChXr93+RyozI1gLlCOwTVtJHhlxUhWeR3hg+w+QRmuilSgv99odGwaEre4WQ==",
"license": "MIT",
"dependencies": {
"@clerk/backend": "^2.1.0",
"@clerk/clerk-react": "^5.32.0",
"@clerk/shared": "^3.9.7",
"@clerk/types": "^4.60.1",
"cookie": "0.7.2",
"tslib": "2.8.1"
},
"engines": {
"node": ">=18.17.0"
},
"peerDependencies": {
"@remix-run/react": "^2.0.0",
"@remix-run/server-runtime": "^2.0.0",
"react": "^18.0.0 || ^19.0.0 || ^19.0.0-0",
"react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-0",
"react-router": "^6.0.0"
}
},
"node_modules/@clerk/remix/node_modules/cookie": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/@clerk/shared": {
"version": "3.9.7",
"resolved": "https://registry.npmjs.org/@clerk/shared/-/shared-3.9.7.tgz",
"integrity": "sha512-D3iuzuDoadK6BKb7qFZEH332z4Sl5mxLuHMf8rKdIMHuOu1RQK0RqFJtKPcFvDxq0RvJ9MC+rK24P2Sa2tOoNA==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@clerk/types": "^4.60.1",
"dequal": "2.0.3",
"glob-to-regexp": "0.4.1",
"js-cookie": "3.0.5",
"std-env": "^3.9.0",
"swr": "^2.3.3"
},
"engines": {
"node": ">=18.17.0"
},
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0 || ^19.0.0-0",
"react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-0"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/@clerk/types": {
"version": "4.60.1",
"resolved": "https://registry.npmjs.org/@clerk/types/-/types-4.60.1.tgz",
"integrity": "sha512-Lbt0/Q9W9BJtGz1IITdmeqal7y1zp9EaxOEMvsTEDhOxKf/0F/M1d0gJmHEU0puQzYPp9zJsIWC2yJjbgy3Y3Q==",
"license": "MIT",
"dependencies": {
"csstype": "3.1.3"
},
"engines": {
"node": ">=18.17.0"
}
},
"node_modules/@cspotcode/source-map-support": { "node_modules/@cspotcode/source-map-support": {
"version": "0.8.1", "version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
@ -3525,6 +3669,10 @@
"web-streams-polyfill": "^3.1.1" "web-streams-polyfill": "^3.1.1"
} }
}, },
"node_modules/@repo/auth": {
"resolved": "packages/auth",
"link": true
},
"node_modules/@repo/eslint-config": { "node_modules/@repo/eslint-config": {
"resolved": "packages/config-eslint", "resolved": "packages/config-eslint",
"link": true "link": true
@ -7663,6 +7811,16 @@
"node": ">=12" "node": ">=12"
} }
}, },
"node_modules/dot-case": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
"integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
"license": "MIT",
"dependencies": {
"no-case": "^3.0.4",
"tslib": "^2.0.3"
}
},
"node_modules/dotenv": { "node_modules/dotenv": {
"version": "16.5.0", "version": "16.5.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz",
@ -9409,6 +9567,12 @@
"node": ">= 6" "node": ">= 6"
} }
}, },
"node_modules/glob-to-regexp": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
"license": "BSD-2-Clause"
},
"node_modules/globals": { "node_modules/globals": {
"version": "16.2.0", "version": "16.2.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-16.2.0.tgz", "resolved": "https://registry.npmjs.org/globals/-/globals-16.2.0.tgz",
@ -11333,6 +11497,15 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/js-cookie": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
"integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==",
"license": "MIT",
"engines": {
"node": ">=14"
}
},
"node_modules/js-tokens": { "node_modules/js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@ -11936,6 +12109,15 @@
"loose-envify": "cli.js" "loose-envify": "cli.js"
} }
}, },
"node_modules/lower-case": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
"integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
"license": "MIT",
"dependencies": {
"tslib": "^2.0.3"
}
},
"node_modules/lru-cache": { "node_modules/lru-cache": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@ -11994,6 +12176,18 @@
"tmpl": "1.0.5" "tmpl": "1.0.5"
} }
}, },
"node_modules/map-obj": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz",
"integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==",
"license": "MIT",
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/markdown-extensions": { "node_modules/markdown-extensions": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-1.1.1.tgz", "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-1.1.1.tgz",
@ -13363,6 +13557,16 @@
"node": "^10 || ^12 || >=14" "node": "^10 || ^12 || >=14"
} }
}, },
"node_modules/no-case": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
"integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
"license": "MIT",
"dependencies": {
"lower-case": "^2.0.2",
"tslib": "^2.0.3"
}
},
"node_modules/node-int64": { "node_modules/node-int64": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
@ -15276,6 +15480,12 @@
"node": ">= 0.8.0" "node": ">= 0.8.0"
} }
}, },
"node_modules/server-only": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/server-only/-/server-only-0.0.1.tgz",
"integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==",
"license": "MIT"
},
"node_modules/set-cookie-parser": { "node_modules/set-cookie-parser": {
"version": "2.7.1", "version": "2.7.1",
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
@ -15509,6 +15719,42 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/snake-case": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz",
"integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==",
"license": "MIT",
"dependencies": {
"dot-case": "^3.0.4",
"tslib": "^2.0.3"
}
},
"node_modules/snakecase-keys": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/snakecase-keys/-/snakecase-keys-8.0.1.tgz",
"integrity": "sha512-Sj51kE1zC7zh6TDlNNz0/Jn1n5HiHdoQErxO8jLtnyrkJW/M5PrI7x05uDgY3BO7OUQYKCvmeMurW6BPUdwEOw==",
"license": "MIT",
"dependencies": {
"map-obj": "^4.1.0",
"snake-case": "^3.0.4",
"type-fest": "^4.15.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/snakecase-keys/node_modules/type-fest": {
"version": "4.41.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
"integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
"license": "(MIT OR CC0-1.0)",
"engines": {
"node": ">=16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/source-map": { "node_modules/source-map": {
"version": "0.7.4", "version": "0.7.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
@ -15636,6 +15882,12 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/std-env": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz",
"integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==",
"license": "MIT"
},
"node_modules/stdin-discarder": { "node_modules/stdin-discarder": {
"version": "0.2.2", "version": "0.2.2",
"resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz",
@ -16097,6 +16349,19 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/swr": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/swr/-/swr-2.3.3.tgz",
"integrity": "sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A==",
"license": "MIT",
"dependencies": {
"dequal": "^2.0.3",
"use-sync-external-store": "^1.4.0"
},
"peerDependencies": {
"react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/symbol-tree": { "node_modules/symbol-tree": {
"version": "3.2.4", "version": "3.2.4",
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
@ -17880,6 +18145,15 @@
"requires-port": "^1.0.0" "requires-port": "^1.0.0"
} }
}, },
"node_modules/use-sync-external-store": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz",
"integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==",
"license": "MIT",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/util": { "node_modules/util": {
"version": "0.12.5", "version": "0.12.5",
"resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
@ -19062,6 +19336,11 @@
"url": "https://github.com/sponsors/wooorm" "url": "https://github.com/sponsors/wooorm"
} }
}, },
"packages/auth": {
"name": "@repo/auth",
"version": "0.0.0",
"license": "MIT"
},
"packages/config-eslint": { "packages/config-eslint": {
"name": "@repo/eslint-config", "name": "@repo/eslint-config",
"version": "0.0.0", "version": "0.0.0",

5
packages/auth/README.md Normal file
View file

@ -0,0 +1,5 @@
# @repo/auth
Helper tools package for Clerk authentication. As of now, this helper package is not being used anywhere in the repo.
Due to Clerk's implementations for different frameworks, each app (Next.js, Remix, etc.) have its own Clerk setup. This package is intended to provide shared utilities and types that can be used across these implementations.

3
packages/auth/helpers.ts Normal file
View file

@ -0,0 +1,3 @@
export function hasRole(userRoles: string[], role: string): boolean {
return userRoles.includes(role);
}

2
packages/auth/index.ts Normal file
View file

@ -0,0 +1,2 @@
export * from "./roles";
export * from "./helpers";

View file

@ -0,0 +1,12 @@
{
"name": "@repo/auth",
"version": "0.0.0",
"author": "Afiq Zudin Hadi",
"private": true,
"license": "MIT",
"main": "index.ts",
"type": "module",
"exports": {
"./indext.ts": "./index.ts"
}
}

5
packages/auth/roles.ts Normal file
View file

@ -0,0 +1,5 @@
export const ROLES = Object.freeze({
SUPER_ADMIN: "super_admin",
ADMIN: "admin",
USER: "user",
});

View file

@ -0,0 +1,14 @@
{
"extends": "@repo/typescript-config/base.json",
"compilerOptions": {
"lib": ["dom", "ES2015"],
"sourceMap": true,
"types": ["jest", "node"],
"baseUrl": ".",
"paths": {
"@repo/auth/index.ts": ["./index.ts"]
}
},
"include": [".", "src"],
"exclude": ["dist", "build", "node_modules"]
}