create-pyro 0.1.0 → 0.2.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.
Files changed (91) hide show
  1. package/README.md +32 -11
  2. package/dist/index.js +150 -30
  3. package/dist/scaffold.js +106 -0
  4. package/package.json +7 -7
  5. package/template/.dockerignore +30 -0
  6. package/template/.env.example +23 -0
  7. package/template/.gitattributes +2 -0
  8. package/template/.github/workflows/ci.yml +43 -0
  9. package/template/.vscode/extensions.json +3 -0
  10. package/template/.vscode/settings.json +8 -0
  11. package/template/README.md +57 -4
  12. package/template/apps/api/Dockerfile +46 -0
  13. package/template/apps/api/package.json +43 -0
  14. package/template/apps/api/src/app.test.ts +44 -0
  15. package/template/apps/api/src/app.ts +54 -0
  16. package/template/apps/api/src/index.ts +13 -0
  17. package/template/apps/api/src/lib/auth.ts +46 -0
  18. package/template/apps/api/src/lib/response.ts +28 -0
  19. package/template/apps/api/src/lib/session.ts +22 -0
  20. package/template/apps/api/src/middleware/auth.ts +34 -0
  21. package/template/apps/api/src/routes/auth.ts +9 -0
  22. package/template/apps/api/src/routes/index.ts +8 -0
  23. package/template/apps/api/src/routes/me.test.ts +45 -0
  24. package/template/apps/api/src/routes/me.ts +29 -0
  25. package/template/apps/api/src/test/database-url.ts +23 -0
  26. package/template/apps/api/src/test/global-setup.ts +36 -0
  27. package/template/apps/api/src/test/setup.ts +8 -0
  28. package/template/apps/api/src/types/context.ts +8 -0
  29. package/template/apps/api/tsconfig.build.json +8 -0
  30. package/template/apps/api/tsconfig.json +9 -0
  31. package/template/apps/api/tsdown.config.ts +13 -0
  32. package/template/apps/api/vitest.config.ts +11 -0
  33. package/template/apps/web/Dockerfile +49 -0
  34. package/template/apps/web/app/app.css +29 -0
  35. package/template/apps/web/app/lib/api.server.ts +13 -0
  36. package/template/apps/web/app/lib/api.test.ts +28 -0
  37. package/template/apps/web/app/lib/api.ts +47 -0
  38. package/template/apps/web/app/lib/auth-client.ts +13 -0
  39. package/template/apps/web/app/lib/env.server.ts +11 -0
  40. package/template/apps/web/app/lib/public-env.ts +17 -0
  41. package/template/apps/web/app/lib/session.server.ts +31 -0
  42. package/template/apps/web/app/lib/utils.ts +6 -0
  43. package/template/apps/web/app/root.tsx +86 -0
  44. package/template/apps/web/app/routes/dashboard.tsx +81 -0
  45. package/template/apps/web/app/routes/home.tsx +161 -0
  46. package/template/apps/web/app/routes.ts +6 -0
  47. package/template/apps/web/package.json +41 -0
  48. package/template/apps/web/public/favicon.svg +5 -0
  49. package/template/apps/web/react-router.config.ts +6 -0
  50. package/template/apps/web/tsconfig.json +23 -0
  51. package/template/apps/web/vite.config.ts +19 -0
  52. package/template/apps/web/vitest.config.ts +13 -0
  53. package/template/biome.json +53 -0
  54. package/template/compose.dev.yml +26 -0
  55. package/template/compose.yml +75 -0
  56. package/template/gitignore +32 -1
  57. package/template/infra/nginx/default.conf +33 -0
  58. package/template/package.json +23 -1
  59. package/template/packages/config/package.json +31 -0
  60. package/template/packages/config/src/index.test.ts +100 -0
  61. package/template/packages/config/src/index.ts +100 -0
  62. package/template/packages/config/tsconfig.build.json +8 -0
  63. package/template/packages/config/tsconfig.json +9 -0
  64. package/template/packages/config/tsdown.config.ts +12 -0
  65. package/template/packages/db/Dockerfile +22 -0
  66. package/template/packages/db/drizzle/0000_init.sql +53 -0
  67. package/template/packages/db/drizzle/meta/0000_snapshot.json +376 -0
  68. package/template/packages/db/drizzle/meta/_journal.json +13 -0
  69. package/template/packages/db/drizzle.config.ts +12 -0
  70. package/template/packages/db/package.json +38 -0
  71. package/template/packages/db/src/client.ts +12 -0
  72. package/template/packages/db/src/index.ts +2 -0
  73. package/template/packages/db/src/schema/auth.ts +96 -0
  74. package/template/packages/db/src/schema/index.ts +1 -0
  75. package/template/packages/db/tsconfig.build.json +7 -0
  76. package/template/packages/db/tsconfig.json +9 -0
  77. package/template/packages/db/tsdown.config.ts +12 -0
  78. package/template/packages/shared/package.json +32 -0
  79. package/template/packages/shared/src/index.ts +2 -0
  80. package/template/packages/shared/src/schemas/auth.test.ts +32 -0
  81. package/template/packages/shared/src/schemas/auth.ts +22 -0
  82. package/template/packages/shared/src/schemas/index.ts +1 -0
  83. package/template/packages/shared/src/types/api.ts +15 -0
  84. package/template/packages/shared/src/types/index.ts +2 -0
  85. package/template/packages/shared/src/types/user.ts +10 -0
  86. package/template/packages/shared/tsconfig.build.json +8 -0
  87. package/template/packages/shared/tsconfig.json +9 -0
  88. package/template/packages/shared/tsdown.config.ts +12 -0
  89. package/template/pnpm-workspace.yaml +25 -0
  90. package/template/tsconfig.json +19 -0
  91. package/template/turbo.json +27 -0
