@battle-racing/br-common-lib 4.4.0 → 4.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.
Files changed (97) hide show
  1. package/dist/domains/command/Command.schema.d.ts +76 -10
  2. package/dist/domains/command/Command.schema.js +17 -3
  3. package/dist/domains/command/Command.types.d.ts +3 -1
  4. package/dist/domains/damage/Damage.schema.d.ts +2 -2
  5. package/dist/domains/flow/Flow.schema.d.ts +39 -6
  6. package/dist/domains/hardware/KartHardwareState.schema.js +8 -8
  7. package/dist/domains/hardware/KartHardwareState.types.d.ts +3 -0
  8. package/dist/domains/hardware/KartHardwareState.types.js +2 -0
  9. package/dist/domains/hardware/ModuleStatus.schema.d.ts +28 -0
  10. package/dist/domains/hardware/ModuleStatus.schema.js +4 -0
  11. package/dist/domains/hardware/ballSensor/BallSensor.const.d.ts +5 -0
  12. package/dist/domains/hardware/ballSensor/BallSensor.const.js +6 -1
  13. package/dist/domains/hardware/ballSensor/BallSensor.schema.d.ts +5 -0
  14. package/dist/domains/hardware/ballSensor/BallSensor.schema.js +2 -1
  15. package/dist/domains/hardware/ballSensor/BallSensor.types.d.ts +2 -1
  16. package/dist/domains/hardware/flag/Flag.const.d.ts +7 -0
  17. package/dist/domains/hardware/flag/Flag.const.js +8 -1
  18. package/dist/domains/hardware/flag/Flag.schema.d.ts +11 -4
  19. package/dist/domains/hardware/flag/Flag.schema.js +5 -2
  20. package/dist/domains/hardware/flag/Flag.types.d.ts +2 -1
  21. package/dist/domains/hardware/index.d.ts +1 -0
  22. package/dist/domains/hardware/index.js +2 -1
  23. package/dist/domains/hardware/lights/Lights.const.d.ts +4 -0
  24. package/dist/domains/hardware/lights/Lights.const.js +5 -1
  25. package/dist/domains/hardware/lights/Lights.schema.d.ts +16 -0
  26. package/dist/domains/hardware/lights/Lights.schema.js +5 -1
  27. package/dist/domains/hardware/lights/Lights.types.d.ts +3 -1
  28. package/dist/domains/hardware/rgb/RGB.const.d.ts +4 -0
  29. package/dist/domains/hardware/rgb/RGB.const.js +5 -1
  30. package/dist/domains/hardware/rgb/RGB.schema.d.ts +4 -0
  31. package/dist/domains/hardware/rgb/RGB.schema.js +2 -1
  32. package/dist/domains/hardware/rgb/RGB.types.d.ts +2 -1
  33. package/dist/domains/hardware/sound/Sound.const.d.ts +3 -0
  34. package/dist/domains/hardware/sound/Sound.const.js +4 -1
  35. package/dist/domains/hardware/sound/Sound.schema.d.ts +3 -0
  36. package/dist/domains/hardware/sound/Sound.schema.js +2 -1
  37. package/dist/domains/hardware/sound/Sound.types.d.ts +2 -1
  38. package/dist/domains/hardware/steering-wheel/SteeringWheel.const.d.ts +4 -0
  39. package/dist/domains/hardware/steering-wheel/SteeringWheel.const.js +7 -0
  40. package/dist/domains/hardware/steering-wheel/SteeringWheel.schema.d.ts +4 -0
  41. package/dist/domains/hardware/steering-wheel/SteeringWheel.schema.js +3 -1
  42. package/dist/domains/hardware/steering-wheel/SteeringWheel.types.d.ts +2 -1
  43. package/dist/domains/player/Player.schema.d.ts +2 -4
  44. package/dist/domains/player/Player.schema.js +3 -6
  45. package/dist/domains/powerUp/PowerUp.schema.d.ts +117 -18
  46. package/dist/domains/racingTeam/RacingTeam.schema.d.ts +77 -3
  47. package/dist/domains/racingTeam/RacingTeam.schema.js +18 -4
  48. package/dist/domains/racingTeam/RacingTeam.types.d.ts +4 -1
  49. package/dist/domains/session/Session.schema.d.ts +5 -0
  50. package/dist/domains/session/Session.schema.js +4 -1
  51. package/dist/domains/socket/index.d.ts +1 -0
  52. package/dist/domains/socket/index.js +1 -0
  53. package/dist/domains/socket/screen/SocketScreenServerToClientEventsParams.d.ts +14 -0
  54. package/dist/domains/socket/screen/SocketScreenServerToClientEventsParams.js +2 -0
  55. package/dist/domains/socket/screen/index.d.ts +1 -0
  56. package/dist/domains/socket/screen/index.js +17 -0
  57. package/dist/domains/ticketing/Ticketing.schema.d.ts +4 -0
  58. package/dist/domains/ticketing/Ticketing.schema.js +2 -0
  59. package/dist/domains/user/User.schema.d.ts +65 -6
  60. package/dist/domains/user/User.schema.js +52 -11
  61. package/dist/domains/user/User.types.d.ts +7 -1
  62. package/docs/TICKETING_AND_PURCHASE_FLOW.md +110 -0
  63. package/package.json +1 -1
  64. package/src/domains/command/Command.schema.ts +30 -12
  65. package/src/domains/command/Command.types.ts +4 -0
  66. package/src/domains/hardware/KartHardwareState.schema.ts +4 -4
  67. package/src/domains/hardware/KartHardwareState.types.ts +4 -0
  68. package/src/domains/hardware/ModuleStatus.schema.ts +4 -0
  69. package/src/domains/hardware/ballSensor/BallSensor.const.ts +6 -0
  70. package/src/domains/hardware/ballSensor/BallSensor.schema.ts +3 -1
  71. package/src/domains/hardware/ballSensor/BallSensor.types.ts +2 -0
  72. package/src/domains/hardware/flag/Flag.const.ts +8 -0
  73. package/src/domains/hardware/flag/Flag.schema.ts +6 -2
  74. package/src/domains/hardware/flag/Flag.types.ts +2 -0
  75. package/src/domains/hardware/index.ts +2 -1
  76. package/src/domains/hardware/lights/Lights.const.ts +5 -0
  77. package/src/domains/hardware/lights/Lights.schema.ts +7 -1
  78. package/src/domains/hardware/lights/Lights.types.ts +9 -1
  79. package/src/domains/hardware/rgb/RGB.const.ts +5 -0
  80. package/src/domains/hardware/rgb/RGB.schema.ts +3 -1
  81. package/src/domains/hardware/rgb/RGB.types.ts +2 -0
  82. package/src/domains/hardware/sound/Sound.const.ts +4 -0
  83. package/src/domains/hardware/sound/Sound.schema.ts +3 -1
  84. package/src/domains/hardware/sound/Sound.types.ts +3 -1
  85. package/src/domains/hardware/steering-wheel/SteeringWheel.const.ts +4 -0
  86. package/src/domains/hardware/steering-wheel/SteeringWheel.schema.ts +3 -0
  87. package/src/domains/hardware/steering-wheel/SteeringWheel.types.ts +3 -1
  88. package/src/domains/player/Player.schema.ts +2 -6
  89. package/src/domains/racingTeam/RacingTeam.schema.ts +20 -3
  90. package/src/domains/racingTeam/RacingTeam.types.ts +7 -0
  91. package/src/domains/session/Session.schema.ts +4 -0
  92. package/src/domains/socket/index.ts +1 -0
  93. package/src/domains/socket/screen/SocketScreenServerToClientEventsParams.ts +19 -0
  94. package/src/domains/socket/screen/index.ts +1 -0
  95. package/src/domains/ticketing/Ticketing.schema.ts +2 -0
  96. package/src/domains/user/User.schema.ts +60 -13
  97. package/src/domains/user/User.types.ts +17 -1
