diff --git a/AGENTS.md b/AGENTS.md index e5ed666e..27f92f62 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -44,3 +44,5 @@ These directories already contain source checkouts for some packages: - Never edit past migrations; create a new migration instead. - Never run "deploy" scripts to test anything, only if explicitly asked - NEVER EVER publish or release packages yourself, ask double confirmation +- No need to pass --config paykit.ts into paykitjs CLI, because it's one of the locations resolved by default +- If you just did some changes but later you see them different in the diff, it means the user likely adjusted the code intentionally, you should not restore them without asking the user diff --git a/apps/demo/package.json b/apps/demo/package.json index 490de3ab..a6244b7e 100644 --- a/apps/demo/package.json +++ b/apps/demo/package.json @@ -10,17 +10,15 @@ "check:write": "biome check --write .", "deploy:sandbox": "bun scripts/deploy-sandbox.ts", "dev": "next dev", - "dev:tunnel": "concurrently -k -n next,tunnel -c blue,magenta \"bun dev\" \"bun tunnel\"", "env:push:sandbox": "bun scripts/push-sandbox-env.ts", "preview": "next build && next start", "push:sandbox": "bun scripts/push-sandbox.ts", "start": "next start", - "tunnel": "set -a; [ -f .env ] && . ./.env; set +a; if [ -n \"$CF_TUNNEL_ID\" ]; then cloudflared tunnel --url http://localhost:3000 run \"$CF_TUNNEL_ID\"; fi", "paykitjs": "bun ../../packages/paykit/src/cli/index.ts", "typecheck": "tsc --noEmit", - "push": "bun push:auth && bun push:paykit && bun push:autumn", - "push:auth": "bunx auth migrate --config src/lib/auth.ts --yes", - "push:paykit": "set -a; [ -f .env ] && . ./.env; set +a; bunx paykitjs push --config paykit.config.ts --yes", + "push": "pnpm push:auth && pnpm push:paykit && pnpm push:autumn", + "push:auth": "auth migrate --config src/lib/auth.ts --yes", + "push:paykit": "pnpm paykitjs push --yes", "push:autumn": "set -a; [ -f .env ] && . ./.env; set +a; if [ -n \"$AUTUMN_SECRET_KEY\" ]; then atmn push; else printf '%s\\n' 'Skipping Autumn push: provider env incomplete'; fi", "db:studio": "drizzle-kit studio" }, diff --git a/apps/demo/paykit.config.ts b/apps/demo/paykit.config.ts deleted file mode 100644 index 4196ea45..00000000 --- a/apps/demo/paykit.config.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { paykit } from "./src/lib/paykit"; - -export { paykit }; -export default paykit; diff --git a/apps/demo/scripts/deploy-sandbox.ts b/apps/demo/scripts/deploy-sandbox.ts index e66de320..cf2ef5ca 100644 --- a/apps/demo/scripts/deploy-sandbox.ts +++ b/apps/demo/scripts/deploy-sandbox.ts @@ -8,7 +8,7 @@ import { function printHelp() { console.log("Deploy standalone demo app copy to Vercel sandbox production"); - console.log("Usage: bun deploy:sandbox --version "); + console.log("Usage: pnpm deploy:sandbox --version "); } async function main() { diff --git a/apps/demo/scripts/push-sandbox.ts b/apps/demo/scripts/push-sandbox.ts index cd7c06be..4876de64 100644 --- a/apps/demo/scripts/push-sandbox.ts +++ b/apps/demo/scripts/push-sandbox.ts @@ -24,20 +24,15 @@ async function main() { console.log("Push auth migrations"); await runCommand( - "bunx", - ["auth", "migrate", "--config", "src/lib/auth.ts", "--yes"], + "pnpm", + ["exec", "auth", "migrate", "--config", "src/lib/auth.ts", "--yes"], demoDir, env, ); if (hasEnv(values, ["PAYKIT_DATABASE_URL", "STRIPE_SECRET_KEY", "STRIPE_WEBHOOK_SECRET"])) { console.log("Push PayKit config"); - await runCommand( - "bunx", - ["paykitjs", "push", "--config", "paykit.config.ts", "--yes"], - demoDir, - env, - ); + await runCommand("pnpm", ["paykitjs", "push", "--yes"], demoDir, env); } else { console.log("Skipping PayKit push: env incomplete"); } diff --git a/apps/demo/scripts/sandbox.ts b/apps/demo/scripts/sandbox.ts index b1f88a76..a094dec2 100644 --- a/apps/demo/scripts/sandbox.ts +++ b/apps/demo/scripts/sandbox.ts @@ -201,9 +201,9 @@ async function rewriteTempNextConfig(nextConfigPath: string) { async function writeTempVercelConfig(vercelConfigPath: string) { const vercelConfig = { - buildCommand: "bun run build", + buildCommand: "pnpm run build", framework: "nextjs", - installCommand: "bun install", + installCommand: "pnpm install", }; await writeFile(vercelConfigPath, `${JSON.stringify(vercelConfig, null, 2)}\n`); diff --git a/apps/demo/src/app/_components/auth-form.tsx b/apps/demo/src/app/_components/auth-form.tsx index 24def59e..ee6bc824 100644 --- a/apps/demo/src/app/_components/auth-form.tsx +++ b/apps/demo/src/app/_components/auth-form.tsx @@ -2,6 +2,7 @@ import { useRouter } from "next/navigation"; import { useState } from "react"; +import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; @@ -9,6 +10,46 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { authClient } from "@/lib/auth-client"; +function createRandomCredentials() { + const id = crypto.randomUUID().slice(0, 8); + const email = `test-${Date.now()}-${id}@example.com`; + const password = `PayKit-test-${crypto.randomUUID()}`; + + return { email, password }; +} + +function formatCredentials(credentials: { email: string; password: string }) { + return `Email: ${credentials.email}\nPassword: ${credentials.password}`; +} + +async function copyCredentials(credentials: { email: string; password: string }) { + const text = formatCredentials(credentials); + + if (navigator.clipboard) { + try { + await navigator.clipboard.writeText(text); + return; + } catch { + // Fall back to the textarea path when clipboard permissions reject writes. + } + } + + const textarea = document.createElement("textarea"); + textarea.value = text; + textarea.style.position = "fixed"; + textarea.style.opacity = "0"; + document.body.append(textarea); + textarea.select(); + + try { + if (!document.execCommand("copy")) { + throw new Error("Clipboard copy failed"); + } + } finally { + textarea.remove(); + } +} + export function AuthForm({ redirectTo }: { redirectTo: string }) { const router = useRouter(); const [email, setEmail] = useState(""); @@ -16,9 +57,14 @@ export function AuthForm({ redirectTo }: { redirectTo: string }) { const [isSignUp, setIsSignUp] = useState(false); const [error, setError] = useState(""); const [loading, setLoading] = useState(false); + const [randomAccountLoading, setRandomAccountLoading] = useState(false); async function handleSubmit(event: React.FormEvent) { event.preventDefault(); + if (loading || randomAccountLoading) { + return; + } + setError(""); setLoading(true); @@ -49,6 +95,65 @@ export function AuthForm({ redirectTo }: { redirectTo: string }) { } } + async function handleRandomAccount() { + if (loading || randomAccountLoading) { + return; + } + + setError(""); + setRandomAccountLoading(true); + + const credentials = createRandomCredentials(); + let credentialsCopied = false; + + try { + setEmail(credentials.email); + setPassword(credentials.password); + + try { + await copyCredentials(credentials); + credentialsCopied = true; + } catch { + credentialsCopied = false; + } + + const result = await authClient.signUp.email({ + email: credentials.email, + password: credentials.password, + name: "Demo User", + }); + + if (result.error) { + const message = result.error.message ?? "Random account sign up failed"; + setError(message); + toast.error(message, { + description: credentialsCopied + ? "Generated credentials were copied, but the account was not created." + : undefined, + }); + return; + } + + if (credentialsCopied) { + toast.success("Signed up with a random test account", { + description: "Credentials copied to clipboard.", + }); + } else { + toast.warning("Signed up with a random test account", { + description: `Clipboard copy failed. ${formatCredentials(credentials)}`, + }); + } + + router.replace(redirectTo); + } catch (err) { + const message = err instanceof Error ? err.message : "Something went wrong"; + setError(message); + toast.error(message); + } finally { + setRandomAccountLoading(false); + } + } + return ( @@ -80,9 +185,17 @@ export function AuthForm({ redirectTo }: { redirectTo: string }) { /> {error ?

{error}

: null} - +