M3JUJ2WWZGCVMBITKRM5FUJMHFYL2QRMXJUVRUE4AC2RF74AOL5AC /*** Smart Placement* Docs: https://developers.cloudflare.com/workers/configuration/smart-placement/#smart-placement*/// "placement": { "mode": "smart" },/*** Bindings* Bindings allow your Worker to interact with resources on the Cloudflare Developer Platform, including* databases, object storage, AI inference, real-time communication and more.* https://developers.cloudflare.com/workers/runtime-apis/bindings/*//*** Environment Variables* https://developers.cloudflare.com/workers/wrangler/configuration/#environment-variables*/// "vars": { "MY_VARIABLE": "production_value" },/*** Note: Use secrets to store sensitive data.* https://developers.cloudflare.com/workers/configuration/secrets/*//*** Static Assets* https://developers.cloudflare.com/workers/static-assets/binding/*/// "assets": { "directory": "./public/", "binding": "ASSETS" },/*** Service Bindings (communicate between multiple Workers)* https://developers.cloudflare.com/workers/wrangler/configuration/#service-bindings*/// "services": [{ "binding": "MY_SERVICE", "service": "my-service" }]}
}
/** @type {import('tailwindcss').Config} */export default {content: ["./index.html","./src/react-app/**/*.{js,ts,jsx,tsx}",],theme: {extend: {},},plugins: [],}
// Datasets API routeapp.get("/api/datasets", async (c) => {
// Authentication middleware for protected routesconst authenticate = async (c: Context<{ Bindings: Env }>, next: () => Promise<void>) => {// Get the authorization headerconst authHeader = c.req.header("Authorization");if (!authHeader || !authHeader.startsWith("Bearer ")) {return c.json({ error: "Unauthorized: Missing or invalid token" }, 401);}try {// Extract tokenconst token = authHeader.split(" ")[1];console.log("Received token:", token.substring(0, 10) + "...");// Get the JWKS endpoint from the Kinde issuerconst issuerUrl = c.env.KINDE_ISSUER_URL;const jwksUrl = `${issuerUrl}/.well-known/jwks.json`;console.log("JWKS URL:", jwksUrl);// Fetch the JWKSconsole.log("Fetching JWKS...");const jwksResponse = await fetch(jwksUrl);console.log("JWKS response status:", jwksResponse.status);if (!jwksResponse.ok) {throw new Error(`Failed to fetch JWKS: ${jwksResponse.status}`);}await jwksResponse.json(); // Parse JSON to validate responseconsole.log("JWKS keys received");// Create JWKS from the fetched keysconst keyStore = jose.createRemoteJWKSet(new URL(jwksUrl));console.log("Verifying token...");console.log("Using issuer:", issuerUrl);try {// Verify the token with issuer only (no audience validation)const { payload } = await jose.jwtVerify(token, keyStore, {issuer: issuerUrl,// No audience validation - Kinde tokens may have empty audience array});console.log("Token verified successfully!");// Store the payload in the context for use in the route handler// Use any-casting to work around Hono's type limitations(c as any).jwtPayload = payload as JWTPayload;} catch (verifyError) {console.error("Token verification failed:", verifyError);throw verifyError;}// If we get here, the token is validawait next();} catch (error) {console.error("JWT validation error:", error);return c.json({ error: "Unauthorized: Invalid token" }, 401);}};// Protected datasets API routeapp.get("/api/datasets", authenticate, async (c) => {
#root {max-width: 1280px;margin: 0 auto;padding: 2rem;text-align: center;}.logo {height: 6em;padding: 1.5em;will-change: filter;transition: filter 300ms;}.logo:hover {filter: drop-shadow(0 0 2em #646cffaa);}.logo.react:hover {filter: drop-shadow(0 0 2em #61dafbaa);}.logo.cloudflare:hover {filter: drop-shadow(0 0 2em #f6821faa);}@keyframes logo-spin {from {transform: rotate(0deg);}to {transform: rotate(360deg);}}@media (prefers-reduced-motion: no-preference) {a:nth-of-type(2) .logo {animation: logo-spin infinite 20s linear;}}.card {padding: 2em;}.read-the-docs {color: #888;}.error {color: #e53935;background-color: #ffebee;padding: 8px;border-radius: 4px;margin: 8px 0;}}.json-display {text-align: left;background-color: #f0f0f0;border-radius: 8px;padding: 16px;overflow: auto;max-height: 300px;font-family: monospace;font-size: 14px;margin-top: 16px;white-space: pre-wrap;word-break: break-word;
font-synthesis: none;text-rendering: optimizeLegibility;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;
@theme inline {--radius-sm: calc(var(--radius) - 4px);--radius-md: calc(var(--radius) - 2px);--radius-lg: var(--radius);--radius-xl: calc(var(--radius) + 4px);--color-background: var(--background);--color-foreground: var(--foreground);--color-card: var(--card);--color-card-foreground: var(--card-foreground);--color-popover: var(--popover);--color-popover-foreground: var(--popover-foreground);--color-primary: var(--primary);--color-primary-foreground: var(--primary-foreground);--color-secondary: var(--secondary);--color-secondary-foreground: var(--secondary-foreground);--color-muted: var(--muted);--color-muted-foreground: var(--muted-foreground);--color-accent: var(--accent);--color-accent-foreground: var(--accent-foreground);--color-destructive: var(--destructive);--color-border: var(--border);--color-input: var(--input);--color-ring: var(--ring);--color-chart-1: var(--chart-1);--color-chart-2: var(--chart-2);--color-chart-3: var(--chart-3);--color-chart-4: var(--chart-4);--color-chart-5: var(--chart-5);--color-sidebar: var(--sidebar);--color-sidebar-foreground: var(--sidebar-foreground);--color-sidebar-primary: var(--sidebar-primary);--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);--color-sidebar-accent: var(--sidebar-accent);--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);--color-sidebar-border: var(--sidebar-border);--color-sidebar-ring: var(--sidebar-ring);
a {font-weight: 500;color: #646cff;text-decoration: inherit;}a:hover {color: #535bf2;}body {margin: 0;display: flex;place-items: center;min-width: 320px;min-height: 100vh;}h1 {font-size: 3.2em;line-height: 1.1;
:root {--radius: 0.625rem;--background: oklch(1 0 0);--foreground: oklch(0.147 0.004 49.25);--card: oklch(1 0 0);--card-foreground: oklch(0.147 0.004 49.25);--popover: oklch(1 0 0);--popover-foreground: oklch(0.147 0.004 49.25);--primary: oklch(0.216 0.006 56.043);--primary-foreground: oklch(0.985 0.001 106.423);--secondary: oklch(0.97 0.001 106.424);--secondary-foreground: oklch(0.216 0.006 56.043);--muted: oklch(0.97 0.001 106.424);--muted-foreground: oklch(0.553 0.013 58.071);--accent: oklch(0.97 0.001 106.424);--accent-foreground: oklch(0.216 0.006 56.043);--destructive: oklch(0.577 0.245 27.325);--border: oklch(0.923 0.003 48.717);--input: oklch(0.923 0.003 48.717);--ring: oklch(0.709 0.01 56.259);--chart-1: oklch(0.646 0.222 41.116);--chart-2: oklch(0.6 0.118 184.704);--chart-3: oklch(0.398 0.07 227.392);--chart-4: oklch(0.828 0.189 84.429);--chart-5: oklch(0.769 0.188 70.08);--sidebar: oklch(0.985 0.001 106.423);--sidebar-foreground: oklch(0.147 0.004 49.25);--sidebar-primary: oklch(0.216 0.006 56.043);--sidebar-primary-foreground: oklch(0.985 0.001 106.423);--sidebar-accent: oklch(0.97 0.001 106.424);--sidebar-accent-foreground: oklch(0.216 0.006 56.043);--sidebar-border: oklch(0.923 0.003 48.717);--sidebar-ring: oklch(0.709 0.01 56.259);
button {border-radius: 8px;border: 1px solid transparent;padding: 0.6em 1.2em;font-size: 1em;font-weight: 500;font-family: inherit;background-color: #1a1a1a;cursor: pointer;transition: border-color 0.25s;
.dark {--background: oklch(0.147 0.004 49.25);--foreground: oklch(0.985 0.001 106.423);--card: oklch(0.216 0.006 56.043);--card-foreground: oklch(0.985 0.001 106.423);--popover: oklch(0.216 0.006 56.043);--popover-foreground: oklch(0.985 0.001 106.423);--primary: oklch(0.923 0.003 48.717);--primary-foreground: oklch(0.216 0.006 56.043);--secondary: oklch(0.268 0.007 34.298);--secondary-foreground: oklch(0.985 0.001 106.423);--muted: oklch(0.268 0.007 34.298);--muted-foreground: oklch(0.709 0.01 56.259);--accent: oklch(0.268 0.007 34.298);--accent-foreground: oklch(0.985 0.001 106.423);--destructive: oklch(0.704 0.191 22.216);--border: oklch(1 0 0 / 10%);--input: oklch(1 0 0 / 15%);--ring: oklch(0.553 0.013 58.071);--chart-1: oklch(0.488 0.243 264.376);--chart-2: oklch(0.696 0.17 162.48);--chart-3: oklch(0.769 0.188 70.08);--chart-4: oklch(0.627 0.265 303.9);--chart-5: oklch(0.645 0.246 16.439);--sidebar: oklch(0.216 0.006 56.043);--sidebar-foreground: oklch(0.985 0.001 106.423);--sidebar-primary: oklch(0.488 0.243 264.376);--sidebar-primary-foreground: oklch(0.985 0.001 106.423);--sidebar-accent: oklch(0.268 0.007 34.298);--sidebar-accent-foreground: oklch(0.985 0.001 106.423);--sidebar-border: oklch(1 0 0 / 10%);--sidebar-ring: oklch(0.553 0.013 58.071);
import { useState } from "react";import "./App.css";interface Dataset {id: string;name: string;public: boolean;description?: string;createdBy: string;createdAt: string;lastModified: string;modifiedBy: string;owner: string;active: boolean;type: string;}
import AuthorisedContent from "@/components/AuthorisedContent";import { KindeProvider } from "@kinde-oss/kinde-auth-react";
const [count, setCount] = useState(0);const [datasets, setDatasets] = useState<string>("No data loaded");const [loading, setLoading] = useState<boolean>(false);const [error, setError] = useState<string | null>(null);const fetchDatasets = async () => {setLoading(true);setError(null);try {const response = await fetch("/api/datasets");if (!response.ok) {throw new Error(`HTTP error! Status: ${response.status}`);}const data = await response.json() as DatasetsResponse;setDatasets(JSON.stringify(data, null, 2));} catch (err) {setError(err instanceof Error ? err.message : "Failed to fetch datasets");console.error("Error fetching datasets:", err);} finally {setLoading(false);}};
<><div className="card"><buttononClick={() => setCount((count) => count + 1)}aria-label="increment">count is {count}</button><p>Edit <code>src/App.tsx</code> and save to test HMR</p></div><div className="card"><h2>Datasets</h2><buttononClick={fetchDatasets}disabled={loading}aria-label="fetch datasets">{loading ? "Loading..." : "Fetch Datasets"}</button>{error && <p className="error">Error: {error}</p>}<pre className="json-display">{datasets}</pre></div></>
<KindeProviderclientId="ef3e44de79594041addc4f744be2a7c7"domain="https://skraak.kinde.com"logoutUri={window.location.origin}redirectUri={window.location.origin}><AuthorisedContent /></KindeProvider>
import { clsx, type ClassValue } from "clsx"import { twMerge } from "tailwind-merge"export function cn(...inputs: ClassValue[]) {return twMerge(clsx(inputs))}
import * as React from "react"import { Slot } from "@radix-ui/react-slot"import { cva, type VariantProps } from "class-variance-authority"import { cn } from "@/lib/utils"const buttonVariants = cva("inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",{variants: {variant: {default:"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",destructive:"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",outline:"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",secondary:"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",ghost:"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",link: "text-primary underline-offset-4 hover:underline",},size: {default: "h-9 px-4 py-2 has-[>svg]:px-3",sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",lg: "h-10 rounded-md px-6 has-[>svg]:px-4",icon: "size-9",},},defaultVariants: {variant: "default",size: "default",},})function Button({className,variant,size,asChild = false,...props}: React.ComponentProps<"button"> &VariantProps<typeof buttonVariants> & {asChild?: boolean}) {const Comp = asChild ? Slot : "button"return (<Compdata-slot="button"className={cn(buttonVariants({ variant, size, className }))}{...props}/>)}export { Button, buttonVariants }
import { useKindeAuth } from "@kinde-oss/kinde-auth-react";import React from "react";const UserProfile: React.FC = () => {const { user, isAuthenticated, isLoading } = useKindeAuth();if (isLoading) {return <p>Loading</p>;}return (isAuthenticated? (<div><h2>{user?.givenName} {user?.familyName}</h2><p>{user?.email}</p><p>{user?.id}</p></div>): <p>Please sign in or register!</p>);};export default UserProfile;
import React, { useState } from "react";import { Button } from "./ui/button";import { useKindeAuth } from "@kinde-oss/kinde-auth-react";import UserProfile from "./UserProfile";import Authenticate from "./Authenticate";interface Dataset {id: string;name: string;public: boolean;description?: string;createdBy: string;createdAt: string;lastModified: string;modifiedBy: string;owner: string;active: boolean;type: string;}interface DatasetsResponse {data: Dataset[];}const AuthorisedContent: React.FC = () => {const { isAuthenticated, isLoading: authLoading, getAccessToken } = useKindeAuth();const [count, setCount] = useState(0);const [datasets, setDatasets] = useState<string>("No data loaded");const [loading, setLoading] = useState<boolean>(false);const [error, setError] = useState<string | null>(null);const fetchDatasets = async () => {if (!isAuthenticated) {setError("Please log in to access datasets");return;}setLoading(true);setError(null);try {// Get the access tokenconst accessToken = await getAccessToken();console.log("Auth status:", isAuthenticated ? "Authenticated" : "Not authenticated");console.log("Token obtained:", accessToken ? "Yes" : "No");// Include the token in the request headerconst response = await fetch("/api/datasets", {headers: {Authorization: `Bearer ${accessToken}`}});console.log("Response status:", response.status);if (!response.ok) {throw new Error(`HTTP error! Status: ${response.status}`);}const data = await response.json() as DatasetsResponse;setDatasets(JSON.stringify(data, null, 2));} catch (err) {setError(err instanceof Error ? err.message : "Failed to fetch datasets");console.error("Error fetching datasets:", err);} finally {setLoading(false);}};return (<div className="container mx-auto text-xl px-5 space-y-2"><div className="card"><ButtononClick={() => setCount((count) => count + 1)}aria-label="increment"variant="default">count is {count}</Button><p>Edit <code>src/components/AuthorisedContent.tsx</code> and save to test HMR</p></div><div className="card"><h2>Datasets</h2><ButtononClick={fetchDatasets}disabled={loading || authLoading}aria-label="fetch datasets"variant="secondary">{loading ? "Loading..." : "Fetch Datasets"}</Button>{!isAuthenticated && !authLoading &&<p className="text-amber-600 mt-2">Please log in to access datasets</p>}{error && <p className="error">Error: {error}</p>}<pre className="json-display">{datasets}</pre></div><UserProfile /><Authenticate /></div>);};export default AuthorisedContent;
// src/components/Authenticate.tsximport { LoginLink, LogoutLink, RegisterLink } from "@kinde-oss/kinde-auth-react/components";import React from "react";import { Button } from "./ui/button";const Authenticate: React.FC = () => {return (<div><Button><LoginLink>Sign in</LoginLink></Button><Button><RegisterLink>Sign up</RegisterLink></Button><Button><LogoutLink>Sign out</LogoutLink></Button></div>);};export default Authenticate;
module.exports = {plugins: {'@tailwindcss/postcss': {},autoprefixer: {},},}
}},"node_modules/@alloc/quick-lru": {"version": "5.2.0","resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz","integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==","dev": true,"license": "MIT","engines": {"node": ">=10"},"funding": {"url": "https://github.com/sponsors/sindresorhus"
},"node_modules/@kinde-oss/kinde-auth-react": {"version": "5.2.0","resolved": "https://registry.npmjs.org/@kinde-oss/kinde-auth-react/-/kinde-auth-react-5.2.0.tgz","integrity": "sha512-QCHt0vXEYHIAeX5Kk70OuQUyeC+xPJDmNiMr1aw5Pw2XZbgUEn9euTdWOFEGRirhSJ8X+cH+Yj4ImlP/hoaXFg==","license": "MIT","dependencies": {"@kinde/js-utils": "0.13.0"},"peerDependencies": {"react": "^17 || ^18 || ^19","react-dom": "^17 || ^18 || ^19"}},"node_modules/@kinde/js-utils": {"version": "0.13.0","resolved": "https://registry.npmjs.org/@kinde/js-utils/-/js-utils-0.13.0.tgz","integrity": "sha512-R0elPxdQXVpdlZg+I9X5g1LlHEScn9VFEFTIUD2SwgdA+wjjik0NpP0Wip/gnz4Y3th+QZ8HxczN7xiTKQKGhw==","license": "MIT","dependencies": {"@kinde/jwt-decoder": "^0.2.0"},"peerDependencies": {"expo-secure-store": ">=11.0.0"},"peerDependenciesMeta": {"expo-secure-store": {"optional": true}}},"node_modules/@kinde/jwt-decoder": {"version": "0.2.0","resolved": "https://registry.npmjs.org/@kinde/jwt-decoder/-/jwt-decoder-0.2.0.tgz","integrity": "sha512-dqtwCmAvywOVLkkUfp4UbqdvVLsK0cvHsJhU3gDY9rgjAdZhGw0vCreBW6j3MFLxbi6cZm7pMU7/O5SJgvN5Rw=="
}},"node_modules/@radix-ui/react-compose-refs": {"version": "1.1.2","resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz","integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==","license": "MIT","peerDependencies": {"@types/react": "*","react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"},"peerDependenciesMeta": {"@types/react": {"optional": true}}},"node_modules/@radix-ui/react-slot": {"version": "1.2.0","resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz","integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==","license": "MIT","dependencies": {"@radix-ui/react-compose-refs": "1.1.2"},"peerDependencies": {"@types/react": "*","react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"},"peerDependenciesMeta": {"@types/react": {"optional": true}
},"node_modules/@tailwindcss/node": {"version": "4.1.4","resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.4.tgz","integrity": "sha512-MT5118zaiO6x6hNA04OWInuAiP1YISXql8Z+/Y8iisV5nuhM8VXlyhRuqc2PEviPszcXI66W44bCIk500Oolhw==","license": "MIT","dependencies": {"enhanced-resolve": "^5.18.1","jiti": "^2.4.2","lightningcss": "1.29.2","tailwindcss": "4.1.4"}},"node_modules/@tailwindcss/oxide": {"version": "4.1.4","resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.4.tgz","integrity": "sha512-p5wOpXyOJx7mKh5MXh5oKk+kqcz8T+bA3z/5VWWeQwFrmuBItGwz8Y2CHk/sJ+dNb9B0nYFfn0rj/cKHZyjahQ==","license": "MIT","engines": {"node": ">= 10"},"optionalDependencies": {"@tailwindcss/oxide-android-arm64": "4.1.4","@tailwindcss/oxide-darwin-arm64": "4.1.4","@tailwindcss/oxide-darwin-x64": "4.1.4","@tailwindcss/oxide-freebsd-x64": "4.1.4","@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.4","@tailwindcss/oxide-linux-arm64-gnu": "4.1.4","@tailwindcss/oxide-linux-arm64-musl": "4.1.4","@tailwindcss/oxide-linux-x64-gnu": "4.1.4","@tailwindcss/oxide-linux-x64-musl": "4.1.4","@tailwindcss/oxide-wasm32-wasi": "4.1.4","@tailwindcss/oxide-win32-arm64-msvc": "4.1.4","@tailwindcss/oxide-win32-x64-msvc": "4.1.4"}},"node_modules/@tailwindcss/oxide-android-arm64": {"version": "4.1.4","resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.4.tgz","integrity": "sha512-xMMAe/SaCN/vHfQYui3fqaBDEXMu22BVwQ33veLc8ep+DNy7CWN52L+TTG9y1K397w9nkzv+Mw+mZWISiqhmlA==","cpu": ["arm64"],"license": "MIT","optional": true,"os": ["android"],"engines": {"node": ">= 10"}},"node_modules/@tailwindcss/oxide-darwin-arm64": {"version": "4.1.4","resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.4.tgz","integrity": "sha512-JGRj0SYFuDuAGilWFBlshcexev2hOKfNkoX+0QTksKYq2zgF9VY/vVMq9m8IObYnLna0Xlg+ytCi2FN2rOL0Sg==","cpu": ["arm64"],"license": "MIT","optional": true,"os": ["darwin"],"engines": {"node": ">= 10"}
"node_modules/@tailwindcss/oxide-darwin-x64": {"version": "4.1.4","resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.4.tgz","integrity": "sha512-sdDeLNvs3cYeWsEJ4H1DvjOzaGios4QbBTNLVLVs0XQ0V95bffT3+scptzYGPMjm7xv4+qMhCDrkHwhnUySEzA==","cpu": ["x64"],"license": "MIT","optional": true,"os": ["darwin"],"engines": {"node": ">= 10"}},"node_modules/@tailwindcss/oxide-freebsd-x64": {"version": "4.1.4","resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.4.tgz","integrity": "sha512-VHxAqxqdghM83HslPhRsNhHo91McsxRJaEnShJOMu8mHmEj9Ig7ToHJtDukkuLWLzLboh2XSjq/0zO6wgvykNA==","cpu": ["x64"],"license": "MIT","optional": true,"os": ["freebsd"],"engines": {"node": ">= 10"}},"node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {"version": "4.1.4","resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.4.tgz","integrity": "sha512-OTU/m/eV4gQKxy9r5acuesqaymyeSCnsx1cFto/I1WhPmi5HDxX1nkzb8KYBiwkHIGg7CTfo/AcGzoXAJBxLfg==","cpu": ["arm"],"license": "MIT","optional": true,"os": ["linux"],"engines": {"node": ">= 10"}},"node_modules/@tailwindcss/oxide-linux-arm64-gnu": {"version": "4.1.4","resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.4.tgz","integrity": "sha512-hKlLNvbmUC6z5g/J4H+Zx7f7w15whSVImokLPmP6ff1QqTVE+TxUM9PGuNsjHvkvlHUtGTdDnOvGNSEUiXI1Ww==","cpu": ["arm64"],"license": "MIT","optional": true,"os": ["linux"],"engines": {"node": ">= 10"}},"node_modules/@tailwindcss/oxide-linux-arm64-musl": {"version": "4.1.4","resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.4.tgz","integrity": "sha512-X3As2xhtgPTY/m5edUtddmZ8rCruvBvtxYLMw9OsZdH01L2gS2icsHRwxdU0dMItNfVmrBezueXZCHxVeeb7Aw==","cpu": ["arm64"],"license": "MIT","optional": true,"os": ["linux"],"engines": {"node": ">= 10"}},"node_modules/@tailwindcss/oxide-linux-x64-gnu": {"version": "4.1.4","resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.4.tgz","integrity": "sha512-2VG4DqhGaDSmYIu6C4ua2vSLXnJsb/C9liej7TuSO04NK+JJJgJucDUgmX6sn7Gw3Cs5ZJ9ZLrnI0QRDOjLfNQ==","cpu": ["x64"],"license": "MIT","optional": true,"os": ["linux"],"engines": {"node": ">= 10"}},"node_modules/@tailwindcss/oxide-linux-x64-musl": {"version": "4.1.4","resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.4.tgz","integrity": "sha512-v+mxVgH2kmur/X5Mdrz9m7TsoVjbdYQT0b4Z+dr+I4RvreCNXyCFELZL/DO0M1RsidZTrm6O1eMnV6zlgEzTMQ==","cpu": ["x64"],"license": "MIT","optional": true,"os": ["linux"],"engines": {"node": ">= 10"}},"node_modules/@tailwindcss/oxide-wasm32-wasi": {"version": "4.1.4","resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.4.tgz","integrity": "sha512-2TLe9ir+9esCf6Wm+lLWTMbgklIjiF0pbmDnwmhR9MksVOq+e8aP3TSsXySnBDDvTTVd/vKu1aNttEGj3P6l8Q==","bundleDependencies": ["@napi-rs/wasm-runtime","@emnapi/core","@emnapi/runtime","@tybys/wasm-util","@emnapi/wasi-threads","tslib"],"cpu": ["wasm32"],"license": "MIT","optional": true,"dependencies": {"@emnapi/core": "^1.4.0","@emnapi/runtime": "^1.4.0","@emnapi/wasi-threads": "^1.0.1","@napi-rs/wasm-runtime": "^0.2.8","@tybys/wasm-util": "^0.9.0","tslib": "^2.8.0"},"engines": {"node": ">=14.0.0"}},"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {"version": "4.1.4","resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.4.tgz","integrity": "sha512-VlnhfilPlO0ltxW9/BgfLI5547PYzqBMPIzRrk4W7uupgCt8z6Trw/tAj6QUtF2om+1MH281Pg+HHUJoLesmng==","cpu": ["arm64"],"license": "MIT","optional": true,"os": ["win32"],"engines": {"node": ">= 10"}},"node_modules/@tailwindcss/oxide-win32-x64-msvc": {"version": "4.1.4","resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.4.tgz","integrity": "sha512-+7S63t5zhYjslUGb8NcgLpFXD+Kq1F/zt5Xv5qTv7HaFTG/DHyHD9GA6ieNAxhgyA4IcKa/zy7Xx4Oad2/wuhw==","cpu": ["x64"],"license": "MIT","optional": true,"os": ["win32"],"engines": {"node": ">= 10"}},"node_modules/@tailwindcss/postcss": {"version": "4.1.4","resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.4.tgz","integrity": "sha512-bjV6sqycCEa+AQSt2Kr7wpGF1bOZJ5wsqnLEkqSbM/JEHxx/yhMH8wHmdkPyApF9xhHeMSwnnkDUUMMM/hYnXw==","dev": true,"license": "MIT","dependencies": {"@alloc/quick-lru": "^5.2.0","@tailwindcss/node": "4.1.4","@tailwindcss/oxide": "4.1.4","postcss": "^8.4.41","tailwindcss": "4.1.4"}},"node_modules/@tailwindcss/vite": {"version": "4.1.4","resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.4.tgz","integrity": "sha512-4UQeMrONbvrsXKXXp/uxmdEN5JIJ9RkH7YVzs6AMxC/KC1+Np7WZBaNIco7TEjlkthqxZbt8pU/ipD+hKjm80A==","license": "MIT","dependencies": {"@tailwindcss/node": "4.1.4","@tailwindcss/oxide": "4.1.4","tailwindcss": "4.1.4"},"peerDependencies": {"vite": "^5.2.0 || ^6"}},
"node_modules/autoprefixer": {"version": "10.4.21","resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz","integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==","dev": true,"funding": [{"type": "opencollective","url": "https://opencollective.com/postcss/"},{"type": "tidelift","url": "https://tidelift.com/funding/github/npm/autoprefixer"},{"type": "github","url": "https://github.com/sponsors/ai"}],"license": "MIT","dependencies": {"browserslist": "^4.24.4","caniuse-lite": "^1.0.30001702","fraction.js": "^4.3.7","normalize-range": "^0.1.2","picocolors": "^1.1.1","postcss-value-parser": "^4.2.0"},"bin": {"autoprefixer": "bin/autoprefixer"},"engines": {"node": "^10 || ^12 || >=14"},"peerDependencies": {"postcss": "^8.1.0"}},
}},"node_modules/class-variance-authority": {"version": "0.7.1","resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz","integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==","license": "Apache-2.0","dependencies": {"clsx": "^2.1.1"},"funding": {"url": "https://polar.sh/cva"}},"node_modules/clsx": {"version": "2.1.1","resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz","integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==","license": "MIT","engines": {"node": ">=6"
},"node_modules/enhanced-resolve": {"version": "5.18.1","resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz","integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==","license": "MIT","dependencies": {"graceful-fs": "^4.2.4","tapable": "^2.2.0"},"engines": {"node": ">=10.13.0"}
},"node_modules/fraction.js": {"version": "4.3.7","resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz","integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==","dev": true,"license": "MIT","engines": {"node": "*"},"funding": {"type": "patreon","url": "https://github.com/sponsors/rawify"}
},"node_modules/jiti": {"version": "2.4.2","resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz","integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==","license": "MIT","bin": {"jiti": "lib/jiti-cli.mjs"}},"node_modules/jose": {"version": "6.0.10","resolved": "https://registry.npmjs.org/jose/-/jose-6.0.10.tgz","integrity": "sha512-skIAxZqcMkOrSwjJvplIPYrlXGpxTPnro2/QWTDCxAdWQrSTV5/KqspMWmi5WAx5+ULswASJiZ0a+1B/Lxt9cw==","license": "MIT","funding": {"url": "https://github.com/sponsors/panva"}
}},"node_modules/lightningcss": {"version": "1.29.2","resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.2.tgz","integrity": "sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==","license": "MPL-2.0","dependencies": {"detect-libc": "^2.0.3"},"engines": {"node": ">= 12.0.0"},"funding": {"type": "opencollective","url": "https://opencollective.com/parcel"},"optionalDependencies": {"lightningcss-darwin-arm64": "1.29.2","lightningcss-darwin-x64": "1.29.2","lightningcss-freebsd-x64": "1.29.2","lightningcss-linux-arm-gnueabihf": "1.29.2","lightningcss-linux-arm64-gnu": "1.29.2","lightningcss-linux-arm64-musl": "1.29.2","lightningcss-linux-x64-gnu": "1.29.2","lightningcss-linux-x64-musl": "1.29.2","lightningcss-win32-arm64-msvc": "1.29.2","lightningcss-win32-x64-msvc": "1.29.2"}},"node_modules/lightningcss-darwin-arm64": {"version": "1.29.2","resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.29.2.tgz","integrity": "sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==","cpu": ["arm64"],"license": "MPL-2.0","optional": true,"os": ["darwin"],"engines": {"node": ">= 12.0.0"},"funding": {"type": "opencollective","url": "https://opencollective.com/parcel"}},"node_modules/lightningcss-darwin-x64": {"version": "1.29.2","resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.29.2.tgz","integrity": "sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==","cpu": ["x64"],"license": "MPL-2.0","optional": true,"os": ["darwin"],"engines": {"node": ">= 12.0.0"},"funding": {"type": "opencollective","url": "https://opencollective.com/parcel"}},"node_modules/lightningcss-freebsd-x64": {"version": "1.29.2","resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.29.2.tgz","integrity": "sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==","cpu": ["x64"],"license": "MPL-2.0","optional": true,"os": ["freebsd"],"engines": {"node": ">= 12.0.0"},"funding": {"type": "opencollective","url": "https://opencollective.com/parcel"}},"node_modules/lightningcss-linux-arm-gnueabihf": {"version": "1.29.2","resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.29.2.tgz","integrity": "sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==","cpu": ["arm"],"license": "MPL-2.0","optional": true,"os": ["linux"],"engines": {"node": ">= 12.0.0"},"funding": {"type": "opencollective","url": "https://opencollective.com/parcel"}},"node_modules/lightningcss-linux-arm64-gnu": {"version": "1.29.2","resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.29.2.tgz","integrity": "sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==","cpu": ["arm64"],"license": "MPL-2.0","optional": true,"os": ["linux"],"engines": {"node": ">= 12.0.0"},"funding": {"type": "opencollective","url": "https://opencollective.com/parcel"}},"node_modules/lightningcss-linux-arm64-musl": {"version": "1.29.2","resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.29.2.tgz","integrity": "sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==","cpu": ["arm64"],"license": "MPL-2.0","optional": true,"os": ["linux"],"engines": {"node": ">= 12.0.0"},"funding": {"type": "opencollective","url": "https://opencollective.com/parcel"}},"node_modules/lightningcss-linux-x64-gnu": {"version": "1.29.2","resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.29.2.tgz","integrity": "sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==","cpu": ["x64"],"license": "MPL-2.0","optional": true,"os": ["linux"],"engines": {"node": ">= 12.0.0"},"funding": {"type": "opencollective","url": "https://opencollective.com/parcel"}},"node_modules/lightningcss-linux-x64-musl": {"version": "1.29.2","resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.29.2.tgz","integrity": "sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==","cpu": ["x64"],"license": "MPL-2.0","optional": true,"os": ["linux"],"engines": {"node": ">= 12.0.0"},"funding": {"type": "opencollective","url": "https://opencollective.com/parcel"}},"node_modules/lightningcss-win32-arm64-msvc": {"version": "1.29.2","resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.29.2.tgz","integrity": "sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==","cpu": ["arm64"],"license": "MPL-2.0","optional": true,"os": ["win32"],"engines": {"node": ">= 12.0.0"},"funding": {"type": "opencollective","url": "https://opencollective.com/parcel"}},"node_modules/lightningcss-win32-x64-msvc": {"version": "1.29.2","resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.29.2.tgz","integrity": "sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==","cpu": ["x64"],"license": "MPL-2.0","optional": true,"os": ["win32"],"engines": {"node": ">= 12.0.0"},"funding": {"type": "opencollective","url": "https://opencollective.com/parcel"}},"node_modules/lilconfig": {"version": "3.1.3","resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz","integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==","dev": true,"license": "MIT","engines": {"node": ">=14"},"funding": {"url": "https://github.com/sponsors/antonk52"
"node_modules/lucide-react": {"version": "0.501.0","resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.501.0.tgz","integrity": "sha512-E2KoyhW59fCb/yUbR3rbDer83fqn7a8NG91ZhIot2yWaPHjPyGzzsNKh40N//GezYShAuycf3TcQksRQznIsRw==","license": "ISC","peerDependencies": {"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"}},
},"node_modules/normalize-range": {"version": "0.1.2","resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz","integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==","dev": true,"license": "MIT","engines": {"node": ">=0.10.0"}
"node_modules/postcss-load-config": {"version": "6.0.1","resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz","integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==","dev": true,"funding": [{"type": "opencollective","url": "https://opencollective.com/postcss/"},{"type": "github","url": "https://github.com/sponsors/ai"}],"license": "MIT","dependencies": {"lilconfig": "^3.1.1"},"engines": {"node": ">= 18"},"peerDependencies": {"jiti": ">=1.21.0","postcss": ">=8.0.9","tsx": "^4.8.1","yaml": "^2.4.2"},"peerDependenciesMeta": {"jiti": {"optional": true},"postcss": {"optional": true},"tsx": {"optional": true},"yaml": {"optional": true}}},"node_modules/postcss-value-parser": {"version": "4.2.0","resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz","integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==","dev": true,"license": "MIT"},
}},"node_modules/tailwind-merge": {"version": "3.2.0","resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.2.0.tgz","integrity": "sha512-FQT/OVqCD+7edmmJpsgCsY820RTD5AkBryuG5IUqR5YQZSdj5xlH5nLgH7YPths7WsLPSpSBNneJdM8aS8aeFA==","license": "MIT","funding": {"type": "github","url": "https://github.com/sponsors/dcastil"
"node_modules/tailwindcss": {"version": "4.1.4","resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.4.tgz","integrity": "sha512-1ZIUqtPITFbv/DxRmDr5/agPqJwF69d24m9qmM1939TJehgY539CtzeZRjbLt5G6fSy/7YqqYsfvoTEw9xUI2A==","license": "MIT"},"node_modules/tapable": {"version": "2.2.1","resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz","integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==","license": "MIT","engines": {"node": ">=6"}},
"node_modules/tw-animate-css": {"version": "1.2.5","resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.2.5.tgz","integrity": "sha512-ABzjfgVo+fDbhRREGL4KQZUqqdPgvc5zVrLyeW9/6mVqvaDepXc7EvedA+pYmMnIOsUAQMwcWzNvom26J2qYvQ==","license": "MIT","funding": {"url": "https://github.com/sponsors/Wombosvideo"}},
{"typescript": {},"json": {},"markdown": {},"toml": {},"malva": {},"markup": {},"yaml": {},"excludes": ["**/node_modules","**/*-lock.json"],"plugins": ["https://plugins.dprint.dev/typescript-0.94.0.wasm","https://plugins.dprint.dev/json-0.20.0.wasm","https://plugins.dprint.dev/markdown-0.18.0.wasm","https://plugins.dprint.dev/toml-0.7.0.wasm","https://plugins.dprint.dev/g-plane/malva-v0.11.2.wasm","https://plugins.dprint.dev/g-plane/markup_fmt-v0.19.1.wasm","https://plugins.dprint.dev/g-plane/pretty_yaml-v0.5.0.wasm"]}
{"$schema": "https://ui.shadcn.com/schema.json","style": "new-york","rsc": false,"tsx": true,"tailwind": {"config": "tailwind.config.js","css": "src/react-app/index.css","baseColor": "stone","cssVariables": true,"prefix": ""},"aliases": {"components": "@/components","utils": "@/lib/utils","ui": "@/components/ui","lib": "@/lib","hooks": "@/hooks"},"iconLibrary": "lucide"}
# Auth# https://docs.kinde.com/developer-tools/sdks/frontend/react-sdk/npm i @kinde-oss/kinde-auth-reactgot auth working on both frrontend and backend
- Project structure: src/react-app (frontend), src/worker (Cloudflare Workers backend)- Import order: React/external libraries first, then internal modules- Naming: PascalCase for components, camelCase for functions/variables- Error handling: use try/catch blocks for async operations- Use Hono for backend API routes with typed request/response interfaces- Avoid any type; use explicit type annotations for function parameters/returns- Follow ESLint recommended configurations for JS and TypeScript
- Project structure:- src/react-app: Frontend React code- src/worker: Cloudflare Workers backend- src/components: UI components (including shadcn)- src/lib: Utility functions- db: Database schema and migrations- UI: Tailwind CSS 4.x with shadcn components- Authentication: Kinde Auth (components in @kinde-oss/kinde-auth-react)- Import order: React/external libraries first, then internal modules (@/ path aliases available)- Naming: PascalCase for components/types, camelCase for functions/variables- Error handling: use try/catch blocks for async operations with proper error typing- API: Hono for backend routes with typed request/response interfaces- Avoid any type; use explicit interfaces for data structures