better-ts-stack 2.0.1 → 2.0.2

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.
@@ -21,9 +21,11 @@ function generateNextSteps(config, targetDir, depsInstalled) {
21
21
  const steps = [];
22
22
  const isFullstack = config.applicationType === "fullstack";
23
23
  // Step 1: cd into project directory, unless it was created in place
24
+ // Quoted when it has spaces, so the command can be pasted as it is
24
25
  const relativeDir = node_path_1.default.relative((0, node_process_1.cwd)(), targetDir);
25
- if (relativeDir)
26
- steps.push(`cd ${relativeDir}`);
26
+ if (relativeDir) {
27
+ steps.push(/\s/.test(relativeDir) ? `cd "${relativeDir}"` : `cd ${relativeDir}`);
28
+ }
27
29
  // Step 2: Set environment variables
28
30
  if (config.database !== "none") {
29
31
  steps.push("Review the generated .env and set your database connection details");
@@ -52,7 +52,7 @@ exports.frontendFrameworkOptions = [
52
52
  {
53
53
  value: "tanstack",
54
54
  label: "TanStack Start",
55
- hint: "React, release candidate",
55
+ hint: "Vite and React",
56
56
  },
57
57
  ];
58
58
  // Database system options; the ORM is chosen in a second step
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "better-ts-stack",
3
- "version": "2.0.1",
3
+ "version": "2.0.2",
4
4
  "description": "A CLI that scaffolds Express, NestJS, Next.js, and TanStack Start projects with a database, authentication, and Docker already set up.",
5
5
  "keywords": [
6
6
  "cli",
@@ -1,45 +1,45 @@
1
- {
2
- "id": "tanstack",
3
- "name": "TanStack Start",
4
- "description": "TanStack Start with TanStack Router, React 19, and Tailwind CSS v4",
5
- "type": "base",
6
- "scripts": {
7
- "dev": "vite dev",
8
- "build": "vite build",
9
- "start": "srvx --prod -s ../client dist/server/server.js",
10
- "preview": "vite preview",
11
- "type:check": "tsc --noEmit",
12
- "lint": "eslint src"
13
- },
14
- "dependencies": {
15
- "@tanstack/react-query": "^5.102.8",
16
- "@tanstack/react-query-devtools": "^5.102.8",
17
- "@tanstack/react-router": "^1.170.36",
18
- "@tanstack/react-router-devtools": "^1.167.2",
19
- "@tanstack/react-router-ssr-query": "^1.167.2",
20
- "@tanstack/react-start": "^1.168.53",
21
- "class-variance-authority": "^0.7.1",
22
- "clsx": "^2.1.1",
23
- "lucide-react": "^1.41.0",
24
- "react": "^19.2.8",
25
- "react-dom": "^19.2.8",
26
- "srvx": "^1.0.4",
27
- "tailwind-merge": "^3.6.0"
28
- },
29
- "devDependencies": {
30
- "@eslint/js": "^10.0.1",
31
- "@tailwindcss/vite": "^4.3.3",
32
- "@types/node": "^24.13.3",
33
- "@types/react": "^19.2.18",
34
- "@types/react-dom": "^19.2.7",
35
- "@vitejs/plugin-react": "^6.1.1",
36
- "eslint": "^10.10.0",
37
- "tailwindcss": "^4.3.3",
38
- "typescript": "^6.0.3",
39
- "typescript-eslint": "^8.69.0",
40
- "vite": "^8.3.0"
41
- },
42
- "envVars": {
43
- "PORT": "3000"
44
- }
45
- }
1
+ {
2
+ "id": "tanstack",
3
+ "name": "TanStack Start",
4
+ "description": "TanStack Start with TanStack Router, React 19, and Tailwind CSS v4",
5
+ "type": "base",
6
+ "scripts": {
7
+ "dev": "vite dev",
8
+ "build": "vite build",
9
+ "start": "srvx --prod -s ../client dist/server/server.js",
10
+ "preview": "vite preview",
11
+ "type:check": "tsc --noEmit",
12
+ "lint": "eslint src"
13
+ },
14
+ "dependencies": {
15
+ "@tanstack/react-query": "^5.104.3",
16
+ "@tanstack/react-query-devtools": "^5.104.3",
17
+ "@tanstack/react-router": "^1.170.45",
18
+ "@tanstack/react-router-devtools": "^1.167.2",
19
+ "@tanstack/react-router-ssr-query": "^1.167.3",
20
+ "@tanstack/react-start": "^1.169.3",
21
+ "class-variance-authority": "^0.7.1",
22
+ "clsx": "^2.1.1",
23
+ "lucide-react": "^1.41.0",
24
+ "react": "^19.2.8",
25
+ "react-dom": "^19.2.8",
26
+ "srvx": "^1.0.5",
27
+ "tailwind-merge": "^3.6.0"
28
+ },
29
+ "devDependencies": {
30
+ "@eslint/js": "^10.0.1",
31
+ "@tailwindcss/vite": "^4.3.3",
32
+ "@types/node": "^24.13.3",
33
+ "@types/react": "^19.2.18",
34
+ "@types/react-dom": "^19.2.7",
35
+ "@vitejs/plugin-react": "^6.1.2",
36
+ "eslint": "^10.10.0",
37
+ "tailwindcss": "^4.3.3",
38
+ "typescript": "^6.0.3",
39
+ "typescript-eslint": "^8.69.0",
40
+ "vite": "^8.3.4"
41
+ },
42
+ "envVars": {
43
+ "PORT": "3000"
44
+ }
45
+ }
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
 
3
3
  export const signInSchema = z.object({
4
- email: z.string().email("Enter a valid email address."),
4
+ email: z.email("Enter a valid email address."),
5
5
  password: z.string().min(8, "Password must be at least 8 characters."),
6
6
  });
7
7
 
@@ -1,39 +1,39 @@
1
- import { z } from "zod";
2
-
3
- export const signInSchema = z.object({
4
- email: z.string().email("Enter a valid email address."),
5
- password: z.string().min(8, "Password must be at least 8 characters."),
6
- });
7
-
8
- export const signUpSchema = signInSchema.extend({
9
- name: z
10
- .string()
11
- .trim()
12
- .min(2, "Name must be at least 2 characters.")
13
- .max(50, "Name must be 50 characters or fewer."),
14
- });
15
-
16
- export type SignInValues = z.infer<typeof signInSchema>;
17
- export type SignUpValues = z.infer<typeof signUpSchema>;
18
-
19
- export function getAuthErrorMessage(error: unknown) {
20
- if (!error) {
21
- return "Something went wrong. Please try again.";
22
- }
23
-
24
- if (typeof error === "string") {
25
- return error;
26
- }
27
-
28
- if (typeof error === "object") {
29
- if ("message" in error && typeof error.message === "string") {
30
- return error.message;
31
- }
32
-
33
- if ("statusText" in error && typeof error.statusText === "string") {
34
- return error.statusText;
35
- }
36
- }
37
-
38
- return "Something went wrong. Please try again.";
1
+ import { z } from "zod";
2
+
3
+ export const signInSchema = z.object({
4
+ email: z.email("Enter a valid email address."),
5
+ password: z.string().min(8, "Password must be at least 8 characters."),
6
+ });
7
+
8
+ export const signUpSchema = signInSchema.extend({
9
+ name: z
10
+ .string()
11
+ .trim()
12
+ .min(2, "Name must be at least 2 characters.")
13
+ .max(50, "Name must be 50 characters or fewer."),
14
+ });
15
+
16
+ export type SignInValues = z.infer<typeof signInSchema>;
17
+ export type SignUpValues = z.infer<typeof signUpSchema>;
18
+
19
+ export function getAuthErrorMessage(error: unknown) {
20
+ if (!error) {
21
+ return "Something went wrong. Please try again.";
22
+ }
23
+
24
+ if (typeof error === "string") {
25
+ return error;
26
+ }
27
+
28
+ if (typeof error === "object") {
29
+ if ("message" in error && typeof error.message === "string") {
30
+ return error.message;
31
+ }
32
+
33
+ if ("statusText" in error && typeof error.statusText === "string") {
34
+ return error.statusText;
35
+ }
36
+ }
37
+
38
+ return "Something went wrong. Please try again.";
39
39
  }
@@ -6,5 +6,7 @@ export const users = pgTable("users", {
6
6
  name: text("name"),
7
7
  password: text("password").notNull(),
8
8
  createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
9
- updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(),
9
+ updatedAt: timestamp("updated_at", { withTimezone: true })
10
+ .defaultNow()
11
+ .$onUpdate(() => new Date()),
10
12
  });
@@ -6,5 +6,7 @@ export const users = pgTable("users", {
6
6
  name: text("name"),
7
7
  password: text("password").notNull(),
8
8
  createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
9
- updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(),
9
+ updatedAt: timestamp("updated_at", { withTimezone: true })
10
+ .defaultNow()
11
+ .$onUpdate(() => new Date()),
10
12
  });
@@ -12,7 +12,8 @@ export const user = pgTable("user", {
12
12
  .defaultNow(),
13
13
  updatedAt: timestamp("updated_at", { withTimezone: true })
14
14
  .notNull()
15
- .defaultNow(),
15
+ .defaultNow()
16
+ .$onUpdate(() => new Date()),
16
17
  });
17
18
 
18
19
  export const session = pgTable("session", {
@@ -24,7 +25,8 @@ export const session = pgTable("session", {
24
25
  .defaultNow(),
25
26
  updatedAt: timestamp("updated_at", { withTimezone: true })
26
27
  .notNull()
27
- .defaultNow(),
28
+ .defaultNow()
29
+ .$onUpdate(() => new Date()),
28
30
  ipAddress: text("ip_address"),
29
31
  userAgent: text("user_agent"),
30
32
  userId: text("user_id")
@@ -55,7 +57,8 @@ export const account = pgTable("account", {
55
57
  .defaultNow(),
56
58
  updatedAt: timestamp("updated_at", { withTimezone: true })
57
59
  .notNull()
58
- .defaultNow(),
60
+ .defaultNow()
61
+ .$onUpdate(() => new Date()),
59
62
  }, (table) => [uniqueIndex("account_provider_account_id_unique").on(table.providerId, table.accountId)]);
60
63
 
61
64
  export const verification = pgTable("verification", {
@@ -68,7 +71,8 @@ export const verification = pgTable("verification", {
68
71
  .defaultNow(),
69
72
  updatedAt: timestamp("updated_at", { withTimezone: true })
70
73
  .notNull()
71
- .defaultNow(),
74
+ .defaultNow()
75
+ .$onUpdate(() => new Date()),
72
76
  });
73
77
  {{else}}
74
78
  import { pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
@@ -79,6 +83,8 @@ export const users = pgTable("users", {
79
83
  name: text("name"),
80
84
  password: text("password").notNull(),
81
85
  createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
82
- updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(),
86
+ updatedAt: timestamp("updated_at", { withTimezone: true })
87
+ .defaultNow()
88
+ .$onUpdate(() => new Date()),
83
89
  });
84
90
  {{/if}}
@@ -1,84 +1,90 @@
1
- {{#if useAuth}}
2
- import { boolean, pgTable, text, timestamp, uniqueIndex } from "drizzle-orm/pg-core";
3
-
4
- export const user = pgTable("user", {
5
- id: text("id").primaryKey(),
6
- name: text("name").notNull(),
7
- email: text("email").notNull().unique(),
8
- emailVerified: boolean("email_verified").notNull().default(false),
9
- image: text("image"),
10
- createdAt: timestamp("created_at", { withTimezone: true })
11
- .notNull()
12
- .defaultNow(),
13
- updatedAt: timestamp("updated_at", { withTimezone: true })
14
- .notNull()
15
- .defaultNow(),
16
- });
17
-
18
- export const session = pgTable("session", {
19
- id: text("id").primaryKey(),
20
- expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
21
- token: text("token").notNull().unique(),
22
- createdAt: timestamp("created_at", { withTimezone: true })
23
- .notNull()
24
- .defaultNow(),
25
- updatedAt: timestamp("updated_at", { withTimezone: true })
26
- .notNull()
27
- .defaultNow(),
28
- ipAddress: text("ip_address"),
29
- userAgent: text("user_agent"),
30
- userId: text("user_id")
31
- .notNull()
32
- .references(() => user.id, { onDelete: "cascade" }),
33
- });
34
-
35
- export const account = pgTable("account", {
36
- id: text("id").primaryKey(),
37
- accountId: text("account_id").notNull(),
38
- providerId: text("provider_id").notNull(),
39
- userId: text("user_id")
40
- .notNull()
41
- .references(() => user.id, { onDelete: "cascade" }),
42
- accessToken: text("access_token"),
43
- refreshToken: text("refresh_token"),
44
- idToken: text("id_token"),
45
- accessTokenExpiresAt: timestamp("access_token_expires_at", {
46
- withTimezone: true,
47
- }),
48
- refreshTokenExpiresAt: timestamp("refresh_token_expires_at", {
49
- withTimezone: true,
50
- }),
51
- scope: text("scope"),
52
- password: text("password"),
53
- createdAt: timestamp("created_at", { withTimezone: true })
54
- .notNull()
55
- .defaultNow(),
56
- updatedAt: timestamp("updated_at", { withTimezone: true })
57
- .notNull()
58
- .defaultNow(),
59
- }, (table) => [uniqueIndex("account_provider_account_id_unique").on(table.providerId, table.accountId)]);
60
-
61
- export const verification = pgTable("verification", {
62
- id: text("id").primaryKey(),
63
- identifier: text("identifier").notNull(),
64
- value: text("value").notNull(),
65
- expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
66
- createdAt: timestamp("created_at", { withTimezone: true })
67
- .notNull()
68
- .defaultNow(),
69
- updatedAt: timestamp("updated_at", { withTimezone: true })
70
- .notNull()
71
- .defaultNow(),
72
- });
73
- {{else}}
74
- import { pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
75
-
76
- export const users = pgTable("users", {
77
- id: uuid("id").primaryKey().defaultRandom(),
78
- email: text("email").notNull().unique(),
79
- name: text("name"),
80
- password: text("password").notNull(),
81
- createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
82
- updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(),
83
- });
84
- {{/if}}
1
+ {{#if useAuth}}
2
+ import { boolean, pgTable, text, timestamp, uniqueIndex } from "drizzle-orm/pg-core";
3
+
4
+ export const user = pgTable("user", {
5
+ id: text("id").primaryKey(),
6
+ name: text("name").notNull(),
7
+ email: text("email").notNull().unique(),
8
+ emailVerified: boolean("email_verified").notNull().default(false),
9
+ image: text("image"),
10
+ createdAt: timestamp("created_at", { withTimezone: true })
11
+ .notNull()
12
+ .defaultNow(),
13
+ updatedAt: timestamp("updated_at", { withTimezone: true })
14
+ .notNull()
15
+ .defaultNow()
16
+ .$onUpdate(() => new Date()),
17
+ });
18
+
19
+ export const session = pgTable("session", {
20
+ id: text("id").primaryKey(),
21
+ expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
22
+ token: text("token").notNull().unique(),
23
+ createdAt: timestamp("created_at", { withTimezone: true })
24
+ .notNull()
25
+ .defaultNow(),
26
+ updatedAt: timestamp("updated_at", { withTimezone: true })
27
+ .notNull()
28
+ .defaultNow()
29
+ .$onUpdate(() => new Date()),
30
+ ipAddress: text("ip_address"),
31
+ userAgent: text("user_agent"),
32
+ userId: text("user_id")
33
+ .notNull()
34
+ .references(() => user.id, { onDelete: "cascade" }),
35
+ });
36
+
37
+ export const account = pgTable("account", {
38
+ id: text("id").primaryKey(),
39
+ accountId: text("account_id").notNull(),
40
+ providerId: text("provider_id").notNull(),
41
+ userId: text("user_id")
42
+ .notNull()
43
+ .references(() => user.id, { onDelete: "cascade" }),
44
+ accessToken: text("access_token"),
45
+ refreshToken: text("refresh_token"),
46
+ idToken: text("id_token"),
47
+ accessTokenExpiresAt: timestamp("access_token_expires_at", {
48
+ withTimezone: true,
49
+ }),
50
+ refreshTokenExpiresAt: timestamp("refresh_token_expires_at", {
51
+ withTimezone: true,
52
+ }),
53
+ scope: text("scope"),
54
+ password: text("password"),
55
+ createdAt: timestamp("created_at", { withTimezone: true })
56
+ .notNull()
57
+ .defaultNow(),
58
+ updatedAt: timestamp("updated_at", { withTimezone: true })
59
+ .notNull()
60
+ .defaultNow()
61
+ .$onUpdate(() => new Date()),
62
+ }, (table) => [uniqueIndex("account_provider_account_id_unique").on(table.providerId, table.accountId)]);
63
+
64
+ export const verification = pgTable("verification", {
65
+ id: text("id").primaryKey(),
66
+ identifier: text("identifier").notNull(),
67
+ value: text("value").notNull(),
68
+ expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
69
+ createdAt: timestamp("created_at", { withTimezone: true })
70
+ .notNull()
71
+ .defaultNow(),
72
+ updatedAt: timestamp("updated_at", { withTimezone: true })
73
+ .notNull()
74
+ .defaultNow()
75
+ .$onUpdate(() => new Date()),
76
+ });
77
+ {{else}}
78
+ import { pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
79
+
80
+ export const users = pgTable("users", {
81
+ id: uuid("id").primaryKey().defaultRandom(),
82
+ email: text("email").notNull().unique(),
83
+ name: text("name"),
84
+ password: text("password").notNull(),
85
+ createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
86
+ updatedAt: timestamp("updated_at", { withTimezone: true })
87
+ .defaultNow()
88
+ .$onUpdate(() => new Date()),
89
+ });
90
+ {{/if}}