@opprs/db-prisma 1.1.4-canary.0ba2f7d → 1.1.4-canary.161d3e4

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/index.cjs CHANGED
@@ -37,9 +37,9 @@ __export(index_exports, {
37
37
  deleteTournament: () => deleteTournament,
38
38
  deleteUser: () => deleteUser,
39
39
  disconnect: () => disconnect,
40
- findPlayerByEmail: () => findPlayerByEmail,
41
40
  findPlayerByExternalId: () => findPlayerByExternalId,
42
41
  findPlayerById: () => findPlayerById,
42
+ findPlayerByUserEmail: () => findPlayerByUserEmail,
43
43
  findPlayers: () => findPlayers,
44
44
  findResultById: () => findResultById,
45
45
  findResultByPlayerAndTournament: () => findResultByPlayerAndTournament,
@@ -66,6 +66,7 @@ __export(index_exports, {
66
66
  getTournamentsByDateRange: () => getTournamentsByDateRange,
67
67
  getUserByEmailWithPlayer: () => getUserByEmailWithPlayer,
68
68
  getUserWithPlayer: () => getUserWithPlayer,
69
+ linkPlayerToUser: () => linkPlayerToUser,
69
70
  prisma: () => prisma,
70
71
  recalculateTimeDecay: () => recalculateTimeDecay,
71
72
  searchPlayers: () => searchPlayers,
@@ -124,11 +125,12 @@ async function findPlayerByExternalId(externalId, include) {
124
125
  include
125
126
  });
126
127
  }
127
- async function findPlayerByEmail(email, include) {
128
- return prisma.player.findUnique({
128
+ async function findPlayerByUserEmail(email, include) {
129
+ const user = await prisma.user.findUnique({
129
130
  where: { email },
130
- include
131
+ include: { player: include ? { include } : true }
131
132
  });
133
+ return user?.player ?? null;
132
134
  }
133
135
  async function findPlayers(options = {}) {
134
136
  return prisma.player.findMany({
@@ -217,10 +219,7 @@ async function searchPlayers(query, limit = 20) {
217
219
  return findPlayers({
218
220
  take: limit,
219
221
  where: {
220
- OR: [
221
- { name: { contains: query, mode: "insensitive" } },
222
- { email: { contains: query, mode: "insensitive" } }
223
- ]
222
+ name: { contains: query, mode: "insensitive" }
224
223
  }
225
224
  });
226
225
  }
@@ -543,8 +542,7 @@ async function createUserWithPlayer(userData, playerData) {
543
542
  return prisma.$transaction(async (tx) => {
544
543
  const player = await tx.player.create({
545
544
  data: {
546
- name: playerData.name,
547
- email: playerData.email
545
+ name: playerData.name
548
546
  }
549
547
  });
550
548
  const user = await tx.user.create({
@@ -667,6 +665,38 @@ async function findUsers(params) {
667
665
  });
668
666
  return users;
669
667
  }
668
+ async function linkPlayerToUser(userId, playerId) {
669
+ return prisma.$transaction(async (tx) => {
670
+ if (playerId) {
671
+ const player = await tx.player.findUnique({ where: { id: playerId } });
672
+ if (!player) {
673
+ throw new Error(`Player with id '${playerId}' not found`);
674
+ }
675
+ await tx.user.updateMany({
676
+ where: { playerId },
677
+ data: { playerId: null }
678
+ });
679
+ }
680
+ const user = await tx.user.update({
681
+ where: { id: userId },
682
+ data: { playerId },
683
+ include: {
684
+ player: {
685
+ select: {
686
+ id: true,
687
+ name: true,
688
+ rating: true,
689
+ ratingDeviation: true,
690
+ ranking: true,
691
+ isRated: true,
692
+ eventCount: true
693
+ }
694
+ }
695
+ }
696
+ });
697
+ return user;
698
+ });
699
+ }
670
700
  // Annotate the CommonJS export names for ESM import in node:
671
701
  0 && (module.exports = {
672
702
  connect,
@@ -686,9 +716,9 @@ async function findUsers(params) {
686
716
  deleteTournament,
687
717
  deleteUser,
688
718
  disconnect,
689
- findPlayerByEmail,
690
719
  findPlayerByExternalId,
691
720
  findPlayerById,
721
+ findPlayerByUserEmail,
692
722
  findPlayers,
693
723
  findResultById,
694
724
  findResultByPlayerAndTournament,
@@ -715,6 +745,7 @@ async function findUsers(params) {
715
745
  getTournamentsByDateRange,
716
746
  getUserByEmailWithPlayer,
717
747
  getUserWithPlayer,
748
+ linkPlayerToUser,
718
749
  prisma,
719
750
  recalculateTimeDecay,
720
751
  searchPlayers,
package/dist/index.d.cts CHANGED
@@ -26,7 +26,6 @@ declare function testConnection(): Promise<boolean>;
26
26
  interface CreatePlayerInput {
27
27
  externalId?: string;
28
28
  name?: string;
29
- email?: string;
30
29
  rating?: number;
31
30
  ratingDeviation?: number;
32
31
  ranking?: number;
@@ -38,7 +37,6 @@ interface CreatePlayerInput {
38
37
  */
39
38
  interface UpdatePlayerInput {
40
39
  name?: string;
41
- email?: string;
42
40
  rating?: number;
43
41
  ratingDeviation?: number;
44
42
  ranking?: number;
@@ -70,9 +68,9 @@ declare function findPlayerById(id: string, include?: Prisma.PlayerInclude): Pro
70
68
  */
71
69
  declare function findPlayerByExternalId(externalId: string, include?: Prisma.PlayerInclude): Promise<Player | null>;
72
70
  /**
73
- * Finds a player by email
71
+ * Finds a player through their linked User's email
74
72
  */
75
- declare function findPlayerByEmail(email: string, include?: Prisma.PlayerInclude): Promise<Player | null>;
73
+ declare function findPlayerByUserEmail(email: string, include?: Prisma.PlayerInclude): Promise<Player | null>;
76
74
  /**
77
75
  * Finds multiple players with optional filters
78
76
  */
@@ -186,7 +184,6 @@ declare function getPlayerWithResults(id: string): Promise<{
186
184
  createdAt: Date;
187
185
  updatedAt: Date;
188
186
  externalId: string | null;
189
- email: string | null;
190
187
  rating: number;
191
188
  ratingDeviation: number;
192
189
  ranking: number | null;
@@ -196,7 +193,7 @@ declare function getPlayerWithResults(id: string): Promise<{
196
193
  lastEventDate: Date | null;
197
194
  } | null>;
198
195
  /**
199
- * Searches players by name or email
196
+ * Searches players by name
200
197
  */
201
198
  declare function searchPlayers(query: string, limit?: number): Promise<Player[]>;
202
199
 
@@ -302,7 +299,6 @@ declare function getTournamentWithResults(id: string): Promise<({
302
299
  createdAt: Date;
303
300
  updatedAt: Date;
304
301
  externalId: string | null;
305
- email: string | null;
306
302
  rating: number;
307
303
  ratingDeviation: number;
308
304
  ranking: number | null;
@@ -362,7 +358,6 @@ declare function getTournamentStats(id: string): Promise<{
362
358
  createdAt: Date;
363
359
  updatedAt: Date;
364
360
  externalId: string | null;
365
- email: string | null;
366
361
  rating: number;
367
362
  ratingDeviation: number;
368
363
  ranking: number | null;
@@ -561,7 +556,7 @@ interface CreateUserInput {
561
556
  interface UpdateUserInput {
562
557
  email?: string;
563
558
  passwordHash?: string;
564
- playerId?: string;
559
+ playerId?: string | null;
565
560
  role?: 'USER' | 'ADMIN';
566
561
  refreshTokenHash?: string | null;
567
562
  }
@@ -593,7 +588,6 @@ declare function createUser(data: CreateUserInput): Promise<User>;
593
588
  */
594
589
  declare function createUserWithPlayer(userData: Omit<CreateUserInput, 'playerId'>, playerData: {
595
590
  name?: string;
596
- email?: string;
597
591
  }): Promise<UserWithPlayer>;
598
592
  /**
599
593
  * Finds a user by ID
@@ -636,6 +630,16 @@ declare function findUsers(params: {
636
630
  where?: Prisma.UserWhereInput;
637
631
  orderBy?: Prisma.UserOrderByWithRelationInput;
638
632
  }): Promise<UserWithPlayer[]>;
633
+ /**
634
+ * Links a player to a user, automatically unlinking from any existing user.
635
+ * Uses a transaction to ensure atomicity.
636
+ *
637
+ * @param userId - The user to link the player to
638
+ * @param playerId - The player to link (null to unlink)
639
+ * @returns The updated user with player data
640
+ * @throws Error if player not found
641
+ */
642
+ declare function linkPlayerToUser(userId: string, playerId: string | null): Promise<UserWithPlayer>;
639
643
 
640
644
  /**
641
645
  * Re-export Prisma generated types
@@ -696,4 +700,4 @@ interface ConnectionStatus {
696
700
  error?: string;
697
701
  }
698
702
 
699
- export { type ConnectionStatus, type CreatePlayerInput, type CreateResultInput, type CreateTournamentInput, type CreateUserInput, type FindPlayersOptions, type FindResultsOptions, type FindTournamentsOptions, type PlayerStatistics, type PlayerWithResults, type TournamentResultWithTournament, type TournamentStatistics, type UpdatePlayerInput, type UpdateResultInput, type UpdateTournamentInput, type UpdateUserInput, type UserWithPlayer, connect, countPlayers, countResults, countTournaments, countUsers, createManyResults, createPlayer, createResult, createTournament, createUser, createUserWithPlayer, deletePlayer, deleteResult, deleteResultsByTournament, deleteTournament, deleteUser, disconnect, findPlayerByEmail, findPlayerByExternalId, findPlayerById, findPlayers, findResultById, findResultByPlayerAndTournament, findResults, findTournamentByExternalId, findTournamentById, findTournaments, findUserByEmail, findUserById, findUsers, getMajorTournaments, getPlayerResults, getPlayerStats, getPlayerTopFinishes, getPlayerWithResults, getRatedPlayers, getRecentTournaments, getTopPlayersByRanking, getTopPlayersByRating, getTournamentResults, getTournamentStats, getTournamentWithResults, getTournamentsByBoosterType, getTournamentsByDateRange, getUserByEmailWithPlayer, getUserWithPlayer, prisma, recalculateTimeDecay, searchPlayers, searchTournaments, testConnection, updatePlayer, updatePlayerRating, updateResult, updateResultPoints, updateTournament, updateUser, updateUserRefreshToken };
703
+ export { type ConnectionStatus, type CreatePlayerInput, type CreateResultInput, type CreateTournamentInput, type CreateUserInput, type FindPlayersOptions, type FindResultsOptions, type FindTournamentsOptions, type PlayerStatistics, type PlayerWithResults, type TournamentResultWithTournament, type TournamentStatistics, type UpdatePlayerInput, type UpdateResultInput, type UpdateTournamentInput, type UpdateUserInput, type UserWithPlayer, connect, countPlayers, countResults, countTournaments, countUsers, createManyResults, createPlayer, createResult, createTournament, createUser, createUserWithPlayer, deletePlayer, deleteResult, deleteResultsByTournament, deleteTournament, deleteUser, disconnect, findPlayerByExternalId, findPlayerById, findPlayerByUserEmail, findPlayers, findResultById, findResultByPlayerAndTournament, findResults, findTournamentByExternalId, findTournamentById, findTournaments, findUserByEmail, findUserById, findUsers, getMajorTournaments, getPlayerResults, getPlayerStats, getPlayerTopFinishes, getPlayerWithResults, getRatedPlayers, getRecentTournaments, getTopPlayersByRanking, getTopPlayersByRating, getTournamentResults, getTournamentStats, getTournamentWithResults, getTournamentsByBoosterType, getTournamentsByDateRange, getUserByEmailWithPlayer, getUserWithPlayer, linkPlayerToUser, prisma, recalculateTimeDecay, searchPlayers, searchTournaments, testConnection, updatePlayer, updatePlayerRating, updateResult, updateResultPoints, updateTournament, updateUser, updateUserRefreshToken };
package/dist/index.d.ts CHANGED
@@ -26,7 +26,6 @@ declare function testConnection(): Promise<boolean>;
26
26
  interface CreatePlayerInput {
27
27
  externalId?: string;
28
28
  name?: string;
29
- email?: string;
30
29
  rating?: number;
31
30
  ratingDeviation?: number;
32
31
  ranking?: number;
@@ -38,7 +37,6 @@ interface CreatePlayerInput {
38
37
  */
39
38
  interface UpdatePlayerInput {
40
39
  name?: string;
41
- email?: string;
42
40
  rating?: number;
43
41
  ratingDeviation?: number;
44
42
  ranking?: number;
@@ -70,9 +68,9 @@ declare function findPlayerById(id: string, include?: Prisma.PlayerInclude): Pro
70
68
  */
71
69
  declare function findPlayerByExternalId(externalId: string, include?: Prisma.PlayerInclude): Promise<Player | null>;
72
70
  /**
73
- * Finds a player by email
71
+ * Finds a player through their linked User's email
74
72
  */
75
- declare function findPlayerByEmail(email: string, include?: Prisma.PlayerInclude): Promise<Player | null>;
73
+ declare function findPlayerByUserEmail(email: string, include?: Prisma.PlayerInclude): Promise<Player | null>;
76
74
  /**
77
75
  * Finds multiple players with optional filters
78
76
  */
@@ -186,7 +184,6 @@ declare function getPlayerWithResults(id: string): Promise<{
186
184
  createdAt: Date;
187
185
  updatedAt: Date;
188
186
  externalId: string | null;
189
- email: string | null;
190
187
  rating: number;
191
188
  ratingDeviation: number;
192
189
  ranking: number | null;
@@ -196,7 +193,7 @@ declare function getPlayerWithResults(id: string): Promise<{
196
193
  lastEventDate: Date | null;
197
194
  } | null>;
198
195
  /**
199
- * Searches players by name or email
196
+ * Searches players by name
200
197
  */
201
198
  declare function searchPlayers(query: string, limit?: number): Promise<Player[]>;
202
199
 
@@ -302,7 +299,6 @@ declare function getTournamentWithResults(id: string): Promise<({
302
299
  createdAt: Date;
303
300
  updatedAt: Date;
304
301
  externalId: string | null;
305
- email: string | null;
306
302
  rating: number;
307
303
  ratingDeviation: number;
308
304
  ranking: number | null;
@@ -362,7 +358,6 @@ declare function getTournamentStats(id: string): Promise<{
362
358
  createdAt: Date;
363
359
  updatedAt: Date;
364
360
  externalId: string | null;
365
- email: string | null;
366
361
  rating: number;
367
362
  ratingDeviation: number;
368
363
  ranking: number | null;
@@ -561,7 +556,7 @@ interface CreateUserInput {
561
556
  interface UpdateUserInput {
562
557
  email?: string;
563
558
  passwordHash?: string;
564
- playerId?: string;
559
+ playerId?: string | null;
565
560
  role?: 'USER' | 'ADMIN';
566
561
  refreshTokenHash?: string | null;
567
562
  }
@@ -593,7 +588,6 @@ declare function createUser(data: CreateUserInput): Promise<User>;
593
588
  */
594
589
  declare function createUserWithPlayer(userData: Omit<CreateUserInput, 'playerId'>, playerData: {
595
590
  name?: string;
596
- email?: string;
597
591
  }): Promise<UserWithPlayer>;
598
592
  /**
599
593
  * Finds a user by ID
@@ -636,6 +630,16 @@ declare function findUsers(params: {
636
630
  where?: Prisma.UserWhereInput;
637
631
  orderBy?: Prisma.UserOrderByWithRelationInput;
638
632
  }): Promise<UserWithPlayer[]>;
633
+ /**
634
+ * Links a player to a user, automatically unlinking from any existing user.
635
+ * Uses a transaction to ensure atomicity.
636
+ *
637
+ * @param userId - The user to link the player to
638
+ * @param playerId - The player to link (null to unlink)
639
+ * @returns The updated user with player data
640
+ * @throws Error if player not found
641
+ */
642
+ declare function linkPlayerToUser(userId: string, playerId: string | null): Promise<UserWithPlayer>;
639
643
 
640
644
  /**
641
645
  * Re-export Prisma generated types
@@ -696,4 +700,4 @@ interface ConnectionStatus {
696
700
  error?: string;
697
701
  }
698
702
 
699
- export { type ConnectionStatus, type CreatePlayerInput, type CreateResultInput, type CreateTournamentInput, type CreateUserInput, type FindPlayersOptions, type FindResultsOptions, type FindTournamentsOptions, type PlayerStatistics, type PlayerWithResults, type TournamentResultWithTournament, type TournamentStatistics, type UpdatePlayerInput, type UpdateResultInput, type UpdateTournamentInput, type UpdateUserInput, type UserWithPlayer, connect, countPlayers, countResults, countTournaments, countUsers, createManyResults, createPlayer, createResult, createTournament, createUser, createUserWithPlayer, deletePlayer, deleteResult, deleteResultsByTournament, deleteTournament, deleteUser, disconnect, findPlayerByEmail, findPlayerByExternalId, findPlayerById, findPlayers, findResultById, findResultByPlayerAndTournament, findResults, findTournamentByExternalId, findTournamentById, findTournaments, findUserByEmail, findUserById, findUsers, getMajorTournaments, getPlayerResults, getPlayerStats, getPlayerTopFinishes, getPlayerWithResults, getRatedPlayers, getRecentTournaments, getTopPlayersByRanking, getTopPlayersByRating, getTournamentResults, getTournamentStats, getTournamentWithResults, getTournamentsByBoosterType, getTournamentsByDateRange, getUserByEmailWithPlayer, getUserWithPlayer, prisma, recalculateTimeDecay, searchPlayers, searchTournaments, testConnection, updatePlayer, updatePlayerRating, updateResult, updateResultPoints, updateTournament, updateUser, updateUserRefreshToken };
703
+ export { type ConnectionStatus, type CreatePlayerInput, type CreateResultInput, type CreateTournamentInput, type CreateUserInput, type FindPlayersOptions, type FindResultsOptions, type FindTournamentsOptions, type PlayerStatistics, type PlayerWithResults, type TournamentResultWithTournament, type TournamentStatistics, type UpdatePlayerInput, type UpdateResultInput, type UpdateTournamentInput, type UpdateUserInput, type UserWithPlayer, connect, countPlayers, countResults, countTournaments, countUsers, createManyResults, createPlayer, createResult, createTournament, createUser, createUserWithPlayer, deletePlayer, deleteResult, deleteResultsByTournament, deleteTournament, deleteUser, disconnect, findPlayerByExternalId, findPlayerById, findPlayerByUserEmail, findPlayers, findResultById, findResultByPlayerAndTournament, findResults, findTournamentByExternalId, findTournamentById, findTournaments, findUserByEmail, findUserById, findUsers, getMajorTournaments, getPlayerResults, getPlayerStats, getPlayerTopFinishes, getPlayerWithResults, getRatedPlayers, getRecentTournaments, getTopPlayersByRanking, getTopPlayersByRating, getTournamentResults, getTournamentStats, getTournamentWithResults, getTournamentsByBoosterType, getTournamentsByDateRange, getUserByEmailWithPlayer, getUserWithPlayer, linkPlayerToUser, prisma, recalculateTimeDecay, searchPlayers, searchTournaments, testConnection, updatePlayer, updatePlayerRating, updateResult, updateResultPoints, updateTournament, updateUser, updateUserRefreshToken };
package/dist/index.js CHANGED
@@ -41,11 +41,12 @@ async function findPlayerByExternalId(externalId, include) {
41
41
  include
42
42
  });
43
43
  }
44
- async function findPlayerByEmail(email, include) {
45
- return prisma.player.findUnique({
44
+ async function findPlayerByUserEmail(email, include) {
45
+ const user = await prisma.user.findUnique({
46
46
  where: { email },
47
- include
47
+ include: { player: include ? { include } : true }
48
48
  });
49
+ return user?.player ?? null;
49
50
  }
50
51
  async function findPlayers(options = {}) {
51
52
  return prisma.player.findMany({
@@ -134,10 +135,7 @@ async function searchPlayers(query, limit = 20) {
134
135
  return findPlayers({
135
136
  take: limit,
136
137
  where: {
137
- OR: [
138
- { name: { contains: query, mode: "insensitive" } },
139
- { email: { contains: query, mode: "insensitive" } }
140
- ]
138
+ name: { contains: query, mode: "insensitive" }
141
139
  }
142
140
  });
143
141
  }
@@ -460,8 +458,7 @@ async function createUserWithPlayer(userData, playerData) {
460
458
  return prisma.$transaction(async (tx) => {
461
459
  const player = await tx.player.create({
462
460
  data: {
463
- name: playerData.name,
464
- email: playerData.email
461
+ name: playerData.name
465
462
  }
466
463
  });
467
464
  const user = await tx.user.create({
@@ -584,6 +581,38 @@ async function findUsers(params) {
584
581
  });
585
582
  return users;
586
583
  }
584
+ async function linkPlayerToUser(userId, playerId) {
585
+ return prisma.$transaction(async (tx) => {
586
+ if (playerId) {
587
+ const player = await tx.player.findUnique({ where: { id: playerId } });
588
+ if (!player) {
589
+ throw new Error(`Player with id '${playerId}' not found`);
590
+ }
591
+ await tx.user.updateMany({
592
+ where: { playerId },
593
+ data: { playerId: null }
594
+ });
595
+ }
596
+ const user = await tx.user.update({
597
+ where: { id: userId },
598
+ data: { playerId },
599
+ include: {
600
+ player: {
601
+ select: {
602
+ id: true,
603
+ name: true,
604
+ rating: true,
605
+ ratingDeviation: true,
606
+ ranking: true,
607
+ isRated: true,
608
+ eventCount: true
609
+ }
610
+ }
611
+ }
612
+ });
613
+ return user;
614
+ });
615
+ }
587
616
  export {
588
617
  connect,
589
618
  countPlayers,
@@ -602,9 +631,9 @@ export {
602
631
  deleteTournament,
603
632
  deleteUser,
604
633
  disconnect,
605
- findPlayerByEmail,
606
634
  findPlayerByExternalId,
607
635
  findPlayerById,
636
+ findPlayerByUserEmail,
608
637
  findPlayers,
609
638
  findResultById,
610
639
  findResultByPlayerAndTournament,
@@ -631,6 +660,7 @@ export {
631
660
  getTournamentsByDateRange,
632
661
  getUserByEmailWithPlayer,
633
662
  getUserWithPlayer,
663
+ linkPlayerToUser,
634
664
  prisma,
635
665
  recalculateTimeDecay,
636
666
  searchPlayers,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opprs/db-prisma",
3
- "version": "1.1.4-canary.0ba2f7d",
3
+ "version": "1.1.4-canary.161d3e4",
4
4
  "description": "Database backend for OPPR (Open Pinball Player Ranking System) using Prisma and PostgreSQL",
5
5
  "keywords": [
6
6
  "oppr",
@@ -40,7 +40,9 @@
40
40
  },
41
41
  "devDependencies": {
42
42
  "@testcontainers/postgresql": "^11.11.0",
43
+ "@types/bcrypt": "^6.0.0",
43
44
  "@types/node": "^22.10.5",
45
+ "bcrypt": "^6.0.0",
44
46
  "@typescript-eslint/eslint-plugin": "^8.19.1",
45
47
  "@typescript-eslint/parser": "^8.19.1",
46
48
  "@vitest/coverage-v8": "^4.0.16",
@@ -54,10 +56,10 @@
54
56
  "vitest": "^4.0.16"
55
57
  },
56
58
  "peerDependencies": {
57
- "@opprs/core": "^1.1.4-canary.0ba2f7d"
59
+ "@opprs/core": "^1.1.4-canary.161d3e4"
58
60
  },
59
61
  "engines": {
60
- "node": ">=18.0.0"
62
+ "node": ">=20.9.0"
61
63
  },
62
64
  "prisma": {
63
65
  "seed": "tsx prisma/seed.ts"
@@ -19,7 +19,6 @@ model Player {
19
19
  // Player identification
20
20
  externalId String? @unique // External ID from OPPR or other systems
21
21
  name String?
22
- email String? @unique
23
22
 
24
23
  // OPPR Rating fields
25
24
  rating Float @default(1500) // Glicko rating
@@ -36,7 +35,6 @@ model Player {
36
35
  tournamentResults TournamentResult[]
37
36
  user User?
38
37
 
39
- @@index([email])
40
38
  @@index([externalId])
41
39
  @@index([rating])
42
40
  @@index([ranking])
package/prisma/seed.ts CHANGED
@@ -1,182 +1,238 @@
1
1
  import { PrismaClient, EventBoosterType } from '@prisma/client';
2
+ import bcrypt from 'bcrypt';
2
3
 
3
4
  const prisma = new PrismaClient();
4
5
 
6
+ const BCRYPT_SALT_ROUNDS = 12;
7
+
5
8
  async function main() {
6
9
  console.log('🌱 Seeding database...');
7
10
 
8
- // Create sample players
11
+ // Create sample players (using upsert for idempotency)
9
12
  console.log('Creating players...');
10
- const player1 = await prisma.player.create({
11
- data: {
13
+ const playerData = [
14
+ {
12
15
  externalId: 'player-1',
13
16
  name: 'Alice Champion',
14
- email: 'alice@example.com',
15
17
  rating: 1850,
16
18
  ratingDeviation: 50,
17
19
  ranking: 5,
18
20
  isRated: true,
19
21
  eventCount: 25,
20
22
  },
21
- });
22
-
23
- const player2 = await prisma.player.create({
24
- data: {
23
+ {
25
24
  externalId: 'player-2',
26
25
  name: 'Bob Wizard',
27
- email: 'bob@example.com',
28
26
  rating: 1750,
29
27
  ratingDeviation: 60,
30
28
  ranking: 12,
31
29
  isRated: true,
32
30
  eventCount: 18,
33
31
  },
34
- });
35
-
36
- const player3 = await prisma.player.create({
37
- data: {
32
+ {
38
33
  externalId: 'player-3',
39
34
  name: 'Charlie Flipper',
40
- email: 'charlie@example.com',
41
35
  rating: 1650,
42
36
  ratingDeviation: 75,
43
37
  ranking: 28,
44
38
  isRated: true,
45
39
  eventCount: 12,
46
40
  },
47
- });
48
-
49
- const player4 = await prisma.player.create({
50
- data: {
41
+ {
51
42
  externalId: 'player-4',
52
43
  name: 'Diana Tilt',
53
- email: 'diana@example.com',
54
44
  rating: 1550,
55
45
  ratingDeviation: 100,
56
46
  ranking: 45,
57
47
  isRated: true,
58
48
  eventCount: 8,
59
49
  },
60
- });
61
-
62
- const player5 = await prisma.player.create({
63
- data: {
50
+ {
64
51
  externalId: 'player-5',
65
52
  name: 'Eve Plunger',
66
- email: 'eve@example.com',
67
53
  rating: 1300,
68
54
  ratingDeviation: 150,
69
55
  ranking: null,
70
56
  isRated: false,
71
57
  eventCount: 3,
72
58
  },
59
+ ];
60
+
61
+ const player1 = await prisma.player.upsert({
62
+ where: { externalId: 'player-1' },
63
+ update: playerData[0],
64
+ create: playerData[0],
65
+ });
66
+
67
+ const player2 = await prisma.player.upsert({
68
+ where: { externalId: 'player-2' },
69
+ update: playerData[1],
70
+ create: playerData[1],
71
+ });
72
+
73
+ const player3 = await prisma.player.upsert({
74
+ where: { externalId: 'player-3' },
75
+ update: playerData[2],
76
+ create: playerData[2],
77
+ });
78
+
79
+ const player4 = await prisma.player.upsert({
80
+ where: { externalId: 'player-4' },
81
+ update: playerData[3],
82
+ create: playerData[3],
83
+ });
84
+
85
+ const player5 = await prisma.player.upsert({
86
+ where: { externalId: 'player-5' },
87
+ update: playerData[4],
88
+ create: playerData[4],
73
89
  });
74
90
 
75
91
  console.log(`✓ Created ${await prisma.player.count()} players`);
76
92
 
77
- // Create sample tournaments
93
+ // Create test user for e2e tests (linked to Alice Champion)
94
+ console.log('Creating test user...');
95
+ const testPassword = 'TestPassword123!';
96
+ const passwordHash = await bcrypt.hash(testPassword, BCRYPT_SALT_ROUNDS);
97
+
98
+ await prisma.user.upsert({
99
+ where: { email: 'e2e-test@example.com' },
100
+ update: {
101
+ passwordHash,
102
+ role: 'USER',
103
+ playerId: player1.id,
104
+ },
105
+ create: {
106
+ email: 'e2e-test@example.com',
107
+ passwordHash,
108
+ role: 'USER',
109
+ playerId: player1.id,
110
+ },
111
+ });
112
+
113
+ console.log(`✓ Created test user (e2e-test@example.com / ${testPassword})`);
114
+
115
+ // Create sample tournaments (using upsert for idempotency)
78
116
  console.log('Creating tournaments...');
79
117
 
80
- // Major Championship Tournament
81
- const tournament1 = await prisma.tournament.create({
82
- data: {
83
- externalId: 'tournament-1',
84
- name: 'World Pinball Championship 2024',
85
- location: 'Las Vegas, NV',
86
- date: new Date('2024-03-15'),
87
- eventBooster: EventBoosterType.MAJOR,
88
- allowsOptOut: false,
89
- tgpConfig: {
90
- qualifying: {
91
- type: 'limited',
92
- meaningfulGames: 12,
93
- fourPlayerGroups: true,
94
- },
95
- finals: {
96
- formatType: 'match-play',
97
- meaningfulGames: 20,
98
- fourPlayerGroups: true,
99
- finalistCount: 16,
100
- },
101
- ballCountAdjustment: 1.0,
118
+ const tournament1Data = {
119
+ externalId: 'tournament-1',
120
+ name: 'World Pinball Championship 2024',
121
+ location: 'Las Vegas, NV',
122
+ date: new Date('2024-03-15'),
123
+ eventBooster: EventBoosterType.MAJOR,
124
+ allowsOptOut: false,
125
+ tgpConfig: {
126
+ qualifying: {
127
+ type: 'limited',
128
+ meaningfulGames: 12,
129
+ fourPlayerGroups: true,
102
130
  },
103
- baseValue: 32.0,
104
- tvaRating: 25.0,
105
- tvaRanking: 50.0,
106
- totalTVA: 75.0,
107
- tgp: 1.92,
108
- eventBoosterMultiplier: 2.0,
109
- firstPlaceValue: 411.84,
131
+ finals: {
132
+ formatType: 'match-play',
133
+ meaningfulGames: 20,
134
+ fourPlayerGroups: true,
135
+ finalistCount: 16,
136
+ },
137
+ ballCountAdjustment: 1.0,
110
138
  },
139
+ baseValue: 32.0,
140
+ tvaRating: 25.0,
141
+ tvaRanking: 50.0,
142
+ totalTVA: 75.0,
143
+ tgp: 1.92,
144
+ eventBoosterMultiplier: 2.0,
145
+ firstPlaceValue: 411.84,
146
+ };
147
+
148
+ const tournament1 = await prisma.tournament.upsert({
149
+ where: { externalId: 'tournament-1' },
150
+ update: tournament1Data,
151
+ create: tournament1Data,
111
152
  });
112
153
 
113
- // Certified Tournament
114
- const tournament2 = await prisma.tournament.create({
115
- data: {
116
- externalId: 'tournament-2',
117
- name: 'Spring Classics 2024',
118
- location: 'Portland, OR',
119
- date: new Date('2024-04-20'),
120
- eventBooster: EventBoosterType.CERTIFIED,
121
- allowsOptOut: true,
122
- tgpConfig: {
123
- qualifying: {
124
- type: 'limited',
125
- meaningfulGames: 7,
126
- },
127
- finals: {
128
- formatType: 'double-elimination',
129
- meaningfulGames: 15,
130
- fourPlayerGroups: false,
131
- finalistCount: 8,
132
- },
154
+ const tournament2Data = {
155
+ externalId: 'tournament-2',
156
+ name: 'Spring Classics 2024',
157
+ location: 'Portland, OR',
158
+ date: new Date('2024-04-20'),
159
+ eventBooster: EventBoosterType.CERTIFIED,
160
+ allowsOptOut: true,
161
+ tgpConfig: {
162
+ qualifying: {
163
+ type: 'limited',
164
+ meaningfulGames: 7,
165
+ },
166
+ finals: {
167
+ formatType: 'double-elimination',
168
+ meaningfulGames: 15,
169
+ fourPlayerGroups: false,
170
+ finalistCount: 8,
133
171
  },
134
- baseValue: 28.0,
135
- tvaRating: 18.5,
136
- tvaRanking: 32.0,
137
- totalTVA: 50.5,
138
- tgp: 0.88,
139
- eventBoosterMultiplier: 1.25,
140
- firstPlaceValue: 87.28,
141
172
  },
173
+ baseValue: 28.0,
174
+ tvaRating: 18.5,
175
+ tvaRanking: 32.0,
176
+ totalTVA: 50.5,
177
+ tgp: 0.88,
178
+ eventBoosterMultiplier: 1.25,
179
+ firstPlaceValue: 87.28,
180
+ };
181
+
182
+ const tournament2 = await prisma.tournament.upsert({
183
+ where: { externalId: 'tournament-2' },
184
+ update: tournament2Data,
185
+ create: tournament2Data,
142
186
  });
143
187
 
144
- // Local Tournament
145
- const tournament3 = await prisma.tournament.create({
146
- data: {
147
- externalId: 'tournament-3',
148
- name: 'Monthly League Finals',
149
- location: 'Seattle, WA',
150
- date: new Date('2024-05-10'),
151
- eventBooster: EventBoosterType.NONE,
152
- allowsOptOut: false,
153
- tgpConfig: {
154
- qualifying: {
155
- type: 'none',
156
- meaningfulGames: 0,
157
- },
158
- finals: {
159
- formatType: 'match-play',
160
- meaningfulGames: 10,
161
- fourPlayerGroups: true,
162
- finalistCount: 8,
163
- },
188
+ const tournament3Data = {
189
+ externalId: 'tournament-3',
190
+ name: 'Monthly League Finals',
191
+ location: 'Seattle, WA',
192
+ date: new Date('2024-05-10'),
193
+ eventBooster: EventBoosterType.NONE,
194
+ allowsOptOut: false,
195
+ tgpConfig: {
196
+ qualifying: {
197
+ type: 'none',
198
+ meaningfulGames: 0,
199
+ },
200
+ finals: {
201
+ formatType: 'match-play',
202
+ meaningfulGames: 10,
203
+ fourPlayerGroups: true,
204
+ finalistCount: 8,
164
205
  },
165
- baseValue: 15.0,
166
- tvaRating: 8.5,
167
- tvaRanking: 12.0,
168
- totalTVA: 20.5,
169
- tgp: 0.80,
170
- eventBoosterMultiplier: 1.0,
171
- firstPlaceValue: 28.4,
172
206
  },
207
+ baseValue: 15.0,
208
+ tvaRating: 8.5,
209
+ tvaRanking: 12.0,
210
+ totalTVA: 20.5,
211
+ tgp: 0.80,
212
+ eventBoosterMultiplier: 1.0,
213
+ firstPlaceValue: 28.4,
214
+ };
215
+
216
+ const tournament3 = await prisma.tournament.upsert({
217
+ where: { externalId: 'tournament-3' },
218
+ update: tournament3Data,
219
+ create: tournament3Data,
173
220
  });
174
221
 
175
222
  console.log(`✓ Created ${await prisma.tournament.count()} tournaments`);
176
223
 
177
- // Create tournament results
224
+ // Create tournament results (delete existing first for idempotency)
178
225
  console.log('Creating tournament results...');
179
226
 
227
+ // Delete existing results for seeded tournaments
228
+ await prisma.tournamentResult.deleteMany({
229
+ where: {
230
+ tournamentId: {
231
+ in: [tournament1.id, tournament2.id, tournament3.id],
232
+ },
233
+ },
234
+ });
235
+
180
236
  // World Championship results
181
237
  await prisma.tournamentResult.createMany({
182
238
  data: [
@@ -334,6 +390,7 @@ async function main() {
334
390
  console.log('');
335
391
  console.log('Summary:');
336
392
  console.log(` - ${await prisma.player.count()} players`);
393
+ console.log(` - ${await prisma.user.count()} users`);
337
394
  console.log(` - ${await prisma.tournament.count()} tournaments`);
338
395
  console.log(` - ${await prisma.tournamentResult.count()} tournament results`);
339
396
  }