@@ -118,6 +118,8 @@ export declare const gameSessionWithPlayersSchema: z.ZodObject<{
118
118
  }, z.core.$strip>>;
119
119
  turns: z.ZodOptional<z.ZodArray<z.ZodObject<{
120
120
  id: z.ZodString;
121
+ turnNumber: z.ZodNumber;
122
+ groupName: z.ZodOptional<z.ZodString>;
121
123
  purchaseId: z.ZodString;
122
124
  sessionId: z.ZodOptional<z.ZodString>;
123
125
  leaderPlayerId: z.ZodString;
@@ -166,6 +168,9 @@ export declare const joinSessionSchema: z.ZodObject<{
166
168
  nickname: z.ZodString;
167
169
  birthDate: z.ZodCoercedDate<unknown>;
168
170
  }, z.core.$strip>;
171
+ export declare const addExistingPlayersSchema: z.ZodObject<{
172
+ playerIds: z.ZodArray<z.ZodUUID>;
173
+ }, z.core.$strip>;
169
174
  export declare const updateSessionStatusSchema: z.ZodObject<{
170
175
  status: z.ZodEnum<{
171
176
  readonly REGISTRATION: "REGISTRATION";
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.bulkCreateGameTurnSchema = exports.assignSessionTicketsInputSchema = exports.createGameTurnInputSchema = exports.updateSessionStatusSchema = exports.joinSessionSchema = exports.createSessionInputSchema = exports.gameSessionWithPlayersSchema = exports.gameSessionSchema = exports.sessionTicketSchema = exports.sessionStatusSchema = void 0;
3
+ exports.bulkCreateGameTurnSchema = exports.assignSessionTicketsInputSchema = exports.createGameTurnInputSchema = exports.updateSessionStatusSchema = exports.addExistingPlayersSchema = exports.joinSessionSchema = exports.createSessionInputSchema = exports.gameSessionWithPlayersSchema = exports.gameSessionSchema = exports.sessionTicketSchema = exports.sessionStatusSchema = void 0;
4
4
  const zod_1 = require("zod");
5
5
  const Session_const_1 = require("./Session.const");
6
6
  const Game_schema_1 = require("../game/Game.schema");
@@ -36,6 +36,9 @@ exports.joinSessionSchema = zod_1.z.object({
36
36
  nickname: zod_1.z.string().min(2),
37
37
  birthDate: zod_1.z.coerce.date(),
38
38
  });
39
+ exports.addExistingPlayersSchema = zod_1.z.object({
40
+ playerIds: zod_1.z.array(zod_1.z.uuidv4()).min(1),
41
+ });
39
42
  exports.updateSessionStatusSchema = zod_1.z.object({
40
43
  status: exports.sessionStatusSchema,
41
44
  });
@@ -5,3 +5,4 @@ export * from './SocketHardwareModulesEventsParams';
5
5
  export * from './SocketHardwareOnDataEventsParams';
6
6
  export * from './SocketPowerUpEventsParams';
7
7
  export * from './utils';
8
+ export * from './screen';
@@ -21,3 +21,4 @@ __exportStar(require("./SocketHardwareModulesEventsParams"), exports);
21
21
  __exportStar(require("./SocketHardwareOnDataEventsParams"), exports);
22
22
  __exportStar(require("./SocketPowerUpEventsParams"), exports);
23
23
  __exportStar(require("./utils"), exports);
24
+ __exportStar(require("./screen"), exports);
@@ -0,0 +1,14 @@
1
+ import type { ScreenCountdownParams, ScreenDebugViewInput, ScreenFinishViewInput, ScreenHomeViewInput, ScreenUpdateGameViewInput } from '../../hardware';
2
+ /**
3
+ * Socket events from Raspberry Pi to Screen
4
+ */
5
+ export interface SocketScreenServerToClientEventsParams {
6
+ 'view:home': ScreenHomeViewInput;
7
+ 'view:countdown': ScreenCountdownParams;
8
+ 'game:sync_state': ScreenUpdateGameViewInput;
9
+ 'view:summary': ScreenFinishViewInput & {
10
+ state: 'Victory' | 'Defeat';
11
+ };
12
+ 'view:debug': ScreenDebugViewInput;
13
+ 'view:instruction_video': Record<string, never>;
14
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1 @@
1
+ export * from './SocketScreenServerToClientEventsParams';
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./SocketScreenServerToClientEventsParams"), exports);
@@ -136,6 +136,8 @@ export declare const gameTurnTaskSchema: z.ZodObject<{
136
136
  }, z.core.$strip>;
137
137
  export declare const gameTurnSchema: z.ZodObject<{
138
138
  id: z.ZodString;
139
+ turnNumber: z.ZodNumber;
140
+ groupName: z.ZodOptional<z.ZodString>;
139
141
  purchaseId: z.ZodString;
140
142
  sessionId: z.ZodOptional<z.ZodString>;
141
143
  leaderPlayerId: z.ZodString;
@@ -170,6 +172,8 @@ export declare const gameTurnSchema: z.ZodObject<{
170
172
  }, z.core.$strip>;
171
173
  export declare const gameTurnQueueItemSchema: z.ZodObject<{
172
174
  id: z.ZodString;
175
+ turnNumber: z.ZodNumber;
176
+ groupName: z.ZodOptional<z.ZodString>;
173
177
  purchaseId: z.ZodString;
174
178
  sessionId: z.ZodOptional<z.ZodString>;
175
179
  leaderPlayerId: z.ZodString;
@@ -63,6 +63,8 @@ exports.gameTurnTaskSchema = zod_1.z.object({
63
63
  });
64
64
  exports.gameTurnSchema = zod_1.z.object({
65
65
  id: zod_1.z.string(),
66
+ turnNumber: zod_1.z.number().int().positive(),
67
+ groupName: zod_1.z.string().optional(),
66
68
  purchaseId: zod_1.z.string(),
67
69
  sessionId: zod_1.z.string().optional(),
68
70
  leaderPlayerId: zod_1.z.string(),
@@ -4,12 +4,13 @@ export declare const userTypeSchema: z.ZodEnum<{
4
4
  readonly PLAYER: "PLAYER";
5
5
  }>;
6
6
  export declare const PHONE_REGEX: RegExp;
7
+ export declare const emailOrPhoneSchema: z.ZodString;
7
8
  export declare const userSchema: z.ZodObject<{
8
9
  id: z.ZodUUID;
9
10
  name: z.ZodString;
10
11
  lastName: z.ZodString;
11
- email: z.ZodOptional<z.ZodEmail>;
12
- phoneNumber: z.ZodOptional<z.ZodString>;
12
+ email: z.ZodNullable<z.ZodOptional<z.ZodEmail>>;
13
+ phoneNumber: z.ZodNullable<z.ZodOptional<z.ZodString>>;
13
14
  role: z.ZodEnum<{
14
15
  readonly SUPER_ADMIN: "SUPER_ADMIN";
15
16
  readonly OPERATOR: "OPERATOR";
@@ -27,12 +28,54 @@ export declare const userSchema: z.ZodObject<{
27
28
  createdAt: z.ZodDate;
28
29
  lastLoginAt: z.ZodOptional<z.ZodDate>;
29
30
  }, z.core.$strip>;
31
+ /**
32
+ * When a user registers, we don't have all the information, so we use this schema
33
+ */
34
+ export declare const initialUserSchema: z.ZodObject<{
35
+ type: z.ZodEnum<{
36
+ readonly STAFF: "STAFF";
37
+ readonly PLAYER: "PLAYER";
38
+ }>;
39
+ id: z.ZodUUID;
40
+ createdAt: z.ZodDate;
41
+ email: z.ZodNullable<z.ZodOptional<z.ZodEmail>>;
42
+ phoneNumber: z.ZodNullable<z.ZodOptional<z.ZodString>>;
43
+ isEmailVerified: z.ZodDefault<z.ZodBoolean>;
44
+ isPhoneVerified: z.ZodDefault<z.ZodBoolean>;
45
+ }, z.core.$strip>;
46
+ /**
47
+ * The schema for the user returned on login
48
+ */
49
+ export declare const authUserSchema: z.ZodObject<{
50
+ id: z.ZodUUID;
51
+ name: z.ZodString;
52
+ lastName: z.ZodString;
53
+ email: z.ZodNullable<z.ZodOptional<z.ZodEmail>>;
54
+ phoneNumber: z.ZodNullable<z.ZodOptional<z.ZodString>>;
55
+ role: z.ZodEnum<{
56
+ readonly SUPER_ADMIN: "SUPER_ADMIN";
57
+ readonly OPERATOR: "OPERATOR";
58
+ readonly PLAYER: "PLAYER";
59
+ readonly CASHIER: "CASHIER";
60
+ }>;
61
+ type: z.ZodEnum<{
62
+ readonly STAFF: "STAFF";
63
+ readonly PLAYER: "PLAYER";
64
+ }>;
65
+ isActive: z.ZodDefault<z.ZodBoolean>;
66
+ isEmailVerified: z.ZodDefault<z.ZodBoolean>;
67
+ isPhoneVerified: z.ZodDefault<z.ZodBoolean>;
68
+ defaultPlayerId: z.ZodOptional<z.ZodUUID>;
69
+ createdAt: z.ZodDate;
70
+ lastLoginAt: z.ZodOptional<z.ZodDate>;
71
+ authToken: z.ZodString;
72
+ }, z.core.$strip>;
30
73
  export declare const userWithPasswordSchema: z.ZodIntersection<z.ZodObject<{
31
74
  id: z.ZodUUID;
32
75
  name: z.ZodString;
33
76
  lastName: z.ZodString;
34
- email: z.ZodOptional<z.ZodEmail>;
35
- phoneNumber: z.ZodOptional<z.ZodString>;
77
+ email: z.ZodNullable<z.ZodOptional<z.ZodEmail>>;
78
+ phoneNumber: z.ZodNullable<z.ZodOptional<z.ZodString>>;
36
79
  role: z.ZodEnum<{
37
80
  readonly SUPER_ADMIN: "SUPER_ADMIN";
38
81
  readonly OPERATOR: "OPERATOR";
@@ -52,11 +95,27 @@ export declare const userWithPasswordSchema: z.ZodIntersection<z.ZodObject<{
52
95
  }, z.core.$strip>, z.ZodObject<{
53
96
  password: z.ZodString;
54
97
  }, z.core.$strip>>;
98
+ export declare const userInitialRegistrationSchema: z.ZodObject<{
99
+ emailOrPhone: z.ZodString;
100
+ password: z.ZodString;
101
+ }, z.core.$strip>;
102
+ export declare const userVerifySchema: z.ZodObject<{
103
+ emailOrPhone: z.ZodString;
104
+ code: z.ZodString;
105
+ }, z.core.$strip>;
106
+ export declare const userCompleteProfileSchema: z.ZodObject<{
107
+ name: z.ZodString;
108
+ lastName: z.ZodOptional<z.ZodString>;
109
+ nickname: z.ZodString;
110
+ birthDate: z.ZodCoercedDate<unknown>;
111
+ waiverSignature: z.ZodBoolean;
112
+ teamName: z.ZodOptional<z.ZodString>;
113
+ }, z.core.$strip>;
55
114
  export declare const userRegistrationInputSchema: z.ZodObject<{
56
115
  name: z.ZodString;
116
+ email: z.ZodNullable<z.ZodOptional<z.ZodEmail>>;
57
117
  lastName: z.ZodString;
58
- email: z.ZodOptional<z.ZodEmail>;
59
- phoneNumber: z.ZodOptional<z.ZodString>;
118
+ phoneNumber: z.ZodNullable<z.ZodOptional<z.ZodString>>;
60
119
  password: z.ZodString;
61
120
  nickname: z.ZodString;
62
121
  birthDate: z.ZodCoercedDate<unknown>;
@@ -1,18 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.userLoginInputSchema = exports.userRegistrationInputSchema = exports.userWithPasswordSchema = exports.userSchema = exports.PHONE_REGEX = exports.userTypeSchema = void 0;
3
+ exports.userLoginInputSchema = exports.userRegistrationInputSchema = exports.userCompleteProfileSchema = exports.userVerifySchema = exports.userInitialRegistrationSchema = exports.userWithPasswordSchema = exports.authUserSchema = exports.initialUserSchema = exports.userSchema = exports.emailOrPhoneSchema = exports.PHONE_REGEX = exports.userTypeSchema = void 0;
4
4
  const zod_1 = require("zod");
5
5
  const role_1 = require("../role");
6
6
  const User_const_1 = require("./User.const");
7
7
  exports.userTypeSchema = zod_1.z.enum(User_const_1.USER_TYPE);
8
8
  exports.PHONE_REGEX = /^([+]?[\\s0-9]+)?(\\d{3}|[(]?[0-9]+[)])?([-]?[\\s]?[0-9])+$/;
9
9
  const MIN_PASSWORD_LENGTH = 6;
10
+ exports.emailOrPhoneSchema = zod_1.z
11
+ .string()
12
+ .min(1, 'Email or phone is required')
13
+ .refine((val) => {
14
+ const isEmail = zod_1.z.email().safeParse(val).success;
15
+ const isPhone = exports.PHONE_REGEX.test(val);
16
+ return isEmail || isPhone;
17
+ }, { message: 'Must be a valid email or phone number' });
10
18
  const baseUserObj = zod_1.z.object({
11
19
  id: zod_1.z.uuidv4(),
12
20
  name: zod_1.z.string(),
13
21
  lastName: zod_1.z.string(),
14
- email: zod_1.z.email().toLowerCase().optional(),
15
- phoneNumber: zod_1.z.string().regex(exports.PHONE_REGEX, 'Invalid phone number format').optional(),
22
+ email: zod_1.z.email().toLowerCase().optional().nullable(),
23
+ phoneNumber: zod_1.z.string().regex(exports.PHONE_REGEX, 'Invalid phone number format').optional().nullable(),
16
24
  // --- ACCESS CONTROL ---
17
25
  role: role_1.userRoleSchema,
18
26
  type: exports.userTypeSchema,
@@ -28,9 +36,49 @@ exports.userSchema = baseUserObj.refine((data) => data.email || data.phoneNumber
28
36
  message: 'User must have either an email or a phone number',
29
37
  path: ['email', 'phoneNumber'],
30
38
  });
39
+ /**
40
+ * When a user registers, we don't have all the information, so we use this schema
41
+ */
42
+ exports.initialUserSchema = baseUserObj.pick({
43
+ id: true,
44
+ email: true,
45
+ phoneNumber: true,
46
+ isEmailVerified: true,
47
+ isPhoneVerified: true,
48
+ type: true,
49
+ createdAt: true,
50
+ });
51
+ /**
52
+ * The schema for the user returned on login
53
+ */
54
+ exports.authUserSchema = baseUserObj.extend({
55
+ authToken: zod_1.z.string(),
56
+ });
31
57
  exports.userWithPasswordSchema = exports.userSchema.and(zod_1.z.object({
32
58
  password: zod_1.z.string().min(MIN_PASSWORD_LENGTH),
33
59
  }));
60
+ exports.userInitialRegistrationSchema = zod_1.z.object({
61
+ emailOrPhone: exports.emailOrPhoneSchema,
62
+ password: zod_1.z.string().min(MIN_PASSWORD_LENGTH, `Password must be at least ${MIN_PASSWORD_LENGTH} characters`),
63
+ });
64
+ exports.userVerifySchema = zod_1.z.object({
65
+ emailOrPhone: exports.emailOrPhoneSchema,
66
+ code: zod_1.z.string().min(6, 'Code must be at least 6 characters').max(10, 'Code is too long'),
67
+ });
68
+ exports.userCompleteProfileSchema = baseUserObj
69
+ .pick({
70
+ name: true,
71
+ })
72
+ .extend({
73
+ lastName: zod_1.z.string().optional(),
74
+ nickname: zod_1.z.string().min(2),
75
+ birthDate: zod_1.z.coerce.date(),
76
+ waiverSignature: zod_1.z.boolean().refine((val) => val === true, {
77
+ message: 'You must accept the waiver signature',
78
+ }),
79
+ teamName: zod_1.z.string().optional(),
80
+ });
81
+ // Deprecated in favor of multi-step registration
34
82
  exports.userRegistrationInputSchema = baseUserObj
35
83
  .pick({
36
84
  name: true,
@@ -48,13 +96,6 @@ exports.userRegistrationInputSchema = baseUserObj
48
96
  path: ['email', 'phoneNumber'],
49
97
  });
50
98
  exports.userLoginInputSchema = zod_1.z.object({
51
- emailOrPhone: zod_1.z
52
- .string()
53
- .min(1, 'Email or phone is required')
54
- .refine((val) => {
55
- const isEmail = zod_1.z.email().safeParse(val).success;
56
- const isPhone = exports.PHONE_REGEX.test(val);
57
- return isEmail || isPhone;
58
- }, { message: 'Must be a valid email or phone number' }),
99
+ emailOrPhone: exports.emailOrPhoneSchema,
59
100
  password: zod_1.z.string().min(MIN_PASSWORD_LENGTH, `Password must be at least ${MIN_PASSWORD_LENGTH} characters`),
60
101
  });
@@ -1,5 +1,11 @@
1
1
  import { z } from 'zod';
2
- import { userSchema, userTypeSchema, userWithPasswordSchema } from './User.schema';
2
+ import { authUserSchema, initialUserSchema, userCompleteProfileSchema, userInitialRegistrationSchema, userLoginInputSchema, userSchema, userTypeSchema, userVerifySchema, userWithPasswordSchema } from './User.schema';
3
3
  export type UserType = z.infer<typeof userTypeSchema>;
4
4
  export type User = z.infer<typeof userSchema>;
5
5
  export type UserWithPassword = z.infer<typeof userWithPasswordSchema>;
6
+ export type AuthUser = z.infer<typeof authUserSchema>;
7
+ export type InitialUser = z.infer<typeof initialUserSchema>;
8
+ export type UserInitialRegistration = z.infer<typeof userInitialRegistrationSchema>;
9
+ export type UserVerify = z.infer<typeof userVerifySchema>;
10
+ export type UserCompleteProfile = z.infer<typeof userCompleteProfileSchema>;
11
+ export type UserLogin = z.infer<typeof userLoginInputSchema>;
@@ -0,0 +1,110 @@
1
+ # Flujo de Compra de Tickets y Separación User/Player
2
+
3
+ Este documento explica de forma detallada cómo está estructurado el flujo de compra de tickets en **Battle Racing**, cómo se separa la entidad que paga (User) de la entidad que corre (Player), qué hemos implementado hasta ahora y qué nos hace falta por implementar.
4
+
5
+ ---
6
+
7
+ ## 👥 1. Separación: Users vs. Players
8
+
9
+ Para asegurar "cero fricción" en el entorno físico de la pista, hemos separado estrictamente el concepto de la cuenta (quien paga) del corredor (quien se sube al Kart).
10
+
11
+ ### `User` (La Cuenta / El Pagador)
12
+
13
+ - **Propósito:** La persona física que se registra, gestiona un método de pago y posee el historial transaccional de compras.
14
+ - **Autenticación:** Usa `email` o `phoneNumber` (sin `username`), contraseña y roles (`SUPER_ADMIN`, `CASHIER`, `PLAYER`).
15
+ - **Relaciones:**
16
+ - Tiene un `defaultPlayerId` que apunta a su propio perfil de corredor.
17
+ - Puede ser tutor y ser "dueño" (`ownerUserId`) de múltiples `Players` menores de edad (`MINOR`).
18
+
19
+ ### `Player` (El Corredor / Perfil Gamificado)
20
+
21
+ - **Propósito:** Es la entidad gamificada que se asigna físicamente a un Kart y cuyo nombre sale en las pantallas.
22
+ - **Identificador (`nickname`):** No son únicos en la base de datos. Varios corredores pueden llamarse "Maverick" al mismo tiempo para no generar errores al registrarse rápidamente en el POS.
23
+ - **Waivers:** Contiene el estado de la firma del documento de exención de responsabilidad (`waiver`).
24
+ - **Relaciones:** Si es un invitado anónimo (`isGuest`), no está atado a ningún `User`. Si es un menor, su firma (`waiver`) es implícitamente otorgada por el `User` tutor.
25
+
26
+ ---
27
+
28
+ ## 🎟️ 2. Flujo de Compras (Purchases y Tickets)
29
+
30
+ La adquisición de carreras sigue un ciclo de vida de tres grandes capas:
31
+
32
+ ### Capa 1: La Compra (`Purchase`)
33
+
34
+ 1. **Creación:** Se crea un `Purchase` agrupando varios ítems de compra (ej. 3 tickets de tipo `RACE`).
35
+ - Se crea en el endpoint: `POST /ticketing/purchases`
36
+ - Si el cliente está logueado en la Web App, se asocia su `buyerUserId`.
37
+ - Si la compra es 100% anónima en la sucursal (POS), `buyerUserId` es `null` y se usan `guestName` y `guestContact`.
38
+ 2. **Pago:** El pago se confirma asíncronamente vía Webhooks (`POST /ticketing/webhook/payment`) o manualmente en caja registradora (`POST /ticketing/purchases/:id/manual-payment`).
39
+ 3. **Generación de GameTickets:** Una vez confirmada y pagada la compra, esta se _desfragmenta_ y la base de datos genera los `GameTicket` individuales correspondientes (con estado `CREATED`).
40
+
41
+ ### Capa 2: La Sesión (`Game Session`)
42
+
43
+ Dado que una persona puede comprar 10 tickets, pero a la pista se van a subir sus 5 amigos 2 veces, se usa una **Session**.
44
+
45
+ 1. **Creación:** Se agrupan tickets recién comprados bajo un código alfanumérico corto (ej. `A1B2C3`).
46
+ 2. **Unión:** Los amigos escanean un QR o dictan el código para unirse a la sesión (`POST /sessions/:code/join`). Aquí sus `Players` ingresan a la sesión.
47
+ 3. **Asignación:** Se asigna cada `GameTicket` disponible en la sesión a un `playerId` específico (`POST /sessions/:code/tickets/assign`).
48
+
49
+ ### Capa 3: El Turno Físico en Pista (`Game Turn`)
50
+
51
+ 1. **Cola de Carrera:** Cuando el grupo de la sesión está listo (o el operador del POS organiza la carrera), se crea un `GameTurn`.
52
+ - Endpoint: `POST /game-turns` o `POST /game-turns/bulk`.
53
+ 2. **Vinculación Final:** El `GameTurn` toma los `GameTickets` asignados y los `playerIds` correspondientes, bloqueándolos para esta carrera específica.
54
+ 3. **Ejecución y Hardware:** El hardware vincula a los jugadores con sus respectivos Karts y avanza las tareas de la carrera (preparación, en sus marcas, carrera, meta).
55
+ - El progreso se guarda mediante: `POST /game-turns/:id/tasks/:taskId/complete`
56
+
57
+ ---
58
+
59
+ ## ✅ 3. ¿Qué tenemos implementado hasta ahora?
60
+
61
+ ### En `br-common-lib` (Schemas)
62
+
63
+ - Zod schemas altamente detallados para `User`, `Player`, `Session`, `Ticketing` (Purchase y Tickets) y `Payment`.
64
+ - Control absoluto y fuente de verdad centralizada para la validación de estos modelos.
65
+
66
+ ### En `game-manager-backend` (REST API)
67
+
68
+ - **Módulo `ticketing`:**
69
+ - Creación de compras.
70
+ - Procesamiento manual de pagos (para POS/Cashiers).
71
+ - Cancelación de compras.
72
+ - Recepción de webhooks de pasarelas de pago.
73
+ - Listado mis compras y resumen de mis tickets (`my-purchases`, `my-tickets`).
74
+ - **Módulo `session`:**
75
+ - Obtener sesión por código corto.
76
+ - Unirse (y unirse en bulk) a una sesión.
77
+ - Asignar tickets a jugadores dentro de la sesión.
78
+ - **Módulo `game-turn`:**
79
+ - Listado general de turnos (`GET /game-turns`).
80
+ - Creación de turnos y turnos en bloque.
81
+ - Avance de etapas (tasks) dentro del turno (`completeTask`).
82
+ - Capacidad para avanzar (`/advance`) o posponer (`/postpone`) el turno en la fila.
83
+ - **Módulo `player` & `user`:**
84
+ - Estructuras en DB/Repositorios y controladores listos para manipular estos perfiles.
85
+
86
+ ---
87
+
88
+ ## 🚧 4. ¿Qué nos hace falta implementar? (Faltantes / TODOs)
89
+
90
+ 1. **Autenticación Severa (Guards):**
91
+ - Varios enpoints importantes como la creación de compras y modificación de sesiones todavía usan `request.user?.id` asumiendo que el usuario está inyectado, pero faltan **Guards** (e.g., JWT Guards) sólidos y globales para denegar acceso a usuarios no autorizados de forma segura o están marcados como `@Public()`.
92
+
93
+ 2. **Validación de Identidad de Email/Teléfono (`User`):**
94
+ - Aunque la base de datos soporta `isEmailVerified` e `isPhoneVerified`, el mecanismo de envío de OTP / SMS / Enlace Mágico para confirmar la propiedad aún no está programado.
95
+
96
+ 3. **Verificación de Seguridad de Asignación de Tickets:**
97
+ - Comprobar que un User no pueda asignar tickets de un Purchase que NO le pertenece (cuando la abstracción de sesión permita saltos de seguridad).
98
+ - Actualmente falta un "Ownership Check" robusto en varios GET de compras y lectura de turnos.
99
+
100
+ 4. **Actualización Automática de Estadísticas del Player:**
101
+ - Actualmente la base de datos de Players tiene sub-schemas listos para Stats (`totalRaces`, `bestTimeMs`, `xp`). Sin embargo, una vez que el `GameTurn` se completa en la pista, falta implementar el webhook o servicio que **calcule los resultados finales y actualice estos stats** dentro de cada `Player` que participó.
102
+
103
+ 5. **Flujos de Error de Pagos y Reembolsos Avanzados:**
104
+ - Manejo exhaustivo de reembolsos desde Stripe hacia el sistema para revocar `GameTickets` si una persona pide chargeback manual.
105
+
106
+ 6. **Integración Completa con POS (Front-end UI):**
107
+ - Falta validar en el frontend de POS que estos flujos asíncronos (como el crear compra en cash, asignar players de forma anónima) no se bloqueen por dependencias de cuentas "Guest". Asegurarse de que el frontend POS pueda transitar estas 3 capas sin recargar.
108
+
109
+ 7. **Asociación Dinámica de Hardware (Karts ↔ Players) en el GameTurn:**
110
+ - Hay que madurar la lógica donde un `playerId` que entra a un `GameTurn` es asignado al IP / Socket / ID Hardware específico del Kart en el que se acaba de sentar.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@battle-racing/br-common-lib",
3
- "version": "4.4.0",
3
+ "version": "4.6.0",
4
4
  "description": "Common library for all Battle Racing Repositorios",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -10,6 +10,8 @@ import {
10
10
  screenCountdownParamsSchema,
11
11
  screenFinishViewInputSchema,
12
12
  screenUpdateGameViewInputSchema,
13
+ showLightsColorParamsSchema,
14
+ showLightsEffectParamsSchema,
13
15
  speedChangeOptionsSchema,
14
16
  speedConfigurationSchema,
15
17
  starLaserConfigureModuleOptionsSchema,
@@ -152,16 +154,32 @@ export const steeringWheelModuleCommandSchema = z.union([
152
154
  z.object({ module: z.literal('steeringWheel'), action: z.literal('turnLedOff') }),
153
155
  ]);
154
156
 
155
- // --- All Commands Union ---
156
- export const allCommandsConfigSchema = z.union([
157
- ballSensorDiskThrowerModuleCommandSchema,
158
- diskSensorBallShooterModuleCommandSchema,
159
- flagModuleCommandSchema,
160
- rgbModuleCommandSchema,
161
- speedModuleCommandSchema,
162
- leftStarBackLaserModuleCommandSchema,
163
- rightStarFrontLaserModuleCommandSchema,
164
- screenModuleCommandSchema,
165
- steeringWheelModuleCommandSchema,
166
- soundModuleCommandSchema,
157
+ // --- Emergency ---
158
+ export const emergencyModuleCommandSchema = z.union([
159
+ z.object({ module: z.literal('emergency'), action: z.literal('activate') }),
160
+ z.object({ module: z.literal('emergency'), action: z.literal('deactivate') }),
161
+ ]);
162
+
163
+ // --- Lights ---
164
+ export const lightsModuleCommandSchema = z.union([
165
+ z.object({ module: z.literal('lights'), action: z.literal('showColor'), params: showLightsColorParamsSchema }),
166
+ z.object({ module: z.literal('lights'), action: z.literal('showEffect'), params: showLightsEffectParamsSchema }),
167
167
  ]);
168
+
169
+ // --- All Commands Union ---
170
+ export const allCommandsConfigSchema = z
171
+ .union([
172
+ ballSensorDiskThrowerModuleCommandSchema,
173
+ diskSensorBallShooterModuleCommandSchema,
174
+ flagModuleCommandSchema,
175
+ rgbModuleCommandSchema,
176
+ speedModuleCommandSchema,
177
+ leftStarBackLaserModuleCommandSchema,
178
+ rightStarFrontLaserModuleCommandSchema,
179
+ screenModuleCommandSchema,
180
+ steeringWheelModuleCommandSchema,
181
+ soundModuleCommandSchema,
182
+ emergencyModuleCommandSchema,
183
+ lightsModuleCommandSchema,
184
+ ])
185
+ .and(z.object({ params: z.unknown().optional() }));
@@ -4,8 +4,10 @@ import {
4
4
  ballSensorDiskThrowerModuleCommandSchema,
5
5
  changeSpeedActionCommandOptionsSchema,
6
6
  diskSensorBallShooterModuleCommandSchema,
7
+ emergencyModuleCommandSchema,
7
8
  flagModuleCommandSchema,
8
9
  leftStarBackLaserModuleCommandSchema,
10
+ lightsModuleCommandSchema,
9
11
  playSoundActionCommandOptionsSchema,
10
12
  rgbModuleCommandSchema,
11
13
  rightStarFrontLaserModuleCommandSchema,
@@ -28,5 +30,7 @@ export type RightStarFrontLaserModuleCommands = z.infer<typeof rightStarFrontLas
28
30
  export type ScreenModuleCommands = z.infer<typeof screenModuleCommandSchema>;
29
31
  export type SteeringWheelModuleCommands = z.infer<typeof steeringWheelModuleCommandSchema>;
30
32
  export type SoundModuleCommands = z.infer<typeof soundModuleCommandSchema>;
33
+ export type EmergencyModuleCommands = z.infer<typeof emergencyModuleCommandSchema>;
34
+ export type LightsModuleCommands = z.infer<typeof lightsModuleCommandSchema>;
31
35
 
32
36
  export type AllCommandsConfig = z.infer<typeof allCommandsConfigSchema>;
@@ -1,9 +1,9 @@
1
1
  import { z } from 'zod';
2
2
  import { checkpointTypeSchema } from '../checkpoint/Checkpoint.schema';
3
- import { flagStatusSchema } from './flag/Flag.schema';
4
- import { rgbStatusSchema } from './rgb/RGB.schema';
5
- import { screenStatusSchema } from './screen/Screen.schema';
6
- import { speedStatusSchema } from './speed/Speed.schema';
3
+ import { flagStatusSchema } from './flag';
4
+ import { rgbStatusSchema } from './rgb';
5
+ import { screenStatusSchema } from './screen';
6
+ import { speedStatusSchema } from './speed';
7
7
 
8
8
  export const kartHardwareStateSchema = z.object({
9
9
  speed: z
@@ -0,0 +1,4 @@
1
+ import { z } from 'zod';
2
+ import { kartHardwareStateSchema } from './KartHardwareState.schema';
3
+
4
+ export type KartHardwareState = z.infer<typeof kartHardwareStateSchema>;
@@ -3,9 +3,11 @@ import { ballSensorDiskThrowerModuleStatusSchema } from './ballSensor';
3
3
  import { diskSensorBallShooterModuleStatusSchema } from './diskSensor';
4
4
  import { emergencyModuleStatusSchema } from './emergency';
5
5
  import { flagModuleStatusSchema } from './flag';
6
+ import { lightsModuleStatusSchema } from './lights';
6
7
  import { MODULE_NAME } from './ModuleName.const';
7
8
  import { rgbModuleStatusSchema } from './rgb';
8
9
  import { speedModuleStatusSchema } from './speed';
10
+ import { screenModuleStatusSchema } from './screen';
9
11
  import { starLaserModuleStatusSchema } from './star-laser';
10
12
  import { steeringWheelModuleStatusSchema } from './steering-wheel';
11
13
 
@@ -20,4 +22,6 @@ export const moduleStatusSchema = z.union([
20
22
  starLaserModuleStatusSchema,
21
23
  steeringWheelModuleStatusSchema,
22
24
  emergencyModuleStatusSchema,
25
+ screenModuleStatusSchema,
26
+ lightsModuleStatusSchema,
23
27
  ]);
@@ -11,3 +11,9 @@ export const BALL_SENSOR_STATUS = {
11
11
  SENSOR_HIT: 'Sensor Hit',
12
12
  UNKNOWN: 'Unknown',
13
13
  } as const;
14
+
15
+ export const DISK_THROWER_COMMAND = {
16
+ ACTIVATE_THROWER: 'Activate Thrower',
17
+ DEACTIVATE_THROWER: 'Deactivate Thrower',
18
+ THROW_DISK: 'Throw Disk',
19
+ } as const;
@@ -1,8 +1,10 @@
1
1
  import { z } from 'zod';
2
- import { BALL_SENSOR_COMMAND, BALL_SENSOR_STATUS } from './BallSensor.const';
2
+ import { BALL_SENSOR_COMMAND, BALL_SENSOR_STATUS, DISK_THROWER_COMMAND } from './BallSensor.const';
3
3
 
4
4
  export const ballSensorCommandSchema = z.enum(BALL_SENSOR_COMMAND);
5
5
 
6
+ export const diskThrowerCommandSchema = z.enum(DISK_THROWER_COMMAND);
7
+
6
8
  export const ballSensorStatusSchema = z.enum(BALL_SENSOR_STATUS);
7
9
 
8
10
  export const configureBallSensorDiskThrowerModuleOptionsSchema = z.object({
@@ -4,9 +4,11 @@ import {
4
4
  ballSensorDiskThrowerModuleStatusSchema,
5
5
  ballSensorStatusSchema,
6
6
  configureBallSensorDiskThrowerModuleOptionsSchema,
7
+ diskThrowerCommandSchema,
7
8
  } from './BallSensor.schema';
8
9
 
9
10
  export type BallSensorCommand = z.infer<typeof ballSensorCommandSchema>;
11
+ export type DiskThrowerCommand = z.infer<typeof diskThrowerCommandSchema>;
10
12
  export type BallSensorStatus = z.infer<typeof ballSensorStatusSchema>;
11
13
  export type ConfigureBallSensorDiskThrowerModuleOptions = z.infer<
12
14
  typeof configureBallSensorDiskThrowerModuleOptionsSchema
@@ -82,3 +82,11 @@ export const FLAG_MEDIA_NAME = {
82
82
  STATIC_BEATING_HEART: 'static_beating_heart',
83
83
  STATIC_SPEED_PAD: 'static_speed_pad',
84
84
  } as const;
85
+
86
+ export const FLAG_ACTION_COMMAND = {
87
+ ACTIVATE: 'Activate',
88
+ DEACTIVATE: 'Deactivate',
89
+ SHOW_MEDIA_INDEFINITELY: 'Show Media Indefinitely',
90
+ SHOW_MEDIA_TEMPORARILY: 'Show Media Temporarily',
91
+ CONFIGURE: 'Configure',
92
+ } as const;
@@ -1,11 +1,15 @@
1
1
  import { z } from 'zod';
2
2
  import { colorNameSchema } from '../../color';
3
3
  import { checkpointTypeSchema } from '../../checkpoint';
4
- import { FLAG_MEDIA_NAME, FLAG_STATUS } from './Flag.const';
4
+ import { FLAG_MEDIA_NAME, FLAG_STATUS, FLAG_ACTION_COMMAND } from './Flag.const';
5
5
 
6
6
  export const flagStatusSchema = z.enum(FLAG_STATUS);
7
7
 
8
- export const flagMediaNameSchema = z.enum(FLAG_MEDIA_NAME);
8
+ export const flagActionCommandSchema = z.enum(FLAG_ACTION_COMMAND);
9
+
10
+ export const flagMediaNameSchema = z
11
+ .union([z.number().min(0).max(255), z.enum(FLAG_MEDIA_NAME)])
12
+ .describe('The Media source could be the name of the icon on Flag Module or a number to render');
9
13
 
10
14
  export const flagModuleStatusSchema = z.object({
11
15
  flagStatus: flagStatusSchema,
@@ -5,9 +5,11 @@ import {
5
5
  flagModuleStatusSchema,
6
6
  flagShowMediaOptionsSchema,
7
7
  flagStatusSchema,
8
+ flagActionCommandSchema,
8
9
  } from './Flag.schema';
9
10
 
10
11
  export type FlagStatus = z.infer<typeof flagStatusSchema>;
12
+ export type FlagActionCommand = z.infer<typeof flagActionCommandSchema>;
11
13
  export type FlagMediaName = z.infer<typeof flagMediaNameSchema>;
12
14
  export type FlagModuleStatus = z.infer<typeof flagModuleStatusSchema>;
13
15
  export type FlagConfigurationOptions = z.infer<typeof flagConfigurationOptionsSchema>;