@@ -0,0 +1,376 @@
1
+ {
2
+ "id": "304fbe1e-b182-4f3f-a7cb-2e92976f5eb5",
3
+ "prevId": "00000000-0000-0000-0000-000000000000",
4
+ "version": "7",
5
+ "dialect": "postgresql",
6
+ "tables": {
7
+ "public.accounts": {
8
+ "name": "accounts",
9
+ "schema": "",
10
+ "columns": {
11
+ "id": {
12
+ "name": "id",
13
+ "type": "text",
14
+ "primaryKey": true,
15
+ "notNull": true
16
+ },
17
+ "account_id": {
18
+ "name": "account_id",
19
+ "type": "text",
20
+ "primaryKey": false,
21
+ "notNull": true
22
+ },
23
+ "provider_id": {
24
+ "name": "provider_id",
25
+ "type": "text",
26
+ "primaryKey": false,
27
+ "notNull": true
28
+ },
29
+ "user_id": {
30
+ "name": "user_id",
31
+ "type": "text",
32
+ "primaryKey": false,
33
+ "notNull": true
34
+ },
35
+ "access_token": {
36
+ "name": "access_token",
37
+ "type": "text",
38
+ "primaryKey": false,
39
+ "notNull": false
40
+ },
41
+ "refresh_token": {
42
+ "name": "refresh_token",
43
+ "type": "text",
44
+ "primaryKey": false,
45
+ "notNull": false
46
+ },
47
+ "id_token": {
48
+ "name": "id_token",
49
+ "type": "text",
50
+ "primaryKey": false,
51
+ "notNull": false
52
+ },
53
+ "access_token_expires_at": {
54
+ "name": "access_token_expires_at",
55
+ "type": "timestamp",
56
+ "primaryKey": false,
57
+ "notNull": false
58
+ },
59
+ "refresh_token_expires_at": {
60
+ "name": "refresh_token_expires_at",
61
+ "type": "timestamp",
62
+ "primaryKey": false,
63
+ "notNull": false
64
+ },
65
+ "scope": {
66
+ "name": "scope",
67
+ "type": "text",
68
+ "primaryKey": false,
69
+ "notNull": false
70
+ },
71
+ "password": {
72
+ "name": "password",
73
+ "type": "text",
74
+ "primaryKey": false,
75
+ "notNull": false
76
+ },
77
+ "created_at": {
78
+ "name": "created_at",
79
+ "type": "timestamp",
80
+ "primaryKey": false,
81
+ "notNull": true,
82
+ "default": "now()"
83
+ },
84
+ "updated_at": {
85
+ "name": "updated_at",
86
+ "type": "timestamp",
87
+ "primaryKey": false,
88
+ "notNull": true,
89
+ "default": "now()"
90
+ }
91
+ },
92
+ "indexes": {
93
+ "accounts_user_id_idx": {
94
+ "name": "accounts_user_id_idx",
95
+ "columns": [
96
+ {
97
+ "expression": "user_id",
98
+ "isExpression": false,
99
+ "asc": true,
100
+ "nulls": "last"
101
+ }
102
+ ],
103
+ "isUnique": false,
104
+ "concurrently": false,
105
+ "method": "btree",
106
+ "with": {}
107
+ }
108
+ },
109
+ "foreignKeys": {
110
+ "accounts_user_id_users_id_fk": {
111
+ "name": "accounts_user_id_users_id_fk",
112
+ "tableFrom": "accounts",
113
+ "tableTo": "users",
114
+ "columnsFrom": [
115
+ "user_id"
116
+ ],
117
+ "columnsTo": [
118
+ "id"
119
+ ],
120
+ "onDelete": "cascade",
121
+ "onUpdate": "no action"
122
+ }
123
+ },
124
+ "compositePrimaryKeys": {},
125
+ "uniqueConstraints": {},
126
+ "policies": {},
127
+ "checkConstraints": {},
128
+ "isRLSEnabled": false
129
+ },
130
+ "public.sessions": {
131
+ "name": "sessions",
132
+ "schema": "",
133
+ "columns": {
134
+ "id": {
135
+ "name": "id",
136
+ "type": "text",
137
+ "primaryKey": true,
138
+ "notNull": true
139
+ },
140
+ "expires_at": {
141
+ "name": "expires_at",
142
+ "type": "timestamp",
143
+ "primaryKey": false,
144
+ "notNull": true
145
+ },
146
+ "token": {
147
+ "name": "token",
148
+ "type": "text",
149
+ "primaryKey": false,
150
+ "notNull": true
151
+ },
152
+ "ip_address": {
153
+ "name": "ip_address",
154
+ "type": "text",
155
+ "primaryKey": false,
156
+ "notNull": false
157
+ },
158
+ "user_agent": {
159
+ "name": "user_agent",
160
+ "type": "text",
161
+ "primaryKey": false,
162
+ "notNull": false
163
+ },
164
+ "user_id": {
165
+ "name": "user_id",
166
+ "type": "text",
167
+ "primaryKey": false,
168
+ "notNull": true
169
+ },
170
+ "created_at": {
171
+ "name": "created_at",
172
+ "type": "timestamp",
173
+ "primaryKey": false,
174
+ "notNull": true,
175
+ "default": "now()"
176
+ },
177
+ "updated_at": {
178
+ "name": "updated_at",
179
+ "type": "timestamp",
180
+ "primaryKey": false,
181
+ "notNull": true,
182
+ "default": "now()"
183
+ }
184
+ },
185
+ "indexes": {
186
+ "sessions_user_id_idx": {
187
+ "name": "sessions_user_id_idx",
188
+ "columns": [
189
+ {
190
+ "expression": "user_id",
191
+ "isExpression": false,
192
+ "asc": true,
193
+ "nulls": "last"
194
+ }
195
+ ],
196
+ "isUnique": false,
197
+ "concurrently": false,
198
+ "method": "btree",
199
+ "with": {}
200
+ }
201
+ },
202
+ "foreignKeys": {
203
+ "sessions_user_id_users_id_fk": {
204
+ "name": "sessions_user_id_users_id_fk",
205
+ "tableFrom": "sessions",
206
+ "tableTo": "users",
207
+ "columnsFrom": [
208
+ "user_id"
209
+ ],
210
+ "columnsTo": [
211
+ "id"
212
+ ],
213
+ "onDelete": "cascade",
214
+ "onUpdate": "no action"
215
+ }
216
+ },
217
+ "compositePrimaryKeys": {},
218
+ "uniqueConstraints": {
219
+ "sessions_token_unique": {
220
+ "name": "sessions_token_unique",
221
+ "nullsNotDistinct": false,
222
+ "columns": [
223
+ "token"
224
+ ]
225
+ }
226
+ },
227
+ "policies": {},
228
+ "checkConstraints": {},
229
+ "isRLSEnabled": false
230
+ },
231
+ "public.users": {
232
+ "name": "users",
233
+ "schema": "",
234
+ "columns": {
235
+ "id": {
236
+ "name": "id",
237
+ "type": "text",
238
+ "primaryKey": true,
239
+ "notNull": true
240
+ },
241
+ "name": {
242
+ "name": "name",
243
+ "type": "text",
244
+ "primaryKey": false,
245
+ "notNull": true
246
+ },
247
+ "email": {
248
+ "name": "email",
249
+ "type": "text",
250
+ "primaryKey": false,
251
+ "notNull": true
252
+ },
253
+ "email_verified": {
254
+ "name": "email_verified",
255
+ "type": "boolean",
256
+ "primaryKey": false,
257
+ "notNull": true,
258
+ "default": false
259
+ },
260
+ "image": {
261
+ "name": "image",
262
+ "type": "text",
263
+ "primaryKey": false,
264
+ "notNull": false
265
+ },
266
+ "created_at": {
267
+ "name": "created_at",
268
+ "type": "timestamp",
269
+ "primaryKey": false,
270
+ "notNull": true,
271
+ "default": "now()"
272
+ },
273
+ "updated_at": {
274
+ "name": "updated_at",
275
+ "type": "timestamp",
276
+ "primaryKey": false,
277
+ "notNull": true,
278
+ "default": "now()"
279
+ }
280
+ },
281
+ "indexes": {},
282
+ "foreignKeys": {},
283
+ "compositePrimaryKeys": {},
284
+ "uniqueConstraints": {
285
+ "users_email_unique": {
286
+ "name": "users_email_unique",
287
+ "nullsNotDistinct": false,
288
+ "columns": [
289
+ "email"
290
+ ]
291
+ }
292
+ },
293
+ "policies": {},
294
+ "checkConstraints": {},
295
+ "isRLSEnabled": false
296
+ },
297
+ "public.verifications": {
298
+ "name": "verifications",
299
+ "schema": "",
300
+ "columns": {
301
+ "id": {
302
+ "name": "id",
303
+ "type": "text",
304
+ "primaryKey": true,
305
+ "notNull": true
306
+ },
307
+ "identifier": {
308
+ "name": "identifier",
309
+ "type": "text",
310
+ "primaryKey": false,
311
+ "notNull": true
312
+ },
313
+ "value": {
314
+ "name": "value",
315
+ "type": "text",
316
+ "primaryKey": false,
317
+ "notNull": true
318
+ },
319
+ "expires_at": {
320
+ "name": "expires_at",
321
+ "type": "timestamp",
322
+ "primaryKey": false,
323
+ "notNull": true
324
+ },
325
+ "created_at": {
326
+ "name": "created_at",
327
+ "type": "timestamp",
328
+ "primaryKey": false,
329
+ "notNull": true,
330
+ "default": "now()"
331
+ },
332
+ "updated_at": {
333
+ "name": "updated_at",
334
+ "type": "timestamp",
335
+ "primaryKey": false,
336
+ "notNull": true,
337
+ "default": "now()"
338
+ }
339
+ },
340
+ "indexes": {
341
+ "verifications_identifier_idx": {
342
+ "name": "verifications_identifier_idx",
343
+ "columns": [
344
+ {
345
+ "expression": "identifier",
346
+ "isExpression": false,
347
+ "asc": true,
348
+ "nulls": "last"
349
+ }
350
+ ],
351
+ "isUnique": false,
352
+ "concurrently": false,
353
+ "method": "btree",
354
+ "with": {}
355
+ }
356
+ },
357
+ "foreignKeys": {},
358
+ "compositePrimaryKeys": {},
359
+ "uniqueConstraints": {},
360
+ "policies": {},
361
+ "checkConstraints": {},
362
+ "isRLSEnabled": false
363
+ }
364
+ },
365
+ "enums": {},
366
+ "schemas": {},
367
+ "sequences": {},
368
+ "roles": {},
369
+ "policies": {},
370
+ "views": {},
371
+ "_meta": {
372
+ "columns": {},
373
+ "schemas": {},
374
+ "tables": {}
375
+ }
376
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "version": "7",
3
+ "dialect": "postgresql",
4
+ "entries": [
5
+ {
6
+ "idx": 0,
7
+ "version": "7",
8
+ "when": 1793028067249,
9
+ "tag": "0000_init",
10
+ "breakpoints": true
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,12 @@
1
+ import { defineConfig } from "drizzle-kit";
2
+ import { dbEnv } from "@repo/config";
3
+
4
+ export default defineConfig({
5
+ out: "./drizzle",
6
+ schema: "./src/schema/index.ts",
7
+ dialect: "postgresql",
8
+ casing: "snake_case",
9
+ dbCredentials: {
10
+ url: dbEnv.DATABASE_URL,
11
+ },
12
+ });
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@repo/db",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "build": "tsdown",
8
+ "dev": "tsdown --watch --no-clean",
9
+ "generate": "dotenv-run -- drizzle-kit generate --config drizzle.config.ts",
10
+ "migrate": "dotenv-run -- drizzle-kit migrate --config drizzle.config.ts",
11
+ "push": "dotenv-run -- drizzle-kit push --config drizzle.config.ts",
12
+ "studio": "dotenv-run -- drizzle-kit studio --config drizzle.config.ts",
13
+ "typecheck": "tsc"
14
+ },
15
+ "dependencies": {
16
+ "@paralleldrive/cuid2": "catalog:",
17
+ "@repo/config": "workspace:*",
18
+ "drizzle-orm": "catalog:",
19
+ "pg": "catalog:"
20
+ },
21
+ "devDependencies": {
22
+ "@dotenv-run/cli": "catalog:",
23
+ "@types/node": "catalog:",
24
+ "drizzle-kit": "^0.31.11",
25
+ "@types/pg": "catalog:",
26
+ "tsdown": "catalog:",
27
+ "typescript": "catalog:"
28
+ },
29
+ "exports": {
30
+ ".": {
31
+ "types": "./dist/index.d.mts",
32
+ "import": "./dist/index.mjs",
33
+ "default": "./dist/index.mjs"
34
+ }
35
+ },
36
+ "main": "./dist/index.mjs",
37
+ "types": "./dist/index.d.mts"
38
+ }
@@ -0,0 +1,12 @@
1
+ import { drizzle } from "drizzle-orm/node-postgres";
2
+ import { Pool } from "pg";
3
+ import { dbEnv } from "@repo/config";
4
+ import * as schema from "./schema";
5
+
6
+ export const pool = new Pool({
7
+ connectionString: dbEnv.DATABASE_URL,
8
+ });
9
+
10
+ export const db = drizzle(pool, { schema, casing: "snake_case" });
11
+
12
+ export type Database = typeof db;
@@ -0,0 +1,2 @@
1
+ export { type Database, db, pool } from "./client";
2
+ export * from "./schema";
@@ -0,0 +1,96 @@
1
+ import { createId } from "@paralleldrive/cuid2";
2
+ import { relations } from "drizzle-orm";
3
+ import { boolean, index, pgTable, text, timestamp } from "drizzle-orm/pg-core";
4
+
5
+ // Better Auth's core tables. The api passes these to the drizzle adapter, so
6
+ // add columns here and mirror them in `user.additionalFields` in apps/api.
7
+
8
+ const id = () =>
9
+ text()
10
+ .primaryKey()
11
+ .$defaultFn(() => createId());
12
+
13
+ const timestamps = {
14
+ createdAt: timestamp().defaultNow().notNull(),
15
+ updatedAt: timestamp()
16
+ .defaultNow()
17
+ .notNull()
18
+ .$onUpdate(() => new Date()),
19
+ };
20
+
21
+ export const usersTable = pgTable("users", {
22
+ id: id(),
23
+ name: text().notNull(),
24
+ email: text().unique().notNull(),
25
+ emailVerified: boolean().default(false).notNull(),
26
+ image: text(),
27
+ ...timestamps,
28
+ });
29
+
30
+ export const sessionsTable = pgTable(
31
+ "sessions",
32
+ {
33
+ id: id(),
34
+ expiresAt: timestamp().notNull(),
35
+ token: text().unique().notNull(),
36
+ ipAddress: text(),
37
+ userAgent: text(),
38
+ userId: text()
39
+ .notNull()
40
+ .references(() => usersTable.id, { onDelete: "cascade" }),
41
+ ...timestamps,
42
+ },
43
+ (t) => [index("sessions_user_id_idx").on(t.userId)],
44
+ );
45
+
46
+ export const accountsTable = pgTable(
47
+ "accounts",
48
+ {
49
+ id: id(),
50
+ accountId: text().notNull(),
51
+ providerId: text().notNull(),
52
+ userId: text()
53
+ .notNull()
54
+ .references(() => usersTable.id, { onDelete: "cascade" }),
55
+ accessToken: text(),
56
+ refreshToken: text(),
57
+ idToken: text(),
58
+ accessTokenExpiresAt: timestamp(),
59
+ refreshTokenExpiresAt: timestamp(),
60
+ scope: text(),
61
+ password: text(),
62
+ ...timestamps,
63
+ },
64
+ (t) => [index("accounts_user_id_idx").on(t.userId)],
65
+ );
66
+
67
+ export const verificationsTable = pgTable(
68
+ "verifications",
69
+ {
70
+ id: id(),
71
+ identifier: text().notNull(),
72
+ value: text().notNull(),
73
+ expiresAt: timestamp().notNull(),
74
+ ...timestamps,
75
+ },
76
+ (t) => [index("verifications_identifier_idx").on(t.identifier)],
77
+ );
78
+
79
+ export const usersRelations = relations(usersTable, ({ many }) => ({
80
+ accounts: many(accountsTable),
81
+ sessions: many(sessionsTable),
82
+ }));
83
+
84
+ export const sessionsRelations = relations(sessionsTable, ({ one }) => ({
85
+ user: one(usersTable, {
86
+ fields: [sessionsTable.userId],
87
+ references: [usersTable.id],
88
+ }),
89
+ }));
90
+
91
+ export const accountsRelations = relations(accountsTable, ({ one }) => ({
92
+ user: one(usersTable, {
93
+ fields: [accountsTable.userId],
94
+ references: [usersTable.id],
95
+ }),
96
+ }));
@@ -0,0 +1 @@
1
+ export * from "./auth";
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "composite": false,
5
+ "noEmit": false
6
+ }
7
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "composite": true,
5
+ "rootDir": "src",
6
+ "types": ["node"]
7
+ },
8
+ "include": ["src"]
9
+ }
@@ -0,0 +1,12 @@
1
+ import { defineConfig } from "tsdown";
2
+
3
+ export default defineConfig({
4
+ tsconfig: "tsconfig.build.json",
5
+ entry: ["src/index.ts"],
6
+ format: ["esm"],
7
+ dts: true,
8
+ sourcemap: true,
9
+ clean: true,
10
+ treeshake: true,
11
+ target: false,
12
+ });
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@repo/shared",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "scripts": {
8
+ "build": "tsdown",
9
+ "dev": "tsdown --watch --no-clean",
10
+ "test": "vitest run",
11
+ "test:watch": "vitest",
12
+ "typecheck": "tsc"
13
+ },
14
+ "dependencies": {
15
+ "zod": "catalog:"
16
+ },
17
+ "devDependencies": {
18
+ "@types/node": "catalog:",
19
+ "tsdown": "catalog:",
20
+ "typescript": "catalog:",
21
+ "vitest": "catalog:"
22
+ },
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/index.d.mts",
26
+ "import": "./dist/index.mjs",
27
+ "default": "./dist/index.mjs"
28
+ }
29
+ },
30
+ "main": "./dist/index.mjs",
31
+ "types": "./dist/index.d.mts"
32
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./schemas";
2
+ export * from "./types";
@@ -0,0 +1,32 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { signInSchema, signUpSchema } from "./auth";
3
+
4
+ describe("signUpSchema", () => {
5
+ it("accepts a valid sign-up and trims the name", () => {
6
+ const result = signUpSchema.parse({
7
+ name: " Ada ",
8
+ email: "ada@example.com",
9
+ password: "correct horse",
10
+ });
11
+ expect(result.name).toBe("Ada");
12
+ });
13
+
14
+ it("rejects a short password", () => {
15
+ const result = signUpSchema.safeParse({
16
+ name: "Ada",
17
+ email: "ada@example.com",
18
+ password: "short",
19
+ });
20
+ expect(result.success).toBe(false);
21
+ });
22
+ });
23
+
24
+ describe("signInSchema", () => {
25
+ it("rejects a malformed email", () => {
26
+ const result = signInSchema.safeParse({
27
+ email: "not-an-email",
28
+ password: "anything",
29
+ });
30
+ expect(result.success).toBe(false);
31
+ });
32
+ });
@@ -0,0 +1,22 @@
1
+ import { z } from "zod";
2
+
3
+ // Better Auth's email + password defaults: 8 to 128 characters.
4
+ export const PASSWORD_MIN_LENGTH = 8;
5
+ export const PASSWORD_MAX_LENGTH = 128;
6
+
7
+ export const signInSchema = z.object({
8
+ email: z.email("Enter a valid email address"),
9
+ password: z.string().min(1, "Enter your password"),
10
+ });
11
+
12
+ export const signUpSchema = z.object({
13
+ name: z.string().trim().min(1, "Enter your name").max(100),
14
+ email: z.email("Enter a valid email address"),
15
+ password: z
16
+ .string()
17
+ .min(PASSWORD_MIN_LENGTH, `Use at least ${PASSWORD_MIN_LENGTH} characters`)
18
+ .max(PASSWORD_MAX_LENGTH, `Use at most ${PASSWORD_MAX_LENGTH} characters`),
19
+ });
20
+
21
+ export type SignInInput = z.infer<typeof signInSchema>;
22
+ export type SignUpInput = z.infer<typeof signUpSchema>;
@@ -0,0 +1 @@
1
+ export * from "./auth";