@opprs/db-prisma 2.2.1 → 2.3.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/index.cjs CHANGED
@@ -39,6 +39,7 @@ __export(index_exports, {
39
39
  disconnect: () => disconnect,
40
40
  findPlayerByExternalId: () => findPlayerByExternalId,
41
41
  findPlayerById: () => findPlayerById,
42
+ findPlayerByPlayerNumber: () => findPlayerByPlayerNumber,
42
43
  findPlayerByUserEmail: () => findPlayerByUserEmail,
43
44
  findPlayers: () => findPlayers,
44
45
  findResultById: () => findResultById,
@@ -50,6 +51,7 @@ __export(index_exports, {
50
51
  findUserByEmail: () => findUserByEmail,
51
52
  findUserById: () => findUserById,
52
53
  findUsers: () => findUsers,
54
+ generateUniquePlayerNumber: () => generateUniquePlayerNumber,
53
55
  getMajorTournaments: () => getMajorTournaments,
54
56
  getPlayerResults: () => getPlayerResults,
55
57
  getPlayerStats: () => getPlayerStats,
@@ -66,6 +68,7 @@ __export(index_exports, {
66
68
  getTournamentsByDateRange: () => getTournamentsByDateRange,
67
69
  getUserByEmailWithPlayer: () => getUserByEmailWithPlayer,
68
70
  getUserWithPlayer: () => getUserWithPlayer,
71
+ isValidPlayerNumber: () => isValidPlayerNumber,
69
72
  linkPlayerToUser: () => linkPlayerToUser,
70
73
  prisma: () => prisma,
71
74
  recalculateTimeDecay: () => recalculateTimeDecay,
@@ -107,10 +110,40 @@ async function testConnection() {
107
110
  }
108
111
  }
109
112
 
113
+ // src/player-number.ts
114
+ var MIN_PLAYER_NUMBER = 1e4;
115
+ var MAX_PLAYER_NUMBER = 99999;
116
+ var MAX_RETRIES = 10;
117
+ function generateRandomPlayerNumber() {
118
+ return Math.floor(Math.random() * (MAX_PLAYER_NUMBER - MIN_PLAYER_NUMBER + 1)) + MIN_PLAYER_NUMBER;
119
+ }
120
+ async function generateUniquePlayerNumber() {
121
+ for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
122
+ const candidate = generateRandomPlayerNumber();
123
+ const existing = await prisma.player.findUnique({
124
+ where: { playerNumber: candidate },
125
+ select: { id: true }
126
+ });
127
+ if (!existing) {
128
+ return candidate;
129
+ }
130
+ }
131
+ throw new Error(
132
+ `Failed to generate unique player number after ${MAX_RETRIES} attempts. Consider increasing the number range or implementing a different allocation strategy.`
133
+ );
134
+ }
135
+ function isValidPlayerNumber(playerNumber) {
136
+ return Number.isInteger(playerNumber) && playerNumber >= MIN_PLAYER_NUMBER && playerNumber <= MAX_PLAYER_NUMBER;
137
+ }
138
+
110
139
  // src/players.ts
111
140
  async function createPlayer(data) {
141
+ const playerNumber = data.playerNumber ?? await generateUniquePlayerNumber();
112
142
  return prisma.player.create({
113
- data
143
+ data: {
144
+ ...data,
145
+ playerNumber
146
+ }
114
147
  });
115
148
  }
116
149
  async function findPlayerById(id, include) {
@@ -125,6 +158,12 @@ async function findPlayerByExternalId(externalId, include) {
125
158
  include
126
159
  });
127
160
  }
161
+ async function findPlayerByPlayerNumber(playerNumber, include) {
162
+ return prisma.player.findUnique({
163
+ where: { playerNumber },
164
+ include
165
+ });
166
+ }
128
167
  async function findPlayerByUserEmail(email, include) {
129
168
  const user = await prisma.user.findUnique({
130
169
  where: { email },
@@ -540,9 +579,11 @@ async function createUser(data) {
540
579
  }
541
580
  async function createUserWithPlayer(userData, playerData) {
542
581
  return prisma.$transaction(async (tx) => {
582
+ const playerNumber = await generateUniquePlayerNumber();
543
583
  const player = await tx.player.create({
544
584
  data: {
545
- name: playerData.name
585
+ name: playerData.name,
586
+ playerNumber
546
587
  }
547
588
  });
548
589
  const user = await tx.user.create({
@@ -554,6 +595,7 @@ async function createUserWithPlayer(userData, playerData) {
554
595
  player: {
555
596
  select: {
556
597
  id: true,
598
+ playerNumber: true,
557
599
  name: true,
558
600
  rating: true,
559
601
  ratingDeviation: true,
@@ -586,6 +628,7 @@ async function getUserWithPlayer(id) {
586
628
  player: {
587
629
  select: {
588
630
  id: true,
631
+ playerNumber: true,
589
632
  name: true,
590
633
  rating: true,
591
634
  ratingDeviation: true,
@@ -608,6 +651,7 @@ async function getUserByEmailWithPlayer(email) {
608
651
  player: {
609
652
  select: {
610
653
  id: true,
654
+ playerNumber: true,
611
655
  name: true,
612
656
  rating: true,
613
657
  ratingDeviation: true,
@@ -653,6 +697,7 @@ async function findUsers(params) {
653
697
  player: {
654
698
  select: {
655
699
  id: true,
700
+ playerNumber: true,
656
701
  name: true,
657
702
  rating: true,
658
703
  ratingDeviation: true,
@@ -684,6 +729,7 @@ async function linkPlayerToUser(userId, playerId) {
684
729
  player: {
685
730
  select: {
686
731
  id: true,
732
+ playerNumber: true,
687
733
  name: true,
688
734
  rating: true,
689
735
  ratingDeviation: true,
@@ -718,6 +764,7 @@ async function linkPlayerToUser(userId, playerId) {
718
764
  disconnect,
719
765
  findPlayerByExternalId,
720
766
  findPlayerById,
767
+ findPlayerByPlayerNumber,
721
768
  findPlayerByUserEmail,
722
769
  findPlayers,
723
770
  findResultById,
@@ -729,6 +776,7 @@ async function linkPlayerToUser(userId, playerId) {
729
776
  findUserByEmail,
730
777
  findUserById,
731
778
  findUsers,
779
+ generateUniquePlayerNumber,
732
780
  getMajorTournaments,
733
781
  getPlayerResults,
734
782
  getPlayerStats,
@@ -745,6 +793,7 @@ async function linkPlayerToUser(userId, playerId) {
745
793
  getTournamentsByDateRange,
746
794
  getUserByEmailWithPlayer,
747
795
  getUserWithPlayer,
796
+ isValidPlayerNumber,
748
797
  linkPlayerToUser,
749
798
  prisma,
750
799
  recalculateTimeDecay,
package/dist/index.d.cts CHANGED
@@ -25,6 +25,7 @@ declare function testConnection(): Promise<boolean>;
25
25
  */
26
26
  interface CreatePlayerInput {
27
27
  externalId?: string;
28
+ playerNumber?: number;
28
29
  name?: string;
29
30
  rating?: number;
30
31
  ratingDeviation?: number;
@@ -56,7 +57,7 @@ interface FindPlayersOptions {
56
57
  include?: Prisma.PlayerInclude;
57
58
  }
58
59
  /**
59
- * Creates a new player
60
+ * Creates a new player with auto-generated playerNumber
60
61
  */
61
62
  declare function createPlayer(data: CreatePlayerInput): Promise<Player>;
62
63
  /**
@@ -67,6 +68,10 @@ declare function findPlayerById(id: string, include?: Prisma.PlayerInclude): Pro
67
68
  * Finds a player by external ID
68
69
  */
69
70
  declare function findPlayerByExternalId(externalId: string, include?: Prisma.PlayerInclude): Promise<Player | null>;
71
+ /**
72
+ * Finds a player by player number
73
+ */
74
+ declare function findPlayerByPlayerNumber(playerNumber: number, include?: Prisma.PlayerInclude): Promise<Player | null>;
70
75
  /**
71
76
  * Finds a player through their linked User's email
72
77
  */
@@ -184,6 +189,7 @@ declare function getPlayerWithResults(id: string): Promise<{
184
189
  createdAt: Date;
185
190
  updatedAt: Date;
186
191
  externalId: string | null;
192
+ playerNumber: number;
187
193
  rating: number;
188
194
  ratingDeviation: number;
189
195
  ranking: number | null;
@@ -197,6 +203,18 @@ declare function getPlayerWithResults(id: string): Promise<{
197
203
  */
198
204
  declare function searchPlayers(query: string, limit?: number): Promise<Player[]>;
199
205
 
206
+ /**
207
+ * Generates a unique player number with collision handling.
208
+ * Retries up to MAX_RETRIES times if collision occurs.
209
+ *
210
+ * @throws Error if unable to generate unique number after max retries
211
+ */
212
+ declare function generateUniquePlayerNumber(): Promise<number>;
213
+ /**
214
+ * Validates that a player number is in the valid range
215
+ */
216
+ declare function isValidPlayerNumber(playerNumber: number): boolean;
217
+
200
218
  /**
201
219
  * Input for creating a new tournament
202
220
  */
@@ -299,6 +317,7 @@ declare function getTournamentWithResults(id: string): Promise<({
299
317
  createdAt: Date;
300
318
  updatedAt: Date;
301
319
  externalId: string | null;
320
+ playerNumber: number;
302
321
  rating: number;
303
322
  ratingDeviation: number;
304
323
  ranking: number | null;
@@ -358,6 +377,7 @@ declare function getTournamentStats(id: string): Promise<{
358
377
  createdAt: Date;
359
378
  updatedAt: Date;
360
379
  externalId: string | null;
380
+ playerNumber: number;
361
381
  rating: number;
362
382
  ratingDeviation: number;
363
383
  ranking: number | null;
@@ -571,6 +591,7 @@ interface UserWithPlayer {
571
591
  updatedAt: Date;
572
592
  player: {
573
593
  id: string;
594
+ playerNumber: number;
574
595
  name: string | null;
575
596
  rating: number;
576
597
  ratingDeviation: number;
@@ -700,4 +721,4 @@ interface ConnectionStatus {
700
721
  error?: string;
701
722
  }
702
723
 
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 };
724
+ 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, findPlayerByPlayerNumber, findPlayerByUserEmail, findPlayers, findResultById, findResultByPlayerAndTournament, findResults, findTournamentByExternalId, findTournamentById, findTournaments, findUserByEmail, findUserById, findUsers, generateUniquePlayerNumber, getMajorTournaments, getPlayerResults, getPlayerStats, getPlayerTopFinishes, getPlayerWithResults, getRatedPlayers, getRecentTournaments, getTopPlayersByRanking, getTopPlayersByRating, getTournamentResults, getTournamentStats, getTournamentWithResults, getTournamentsByBoosterType, getTournamentsByDateRange, getUserByEmailWithPlayer, getUserWithPlayer, isValidPlayerNumber, linkPlayerToUser, prisma, recalculateTimeDecay, searchPlayers, searchTournaments, testConnection, updatePlayer, updatePlayerRating, updateResult, updateResultPoints, updateTournament, updateUser, updateUserRefreshToken };
package/dist/index.d.ts CHANGED
@@ -25,6 +25,7 @@ declare function testConnection(): Promise<boolean>;
25
25
  */
26
26
  interface CreatePlayerInput {
27
27
  externalId?: string;
28
+ playerNumber?: number;
28
29
  name?: string;
29
30
  rating?: number;
30
31
  ratingDeviation?: number;
@@ -56,7 +57,7 @@ interface FindPlayersOptions {
56
57
  include?: Prisma.PlayerInclude;
57
58
  }
58
59
  /**
59
- * Creates a new player
60
+ * Creates a new player with auto-generated playerNumber
60
61
  */
61
62
  declare function createPlayer(data: CreatePlayerInput): Promise<Player>;
62
63
  /**
@@ -67,6 +68,10 @@ declare function findPlayerById(id: string, include?: Prisma.PlayerInclude): Pro
67
68
  * Finds a player by external ID
68
69
  */
69
70
  declare function findPlayerByExternalId(externalId: string, include?: Prisma.PlayerInclude): Promise<Player | null>;
71
+ /**
72
+ * Finds a player by player number
73
+ */
74
+ declare function findPlayerByPlayerNumber(playerNumber: number, include?: Prisma.PlayerInclude): Promise<Player | null>;
70
75
  /**
71
76
  * Finds a player through their linked User's email
72
77
  */
@@ -184,6 +189,7 @@ declare function getPlayerWithResults(id: string): Promise<{
184
189
  createdAt: Date;
185
190
  updatedAt: Date;
186
191
  externalId: string | null;
192
+ playerNumber: number;
187
193
  rating: number;
188
194
  ratingDeviation: number;
189
195
  ranking: number | null;
@@ -197,6 +203,18 @@ declare function getPlayerWithResults(id: string): Promise<{
197
203
  */
198
204
  declare function searchPlayers(query: string, limit?: number): Promise<Player[]>;
199
205
 
206
+ /**
207
+ * Generates a unique player number with collision handling.
208
+ * Retries up to MAX_RETRIES times if collision occurs.
209
+ *
210
+ * @throws Error if unable to generate unique number after max retries
211
+ */
212
+ declare function generateUniquePlayerNumber(): Promise<number>;
213
+ /**
214
+ * Validates that a player number is in the valid range
215
+ */
216
+ declare function isValidPlayerNumber(playerNumber: number): boolean;
217
+
200
218
  /**
201
219
  * Input for creating a new tournament
202
220
  */
@@ -299,6 +317,7 @@ declare function getTournamentWithResults(id: string): Promise<({
299
317
  createdAt: Date;
300
318
  updatedAt: Date;
301
319
  externalId: string | null;
320
+ playerNumber: number;
302
321
  rating: number;
303
322
  ratingDeviation: number;
304
323
  ranking: number | null;
@@ -358,6 +377,7 @@ declare function getTournamentStats(id: string): Promise<{
358
377
  createdAt: Date;
359
378
  updatedAt: Date;
360
379
  externalId: string | null;
380
+ playerNumber: number;
361
381
  rating: number;
362
382
  ratingDeviation: number;
363
383
  ranking: number | null;
@@ -571,6 +591,7 @@ interface UserWithPlayer {
571
591
  updatedAt: Date;
572
592
  player: {
573
593
  id: string;
594
+ playerNumber: number;
574
595
  name: string | null;
575
596
  rating: number;
576
597
  ratingDeviation: number;
@@ -700,4 +721,4 @@ interface ConnectionStatus {
700
721
  error?: string;
701
722
  }
702
723
 
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 };
724
+ 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, findPlayerByPlayerNumber, findPlayerByUserEmail, findPlayers, findResultById, findResultByPlayerAndTournament, findResults, findTournamentByExternalId, findTournamentById, findTournaments, findUserByEmail, findUserById, findUsers, generateUniquePlayerNumber, getMajorTournaments, getPlayerResults, getPlayerStats, getPlayerTopFinishes, getPlayerWithResults, getRatedPlayers, getRecentTournaments, getTopPlayersByRanking, getTopPlayersByRating, getTournamentResults, getTournamentStats, getTournamentWithResults, getTournamentsByBoosterType, getTournamentsByDateRange, getUserByEmailWithPlayer, getUserWithPlayer, isValidPlayerNumber, linkPlayerToUser, prisma, recalculateTimeDecay, searchPlayers, searchTournaments, testConnection, updatePlayer, updatePlayerRating, updateResult, updateResultPoints, updateTournament, updateUser, updateUserRefreshToken };
package/dist/index.js CHANGED
@@ -23,10 +23,40 @@ async function testConnection() {
23
23
  }
24
24
  }
25
25
 
26
+ // src/player-number.ts
27
+ var MIN_PLAYER_NUMBER = 1e4;
28
+ var MAX_PLAYER_NUMBER = 99999;
29
+ var MAX_RETRIES = 10;
30
+ function generateRandomPlayerNumber() {
31
+ return Math.floor(Math.random() * (MAX_PLAYER_NUMBER - MIN_PLAYER_NUMBER + 1)) + MIN_PLAYER_NUMBER;
32
+ }
33
+ async function generateUniquePlayerNumber() {
34
+ for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
35
+ const candidate = generateRandomPlayerNumber();
36
+ const existing = await prisma.player.findUnique({
37
+ where: { playerNumber: candidate },
38
+ select: { id: true }
39
+ });
40
+ if (!existing) {
41
+ return candidate;
42
+ }
43
+ }
44
+ throw new Error(
45
+ `Failed to generate unique player number after ${MAX_RETRIES} attempts. Consider increasing the number range or implementing a different allocation strategy.`
46
+ );
47
+ }
48
+ function isValidPlayerNumber(playerNumber) {
49
+ return Number.isInteger(playerNumber) && playerNumber >= MIN_PLAYER_NUMBER && playerNumber <= MAX_PLAYER_NUMBER;
50
+ }
51
+
26
52
  // src/players.ts
27
53
  async function createPlayer(data) {
54
+ const playerNumber = data.playerNumber ?? await generateUniquePlayerNumber();
28
55
  return prisma.player.create({
29
- data
56
+ data: {
57
+ ...data,
58
+ playerNumber
59
+ }
30
60
  });
31
61
  }
32
62
  async function findPlayerById(id, include) {
@@ -41,6 +71,12 @@ async function findPlayerByExternalId(externalId, include) {
41
71
  include
42
72
  });
43
73
  }
74
+ async function findPlayerByPlayerNumber(playerNumber, include) {
75
+ return prisma.player.findUnique({
76
+ where: { playerNumber },
77
+ include
78
+ });
79
+ }
44
80
  async function findPlayerByUserEmail(email, include) {
45
81
  const user = await prisma.user.findUnique({
46
82
  where: { email },
@@ -456,9 +492,11 @@ async function createUser(data) {
456
492
  }
457
493
  async function createUserWithPlayer(userData, playerData) {
458
494
  return prisma.$transaction(async (tx) => {
495
+ const playerNumber = await generateUniquePlayerNumber();
459
496
  const player = await tx.player.create({
460
497
  data: {
461
- name: playerData.name
498
+ name: playerData.name,
499
+ playerNumber
462
500
  }
463
501
  });
464
502
  const user = await tx.user.create({
@@ -470,6 +508,7 @@ async function createUserWithPlayer(userData, playerData) {
470
508
  player: {
471
509
  select: {
472
510
  id: true,
511
+ playerNumber: true,
473
512
  name: true,
474
513
  rating: true,
475
514
  ratingDeviation: true,
@@ -502,6 +541,7 @@ async function getUserWithPlayer(id) {
502
541
  player: {
503
542
  select: {
504
543
  id: true,
544
+ playerNumber: true,
505
545
  name: true,
506
546
  rating: true,
507
547
  ratingDeviation: true,
@@ -524,6 +564,7 @@ async function getUserByEmailWithPlayer(email) {
524
564
  player: {
525
565
  select: {
526
566
  id: true,
567
+ playerNumber: true,
527
568
  name: true,
528
569
  rating: true,
529
570
  ratingDeviation: true,
@@ -569,6 +610,7 @@ async function findUsers(params) {
569
610
  player: {
570
611
  select: {
571
612
  id: true,
613
+ playerNumber: true,
572
614
  name: true,
573
615
  rating: true,
574
616
  ratingDeviation: true,
@@ -600,6 +642,7 @@ async function linkPlayerToUser(userId, playerId) {
600
642
  player: {
601
643
  select: {
602
644
  id: true,
645
+ playerNumber: true,
603
646
  name: true,
604
647
  rating: true,
605
648
  ratingDeviation: true,
@@ -633,6 +676,7 @@ export {
633
676
  disconnect,
634
677
  findPlayerByExternalId,
635
678
  findPlayerById,
679
+ findPlayerByPlayerNumber,
636
680
  findPlayerByUserEmail,
637
681
  findPlayers,
638
682
  findResultById,
@@ -644,6 +688,7 @@ export {
644
688
  findUserByEmail,
645
689
  findUserById,
646
690
  findUsers,
691
+ generateUniquePlayerNumber,
647
692
  getMajorTournaments,
648
693
  getPlayerResults,
649
694
  getPlayerStats,
@@ -660,6 +705,7 @@ export {
660
705
  getTournamentsByDateRange,
661
706
  getUserByEmailWithPlayer,
662
707
  getUserWithPlayer,
708
+ isValidPlayerNumber,
663
709
  linkPlayerToUser,
664
710
  prisma,
665
711
  recalculateTimeDecay,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opprs/db-prisma",
3
- "version": "2.2.1",
3
+ "version": "2.3.0",
4
4
  "description": "Database backend for OPPR (Open Pinball Player Ranking System) using Prisma and PostgreSQL",
5
5
  "keywords": [
6
6
  "oppr",
@@ -0,0 +1,23 @@
1
+ -- AlterTable
2
+ -- First add playerNumber as nullable, populate it, then make it required
3
+ ALTER TABLE "Player" ADD COLUMN "playerNumber" INTEGER;
4
+
5
+ -- Populate existing players with unique 5-digit player numbers
6
+ -- Use a sequence starting from 10000, incrementing for each existing row
7
+ WITH numbered_players AS (
8
+ SELECT id, ROW_NUMBER() OVER (ORDER BY "createdAt") + 9999 AS num
9
+ FROM "Player"
10
+ )
11
+ UPDATE "Player"
12
+ SET "playerNumber" = numbered_players.num::INTEGER
13
+ FROM numbered_players
14
+ WHERE "Player".id = numbered_players.id;
15
+
16
+ -- Make the column required and add unique constraint
17
+ ALTER TABLE "Player" ALTER COLUMN "playerNumber" SET NOT NULL;
18
+
19
+ -- CreateIndex
20
+ CREATE UNIQUE INDEX "Player_playerNumber_key" ON "Player"("playerNumber");
21
+
22
+ -- CreateIndex
23
+ CREATE INDEX "Player_playerNumber_idx" ON "Player"("playerNumber");
@@ -18,6 +18,7 @@ model Player {
18
18
 
19
19
  // Player identification
20
20
  externalId String? @unique // External ID from OPPR or other systems
21
+ playerNumber Int @unique // 5-digit unique identifier (10000-99999)
21
22
  name String?
22
23
 
23
24
  // OPPR Rating fields
@@ -36,6 +37,7 @@ model Player {
36
37
  user User?
37
38
 
38
39
  @@index([externalId])
40
+ @@index([playerNumber])
39
41
  @@index([rating])
40
42
  @@index([ranking])
41
43
  }
package/prisma/seed.ts CHANGED
@@ -13,6 +13,7 @@ async function main() {
13
13
  const playerData = [
14
14
  {
15
15
  externalId: 'player-1',
16
+ playerNumber: 10001,
16
17
  name: 'Alice Champion',
17
18
  rating: 1850,
18
19
  ratingDeviation: 50,
@@ -22,6 +23,7 @@ async function main() {
22
23
  },
23
24
  {
24
25
  externalId: 'player-2',
26
+ playerNumber: 10002,
25
27
  name: 'Bob Wizard',
26
28
  rating: 1750,
27
29
  ratingDeviation: 60,
@@ -31,6 +33,7 @@ async function main() {
31
33
  },
32
34
  {
33
35
  externalId: 'player-3',
36
+ playerNumber: 10003,
34
37
  name: 'Charlie Flipper',
35
38
  rating: 1650,
36
39
  ratingDeviation: 75,
@@ -40,6 +43,7 @@ async function main() {
40
43
  },
41
44
  {
42
45
  externalId: 'player-4',
46
+ playerNumber: 10004,
43
47
  name: 'Diana Tilt',
44
48
  rating: 1550,
45
49
  ratingDeviation: 100,
@@ -49,6 +53,7 @@ async function main() {
49
53
  },
50
54
  {
51
55
  externalId: 'player-5',
56
+ playerNumber: 10005,
52
57
  name: 'Eve Plunger',
53
58
  rating: 1300,
54
59
  ratingDeviation: 150,
@@ -90,27 +95,59 @@ async function main() {
90
95
 
91
96
  console.log(`✓ Created ${await prisma.player.count()} players`);
92
97
 
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);
98
+ // Seed users from environment variables (development only)
99
+ const seedAdminEmail = process.env.SEED_ADMIN_EMAIL;
100
+ const seedAdminPassword = process.env.SEED_ADMIN_PASSWORD;
101
+ const seedTestEmail = process.env.SEED_TEST_EMAIL;
102
+ const seedTestPassword = process.env.SEED_TEST_PASSWORD;
103
+
104
+ // Create admin user if credentials provided
105
+ if (seedAdminEmail && seedAdminPassword) {
106
+ console.log('Creating admin user...');
107
+ const adminPasswordHash = await bcrypt.hash(seedAdminPassword, BCRYPT_SALT_ROUNDS);
108
+ await prisma.user.upsert({
109
+ where: { email: seedAdminEmail },
110
+ update: { passwordHash: adminPasswordHash, role: 'ADMIN' },
111
+ create: { email: seedAdminEmail, passwordHash: adminPasswordHash, role: 'ADMIN' },
112
+ });
113
+ console.log(`✓ Created admin user (${seedAdminEmail})`);
114
+ } else {
115
+ console.log('⏭ Skipping admin user (SEED_ADMIN_EMAIL/SEED_ADMIN_PASSWORD not set)');
116
+ }
117
+
118
+ // Create test user if credentials provided (linked to Alice Champion)
119
+ if (seedTestEmail && seedTestPassword) {
120
+ console.log('Creating test user...');
121
+ const testPasswordHash = await bcrypt.hash(seedTestPassword, BCRYPT_SALT_ROUNDS);
122
+ await prisma.user.upsert({
123
+ where: { email: seedTestEmail },
124
+ update: { passwordHash: testPasswordHash, role: 'USER', playerId: player1.id },
125
+ create: { email: seedTestEmail, passwordHash: testPasswordHash, role: 'USER', playerId: player1.id },
126
+ });
127
+ console.log(`✓ Created test user (${seedTestEmail}) linked to ${player1.name}`);
128
+ } else {
129
+ console.log('⏭ Skipping test user (SEED_TEST_EMAIL/SEED_TEST_PASSWORD not set)');
130
+ }
131
+
132
+ // Create admin user
133
+ console.log('Creating admin user...');
134
+ const adminPassword = 'AdminPassword123!';
135
+ const adminPasswordHash = await bcrypt.hash(adminPassword, BCRYPT_SALT_ROUNDS);
97
136
 
98
137
  await prisma.user.upsert({
99
- where: { email: 'e2e-test@example.com' },
138
+ where: { email: 'admin@example.com' },
100
139
  update: {
101
- passwordHash,
102
- role: 'USER',
103
- playerId: player1.id,
140
+ passwordHash: adminPasswordHash,
141
+ role: 'ADMIN',
104
142
  },
105
143
  create: {
106
- email: 'e2e-test@example.com',
107
- passwordHash,
108
- role: 'USER',
109
- playerId: player1.id,
144
+ email: 'admin@example.com',
145
+ passwordHash: adminPasswordHash,
146
+ role: 'ADMIN',
110
147
  },
111
148
  });
112
149
 
113
- console.log(`✓ Created test user (e2e-test@example.com / ${testPassword})`);
150
+ console.log(`✓ Created admin user (admin@example.com / ${adminPassword})`);
114
151
 
115
152
  // Create sample tournaments (using upsert for idempotency)
116
153
  console.log('Creating tournaments...');