create-z3 0.0.11 → 0.0.13

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.
Files changed (76) hide show
  1. package/dist/index.js +196 -51
  2. package/package.json +4 -2
  3. package/templates/nextjs/README.md +242 -18
  4. package/templates/nextjs/components.json +24 -0
  5. package/templates/nextjs/convex/_generated/api.d.ts +65 -0
  6. package/templates/nextjs/convex/_generated/api.js +23 -0
  7. package/templates/nextjs/convex/_generated/dataModel.d.ts +60 -0
  8. package/templates/nextjs/convex/_generated/server.d.ts +143 -0
  9. package/templates/nextjs/convex/_generated/server.js +93 -0
  10. package/templates/nextjs/convex/auth/adapter/index.ts +222 -0
  11. package/templates/nextjs/convex/auth/adapter/utils.ts +597 -0
  12. package/templates/nextjs/convex/auth/api.ts +8 -0
  13. package/templates/nextjs/convex/auth/config.ts +10 -0
  14. package/templates/nextjs/convex/auth/db.ts +299 -0
  15. package/templates/nextjs/convex/auth/index.ts +54 -0
  16. package/templates/nextjs/convex/auth/plugins/index.ts +9 -0
  17. package/templates/nextjs/convex/auth/sessions.ts +60 -0
  18. package/templates/nextjs/convex/convex.config.ts +5 -0
  19. package/templates/nextjs/convex/http.ts +28 -0
  20. package/templates/nextjs/convex/schema.ts +69 -0
  21. package/templates/nextjs/eslint.config.mjs +157 -0
  22. package/templates/nextjs/next.config.ts +10 -0
  23. package/templates/nextjs/package.json +43 -11
  24. package/templates/nextjs/pnpm-workspace.yaml +5 -0
  25. package/templates/nextjs/postcss.config.mjs +7 -0
  26. package/templates/nextjs/public/favicons/favicon.ico +0 -0
  27. package/templates/nextjs/public/file.svg +1 -0
  28. package/templates/nextjs/public/globe.svg +1 -0
  29. package/templates/nextjs/public/next.svg +1 -0
  30. package/templates/nextjs/public/vercel.svg +1 -0
  31. package/templates/nextjs/public/window.svg +1 -0
  32. package/templates/nextjs/src/app/(frontend)/@auth/(...)auth/[pathname]/page.tsx +13 -0
  33. package/templates/nextjs/src/app/(frontend)/@auth/(...)auth/[pathname]/view.tsx +23 -0
  34. package/templates/nextjs/src/app/(frontend)/@auth/default.tsx +3 -0
  35. package/templates/nextjs/src/app/(frontend)/@auth/page.tsx +3 -0
  36. package/templates/nextjs/src/app/(frontend)/auth/[pathname]/page.tsx +13 -0
  37. package/templates/nextjs/src/app/(frontend)/auth/[pathname]/view.tsx +11 -0
  38. package/templates/nextjs/src/app/(frontend)/globals.css +65 -0
  39. package/templates/nextjs/src/app/(frontend)/layout.tsx +53 -0
  40. package/templates/nextjs/src/app/(frontend)/page.tsx +5 -0
  41. package/templates/nextjs/src/app/api/auth/[...all]/route.ts +6 -0
  42. package/templates/nextjs/src/app/favicon.ico +0 -0
  43. package/templates/nextjs/src/auth/client.tsx +50 -0
  44. package/templates/nextjs/src/auth/server.ts +54 -0
  45. package/templates/nextjs/src/auth/types.ts +3 -0
  46. package/templates/nextjs/src/auth/utils.ts +33 -0
  47. package/templates/nextjs/src/components/component-example.tsx +501 -0
  48. package/templates/nextjs/src/components/example.tsx +54 -0
  49. package/templates/nextjs/src/components/providers/client.tsx +13 -0
  50. package/templates/nextjs/src/components/providers/convex.tsx +34 -0
  51. package/templates/nextjs/src/components/providers/server.tsx +14 -0
  52. package/templates/nextjs/src/components/ui/alert-dialog.tsx +175 -0
  53. package/templates/nextjs/src/components/ui/badge.tsx +48 -0
  54. package/templates/nextjs/src/components/ui/button.tsx +53 -0
  55. package/templates/nextjs/src/components/ui/card.tsx +94 -0
  56. package/templates/nextjs/src/components/ui/combobox.tsx +292 -0
  57. package/templates/nextjs/src/components/ui/dialog.tsx +151 -0
  58. package/templates/nextjs/src/components/ui/dropdown-menu.tsx +254 -0
  59. package/templates/nextjs/src/components/ui/field.tsx +227 -0
  60. package/templates/nextjs/src/components/ui/input-group.tsx +149 -0
  61. package/templates/nextjs/src/components/ui/input.tsx +20 -0
  62. package/templates/nextjs/src/components/ui/label.tsx +20 -0
  63. package/templates/nextjs/src/components/ui/select.tsx +192 -0
  64. package/templates/nextjs/src/components/ui/separator.tsx +25 -0
  65. package/templates/nextjs/src/components/ui/textarea.tsx +18 -0
  66. package/templates/nextjs/src/db/constants/auth.ts +6 -0
  67. package/templates/nextjs/src/db/constants/index.ts +45 -0
  68. package/templates/nextjs/src/env.mjs +48 -0
  69. package/templates/nextjs/src/lib/auth/server.ts +15 -0
  70. package/templates/nextjs/src/lib/utils.ts +6 -0
  71. package/templates/nextjs/src/proxy.ts +21 -0
  72. package/templates/nextjs/tsconfig.json +42 -0
  73. package/templates/tanstack-start/convex/auth/adapter/index.ts +1 -1
  74. package/templates/tanstack-start/convex/auth/index.ts +1 -1
  75. package/templates/tanstack-start/package.json +1 -0
  76. package/templates/nextjs/src/.gitkeep +0 -1
