create-better-t-stack 3.5.3 → 3.6.0

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/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { n as createBtsCli } from "./src-C_MeUxmG.js";
2
+ import { n as createBtsCli } from "./src-CWiiwWrU.js";
3
3
 
4
4
  //#region src/cli.ts
5
5
  createBtsCli().run();
package/dist/index.d.ts CHANGED
@@ -122,8 +122,16 @@ declare const DirectoryConflictSchema: z.ZodEnum<{
122
122
  increment: "increment";
123
123
  }>;
124
124
  type DirectoryConflict = z.infer<typeof DirectoryConflictSchema>;
125
+ declare const TemplateSchema: z.ZodEnum<{
126
+ mern: "mern";
127
+ pern: "pern";
128
+ t3: "t3";
129
+ none: "none";
130
+ }>;
131
+ type Template = z.infer<typeof TemplateSchema>;
125
132
  type CreateInput = {
126
133
  projectName?: string;
134
+ template?: Template;
127
135
  yes?: boolean;
128
136
  yolo?: boolean;
129
137
  verbose?: boolean;
@@ -209,6 +217,12 @@ interface InitResult {
209
217
  //#region src/index.d.ts
210
218
  declare const router: {
211
219
  init: _orpc_server0.Procedure<_orpc_server0.MergedInitialContext<Record<never, never>, Record<never, never>, Record<never, never>>, Record<never, never>, z$1.ZodTuple<[z$1.ZodOptional<z$1.ZodString>, z$1.ZodObject<{
220
+ template: z$1.ZodOptional<z$1.ZodEnum<{
221
+ mern: "mern";
222
+ pern: "pern";
223
+ t3: "t3";
224
+ none: "none";
225
+ }>>;
212
226
  yes: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodBoolean>>;
213
227
  yolo: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodBoolean>>;
214
228
  verbose: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodBoolean>>;
@@ -470,4 +484,4 @@ declare function sponsors(): Promise<void>;
470
484
  declare function docs(): Promise<void>;
471
485
  declare function builder(): Promise<void>;
472
486
  //#endregion
473
- export { type API, type AddInput, type Addons, type Backend, type BetterTStackConfig, type CreateInput, type Database, type DatabaseSetup, type DirectoryConflict, type Examples, type Frontend, type InitResult, type ORM, type PackageManager, type ProjectConfig, type Runtime, type ServerDeploy, type WebDeploy, builder, createBtsCli, docs, init, router, sponsors };
487
+ export { type API, type AddInput, type Addons, type Backend, type BetterTStackConfig, type CreateInput, type Database, type DatabaseSetup, type DirectoryConflict, type Examples, type Frontend, type InitResult, type ORM, type PackageManager, type ProjectConfig, type Runtime, type ServerDeploy, type Template, type WebDeploy, builder, createBtsCli, docs, init, router, sponsors };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { a as router, i as init, n as createBtsCli, o as sponsors, r as docs, t as builder } from "./src-C_MeUxmG.js";
2
+ import { a as router, i as init, n as createBtsCli, o as sponsors, r as docs, t as builder } from "./src-CWiiwWrU.js";
3
3
 
4
4
  export { builder, createBtsCli, docs, init, router, sponsors };
@@ -62,8 +62,8 @@ function getDefaultConfig() {
62
62
  }
63
63
  const DEFAULT_CONFIG = getDefaultConfig();
64
64
  const dependencyVersionMap = {
65
- "better-auth": "^1.3.28",
66
- "@better-auth/expo": "^1.3.28",
65
+ "better-auth": "^1.4.0",
66
+ "@better-auth/expo": "^1.4.0",
67
67
  "@clerk/nextjs": "^6.31.5",
68
68
  "@clerk/clerk-react": "^5.45.0",
69
69
  "@clerk/tanstack-react-start": "^0.26.3",
@@ -291,6 +291,12 @@ const DirectoryConflictSchema = z.enum([
291
291
  "increment",
292
292
  "error"
293
293
  ]).describe("How to handle existing directory conflicts");
294
+ const TemplateSchema = z.enum([
295
+ "mern",
296
+ "pern",
297
+ "t3",
298
+ "none"
299
+ ]).describe("Predefined project template");
294
300
 
295
301
  //#endregion
296
302
  //#region src/utils/compatibility.ts
@@ -1578,6 +1584,71 @@ const renderTitle = () => {
1578
1584
  else console.log(gradient(Object.values(catppuccinTheme)).multiline(TITLE_TEXT));
1579
1585
  };
1580
1586
 
1587
+ //#endregion
1588
+ //#region src/utils/templates.ts
1589
+ const TEMPLATE_PRESETS = {
1590
+ mern: {
1591
+ database: "mongodb",
1592
+ orm: "mongoose",
1593
+ backend: "express",
1594
+ runtime: "node",
1595
+ frontend: ["react-router"],
1596
+ api: "orpc",
1597
+ auth: "better-auth",
1598
+ payments: "none",
1599
+ addons: ["turborepo"],
1600
+ examples: ["todo"],
1601
+ dbSetup: "mongodb-atlas",
1602
+ webDeploy: "none",
1603
+ serverDeploy: "none"
1604
+ },
1605
+ pern: {
1606
+ database: "postgres",
1607
+ orm: "drizzle",
1608
+ backend: "express",
1609
+ runtime: "node",
1610
+ frontend: ["tanstack-router"],
1611
+ api: "trpc",
1612
+ auth: "better-auth",
1613
+ payments: "none",
1614
+ addons: ["turborepo"],
1615
+ examples: ["todo"],
1616
+ dbSetup: "none",
1617
+ webDeploy: "none",
1618
+ serverDeploy: "none"
1619
+ },
1620
+ t3: {
1621
+ database: "postgres",
1622
+ orm: "prisma",
1623
+ backend: "self",
1624
+ runtime: "none",
1625
+ frontend: ["next"],
1626
+ api: "trpc",
1627
+ auth: "better-auth",
1628
+ payments: "none",
1629
+ addons: ["biome", "turborepo"],
1630
+ examples: ["none"],
1631
+ dbSetup: "none",
1632
+ webDeploy: "none",
1633
+ serverDeploy: "none"
1634
+ },
1635
+ none: null
1636
+ };
1637
+ function getTemplateConfig(template) {
1638
+ if (template === "none" || !template) return null;
1639
+ const config = TEMPLATE_PRESETS[template];
1640
+ if (!config) throw new Error(`Unknown template: ${template}`);
1641
+ return config;
1642
+ }
1643
+ function getTemplateDescription(template) {
1644
+ return {
1645
+ mern: "MongoDB + Express + React + Node.js - Classic MERN stack",
1646
+ pern: "PostgreSQL + Express + React + Node.js - Popular PERN stack",
1647
+ t3: "T3 Stack - Next.js + tRPC + Prisma + PostgreSQL + Better Auth",
1648
+ none: "No template - Full customization"
1649
+ }[template] || "";
1650
+ }
1651
+
1581
1652
  //#endregion
1582
1653
  //#region src/utils/config-processing.ts
1583
1654
  function processArrayOption(options) {
@@ -1829,6 +1900,7 @@ const CORE_STACK_FLAGS = new Set([
1829
1900
  ]);
1830
1901
  function validateYesFlagCombination(options, providedFlags) {
1831
1902
  if (!options.yes) return;
1903
+ if (options.template && options.template !== "none") return;
1832
1904
  const coreStackFlagsProvided = Array.from(providedFlags).filter((flag) => CORE_STACK_FLAGS.has(flag));
1833
1905
  if (coreStackFlagsProvided.length > 0) exitWithError(`Cannot combine --yes with core stack configuration flags: ${coreStackFlagsProvided.map((f) => `--${f}`).join(", ")}. The --yes flag uses default configuration. Remove these flags or use --yes without them.`);
1834
1906
  }
@@ -3784,6 +3856,7 @@ async function setupCatalogs(projectDir, options) {
3784
3856
  const packagePaths = [
3785
3857
  "apps/server",
3786
3858
  "apps/web",
3859
+ "apps/native",
3787
3860
  "apps/fumadocs",
3788
3861
  "apps/docs",
3789
3862
  "packages/api",
@@ -3822,15 +3895,17 @@ function findDuplicateDependencies(packagesInfo, projectName) {
3822
3895
  if (depName.startsWith(projectScope)) continue;
3823
3896
  if (version.startsWith("workspace:")) continue;
3824
3897
  const existing = depCount.get(depName);
3825
- if (existing) existing.packages.push(pkg.path);
3826
- else depCount.set(depName, {
3827
- version,
3898
+ if (existing) {
3899
+ existing.versions.add(version);
3900
+ existing.packages.push(pkg.path);
3901
+ } else depCount.set(depName, {
3902
+ versions: new Set([version]),
3828
3903
  packages: [pkg.path]
3829
3904
  });
3830
3905
  }
3831
3906
  }
3832
3907
  const catalog = {};
3833
- for (const [depName, info] of depCount.entries()) if (info.packages.length > 1) catalog[depName] = info.version;
3908
+ for (const [depName, info] of depCount.entries()) if (info.packages.length > 1 && info.versions.size === 1) catalog[depName] = Array.from(info.versions)[0];
3834
3909
  return catalog;
3835
3910
  }
3836
3911
  async function setupBunCatalogs(projectDir, catalog) {
@@ -6825,13 +6900,31 @@ async function createProjectHandler(input) {
6825
6900
  };
6826
6901
  }
6827
6902
  const { finalResolvedPath, finalBaseName } = await setupProjectDirectory(finalPathInput, shouldClearDirectory);
6828
- const cliInput = {
6903
+ const originalInput = {
6829
6904
  ...input,
6830
6905
  projectDirectory: input.projectName
6831
6906
  };
6832
- const providedFlags = getProvidedFlags(cliInput);
6907
+ const providedFlags = getProvidedFlags(originalInput);
6908
+ let cliInput = originalInput;
6909
+ if (input.template && input.template !== "none") {
6910
+ const templateConfig = getTemplateConfig(input.template);
6911
+ if (templateConfig) {
6912
+ const templateName = input.template.toUpperCase();
6913
+ const templateDescription = getTemplateDescription(input.template);
6914
+ log.message(pc.bold(pc.cyan(`Using template: ${pc.white(templateName)}`)));
6915
+ log.message(pc.dim(` ${templateDescription}`));
6916
+ const userOverrides = {};
6917
+ for (const [key, value] of Object.entries(originalInput)) if (value !== void 0) userOverrides[key] = value;
6918
+ cliInput = {
6919
+ ...templateConfig,
6920
+ ...userOverrides,
6921
+ template: input.template,
6922
+ projectDirectory: originalInput.projectDirectory
6923
+ };
6924
+ }
6925
+ }
6833
6926
  let config;
6834
- if (input.yes) {
6927
+ if (cliInput.yes) {
6835
6928
  const flagConfig = processProvidedFlagsWithoutValidation(cliInput, finalBaseName);
6836
6929
  config = {
6837
6930
  ...getDefaultConfig(),
@@ -6843,7 +6936,6 @@ async function createProjectHandler(input) {
6843
6936
  validateConfigCompatibility(config, providedFlags, cliInput);
6844
6937
  log.info(pc.yellow("Using default/flag options (config prompts skipped):"));
6845
6938
  log.message(displayConfig(config));
6846
- log.message("");
6847
6939
  } else {
6848
6940
  const flagConfig = processAndValidateFlags(cliInput, providedFlags, finalBaseName);
6849
6941
  const { projectName: _projectNameFromFlags,...otherFlags } = flagConfig;
@@ -6854,7 +6946,7 @@ async function createProjectHandler(input) {
6854
6946
  }
6855
6947
  config = await gatherConfig(flagConfig, finalBaseName, finalResolvedPath, finalPathInput);
6856
6948
  }
6857
- await createProject(config, { manualDb: input.manualDb });
6949
+ await createProject(config, { manualDb: cliInput.manualDb ?? input.manualDb });
6858
6950
  const reproducibleCommand = generateReproducibleCommand(config);
6859
6951
  log.success(pc.blue(`You can reproduce this setup with the following command:\n${reproducibleCommand}`));
6860
6952
  await trackProjectCreation(config, input.disableAnalytics);
@@ -7045,6 +7137,7 @@ const router = os.router({
7045
7137
  default: true,
7046
7138
  negateBooleans: true
7047
7139
  }).input(z$1.tuple([ProjectNameSchema.optional(), z$1.object({
7140
+ template: TemplateSchema.optional().describe("Use a predefined template"),
7048
7141
  yes: z$1.boolean().optional().default(false).describe("Use default configuration"),
7049
7142
  yolo: z$1.boolean().optional().default(false).describe("(WARNING - NOT RECOMMENDED) Bypass validations and compatibility checks"),
7050
7143
  verbose: z$1.boolean().optional().default(false).describe("Show detailed result information"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-better-t-stack",
3
- "version": "3.5.3",
3
+ "version": "3.6.0",
4
4
  "description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,58 +1,100 @@
1
+ import { relations } from "drizzle-orm";
1
2
  import {
2
3
  mysqlTable,
3
4
  varchar,
4
5
  text,
5
6
  timestamp,
6
7
  boolean,
8
+ index,
7
9
  } from "drizzle-orm/mysql-core";
8
10
 
9
11
  export const user = mysqlTable("user", {
10
12
  id: varchar("id", { length: 36 }).primaryKey(),
11
- name: text("name").notNull(),
13
+ name: varchar("name", { length: 255 }).notNull(),
12
14
  email: varchar("email", { length: 255 }).notNull().unique(),
13
- emailVerified: boolean("email_verified").notNull(),
15
+ emailVerified: boolean("email_verified").default(false).notNull(),
14
16
  image: text("image"),
15
- createdAt: timestamp("created_at").notNull(),
16
- updatedAt: timestamp("updated_at").notNull(),
17
+ createdAt: timestamp("created_at", { fsp: 3 }).defaultNow().notNull(),
18
+ updatedAt: timestamp("updated_at", { fsp: 3 })
19
+ .defaultNow()
20
+ .$onUpdate(() => /* @__PURE__ */ new Date())
21
+ .notNull(),
17
22
  });
18
23
 
19
- export const session = mysqlTable("session", {
20
- id: varchar("id", { length: 36 }).primaryKey(),
21
- expiresAt: timestamp("expires_at").notNull(),
22
- token: varchar("token", { length: 255 }).notNull().unique(),
23
- createdAt: timestamp("created_at").notNull(),
24
- updatedAt: timestamp("updated_at").notNull(),
25
- ipAddress: text("ip_address"),
26
- userAgent: text("user_agent"),
27
- userId: varchar("user_id", { length: 36 })
28
- .notNull()
29
- .references(() => user.id, { onDelete: "cascade" }),
30
- });
24
+ export const session = mysqlTable(
25
+ "session",
26
+ {
27
+ id: varchar("id", { length: 36 }).primaryKey(),
28
+ expiresAt: timestamp("expires_at", { fsp: 3 }).notNull(),
29
+ token: varchar("token", { length: 255 }).notNull().unique(),
30
+ createdAt: timestamp("created_at", { fsp: 3 }).defaultNow().notNull(),
31
+ updatedAt: timestamp("updated_at", { fsp: 3 })
32
+ .$onUpdate(() => /* @__PURE__ */ new Date())
33
+ .notNull(),
34
+ ipAddress: text("ip_address"),
35
+ userAgent: text("user_agent"),
36
+ userId: varchar("user_id", { length: 36 })
37
+ .notNull()
38
+ .references(() => user.id, { onDelete: "cascade" }),
39
+ },
40
+ (table) => [index("session_userId_idx").on(table.userId)],
41
+ );
31
42
 
32
- export const account = mysqlTable("account", {
33
- id: varchar("id", { length: 36 }).primaryKey(),
34
- accountId: text("account_id").notNull(),
35
- providerId: text("provider_id").notNull(),
36
- userId: varchar("user_id", { length: 36 })
37
- .notNull()
38
- .references(() => user.id, { onDelete: "cascade" }),
39
- accessToken: text("access_token"),
40
- refreshToken: text("refresh_token"),
41
- idToken: text("id_token"),
43
+ export const account = mysqlTable(
44
+ "account",
45
+ {
46
+ id: varchar("id", { length: 36 }).primaryKey(),
47
+ accountId: text("account_id").notNull(),
48
+ providerId: text("provider_id").notNull(),
49
+ userId: varchar("user_id", { length: 36 })
50
+ .notNull()
51
+ .references(() => user.id, { onDelete: "cascade" }),
52
+ accessToken: text("access_token"),
53
+ refreshToken: text("refresh_token"),
54
+ idToken: text("id_token"),
55
+ accessTokenExpiresAt: timestamp("access_token_expires_at", { fsp: 3 }),
56
+ refreshTokenExpiresAt: timestamp("refresh_token_expires_at", { fsp: 3 }),
57
+ scope: text("scope"),
58
+ password: text("password"),
59
+ createdAt: timestamp("created_at", { fsp: 3 }).defaultNow().notNull(),
60
+ updatedAt: timestamp("updated_at", { fsp: 3 })
61
+ .$onUpdate(() => /* @__PURE__ */ new Date())
62
+ .notNull(),
63
+ },
64
+ (table) => [index("account_userId_idx").on(table.userId)],
65
+ );
42
66
 
43
- accessTokenExpiresAt: timestamp("access_token_expires_at"),
44
- refreshTokenExpiresAt: timestamp("refresh_token_expires_at"),
45
- scope: text("scope"),
46
- password: text("password"),
47
- createdAt: timestamp("created_at").notNull(),
48
- updatedAt: timestamp("updated_at").notNull(),
49
- });
67
+ export const verification = mysqlTable(
68
+ "verification",
69
+ {
70
+ id: varchar("id", { length: 36 }).primaryKey(),
71
+ identifier: varchar("identifier", { length: 255 }).notNull(),
72
+ value: text("value").notNull(),
73
+ expiresAt: timestamp("expires_at", { fsp: 3 }).notNull(),
74
+ createdAt: timestamp("created_at", { fsp: 3 }).defaultNow().notNull(),
75
+ updatedAt: timestamp("updated_at", { fsp: 3 })
76
+ .defaultNow()
77
+ .$onUpdate(() => /* @__PURE__ */ new Date())
78
+ .notNull(),
79
+ },
80
+ (table) => [index("verification_identifier_idx").on(table.identifier)],
81
+ );
50
82
 
51
- export const verification = mysqlTable("verification", {
52
- id: varchar("id", { length: 36 }).primaryKey(),
53
- identifier: text("identifier").notNull(),
54
- value: text("value").notNull(),
55
- expiresAt: timestamp("expires_at").notNull(),
56
- createdAt: timestamp("created_at"),
57
- updatedAt: timestamp("updated_at"),
58
- });
83
+ export const userRelations = relations(user, ({ many }) => ({
84
+ sessions: many(session),
85
+ accounts: many(account),
86
+ }));
87
+
88
+ export const sessionRelations = relations(session, ({ one }) => ({
89
+ user: one(user, {
90
+ fields: [session.userId],
91
+ references: [user.id],
92
+ }),
93
+ }));
94
+
95
+ export const accountRelations = relations(account, ({ one }) => ({
96
+ user: one(user, {
97
+ fields: [account.userId],
98
+ references: [user.id],
99
+ }),
100
+ }));
@@ -1,47 +1,93 @@
1
- import { pgTable, text, timestamp, boolean, serial } from "drizzle-orm/pg-core";
1
+ import { relations } from "drizzle-orm";
2
+ import { pgTable, text, timestamp, boolean, index } from "drizzle-orm/pg-core";
2
3
 
3
4
  export const user = pgTable("user", {
4
- id: text("id").primaryKey(),
5
- name: text('name').notNull(),
6
- email: text('email').notNull().unique(),
7
- emailVerified: boolean('email_verified').notNull(),
8
- image: text('image'),
9
- createdAt: timestamp('created_at').notNull(),
10
- updatedAt: timestamp('updated_at').notNull()
11
- });
5
+ id: text("id").primaryKey(),
6
+ name: text("name").notNull(),
7
+ email: text("email").notNull().unique(),
8
+ emailVerified: boolean("email_verified").default(false).notNull(),
9
+ image: text("image"),
10
+ createdAt: timestamp("created_at").defaultNow().notNull(),
11
+ updatedAt: timestamp("updated_at")
12
+ .defaultNow()
13
+ .$onUpdate(() => /* @__PURE__ */ new Date())
14
+ .notNull(),
15
+ });
12
16
 
13
- export const session = pgTable("session", {
14
- id: text("id").primaryKey(),
15
- expiresAt: timestamp('expires_at').notNull(),
16
- token: text('token').notNull().unique(),
17
- createdAt: timestamp('created_at').notNull(),
18
- updatedAt: timestamp('updated_at').notNull(),
19
- ipAddress: text('ip_address'),
20
- userAgent: text('user_agent'),
21
- userId: text('user_id').notNull().references(()=> user.id, { onDelete: 'cascade' })
22
- });
17
+ export const session = pgTable(
18
+ "session",
19
+ {
20
+ id: text("id").primaryKey(),
21
+ expiresAt: timestamp("expires_at").notNull(),
22
+ token: text("token").notNull().unique(),
23
+ createdAt: timestamp("created_at").defaultNow().notNull(),
24
+ updatedAt: timestamp("updated_at")
25
+ .$onUpdate(() => /* @__PURE__ */ new Date())
26
+ .notNull(),
27
+ ipAddress: text("ip_address"),
28
+ userAgent: text("user_agent"),
29
+ userId: text("user_id")
30
+ .notNull()
31
+ .references(() => user.id, { onDelete: "cascade" }),
32
+ },
33
+ (table) => [index("session_userId_idx").on(table.userId)],
34
+ );
23
35
 
24
- export const account = pgTable("account", {
25
- id: text("id").primaryKey(),
26
- accountId: text('account_id').notNull(),
27
- providerId: text('provider_id').notNull(),
28
- userId: text('user_id').notNull().references(()=> user.id, { onDelete: 'cascade' }),
29
- accessToken: text('access_token'),
30
- refreshToken: text('refresh_token'),
31
- idToken: text('id_token'),
32
- accessTokenExpiresAt: timestamp('access_token_expires_at'),
33
- refreshTokenExpiresAt: timestamp('refresh_token_expires_at'),
34
- scope: text('scope'),
35
- password: text('password'),
36
- createdAt: timestamp('created_at').notNull(),
37
- updatedAt: timestamp('updated_at').notNull()
38
- });
36
+ export const account = pgTable(
37
+ "account",
38
+ {
39
+ id: text("id").primaryKey(),
40
+ accountId: text("account_id").notNull(),
41
+ providerId: text("provider_id").notNull(),
42
+ userId: text("user_id")
43
+ .notNull()
44
+ .references(() => user.id, { onDelete: "cascade" }),
45
+ accessToken: text("access_token"),
46
+ refreshToken: text("refresh_token"),
47
+ idToken: text("id_token"),
48
+ accessTokenExpiresAt: timestamp("access_token_expires_at"),
49
+ refreshTokenExpiresAt: timestamp("refresh_token_expires_at"),
50
+ scope: text("scope"),
51
+ password: text("password"),
52
+ createdAt: timestamp("created_at").defaultNow().notNull(),
53
+ updatedAt: timestamp("updated_at")
54
+ .$onUpdate(() => /* @__PURE__ */ new Date())
55
+ .notNull(),
56
+ },
57
+ (table) => [index("account_userId_idx").on(table.userId)],
58
+ );
39
59
 
40
- export const verification = pgTable("verification", {
41
- id: text("id").primaryKey(),
42
- identifier: text('identifier').notNull(),
43
- value: text('value').notNull(),
44
- expiresAt: timestamp('expires_at').notNull(),
45
- createdAt: timestamp('created_at'),
46
- updatedAt: timestamp('updated_at')
47
- });
60
+ export const verification = pgTable(
61
+ "verification",
62
+ {
63
+ id: text("id").primaryKey(),
64
+ identifier: text("identifier").notNull(),
65
+ value: text("value").notNull(),
66
+ expiresAt: timestamp("expires_at").notNull(),
67
+ createdAt: timestamp("created_at").defaultNow().notNull(),
68
+ updatedAt: timestamp("updated_at")
69
+ .defaultNow()
70
+ .$onUpdate(() => /* @__PURE__ */ new Date())
71
+ .notNull(),
72
+ },
73
+ (table) => [index("verification_identifier_idx").on(table.identifier)],
74
+ );
75
+
76
+ export const userRelations = relations(user, ({ many }) => ({
77
+ sessions: many(session),
78
+ accounts: many(account),
79
+ }));
80
+
81
+ export const sessionRelations = relations(session, ({ one }) => ({
82
+ user: one(user, {
83
+ fields: [session.userId],
84
+ references: [user.id],
85
+ }),
86
+ }));
87
+
88
+ export const accountRelations = relations(account, ({ one }) => ({
89
+ user: one(user, {
90
+ fields: [account.userId],
91
+ references: [user.id],
92
+ }),
93
+ }));
@@ -1,55 +1,107 @@
1
- import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
1
+ import { relations, sql } from "drizzle-orm";
2
+ import { sqliteTable, text, integer, index } from "drizzle-orm/sqlite-core";
2
3
 
3
4
  export const user = sqliteTable("user", {
4
5
  id: text("id").primaryKey(),
5
6
  name: text("name").notNull(),
6
7
  email: text("email").notNull().unique(),
7
- emailVerified: integer("email_verified", { mode: "boolean" }).notNull(),
8
+ emailVerified: integer("email_verified", { mode: "boolean" })
9
+ .default(false)
10
+ .notNull(),
8
11
  image: text("image"),
9
- createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
10
- updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(),
12
+ createdAt: integer("created_at", { mode: "timestamp_ms" })
13
+ .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
14
+ .notNull(),
15
+ updatedAt: integer("updated_at", { mode: "timestamp_ms" })
16
+ .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
17
+ .$onUpdate(() => /* @__PURE__ */ new Date())
18
+ .notNull(),
11
19
  });
12
20
 
13
- export const session = sqliteTable("session", {
14
- id: text("id").primaryKey(),
15
- expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
16
- token: text("token").notNull().unique(),
17
- createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
18
- updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(),
19
- ipAddress: text("ip_address"),
20
- userAgent: text("user_agent"),
21
- userId: text("user_id")
22
- .notNull()
23
- .references(() => user.id),
24
- });
21
+ export const session = sqliteTable(
22
+ "session",
23
+ {
24
+ id: text("id").primaryKey(),
25
+ expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(),
26
+ token: text("token").notNull().unique(),
27
+ createdAt: integer("created_at", { mode: "timestamp_ms" })
28
+ .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
29
+ .notNull(),
30
+ updatedAt: integer("updated_at", { mode: "timestamp_ms" })
31
+ .$onUpdate(() => /* @__PURE__ */ new Date())
32
+ .notNull(),
33
+ ipAddress: text("ip_address"),
34
+ userAgent: text("user_agent"),
35
+ userId: text("user_id")
36
+ .notNull()
37
+ .references(() => user.id, { onDelete: "cascade" }),
38
+ },
39
+ (table) => [index("session_userId_idx").on(table.userId)],
40
+ );
25
41
 
26
- export const account = sqliteTable("account", {
27
- id: text("id").primaryKey(),
28
- accountId: text("account_id").notNull(),
29
- providerId: text("provider_id").notNull(),
30
- userId: text("user_id")
31
- .notNull()
32
- .references(() => user.id),
33
- accessToken: text("access_token"),
34
- refreshToken: text("refresh_token"),
35
- idToken: text("id_token"),
36
- accessTokenExpiresAt: integer("access_token_expires_at", {
37
- mode: "timestamp",
38
- }),
39
- refreshTokenExpiresAt: integer("refresh_token_expires_at", {
40
- mode: "timestamp",
42
+ export const account = sqliteTable(
43
+ "account",
44
+ {
45
+ id: text("id").primaryKey(),
46
+ accountId: text("account_id").notNull(),
47
+ providerId: text("provider_id").notNull(),
48
+ userId: text("user_id")
49
+ .notNull()
50
+ .references(() => user.id, { onDelete: "cascade" }),
51
+ accessToken: text("access_token"),
52
+ refreshToken: text("refresh_token"),
53
+ idToken: text("id_token"),
54
+ accessTokenExpiresAt: integer("access_token_expires_at", {
55
+ mode: "timestamp_ms",
56
+ }),
57
+ refreshTokenExpiresAt: integer("refresh_token_expires_at", {
58
+ mode: "timestamp_ms",
59
+ }),
60
+ scope: text("scope"),
61
+ password: text("password"),
62
+ createdAt: integer("created_at", { mode: "timestamp_ms" })
63
+ .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
64
+ .notNull(),
65
+ updatedAt: integer("updated_at", { mode: "timestamp_ms" })
66
+ .$onUpdate(() => /* @__PURE__ */ new Date())
67
+ .notNull(),
68
+ },
69
+ (table) => [index("account_userId_idx").on(table.userId)],
70
+ );
71
+
72
+ export const verification = sqliteTable(
73
+ "verification",
74
+ {
75
+ id: text("id").primaryKey(),
76
+ identifier: text("identifier").notNull(),
77
+ value: text("value").notNull(),
78
+ expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(),
79
+ createdAt: integer("created_at", { mode: "timestamp_ms" })
80
+ .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
81
+ .notNull(),
82
+ updatedAt: integer("updated_at", { mode: "timestamp_ms" })
83
+ .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
84
+ .$onUpdate(() => /* @__PURE__ */ new Date())
85
+ .notNull(),
86
+ },
87
+ (table) => [index("verification_identifier_idx").on(table.identifier)],
88
+ );
89
+
90
+ export const userRelations = relations(user, ({ many }) => ({
91
+ sessions: many(session),
92
+ accounts: many(account),
93
+ }));
94
+
95
+ export const sessionRelations = relations(session, ({ one }) => ({
96
+ user: one(user, {
97
+ fields: [session.userId],
98
+ references: [user.id],
41
99
  }),
42
- scope: text("scope"),
43
- password: text("password"),
44
- createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
45
- updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(),
46
- });
100
+ }));
47
101
 
48
- export const verification = sqliteTable("verification", {
49
- id: text("id").primaryKey(),
50
- identifier: text("identifier").notNull(),
51
- value: text("value").notNull(),
52
- expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
53
- createdAt: integer("created_at", { mode: "timestamp" }),
54
- updatedAt: integer("updated_at", { mode: "timestamp" }),
55
- });
102
+ export const accountRelations = relations(account, ({ one }) => ({
103
+ user: one(user, {
104
+ fields: [account.userId],
105
+ references: [user.id],
106
+ }),
107
+ }));
@@ -2,10 +2,10 @@ model User {
2
2
  id String @id @map("_id")
3
3
  name String
4
4
  email String
5
- emailVerified Boolean
5
+ emailVerified Boolean @default(false)
6
6
  image String?
7
- createdAt DateTime
8
- updatedAt DateTime
7
+ createdAt DateTime @default(now())
8
+ updatedAt DateTime @updatedAt
9
9
  sessions Session[]
10
10
  accounts Account[]
11
11
 
@@ -17,14 +17,15 @@ model Session {
17
17
  id String @id @map("_id")
18
18
  expiresAt DateTime
19
19
  token String
20
- createdAt DateTime
21
- updatedAt DateTime
20
+ createdAt DateTime @default(now())
21
+ updatedAt DateTime @updatedAt
22
22
  ipAddress String?
23
23
  userAgent String?
24
24
  userId String
25
25
  user User @relation(fields: [userId], references: [id], onDelete: Cascade)
26
26
 
27
27
  @@unique([token])
28
+ @@index([userId])
28
29
  @@map("session")
29
30
  }
30
31
 
@@ -41,19 +42,21 @@ model Account {
41
42
  refreshTokenExpiresAt DateTime?
42
43
  scope String?
43
44
  password String?
44
- createdAt DateTime
45
- updatedAt DateTime
45
+ createdAt DateTime @default(now())
46
+ updatedAt DateTime @updatedAt
46
47
 
48
+ @@index([userId])
47
49
  @@map("account")
48
50
  }
49
51
 
50
52
  model Verification {
51
- id String @id @map("_id")
53
+ id String @id @map("_id")
52
54
  identifier String
53
55
  value String
54
56
  expiresAt DateTime
55
- createdAt DateTime?
56
- updatedAt DateTime?
57
+ createdAt DateTime @default(now())
58
+ updatedAt DateTime @updatedAt
57
59
 
60
+ @@index([identifier])
58
61
  @@map("verification")
59
62
  }
@@ -2,10 +2,10 @@ model User {
2
2
  id String @id
3
3
  name String @db.Text
4
4
  email String
5
- emailVerified Boolean
5
+ emailVerified Boolean @default(false)
6
6
  image String? @db.Text
7
- createdAt DateTime
8
- updatedAt DateTime
7
+ createdAt DateTime @default(now())
8
+ updatedAt DateTime @updatedAt
9
9
  sessions Session[]
10
10
  accounts Account[]
11
11
 
@@ -17,14 +17,15 @@ model Session {
17
17
  id String @id
18
18
  expiresAt DateTime
19
19
  token String
20
- createdAt DateTime
21
- updatedAt DateTime
20
+ createdAt DateTime @default(now())
21
+ updatedAt DateTime @updatedAt
22
22
  ipAddress String? @db.Text
23
23
  userAgent String? @db.Text
24
24
  userId String
25
25
  user User @relation(fields: [userId], references: [id], onDelete: Cascade)
26
26
 
27
27
  @@unique([token])
28
+ @@index([userId(length: 191)])
28
29
  @@map("session")
29
30
  }
30
31
 
@@ -41,19 +42,21 @@ model Account {
41
42
  refreshTokenExpiresAt DateTime?
42
43
  scope String? @db.Text
43
44
  password String? @db.Text
44
- createdAt DateTime
45
- updatedAt DateTime
45
+ createdAt DateTime @default(now())
46
+ updatedAt DateTime @updatedAt
46
47
 
48
+ @@index([userId(length: 191)])
47
49
  @@map("account")
48
50
  }
49
51
 
50
52
  model Verification {
51
- id String @id
52
- identifier String @db.Text
53
- value String @db.Text
53
+ id String @id
54
+ identifier String @db.Text
55
+ value String @db.Text
54
56
  expiresAt DateTime
55
- createdAt DateTime?
56
- updatedAt DateTime?
57
+ createdAt DateTime @default(now())
58
+ updatedAt DateTime @updatedAt
57
59
 
60
+ @@index([identifier(length: 191)])
58
61
  @@map("verification")
59
62
  }
@@ -1,11 +1,11 @@
1
1
  model User {
2
- id String @id @map("_id")
2
+ id String @id
3
3
  name String
4
4
  email String
5
- emailVerified Boolean
5
+ emailVerified Boolean @default(false)
6
6
  image String?
7
- createdAt DateTime
8
- updatedAt DateTime
7
+ createdAt DateTime @default(now())
8
+ updatedAt DateTime @updatedAt
9
9
  sessions Session[]
10
10
  accounts Account[]
11
11
 
@@ -14,22 +14,23 @@ model User {
14
14
  }
15
15
 
16
16
  model Session {
17
- id String @id @map("_id")
17
+ id String @id
18
18
  expiresAt DateTime
19
19
  token String
20
- createdAt DateTime
21
- updatedAt DateTime
20
+ createdAt DateTime @default(now())
21
+ updatedAt DateTime @updatedAt
22
22
  ipAddress String?
23
23
  userAgent String?
24
24
  userId String
25
25
  user User @relation(fields: [userId], references: [id], onDelete: Cascade)
26
26
 
27
27
  @@unique([token])
28
+ @@index([userId])
28
29
  @@map("session")
29
30
  }
30
31
 
31
32
  model Account {
32
- id String @id @map("_id")
33
+ id String @id
33
34
  accountId String
34
35
  providerId String
35
36
  userId String
@@ -41,19 +42,21 @@ model Account {
41
42
  refreshTokenExpiresAt DateTime?
42
43
  scope String?
43
44
  password String?
44
- createdAt DateTime
45
- updatedAt DateTime
45
+ createdAt DateTime @default(now())
46
+ updatedAt DateTime @updatedAt
46
47
 
48
+ @@index([userId])
47
49
  @@map("account")
48
50
  }
49
51
 
50
52
  model Verification {
51
- id String @id @map("_id")
53
+ id String @id
52
54
  identifier String
53
55
  value String
54
56
  expiresAt DateTime
55
- createdAt DateTime?
56
- updatedAt DateTime?
57
+ createdAt DateTime @default(now())
58
+ updatedAt DateTime @updatedAt
57
59
 
60
+ @@index([identifier])
58
61
  @@map("verification")
59
62
  }
@@ -1,11 +1,11 @@
1
1
  model User {
2
- id String @id @map("_id")
2
+ id String @id
3
3
  name String
4
4
  email String
5
- emailVerified Boolean
5
+ emailVerified Boolean @default(false)
6
6
  image String?
7
- createdAt DateTime
8
- updatedAt DateTime
7
+ createdAt DateTime @default(now())
8
+ updatedAt DateTime @updatedAt
9
9
  sessions Session[]
10
10
  accounts Account[]
11
11
 
@@ -14,22 +14,23 @@ model User {
14
14
  }
15
15
 
16
16
  model Session {
17
- id String @id @map("_id")
17
+ id String @id
18
18
  expiresAt DateTime
19
19
  token String
20
- createdAt DateTime
21
- updatedAt DateTime
20
+ createdAt DateTime @default(now())
21
+ updatedAt DateTime @updatedAt
22
22
  ipAddress String?
23
23
  userAgent String?
24
24
  userId String
25
25
  user User @relation(fields: [userId], references: [id], onDelete: Cascade)
26
26
 
27
27
  @@unique([token])
28
+ @@index([userId])
28
29
  @@map("session")
29
30
  }
30
31
 
31
32
  model Account {
32
- id String @id @map("_id")
33
+ id String @id
33
34
  accountId String
34
35
  providerId String
35
36
  userId String
@@ -41,19 +42,21 @@ model Account {
41
42
  refreshTokenExpiresAt DateTime?
42
43
  scope String?
43
44
  password String?
44
- createdAt DateTime
45
- updatedAt DateTime
45
+ createdAt DateTime @default(now())
46
+ updatedAt DateTime @updatedAt
46
47
 
48
+ @@index([userId])
47
49
  @@map("account")
48
50
  }
49
51
 
50
52
  model Verification {
51
- id String @id @map("_id")
53
+ id String @id
52
54
  identifier String
53
55
  value String
54
56
  expiresAt DateTime
55
- createdAt DateTime?
56
- updatedAt DateTime?
57
+ createdAt DateTime @default(now())
58
+ updatedAt DateTime @updatedAt
57
59
 
60
+ @@index([identifier])
58
61
  @@map("verification")
59
62
  }
@@ -25,6 +25,7 @@
25
25
  "expo-crypto": "~15.0.6",
26
26
  "expo-linking": "~8.0.7",
27
27
  "expo-navigation-bar": "~5.0.8",
28
+ "expo-network": "~8.0.7",
28
29
  "expo-router": "~6.0.0",
29
30
  "expo-secure-store": "~15.0.6",
30
31
  "expo-splash-screen": "~31.0.8",
@@ -25,6 +25,7 @@
25
25
  "expo-font": "~14.0.9",
26
26
  "expo-haptics": "^15.0.7",
27
27
  "expo-linking": "~8.0.8",
28
+ "expo-network": "~8.0.7",
28
29
  "expo-router": "~6.0.14",
29
30
  "expo-secure-store": "~15.0.7",
30
31
  "expo-status-bar": "~3.0.8",