stackpatch 1.2.2 → 1.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "stackpatch",
3
- "version": "1.2.2",
3
+ "version": "1.2.4",
4
4
  "description": "Composable frontend features for modern React & Next.js apps - Add authentication, UI components, and more with zero configuration",
5
5
  "main": "dist/stackpatch.js",
6
6
  "bin": {
7
7
  "stackpatch": "dist/stackpatch.js",
8
8
  "create-stackpatch": "dist/stackpatch.js"
9
9
  },
10
+ "type": "module",
10
11
  "files": [
11
12
  "dist",
12
13
  "boilerplate",
@@ -14,7 +15,7 @@
14
15
  ],
15
16
  "scripts": {
16
17
  "install-deps": "pnpm install @rollup/plugin-node-resolve @rollup/plugin-commonjs @rollup/plugin-typescript @rollup/plugin-json rollup typescript -D",
17
- "build": "rollup -c",
18
+ "build": "rollup -c rollup.config.cjs",
18
19
  "test": "vitest run",
19
20
  "test:watch": "vitest",
20
21
  "test:coverage": "vitest run --coverage",
@@ -55,10 +56,12 @@
55
56
  },
56
57
  "packageManager": "pnpm@10.28.0",
57
58
  "dependencies": {
58
- "chalk": "^4.1.2",
59
+ "@inquirer/select": "^5.0.4",
60
+ "chalk": "^5.6.2",
59
61
  "fs-extra": "^11.3.3",
60
- "inquirer": "^13.1.0",
61
- "jimp": "^0.22.10"
62
+ "inquirer": "^13.2.0",
63
+ "jimp": "^0.22.10",
64
+ "tsx": "^4.19.2"
62
65
  },