package/dist/index.js CHANGED
@@ -72,7 +72,10 @@ async function copyTemplate(framework, targetPath) {
72
72
  const __filename2 = fileURLToPath(import.meta.url);
73
73
  const __dirname2 = dirname(__filename2);
74
74
  const templateDir = framework === "tanstack" ? "tanstack-start" : "nextjs";
75
- const templatePath = join(__dirname2, "../templates", templateDir);
75
+ let templatePath = join(__dirname2, "../../templates", templateDir);
76
+ if (!fs2.existsSync(templatePath)) {
77
+ templatePath = join(__dirname2, "../templates", templateDir);
78
+ }
76
79
  await fs2.copy(templatePath, targetPath, {
77
80
  overwrite: false,
78
81
  errorOnExist: false
@@ -1725,25 +1728,25 @@ function getAdditionalProviders() {
1725
1728
 
1726
1729
  // src/installers/string-utils.ts
1727
1730
  import fs3 from "fs-extra";
1728
- var DEFAULT_THEME = `--background: 0 0% 100%;
1729
- --foreground: 240 10% 3.9%;
1730
- --card: 0 0% 100%;
1731
- --card-foreground: 240 10% 3.9%;
1732
- --popover: 0 0% 100%;
1733
- --popover-foreground: 240 10% 3.9%;
1734
- --primary: 240 5.9% 10%;
1735
- --primary-foreground: 0 0% 98%;
1736
- --secondary: 240 4.8% 95.9%;
1737
- --secondary-foreground: 240 5.9% 10%;
1738
- --muted: 240 4.8% 95.9%;
1739
- --muted-foreground: 240 3.8% 46.1%;
1740
- --accent: 240 4.8% 95.9%;
1741
- --accent-foreground: 240 5.9% 10%;
1742
- --destructive: 0 84.2% 60.2%;
1743
- --destructive-foreground: 0 0% 98%;
1744
- --border: 240 5.9% 90%;
1745
- --input: 240 5.9% 90%;
1746
- --ring: 240 5.9% 10%;
1731
+ var DEFAULT_THEME = `--background: 100% 0.000 0;
1732
+ --foreground: 3.9% 0.006 240;
1733
+ --card: 100% 0.000 0;
1734
+ --card-foreground: 3.9% 0.006 240;
1735
+ --popover: 100% 0.000 0;
1736
+ --popover-foreground: 3.9% 0.006 240;
1737
+ --primary: 10% 0.003 240;
1738
+ --primary-foreground: 98% 0.000 0;
1739
+ --secondary: 95.9% 0.002 240;
1740
+ --secondary-foreground: 10% 0.003 240;
1741
+ --muted: 95.9% 0.002 240;
1742
+ --muted-foreground: 46.1% 0.002 240;
1743
+ --accent: 95.9% 0.002 240;
1744
+ --accent-foreground: 10% 0.003 240;
1745
+ --destructive: 60.2% 0.168 0;
1746
+ --destructive-foreground: 98% 0.000 0;
1747
+ --border: 90% 0.003 240;
1748
+ --input: 90% 0.003 240;
1749
+ --ring: 10% 0.003 240;
1747
1750
  --radius: 0.5rem;`;
1748
1751
  function detectIndentation(line) {
1749
1752
  const match = line.match(/^(\s*)/);
@@ -1927,6 +1930,129 @@ import { join as join2 } from "path";
1927
1930
  import { execa } from "execa";
1928
1931
  import ora from "ora";
1929
1932
  import crypto from "crypto";
1933
+
1934
+ // src/utils/tweakcn-converter.ts
1935
+ import fs4 from "fs-extra";
1936
+ import convert from "color-convert";
1937
+ async function convertTweakCNToOKLCH(options) {
1938
+ const { source, format = "oklch" } = options;
1939
+ const cssContent = await fetchOrReadCSS(source);
1940
+ const colors = parseColorsFromCSS(cssContent);
1941
+ const convertedColors = colors.map((color) => ({
1942
+ ...color,
1943
+ oklch: convertColorToOKLCH(color.value)
1944
+ }));
1945
+ return generateCSSOutput(convertedColors, format);
1946
+ }
1947
+ async function fetchOrReadCSS(source) {
1948
+ if (source.startsWith("http://") || source.startsWith("https://")) {
1949
+ try {
1950
+ const response = await fetch(source);
1951
+ if (!response.ok) {
1952
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
1953
+ }
1954
+ return await response.text();
1955
+ } catch (error) {
1956
+ throw new Error(
1957
+ `Failed to fetch CSS from URL: ${error instanceof Error ? error.message : "Unknown error"}`
1958
+ );
1959
+ }
1960
+ }
1961
+ try {
1962
+ return await fs4.readFile(source, "utf-8");
1963
+ } catch (error) {
1964
+ throw new Error(
1965
+ `Failed to read CSS from file: ${error instanceof Error ? error.message : "Unknown error"}`
1966
+ );
1967
+ }
1968
+ }
1969
+ function parseColorsFromCSS(css) {
1970
+ const colors = [];
1971
+ const cssVarRegex = /--([\w-]+)\s*:\s*([^;]+);/g;
1972
+ let match;
1973
+ while ((match = cssVarRegex.exec(css)) !== null) {
1974
+ const name = `--${match[1]}`;
1975
+ const value = match[2].trim();
1976
+ if (isColorValue(value)) {
1977
+ colors.push({ name, value });
1978
+ }
1979
+ }
1980
+ return colors;
1981
+ }
1982
+ function isColorValue(value) {
1983
+ if (value.startsWith("#")) {
1984
+ return true;
1985
+ }
1986
+ if (value.startsWith("rgb(") || value.startsWith("rgba(")) {
1987
+ return true;
1988
+ }
1989
+ if (value.startsWith("hsl(") || value.startsWith("hsla(")) {
1990
+ return true;
1991
+ }
1992
+ if (value.startsWith("oklch(")) {
1993
+ return true;
1994
+ }
1995
+ if (/^\d+(\.\d+)?\s+\d+(\.\d+)?%\s+\d+(\.\d+)?%$/.test(value)) {
1996
+ return true;
1997
+ }
1998
+ return false;
1999
+ }
2000
+ function convertColorToOKLCH(colorValue) {
2001
+ const trimmed = colorValue.trim();
2002
+ if (trimmed.startsWith("oklch(")) {
2003
+ const match = trimmed.match(/oklch\(([\d.]+%?)\s+([\d.]+)\s+([\d.]+)\)/);
2004
+ if (match) {
2005
+ return `${match[1]} ${match[2]} ${match[3]}`;
2006
+ }
2007
+ }
2008
+ let rgb;
2009
+ try {
2010
+ if (trimmed.startsWith("#")) {
2011
+ rgb = convert.hex.rgb(trimmed.slice(1));
2012
+ } else if (trimmed.startsWith("rgb(") || trimmed.startsWith("rgba(")) {
2013
+ const match = trimmed.match(/rgba?\((\d+),?\s*(\d+),?\s*(\d+)/);
2014
+ if (!match) throw new Error("Invalid RGB format");
2015
+ rgb = [parseInt(match[1]), parseInt(match[2]), parseInt(match[3])];
2016
+ } else if (trimmed.startsWith("hsl(") || trimmed.startsWith("hsla(")) {
2017
+ const match = trimmed.match(/hsla?\((\d+),?\s*(\d+)%?,?\s*(\d+)%?/);
2018
+ if (!match) throw new Error("Invalid HSL format");
2019
+ const hsl2 = [
2020
+ parseInt(match[1]),
2021
+ parseInt(match[2]),
2022
+ parseInt(match[3])
2023
+ ];
2024
+ rgb = convert.hsl.rgb(hsl2);
2025
+ } else if (/^\d+(\.\d+)?\s+\d+(\.\d+)?%\s+\d+(\.\d+)?%$/.test(trimmed)) {
2026
+ const parts = trimmed.split(/\s+/);
2027
+ const h2 = parseFloat(parts[0]);
2028
+ const s = parseFloat(parts[1].replace("%", ""));
2029
+ const l2 = parseFloat(parts[2].replace("%", ""));
2030
+ rgb = convert.hsl.rgb([h2, s, l2]);
2031
+ } else {
2032
+ return trimmed;
2033
+ }
2034
+ const hsl = convert.rgb.hsl(rgb);
2035
+ const l = hsl[2];
2036
+ const c = hsl[1] / 100 * 0.4;
2037
+ const h = hsl[0];
2038
+ return `${l}% ${c.toFixed(3)} ${h}`;
2039
+ } catch (error) {
2040
+ console.warn(`Failed to convert color "${colorValue}": ${error instanceof Error ? error.message : "Unknown error"}`);
2041
+ return trimmed;
2042
+ }
2043
+ }
2044
+ function generateCSSOutput(colors, format) {
2045
+ if (colors.length === 0) {
2046
+ return "";
2047
+ }
2048
+ const declarations = colors.map((color) => {
2049
+ const value = format === "oklch" && color.oklch ? color.oklch : color.value;
2050
+ return `${color.name}: ${value};`;
2051
+ }).join("\n");
2052
+ return declarations;
2053
+ }
2054
+
2055
+ // src/installers/base.ts
1930
2056
  var FrameworkInstaller = class {
1931
2057
  /**
1932
2058
  * Constructor for FrameworkInstaller
@@ -1947,24 +2073,22 @@ var FrameworkInstaller = class {
1947
2073
  }
1948
2074
  /**
1949
2075
  * Detect the package manager used to invoke the CLI
1950
- * Checks npm_config_user_agent environment variable
2076
+ * Checks environment variables set by package managers
1951
2077
  *
1952
- * @returns Detected package manager, defaults to 'npm'
2078
+ * @returns Detected package manager ('npm', 'yarn', 'pnpm', or 'bun')
1953
2079
  */
1954
2080
  detectPackageManager() {
1955
- const userAgent = process.env.npm_config_user_agent || "";
1956
- if (userAgent.includes("pnpm")) {
1957
- return "pnpm";
1958
- } else if (userAgent.includes("yarn")) {
1959
- return "yarn";
1960
- } else if (userAgent.includes("bun")) {
1961
- return "bun";
2081
+ const userAgent = process.env.npm_config_user_agent;
2082
+ if (userAgent) {
2083
+ if (userAgent.includes("pnpm")) return "pnpm";
2084
+ if (userAgent.includes("yarn")) return "yarn";
2085
+ if (userAgent.includes("bun")) return "bun";
1962
2086
  }
1963
2087
  return "npm";
1964
2088
  }
1965
2089
  /**
1966
2090
  * Install project dependencies using detected package manager
1967
- * Shows progress with ora spinner
2091
+ * Shows progress spinner during installation
1968
2092
  */
1969
2093
  async installDependencies() {
1970
2094
  const packageManager = this.detectPackageManager();
@@ -1974,21 +2098,21 @@ var FrameworkInstaller = class {
1974
2098
  cwd: this.targetPath,
1975
2099
  stdio: "pipe"
1976
2100
  });
1977
- spinner.succeed(`Dependencies installed with ${packageManager}`);
2101
+ spinner.succeed("Dependencies installed successfully");
1978
2102
  } catch (error) {
1979
- spinner.fail(`Failed to install dependencies with ${packageManager}`);
2103
+ spinner.fail("Failed to install dependencies");
1980
2104
  throw new Error(
1981
2105
  `Dependency installation failed: ${error instanceof Error ? error.message : "Unknown error"}`
1982
2106
  );
1983
2107
  }
1984
2108
  }
1985
2109
  /**
1986
- * Format generated files using the project's format command
1987
- * Runs after all file modifications to ensure consistent code style
2110
+ * Format code using project's formatter (Prettier via package.json script)
2111
+ * Non-blocking - continues even if formatting fails
1988
2112
  */
1989
2113
  async formatCode() {
1990
2114
  const packageManager = this.detectPackageManager();
1991
- const spinner = ora("Formatting generated files...").start();
2115
+ const spinner = ora("Formatting code...").start();
1992
2116
  try {
1993
2117
  await execa(packageManager, ["run", "format"], {
1994
2118
  cwd: this.targetPath,
@@ -2072,13 +2196,15 @@ Make sure you are authenticated with GitHub CLI (run "gh auth login")`
2072
2196
  return crypto.randomBytes(32).toString("hex");
2073
2197
  }
2074
2198
  /**
2075
- * Fetch TweakCN theme CSS from URL
2199
+ * Fetch TweakCN theme CSS from URL and convert to OKLCH format
2076
2200
  * Handles network errors and provides retry suggestions
2201
+ * Optionally converts fetched theme through OKLCH converter for consistent color format
2077
2202
  *
2078
2203
  * @param url - URL to fetch theme from
2079
- * @returns Theme CSS content
2204
+ * @param convertToOklch - Whether to convert theme colors to OKLCH format (default: true)
2205
+ * @returns Theme CSS content in OKLCH format (if conversion enabled)
2080
2206
  */
2081
- async fetchThemeFromUrl(url) {
2207
+ async fetchThemeFromUrl(url, convertToOklch = true) {
2082
2208
  const spinner = ora("Fetching TweakCN theme...").start();
2083
2209
  try {
2084
2210
  const response = await fetch(url);
@@ -2087,6 +2213,23 @@ Make sure you are authenticated with GitHub CLI (run "gh auth login")`
2087
2213
  }
2088
2214
  const content = await response.text();
2089
2215
  spinner.succeed("TweakCN theme fetched");
2216
+ if (convertToOklch) {
2217
+ const convertSpinner = ora("Converting theme to OKLCH format...").start();
2218
+ try {
2219
+ const convertedContent = await convertTweakCNToOKLCH({
2220
+ source: url,
2221
+ format: "oklch"
2222
+ });
2223
+ convertSpinner.succeed("Theme converted to OKLCH format");
2224
+ return convertedContent;
2225
+ } catch (convertError) {
2226
+ convertSpinner.warn("OKLCH conversion failed, using raw theme CSS");
2227
+ console.warn(
2228
+ `Warning: Failed to convert theme to OKLCH: ${convertError instanceof Error ? convertError.message : "Unknown error"}`
2229
+ );
2230
+ return content;
2231
+ }
2232
+ }
2090
2233
  return content;
2091
2234
  } catch (error) {
2092
2235
  spinner.fail("Failed to fetch TweakCN theme");
@@ -2111,15 +2254,17 @@ Please check the URL and your internet connection, then try again.`
2111
2254
  copySpinner.fail("Failed to copy template files");
2112
2255
  throw error;
2113
2256
  }
2114
- if (options.emailPasswordAuth || options.oauthProviders.length > 0) {
2115
- const authSpinner = ora("Configuring authentication...").start();
2116
- try {
2117
- await this.updateOAuthConfig(options.oauthProviders, options.emailPasswordAuth);
2257
+ const authSpinner = ora("Configuring authentication...").start();
2258
+ try {
2259
+ await this.updateOAuthConfig(options.oauthProviders, options.emailPasswordAuth);
2260
+ if (options.emailPasswordAuth || options.oauthProviders.length > 0) {
2118
2261
  authSpinner.succeed("Authentication configuration updated");
2119
- } catch (error) {
2120
- authSpinner.fail("Failed to configure authentication");
2121
- throw error;
2262
+ } else {
2263
+ authSpinner.succeed("Authentication placeholders cleaned up");
2122
2264
  }
2265
+ } catch (error) {
2266
+ authSpinner.fail("Failed to configure authentication");
2267
+ throw error;
2123
2268
  }
2124
2269
  const oauthUISpinner = ora("Configuring OAuth UI...").start();
2125
2270
  try {
@@ -2175,16 +2320,17 @@ Please check the URL and your internet connection, then try again.`
2175
2320
  if (options.tweakcnTheme) {
2176
2321
  if (options.tweakcnTheme.type === "url") {
2177
2322
  themeContent = await this.fetchThemeFromUrl(options.tweakcnTheme.content);
2323
+ } else if (options.tweakcnTheme.type === "css") {
2324
+ themeContent = options.tweakcnTheme.content;
2178
2325
  } else {
2179
2326
  themeContent = options.tweakcnTheme.content;
2180
2327
  }
2328
+ await this.applyTweakCNTheme(themeContent);
2329
+ themeSpinner.succeed("TweakCN theme applied");
2181
2330
  } else {
2182
- themeContent = DEFAULT_THEME;
2331
+ await this.applyTweakCNTheme(DEFAULT_THEME);
2332
+ themeSpinner.succeed("Default theme applied");
2183
2333
  }
2184
- await this.applyTweakCNTheme(themeContent);
2185
- themeSpinner.succeed(
2186
- options.tweakcnTheme ? "TweakCN theme applied" : "Default theme applied"
2187
- );
2188
2334
  } catch (error) {
2189
2335
  themeSpinner.fail("Failed to apply theme");
2190
2336
  throw error;
@@ -2194,7 +2340,6 @@ Please check the URL and your internet connection, then try again.`
2194
2340
  }
2195
2341
  if (options.installDependencies) {
2196
2342
  await this.installDependencies();
2197
- await this.formatCode();
2198
2343
  }
2199
2344
  }
2200
2345
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-z3",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "type": "module",
5
5
  "description": "CLI for scaffolding Z3 Stack applications (TanStack/Next.js + Convex + Better Auth)",
6
6
  "bin": {
@@ -25,7 +25,8 @@
25
25
  "ora": "^8.1.0",
26
26
  "execa": "^9.5.2",
27
27
  "sort-package-json": "^2.10.0",
28
- "validate-npm-package-name": "^5.0.0"
28
+ "validate-npm-package-name": "^5.0.0",
29
+ "color-convert": "^2.0.1"
29
30
  },
30
31
  "devDependencies": {
31
32
  "typescript": "^5.7.0",
@@ -33,6 +34,7 @@
33
34
  "@types/node": "^22.0.0",
34
35
  "@types/fs-extra": "^11.0.0",
35
36
  "@types/validate-npm-package-name": "^4.0.0",
37
+ "@types/color-convert": "^2.0.3",
36
38
  "vitest": "^2.0.0"
37
39
  },
38
40
  "engines": {
@@ -1,26 +1,250 @@
1
- # Next.js Template - Placeholder
1
+ # Next.js + Better Auth + Convex + shadcn/ui Template
2
2
 
3
- This is a placeholder template for Next.js projects in the Z3 Stack CLI.
3
+ A modern, production-ready SaaS template built with:
4
4
 
5
- ## Status
5
+ - **[Next.js](https://nextjs.org)** - The React framework for production with App Router
6
+ - **[Better Auth](https://www.better-auth.com/)** - Modern authentication with email/password and OAuth providers
7
+ - **[Convex](https://convex.dev/)** - Real-time database and backend
8
+ - **[shadcn/ui](https://ui.shadcn.com/)** - Beautifully designed components built on Radix UI
9
+ - **[Tailwind CSS](https://tailwindcss.com/)** - Utility-first CSS framework
6
10
 
7
- This template is currently a minimal placeholder structure. The full Next.js template with Convex, Better Auth, and shadcn/ui integration will be implemented in a future specification.
11
+ ## Getting Started
8
12
 
9
- ## Current Structure
13
+ ### 1. Prerequisites
10
14
 
11
- - `package.json` - Minimal Next.js dependencies with project name placeholder
12
- - `.gitignore` - Standard Next.js ignore patterns
13
- - `src/` - Empty source directory (placeholder)
15
+ - Node.js 18+ and npm/pnpm/yarn installed
16
+ - A Convex account (free at [convex.dev](https://convex.dev))
14
17
 
15
- ## Future Implementation
18
+ ### 2. Clone and Install
16
19
 
17
- The complete Next.js template will include:
18
- - Full Convex integration and schema
19
- - Better Auth setup with OAuth providers
20
- - shadcn/ui components
21
- - TypeScript configuration
22
- - Tailwind CSS setup
23
- - App Router structure
24
- - Environment configuration
20
+ ```bash
21
+ # Clone this template
22
+ git clone <your-repo-url>
23
+ cd <your-project-name>
25
24
 
26
- For now, this structure serves as a placeholder to support Next.js selection in the CLI framework prompts.
25
+ # Install dependencies
26
+ npm install
27
+ # or
28
+ pnpm install
29
+ # or
30
+ yarn install
31
+ ```
32
+
33
+ ### 3. Environment Setup
34
+
35
+ Copy the example environment file:
36
+
37
+ ```bash
38
+ cp .env.example .env
39
+ ```
40
+
41
+ ### 4. Generate Better Auth Secret
42
+
43
+ Generate a secure secret for Better Auth:
44
+
45
+ ```bash
46
+ npm run secret:create
47
+ # or
48
+ pnpm secret:create
49
+ # or
50
+ yarn secret:create
51
+ ```
52
+
53
+ This will generate a random 32-character hex string and copy it to your clipboard.
54
+
55
+ ### 5. Configure Convex
56
+
57
+ #### Set up Convex project
58
+
59
+ ```bash
60
+ npx convex dev
61
+ ```
62
+
63
+ This will:
64
+ - Create a new Convex project (or link to an existing one)
65
+ - Generate your `CONVEX_URL` and `NEXT_PUBLIC_CONVEX_URL`
66
+ - Start the Convex development server
67
+
68
+ #### Add environment variables to Convex
69
+
70
+ You need to add two critical environment variables to your Convex deployment via the [Convex Dashboard](https://dashboard.convex.dev):
71
+
72
+ 1. **BETTER_AUTH_SECRET**: Paste the secret you generated in step 4
73
+ 2. **SITE_URL**: Set to your application URL
74
+ - Development: `http://localhost:3000` (or whatever port your dev server uses)
75
+ - Production: Your production domain (e.g., `https://yourdomain.com`)
76
+
77
+ To add these in the Convex Dashboard:
78
+ 1. Go to your project at [dashboard.convex.dev](https://dashboard.convex.dev)
79
+ 2. Navigate to "Settings" → "Environment Variables"
80
+ 3. Add both `BETTER_AUTH_SECRET` and `SITE_URL`
81
+
82
+ ### 6. Update Your .env File
83
+
84
+ Update your `.env` file with the values from Convex setup:
85
+
86
+ ```env
87
+ NODE_ENV=development
88
+
89
+ # From Convex setup
90
+ CONVEX_URL=https://your-deployment.convex.cloud
91
+ NEXT_PUBLIC_CONVEX_URL=https://your-deployment.convex.cloud
92
+ CONVEX_SITE_URL=https://your-deployment.convex.site
93
+ NEXT_PUBLIC_CONVEX_SITE_URL=https://your-deployment.convex.site
94
+
95
+ # Better Auth (same secret you added to Convex Dashboard)
96
+ BETTER_AUTH_SECRET=your-generated-secret-here
97
+ NEXT_PUBLIC_SITE_URL=http://localhost:3000
98
+ ```
99
+
100
+ ### 7. Start Development
101
+
102
+ ```bash
103
+ # Terminal 1: Run Convex dev server
104
+ npx convex dev
105
+
106
+ # Terminal 2: Run the application
107
+ npm run dev
108
+ # or
109
+ pnpm dev
110
+ # or
111
+ yarn dev
112
+ ```
113
+
114
+ Visit `http://localhost:3000` to see your application running.
115
+
116
+ ## Authentication Setup
117
+
118
+ This project uses Better Auth for authentication.
119
+
120
+ <!-- {{OAUTH_SETUP_GUIDE}} -->
121
+
122
+ ## Customizing Authentication
123
+
124
+ ### Adding OAuth Providers
125
+
126
+ To add or modify OAuth providers:
127
+
128
+ 1. **Update Better Auth configuration** in `convex/auth/index.ts`:
129
+
130
+ ```typescript
131
+ export const createAuth = (ctx: GenericActionCtx<DataModel>) => {
132
+ return betterAuth({
133
+ socialProviders: {
134
+ google: {
135
+ clientId: process.env.GOOGLE_CLIENT_ID!,
136
+ clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
137
+ redirectURI: `${process.env.SITE_URL}/api/auth/callback/google`
138
+ },
139
+ // Add more providers
140
+ github: {
141
+ clientId: process.env.GITHUB_CLIENT_ID!,
142
+ clientSecret: process.env.GITHUB_CLIENT_SECRET!,
143
+ }
144
+ },
145
+ // ... rest of config
146
+ })
147
+ }
148
+ ```
149
+
150
+ 2. **Add credentials to Convex Dashboard** as environment variables
151
+
152
+ 3. **Consult the Better Auth documentation** for provider-specific configuration:
153
+ - [Better Auth Providers](https://www.better-auth.com/docs/authentication/social-login)
154
+
155
+ ### Customizing User Schema
156
+
157
+ To add custom fields to your user model:
158
+
159
+ 1. Update the `user.additionalFields` in `convex/auth/index.ts`
160
+ 2. Update your Convex schema accordingly
161
+ 3. See [Better Auth User Management](https://www.better-auth.com/docs/concepts/user-management) for details
162
+
163
+ ### Email/Password Configuration
164
+
165
+ The template has email/password authentication enabled by default. To customize:
166
+
167
+ - [Better Auth Email/Password Docs](https://www.better-auth.com/docs/authentication/email-password)
168
+ - [Email Verification Setup](https://www.better-auth.com/docs/authentication/email-verification)
169
+
170
+ ## Project Structure
171
+
172
+ ```
173
+ .
174
+ ├── src/
175
+ │ ├── app/ # Next.js App Router
176
+ │ │ ├── (frontend)/ # Frontend routes group
177
+ │ │ ├── api/ # API routes
178
+ │ │ └── layout.tsx # Root layout
179
+ │ ├── auth/ # Authentication components
180
+ │ ├── components/ # React components
181
+ │ ├── lib/ # Utilities and helpers
182
+ │ └── env.mjs # Typed environment variables
183
+ ├── convex/ # Convex backend
184
+ │ ├── auth/ # Better Auth integration
185
+ │ ├── schema.ts # Database schema
186
+ │ └── *.ts # Convex functions (queries, mutations, actions)
187
+ ├── public/ # Static assets
188
+ └── .env # Environment variables (not committed)
189
+ ```
190
+
191
+ ## Development Scripts
192
+
193
+ ```bash
194
+ npm run dev # Start development server
195
+ npm run build # Build for production
196
+ npm run start # Start production server
197
+ npm run lint # Lint code
198
+ npm run typecheck # Type check without emitting
199
+ npm run format # Format code with Prettier
200
+ npm run format:check # Check code formatting
201
+ npm run secret:create # Generate a Better Auth secret
202
+ ```
203
+
204
+ ## Documentation Resources
205
+
206
+ ### Core Technologies
207
+ - **Next.js**: [nextjs.org/docs](https://nextjs.org/docs)
208
+ - **Better Auth**: [better-auth.com/docs](https://www.better-auth.com/docs)
209
+ - **Convex**: [docs.convex.dev](https://docs.convex.dev)
210
+ - **shadcn/ui**: [ui.shadcn.com](https://ui.shadcn.com)
211
+ - **Tailwind CSS**: [tailwindcss.com/docs](https://tailwindcss.com/docs)
212
+
213
+ ### Integration Guides
214
+ - **Better Auth + Convex**: [better-auth.com/docs/integrations/convex](https://www.better-auth.com/docs/integrations/convex)
215
+ - **Better Auth + Next.js**: [better-auth.com/docs/integrations/nextjs](https://www.better-auth.com/docs/integrations/nextjs)
216
+
217
+ ## Deployment
218
+
219
+ ### Deploy to Production
220
+
221
+ 1. **Deploy Convex Backend**:
222
+ ```bash
223
+ npx convex deploy
224
+ ```
225
+
226
+ 2. **Update Convex environment variables** in production:
227
+ - Set `SITE_URL` to your production domain
228
+ - Keep `BETTER_AUTH_SECRET` the same (or rotate it)
229
+
230
+ 3. **Deploy your application** to your hosting provider (Vercel recommended):
231
+
232
+ For Vercel:
233
+ ```bash
234
+ npm install -g vercel
235
+ vercel
236
+ ```
237
+
238
+ Or connect your GitHub repository to Vercel for automatic deployments.
239
+
240
+ 4. **Update environment variables** in your hosting provider with production values:
241
+ - `NEXT_PUBLIC_CONVEX_URL`
242
+ - `NEXT_PUBLIC_CONVEX_SITE_URL`
243
+ - `NEXT_PUBLIC_SITE_URL`
244
+ - `BETTER_AUTH_SECRET`
245
+
246
+ See [Next.js Deployment](https://nextjs.org/docs/app/building-your-application/deploying) and [Convex Production Deployment](https://docs.convex.dev/production/hosting) for detailed deployment instructions.
247
+
248
+ ## License
249
+
250
+ MIT
@@ -0,0 +1,24 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "base-vega",
4
+ "rsc": true,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "",
8
+ "css": "src/app/globals.css",
9
+ "baseColor": "neutral",
10
+ "cssVariables": true,
11
+ "prefix": ""
12
+ },
13
+ "iconLibrary": "lucide",
14
+ "aliases": {
15
+ "components": "~/components",
16
+ "utils": "~/lib/utils",
17
+ "ui": "~/components/ui",
18
+ "lib": "~/lib",
19
+ "hooks": "~/hooks"
20
+ },
21
+ "menuColor": "default",
22
+ "menuAccent": "subtle",
23
+ "registries": {}
24
+ }