63
66
  "devDependencies": {
64
67
  "@rollup/plugin-commonjs": "^28.0.9",
@@ -1,124 +0,0 @@
1
- import NextAuth from "next-auth";
2
- import type { NextAuthOptions } from "next-auth";
3
- import GoogleProvider from "next-auth/providers/google";
4
- import GitHubProvider from "next-auth/providers/github";
5
- import CredentialsProvider from "next-auth/providers/credentials";
6
-
7
- export const authOptions: NextAuthOptions = {
8
- providers: [
9
- CredentialsProvider({
10
- name: "Credentials",
11
- credentials: {
12
- email: { label: "Email", type: "email" },
13
- password: { label: "Password", type: "password" },
14
- },
15
- async authorize(credentials) {
16
- // ⚠️ DEMO MODE: This is a placeholder implementation
17
- //
18
- // TO IMPLEMENT REAL AUTHENTICATION:
19
- // 1. Set up a database (PostgreSQL, MongoDB, Prisma, etc.)
20
- // 2. Install bcrypt: npm install bcryptjs @types/bcryptjs
21
- // 3. Replace this function with database lookup:
22
- //
23
- // Example implementation:
24
- // ```ts
25
- // import bcrypt from "bcryptjs";
26
- // import { db } from "@/lib/db"; // Your database connection
27
- //
28
- // async authorize(credentials) {
29
- // if (!credentials?.email || !credentials?.password) {
30
- // return null;
31
- // }
32
- //
33
- // // Find user in database
34
- // const user = await db.user.findUnique({
35
- // where: { email: credentials.email },
36
- // });
37
- //
38
- // if (!user) {
39
- // return null;
40
- // }
41
- //
42
- // // Verify password
43
- // const isValid = await bcrypt.compare(
44
- // credentials.password,
45
- // user.password
46
- // );
47
- //
48
- // if (!isValid) {
49
- // return null;
50
- // }
51
- //
52
- // return {
53
- // id: user.id,
54
- // email: user.email,
55
- // name: user.name,
56
- // };
57
- // }
58
- // ```
59
- //
60
- // Current demo credentials (REMOVE IN PRODUCTION):
61
- // Email: demo@example.com
62
- // Password: demo123
63
-
64
- if (!credentials?.email || !credentials?.password) {
65
- return null;
66
- }
67
-
68
- // Demo check - REMOVE THIS IN PRODUCTION
69
- if (
70
- credentials.email === "demo@example.com" &&
71
- credentials.password === "demo123"
72
- ) {
73
- return {
74
- id: "1",
75
- email: credentials.email,
76
- name: "Demo User",
77
- };
78
- }
79
-
80
- return null;
81
- },
82
- }),
83
- GoogleProvider({
84
- clientId: process.env.GOOGLE_CLIENT_ID!,
85
- clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
86
- }),
87
- GitHubProvider({
88
- clientId: process.env.GITHUB_CLIENT_ID!,
89
- clientSecret: process.env.GITHUB_CLIENT_SECRET!,
90
- }),
91
- ],
92
- pages: {
93
- signIn: "/auth/login",
94
- error: "/auth/error",
95
- },
96
- session: {
97
- strategy: "jwt",
98
- },
99
- callbacks: {
100
- async jwt({ token, user, account }) {
101
- if (user) {
102
- token.id = user.id;
103
- token.email = user.email;
104
- token.name = user.name;
105
- }
106
- if (account) {
107
- token.accessToken = account.access_token;
108
- token.provider = account.provider;
109
- }
110
- return token;
111
- },
112
- async session({ session, token }) {
113
- if (session.user) {
114
- session.user.id = token.id as string;
115
- session.accessToken = token.accessToken as string;
116
- }
117
- return session;
118
- },
119
- },
120
- };
121
-
122
- const handler = NextAuth(authOptions);
123
-
124
- export { handler as GET, handler as POST };
@@ -1,45 +0,0 @@
1
- import { NextResponse } from "next/server";
2
-
3
- export async function POST(request: Request) {
4
- try {
5
- const { email, password, name } = await request.json();
6
-
7
- // TODO: Replace with your actual signup logic
8
- // This is a placeholder - you should:
9
- // 1. Validate input
10
- // 2. Check if user already exists
11
- // 3. Hash password (use bcrypt or similar)
12
- // 4. Save user to database
13
- // 5. Return success or error
14
-
15
- // Example validation
16
- if (!email || !password || !name) {
17
- return NextResponse.json(
18
- { error: "Missing required fields" },
19
- { status: 400 }
20
- );
21
- }
22
-
23
- if (password.length < 6) {
24
- return NextResponse.json(
25
- { error: "Password must be at least 6 characters" },
26
- { status: 400 }
27
- );
28
- }
29
-
30
- // Placeholder: In production, save to database here
31
- // const hashedPassword = await bcrypt.hash(password, 10);
32
- // const user = await db.user.create({ email, password: hashedPassword, name });
33
-
34
- return NextResponse.json(
35
- { message: "Account created successfully" },
36
- { status: 201 }
37
- );
38
- } catch (error) {
39
- console.error("Signup error:", error);
40
- return NextResponse.json(
41
- { error: "Failed to create account" },
42
- { status: 500 }
43
- );
44
- }
45
- }
@@ -1,82 +0,0 @@
1
- "use client";
2
-
3
- import React from "react";
4
- import { useSession } from "next-auth/react";
5
- import { ProtectedRoute } from "@/components/protected-route";
6
- import { AuthNavbar } from "@/components/auth-navbar";
7
-
8
- /**
9
- * Dashboard Page Example
10
- *
11
- * This is an example protected dashboard page.
12
- *
13
- * To use this:
14
- * 1. Copy this file to your app/dashboard/page.tsx
15
- * 2. The page is automatically protected using ProtectedRoute
16
- * 3. The AuthNavbar shows session status and sign out button
17
- *
18
- * You can customize this page to show your dashboard content.
19
- * If you have an existing navbar, replace AuthNavbar with your own.
20
- */
21
- export default function DashboardPage() {
22
- const { data: session } = useSession();
23
-
24
- return (
25
- <ProtectedRoute>
26
- <div className="min-h-screen bg-zinc-50 dark:bg-black">
27
- <AuthNavbar />
28
- <main className="mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8">
29
- <div className="rounded-lg bg-white p-8 shadow dark:bg-zinc-900">
30
- <h1 className="text-3xl font-bold text-zinc-900 dark:text-zinc-50">
31
- Dashboard
32
- </h1>
33
- <p className="mt-2 text-zinc-600 dark:text-zinc-400">
34
- Welcome to your protected dashboard!
35
- </p>
36
-
37
- {session && (
38
- <div className="mt-6 rounded-md bg-zinc-100 p-4 dark:bg-zinc-800">
39
- <h2 className="text-lg font-semibold text-zinc-900 dark:text-zinc-50">
40
- Session Information
41
- </h2>
42
- <div className="mt-2 space-y-1 text-sm text-zinc-600 dark:text-zinc-400">
43
- <p>
44
- <span className="font-medium">Name:</span>{" "}
45
- {session.user?.name || "Not provided"}
46
- </p>
47
- <p>
48
- <span className="font-medium">Email:</span>{" "}
49
- {session.user?.email || "Not provided"}
50
- </p>
51
- {session.user?.image && (
52
- <p>
53
- <span className="font-medium">Image:</span>{" "}
54
- <img
55
- src={session.user.image}
56
- alt={session.user.name || "User"}
57
- className="mt-2 h-16 w-16 rounded-full"
58
- />
59
- </p>
60
- )}
61
- </div>
62
- </div>
63
- )}
64
-
65
- <div className="mt-8">
66
- <h2 className="text-xl font-semibold text-zinc-900 dark:text-zinc-50">
67
- Getting Started
68
- </h2>
69
- <p className="mt-2 text-zinc-600 dark:text-zinc-400">
70
- This is a protected page. Only authenticated users can see this
71
- content.
72
- </p>
73
- <p className="mt-2 text-zinc-600 dark:text-zinc-400">
74
- Customize this page to add your dashboard features.
75
- </p>
76
- </div>
77
- </div>
78
- </main>
79
- </div>
80
- </ProtectedRoute>
81
- );
82
- }
@@ -1,136 +0,0 @@
1
- "use client";
2
-
3
- import { signIn } from "next-auth/react";
4
- import { useState } from "react";
5
- import { useRouter } from "next/navigation";
6
-
7
- export default function LoginPage() {
8
- const router = useRouter();
9
- const [email, setEmail] = useState("");
10
- const [password, setPassword] = useState("");
11
- const [error, setError] = useState("");
12
- const [loading, setLoading] = useState(false);
13
-
14
- const handleSubmit = async (e: React.FormEvent) => {
15
- e.preventDefault();
16
- setError("");
17
- setLoading(true);
18
-
19
- try {
20
- const result = await signIn("credentials", {
21
- email,
22
- password,
23
- redirect: false,
24
- });
25
-
26
- if (result?.error) {
27
- setError("Invalid email or password");
28
- } else {
29
- router.push("/");
30
- router.refresh();
31
- }
32
- } catch (err) {
33
- setError("Something went wrong. Please try again.");
34
- } finally {
35
- setLoading(false);
36
- }
37
- };
38
-
39
- return (
40
- <div className="flex min-h-screen items-center justify-center bg-zinc-50 px-4 py-12 dark:bg-black sm:px-6 lg:px-8">
41
- <div className="w-full max-w-md space-y-8">
42
- <div>
43
- <h2 className="mt-6 text-center text-3xl font-bold tracking-tight text-zinc-900 dark:text-zinc-50">
44
- Sign in to your account
45
- </h2>
46
- <p className="mt-2 text-center text-sm text-zinc-600 dark:text-zinc-400">
47
- Or{" "}
48
- <a
49
- href="/auth/signup"
50
- className="font-medium text-zinc-950 hover:text-zinc-700 dark:text-zinc-50 dark:hover:text-zinc-300"
51
- >
52
- create a new account
53
- </a>
54
- </p>
55
- </div>
56
- <form className="mt-8 space-y-6" onSubmit={handleSubmit}>
57
- {error && (
58
- <div className="rounded-md bg-red-50 p-4 dark:bg-red-900/20">
59
- <p className="text-sm text-red-800 dark:text-red-200">{error}</p>
60
- </div>
61
- )}
62
- <div className="-space-y-px rounded-md shadow-sm">
63
- <div>
64
- <label htmlFor="email" className="sr-only">
65
- Email address
66
- </label>
67
- <input
68
- id="email"
69
- name="email"
70
- type="email"
71
- autoComplete="email"
72
- required
73
- value={email}
74
- onChange={(e) => setEmail(e.target.value)}
75
- className="relative block w-full rounded-t-md border-0 px-3 py-2 text-zinc-900 ring-1 ring-inset ring-zinc-300 placeholder:text-zinc-400 focus:z-10 focus:ring-2 focus:ring-inset focus:ring-zinc-600 dark:bg-zinc-800 dark:text-zinc-50 dark:ring-zinc-700 dark:placeholder:text-zinc-500 dark:focus:ring-zinc-400 sm:text-sm sm:leading-6"
76
- placeholder="Email address"
77
- />
78
- </div>
79
- <div>
80
- <label htmlFor="password" className="sr-only">
81
- Password
82
- </label>
83
- <input
84
- id="password"
85
- name="password"
86
- type="password"
87
- autoComplete="current-password"
88
- required
89
- value={password}
90
- onChange={(e) => setPassword(e.target.value)}
91
- className="relative block w-full rounded-b-md border-0 px-3 py-2 text-zinc-900 ring-1 ring-inset ring-zinc-300 placeholder:text-zinc-400 focus:z-10 focus:ring-2 focus:ring-inset focus:ring-zinc-600 dark:bg-zinc-800 dark:text-zinc-50 dark:ring-zinc-700 dark:placeholder:text-zinc-500 dark:focus:ring-zinc-400 sm:text-sm sm:leading-6"
92
- placeholder="Password"
93
- />
94
- </div>
95
- </div>
96
-
97
- <div className="flex items-center justify-between">
98
- <div className="flex items-center">
99
- <input
100
- id="remember-me"
101
- name="remember-me"
102
- type="checkbox"
103
- className="h-4 w-4 rounded border-zinc-300 text-zinc-600 focus:ring-zinc-600 dark:border-zinc-700 dark:bg-zinc-800"
104
- />
105
- <label
106
- htmlFor="remember-me"
107
- className="ml-2 block text-sm text-zinc-900 dark:text-zinc-50"
108
- >
109
- Remember me
110
- </label>
111
- </div>
112
-
113
- <div className="text-sm">
114
- <a
115
- href="/auth/forgot-password"
116
- className="font-medium text-zinc-600 hover:text-zinc-500 dark:text-zinc-400 dark:hover:text-zinc-300"
117
- >
118
- Forgot your password?
119
- </a>
120
- </div>
121
- </div>
122
-
123
- <div>
124
- <button
125
- type="submit"
126
- disabled={loading}
127
- className="group relative flex w-full justify-center rounded-md bg-zinc-900 px-3 py-2 text-sm font-semibold text-white hover:bg-zinc-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-zinc-600 disabled:opacity-50 dark:bg-zinc-50 dark:text-zinc-900 dark:hover:bg-zinc-200"
128
- >
129
- {loading ? "Signing in..." : "Sign in"}
130
- </button>
131
- </div>
132
- </form>
133
- </div>
134
- </div>
135
- );
136
- }
@@ -1,48 +0,0 @@
1
- "use client";
2
-
3
- import React from "react";
4
- import { AuthNavbar } from "@/components/auth-navbar";
5
- import Link from "next/link";
6
-
7
- /**
8
- * Landing Page Example
9
- *
10
- * This is an example landing page with auth navbar.
11
- *
12
- * To use this:
13
- * 1. This replaces your existing app/page.tsx (only if it's the default Next.js page)
14
- * 2. The AuthNavbar automatically shows sign in/out based on session
15
- * 3. Customize this page with your content
16
- * 4. If you have an existing navbar, you can use AuthNavbar as reference or integrate its features
17
- */
18
- export default function HomePage() {
19
- return (
20
- <div className="min-h-screen bg-zinc-50 dark:bg-black">
21
- <AuthNavbar />
22
- <main className="mx-auto max-w-7xl px-4 py-16 sm:px-6 lg:px-8">
23
- <div className="text-center">
24
- <h1 className="text-4xl font-bold tracking-tight text-zinc-900 dark:text-zinc-50 sm:text-6xl">
25
- Welcome to Your App
26
- </h1>
27
- <p className="mt-6 text-lg leading-8 text-zinc-600 dark:text-zinc-400">
28
- Get started by exploring the features below.
29
- </p>
30
- <div className="mt-10 flex items-center justify-center gap-x-6">
31
- <Link
32
- href="/dashboard"
33
- className="rounded-md bg-zinc-900 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-zinc-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-zinc-600 dark:bg-zinc-50 dark:text-zinc-900 dark:hover:bg-zinc-200"
34
- >
35
- Go to Dashboard
36
- </Link>
37
- <Link
38
- href="/auth/login"
39
- className="text-sm font-semibold leading-6 text-zinc-900 dark:text-zinc-50"
40
- >
41
- Sign In <span aria-hidden="true">→</span>
42
- </Link>
43
- </div>
44
- </div>
45
- </main>
46
- </div>
47
- );
48
- }
@@ -1,43 +0,0 @@
1
- "use client";
2
-
3
- import { signIn, signOut, useSession } from "next-auth/react";
4
-
5
- export function AuthButton() {
6
- const { data: session, status } = useSession();
7
-
8
- if (status === "loading") {
9
- return (
10
- <button
11
- disabled
12
- className="rounded-md bg-zinc-100 px-4 py-2 text-sm font-medium text-zinc-900 dark:bg-zinc-800 dark:text-zinc-50"
13
- >
14
- Loading...
15
- </button>
16
- );
17
- }
18
-
19
- if (session) {
20
- return (
21
- <div className="flex items-center gap-4">
22
- <span className="text-sm text-zinc-600 dark:text-zinc-400">
23
- {session.user?.email}
24
- </span>
25
- <button
26
- onClick={() => signOut()}
27
- className="rounded-md bg-zinc-900 px-4 py-2 text-sm font-medium text-white hover:bg-zinc-700 dark:bg-zinc-50 dark:text-zinc-900 dark:hover:bg-zinc-200"
28
- >
29
- Sign out
30
- </button>
31
- </div>
32
- );
33
- }
34
-
35
- return (
36
- <button
37
- onClick={() => signIn()}
38
- className="rounded-md bg-zinc-900 px-4 py-2 text-sm font-medium text-white hover:bg-zinc-700 dark:bg-zinc-50 dark:text-zinc-900 dark:hover:bg-zinc-200"
39
- >
40
- Sign in
41
- </button>
42
- );
43
- }
@@ -1,118 +0,0 @@
1
- "use client";
2
-
3
- import React from "react";
4
- import { useSession, signOut } from "next-auth/react";
5
- import Link from "next/link";
6
-
7
- /**
8
- * Auth Navbar Component (Demo/Example)
9
- *
10
- * Displays a navigation bar with session status and sign in/out button.
11
- * This is a demo component - rename or customize it to fit your needs.
12
- *
13
- * Usage:
14
- * ```tsx
15
- * import { AuthNavbar } from "@/components/auth-navbar";
16
- *
17
- * export default function Layout({ children }) {
18
- * return (
19
- * <>
20
- * <AuthNavbar />
21
- * {children}
22
- * </>
23
- * );
24
- * }
25
- * ```
26
- */
27
- export function AuthNavbar() {
28
- const { data: session, status } = useSession();
29
-
30
- const handleSignOut = async () => {
31
- await signOut({ callbackUrl: "/" });
32
- };
33
-
34
- return (
35
- <nav className="border-b border-zinc-200 bg-white dark:border-zinc-800 dark:bg-zinc-900">
36
- <div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
37
- <div className="flex h-16 items-center justify-between">
38
- {/* Logo/Brand */}
39
- <div className="flex items-center">
40
- <Link
41
- href="/"
42
- className="text-xl font-bold text-zinc-900 hover:text-zinc-700 dark:text-zinc-50 dark:hover:text-zinc-200"
43
- >
44
- Your App
45
- </Link>
46
- </div>
47
-
48
- {/* Navigation Links */}
49
- <div className="hidden md:flex md:items-center md:space-x-6">
50
- <Link
51
- href="/"
52
- className="text-sm font-medium text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-50"
53
- >
54
- Home
55
- </Link>
56
- {session && (
57
- <Link
58
- href="/dashboard"
59
- className="text-sm font-medium text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-50"
60
- >
61
- Dashboard
62
- </Link>
63
- )}
64
- </div>
65
-
66
- {/* Auth Section */}
67
- <div className="flex items-center space-x-4">
68
- {status === "loading" ? (
69
- <div className="h-8 w-8 animate-spin rounded-full border-2 border-zinc-300 border-t-zinc-600 dark:border-zinc-700 dark:border-t-zinc-400"></div>
70
- ) : session ? (
71
- <div className="flex items-center space-x-4">
72
- {/* User Info */}
73
- <div className="hidden sm:flex sm:flex-col sm:items-end">
74
- <span className="text-sm font-medium text-zinc-900 dark:text-zinc-50">
75
- {session.user?.name || session.user?.email}
76
- </span>
77
- <span className="text-xs text-zinc-500 dark:text-zinc-400">
78
- {session.user?.email}
79
- </span>
80
- </div>
81
-
82
- {/* User Avatar */}
83
- {session.user?.image ? (
84
- <img
85
- src={session.user.image}
86
- alt={session.user.name || "User"}
87
- className="h-8 w-8 rounded-full"
88
- />
89
- ) : (
90
- <div className="flex h-8 w-8 items-center justify-center rounded-full bg-zinc-600 text-sm font-medium text-white dark:bg-zinc-700">
91
- {session.user?.name?.charAt(0).toUpperCase() ||
92
- session.user?.email?.charAt(0).toUpperCase() ||
93
- "U"}
94
- </div>
95
- )}
96
-
97
- {/* Sign Out Button */}
98
- <button
99
- onClick={handleSignOut}
100
- className="rounded-md bg-zinc-900 px-4 py-2 text-sm font-semibold text-white hover:bg-zinc-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-zinc-600 dark:bg-zinc-50 dark:text-zinc-900 dark:hover:bg-zinc-200"
101
- >
102
- Sign Out
103
- </button>
104
- </div>
105
- ) : (
106
- <Link
107
- href="/auth/login"
108
- className="rounded-md bg-zinc-900 px-4 py-2 text-sm font-semibold text-white hover:bg-zinc-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-zinc-600 dark:bg-zinc-50 dark:text-zinc-900 dark:hover:bg-zinc-200"
109
- >
110
- Sign In
111
- </Link>
112
- )}
113
- </div>
114
- </div>
115
- </div>
116
- </nav>
117
- );
118
- }