volleyballsimtypes 0.0.477 → 0.0.479

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 (63) hide show
  1. package/dist/cjs/src/service/index.d.ts +1 -0
  2. package/dist/cjs/src/service/index.js +1 -0
  3. package/dist/cjs/src/service/notification/catalog/en.json +41 -0
  4. package/dist/cjs/src/service/notification/catalog/es.json +41 -0
  5. package/dist/cjs/src/service/notification/catalog/fr.json +41 -0
  6. package/dist/cjs/src/service/notification/catalog/index.d.ts +3 -0
  7. package/dist/cjs/src/service/notification/catalog/index.js +22 -0
  8. package/dist/cjs/src/service/notification/catalog/it.json +41 -0
  9. package/dist/cjs/src/service/notification/catalog/pl.json +41 -0
  10. package/dist/cjs/src/service/notification/catalog/pt-BR.json +41 -0
  11. package/dist/cjs/src/service/notification/catalog/tr.json +41 -0
  12. package/dist/cjs/src/service/notification/index.d.ts +5 -0
  13. package/dist/cjs/src/service/notification/index.js +23 -0
  14. package/dist/cjs/src/service/notification/notification-type.d.ts +6 -0
  15. package/dist/cjs/src/service/notification/notification-type.js +30 -0
  16. package/dist/cjs/src/service/notification/notification.test.d.ts +1 -0
  17. package/dist/cjs/src/service/notification/notification.test.js +128 -0
  18. package/dist/cjs/src/service/notification/payloads.d.ts +34 -0
  19. package/dist/cjs/src/service/notification/payloads.js +5 -0
  20. package/dist/cjs/src/service/notification/registry.d.ts +13 -0
  21. package/dist/cjs/src/service/notification/registry.js +112 -0
  22. package/dist/cjs/src/service/notification/render.d.ts +10 -0
  23. package/dist/cjs/src/service/notification/render.js +64 -0
  24. package/dist/cjs/src/service/team/pinch-condition.d.ts +7 -0
  25. package/dist/cjs/src/service/team/pinch-condition.js +10 -1
  26. package/dist/cjs/src/service/team/schemas/designated-sub.z.d.ts +22 -1
  27. package/dist/cjs/src/service/team/schemas/designated-sub.z.js +5 -3
  28. package/dist/cjs/src/service/team/schemas/designated-sub.z.test.js +10 -0
  29. package/dist/cjs/src/service/team/schemas/libero-sub.z.d.ts +9 -0
  30. package/dist/cjs/src/service/team/schemas/tactics.z.d.ts +18 -0
  31. package/dist/cjs/src/service/team/schemas/team.z.d.ts +18 -0
  32. package/dist/esm/src/service/index.d.ts +1 -0
  33. package/dist/esm/src/service/index.js +1 -0
  34. package/dist/esm/src/service/notification/catalog/en.json +41 -0
  35. package/dist/esm/src/service/notification/catalog/es.json +41 -0
  36. package/dist/esm/src/service/notification/catalog/fr.json +41 -0
  37. package/dist/esm/src/service/notification/catalog/index.d.ts +3 -0
  38. package/dist/esm/src/service/notification/catalog/index.js +16 -0
  39. package/dist/esm/src/service/notification/catalog/it.json +41 -0
  40. package/dist/esm/src/service/notification/catalog/pl.json +41 -0
  41. package/dist/esm/src/service/notification/catalog/pt-BR.json +41 -0
  42. package/dist/esm/src/service/notification/catalog/tr.json +41 -0
  43. package/dist/esm/src/service/notification/index.d.ts +5 -0
  44. package/dist/esm/src/service/notification/index.js +5 -0
  45. package/dist/esm/src/service/notification/notification-type.d.ts +6 -0
  46. package/dist/esm/src/service/notification/notification-type.js +26 -0
  47. package/dist/esm/src/service/notification/notification.test.d.ts +1 -0
  48. package/dist/esm/src/service/notification/notification.test.js +126 -0
  49. package/dist/esm/src/service/notification/payloads.d.ts +34 -0
  50. package/dist/esm/src/service/notification/payloads.js +4 -0
  51. package/dist/esm/src/service/notification/registry.d.ts +13 -0
  52. package/dist/esm/src/service/notification/registry.js +109 -0
  53. package/dist/esm/src/service/notification/render.d.ts +10 -0
  54. package/dist/esm/src/service/notification/render.js +58 -0
  55. package/dist/esm/src/service/team/pinch-condition.d.ts +7 -0
  56. package/dist/esm/src/service/team/pinch-condition.js +9 -0
  57. package/dist/esm/src/service/team/schemas/designated-sub.z.d.ts +22 -1
  58. package/dist/esm/src/service/team/schemas/designated-sub.z.js +6 -4
  59. package/dist/esm/src/service/team/schemas/designated-sub.z.test.js +11 -1
  60. package/dist/esm/src/service/team/schemas/libero-sub.z.d.ts +9 -0
  61. package/dist/esm/src/service/team/schemas/tactics.z.d.ts +18 -0
  62. package/dist/esm/src/service/team/schemas/team.z.d.ts +18 -0
  63. package/package.json +6 -1
@@ -46,6 +46,7 @@ export declare const TeamInputSchema: z.ZodObject<{
46
46
  type: z.ZodLiteral<import("..").PinchConditionType.SCORE_DIFF>;
47
47
  direction: z.ZodEnum<typeof import("..").ScoreDirection>;
48
48
  points: z.ZodNumber;
49
+ comparison: z.ZodOptional<z.ZodEnum<typeof import("..").Comparison>>;
49
50
  }, z.core.$strip>, z.ZodObject<{
50
51
  type: z.ZodLiteral<import("..").PinchConditionType.AFTER_ROTATIONS>;
51
52
  rotations: z.ZodNumber;
@@ -55,9 +56,11 @@ export declare const TeamInputSchema: z.ZodObject<{
55
56
  }, z.core.$strip>, z.ZodObject<{
56
57
  type: z.ZodLiteral<import("..").PinchConditionType.MIN_OWN_SCORE>;
57
58
  score: z.ZodNumber;
59
+ comparison: z.ZodOptional<z.ZodEnum<typeof import("..").Comparison>>;
58
60
  }, z.core.$strip>, z.ZodObject<{
59
61
  type: z.ZodLiteral<import("..").PinchConditionType.MIN_OPPONENT_SCORE>;
60
62
  score: z.ZodNumber;
63
+ comparison: z.ZodOptional<z.ZodEnum<typeof import("..").Comparison>>;
61
64
  }, z.core.$strip>, z.ZodObject<{
62
65
  type: z.ZodLiteral<import("..").PinchConditionType.PHASE>;
63
66
  phase: z.ZodEnum<typeof import("..").MatchPhase>;
@@ -81,6 +84,7 @@ export declare const TeamInputSchema: z.ZodObject<{
81
84
  type: z.ZodLiteral<import("..").PinchConditionType.SCORE_DIFF>;
82
85
  direction: z.ZodEnum<typeof import("..").ScoreDirection>;
83
86
  points: z.ZodNumber;
87
+ comparison: z.ZodOptional<z.ZodEnum<typeof import("..").Comparison>>;
84
88
  }, z.core.$strip>, z.ZodObject<{
85
89
  type: z.ZodLiteral<import("..").PinchConditionType.AFTER_ROTATIONS>;
86
90
  rotations: z.ZodNumber;
@@ -90,9 +94,11 @@ export declare const TeamInputSchema: z.ZodObject<{
90
94
  }, z.core.$strip>, z.ZodObject<{
91
95
  type: z.ZodLiteral<import("..").PinchConditionType.MIN_OWN_SCORE>;
92
96
  score: z.ZodNumber;
97
+ comparison: z.ZodOptional<z.ZodEnum<typeof import("..").Comparison>>;
93
98
  }, z.core.$strip>, z.ZodObject<{
94
99
  type: z.ZodLiteral<import("..").PinchConditionType.MIN_OPPONENT_SCORE>;
95
100
  score: z.ZodNumber;
101
+ comparison: z.ZodOptional<z.ZodEnum<typeof import("..").Comparison>>;
96
102
  }, z.core.$strip>, z.ZodObject<{
97
103
  type: z.ZodLiteral<import("..").PinchConditionType.PHASE>;
98
104
  phase: z.ZodEnum<typeof import("..").MatchPhase>;
@@ -120,6 +126,7 @@ export declare const TeamInputSchema: z.ZodObject<{
120
126
  type: z.ZodLiteral<import("..").PinchConditionType.SCORE_DIFF>;
121
127
  direction: z.ZodEnum<typeof import("..").ScoreDirection>;
122
128
  points: z.ZodNumber;
129
+ comparison: z.ZodOptional<z.ZodEnum<typeof import("..").Comparison>>;
123
130
  }, z.core.$strip>, z.ZodObject<{
124
131
  type: z.ZodLiteral<import("..").PinchConditionType.AFTER_ROTATIONS>;
125
132
  rotations: z.ZodNumber;
@@ -129,9 +136,11 @@ export declare const TeamInputSchema: z.ZodObject<{
129
136
  }, z.core.$strip>, z.ZodObject<{
130
137
  type: z.ZodLiteral<import("..").PinchConditionType.MIN_OWN_SCORE>;
131
138
  score: z.ZodNumber;
139
+ comparison: z.ZodOptional<z.ZodEnum<typeof import("..").Comparison>>;
132
140
  }, z.core.$strip>, z.ZodObject<{
133
141
  type: z.ZodLiteral<import("..").PinchConditionType.MIN_OPPONENT_SCORE>;
134
142
  score: z.ZodNumber;
143
+ comparison: z.ZodOptional<z.ZodEnum<typeof import("..").Comparison>>;
135
144
  }, z.core.$strip>, z.ZodObject<{
136
145
  type: z.ZodLiteral<import("..").PinchConditionType.PHASE>;
137
146
  phase: z.ZodEnum<typeof import("..").MatchPhase>;
@@ -155,6 +164,7 @@ export declare const TeamInputSchema: z.ZodObject<{
155
164
  type: z.ZodLiteral<import("..").PinchConditionType.SCORE_DIFF>;
156
165
  direction: z.ZodEnum<typeof import("..").ScoreDirection>;
157
166
  points: z.ZodNumber;
167
+ comparison: z.ZodOptional<z.ZodEnum<typeof import("..").Comparison>>;
158
168
  }, z.core.$strip>, z.ZodObject<{
159
169
  type: z.ZodLiteral<import("..").PinchConditionType.AFTER_ROTATIONS>;
160
170
  rotations: z.ZodNumber;
@@ -164,9 +174,11 @@ export declare const TeamInputSchema: z.ZodObject<{
164
174
  }, z.core.$strip>, z.ZodObject<{
165
175
  type: z.ZodLiteral<import("..").PinchConditionType.MIN_OWN_SCORE>;
166
176
  score: z.ZodNumber;
177
+ comparison: z.ZodOptional<z.ZodEnum<typeof import("..").Comparison>>;
167
178
  }, z.core.$strip>, z.ZodObject<{
168
179
  type: z.ZodLiteral<import("..").PinchConditionType.MIN_OPPONENT_SCORE>;
169
180
  score: z.ZodNumber;
181
+ comparison: z.ZodOptional<z.ZodEnum<typeof import("..").Comparison>>;
170
182
  }, z.core.$strip>, z.ZodObject<{
171
183
  type: z.ZodLiteral<import("..").PinchConditionType.PHASE>;
172
184
  phase: z.ZodEnum<typeof import("..").MatchPhase>;
@@ -198,6 +210,7 @@ export declare const TeamInputSchema: z.ZodObject<{
198
210
  type: z.ZodLiteral<import("..").PinchConditionType.SCORE_DIFF>;
199
211
  direction: z.ZodEnum<typeof import("..").ScoreDirection>;
200
212
  points: z.ZodNumber;
213
+ comparison: z.ZodOptional<z.ZodEnum<typeof import("..").Comparison>>;
201
214
  }, z.core.$strip>, z.ZodObject<{
202
215
  type: z.ZodLiteral<import("..").PinchConditionType.AFTER_ROTATIONS>;
203
216
  rotations: z.ZodNumber;
@@ -207,9 +220,11 @@ export declare const TeamInputSchema: z.ZodObject<{
207
220
  }, z.core.$strip>, z.ZodObject<{
208
221
  type: z.ZodLiteral<import("..").PinchConditionType.MIN_OWN_SCORE>;
209
222
  score: z.ZodNumber;
223
+ comparison: z.ZodOptional<z.ZodEnum<typeof import("..").Comparison>>;
210
224
  }, z.core.$strip>, z.ZodObject<{
211
225
  type: z.ZodLiteral<import("..").PinchConditionType.MIN_OPPONENT_SCORE>;
212
226
  score: z.ZodNumber;
227
+ comparison: z.ZodOptional<z.ZodEnum<typeof import("..").Comparison>>;
213
228
  }, z.core.$strip>, z.ZodObject<{
214
229
  type: z.ZodLiteral<import("..").PinchConditionType.PHASE>;
215
230
  phase: z.ZodEnum<typeof import("..").MatchPhase>;
@@ -236,6 +251,7 @@ export declare const TeamInputSchema: z.ZodObject<{
236
251
  type: z.ZodLiteral<import("..").PinchConditionType.SCORE_DIFF>;
237
252
  direction: z.ZodEnum<typeof import("..").ScoreDirection>;
238
253
  points: z.ZodNumber;
254
+ comparison: z.ZodOptional<z.ZodEnum<typeof import("..").Comparison>>;
239
255
  }, z.core.$strip>, z.ZodObject<{
240
256
  type: z.ZodLiteral<import("..").PinchConditionType.AFTER_ROTATIONS>;
241
257
  rotations: z.ZodNumber;
@@ -245,9 +261,11 @@ export declare const TeamInputSchema: z.ZodObject<{
245
261
  }, z.core.$strip>, z.ZodObject<{
246
262
  type: z.ZodLiteral<import("..").PinchConditionType.MIN_OWN_SCORE>;
247
263
  score: z.ZodNumber;
264
+ comparison: z.ZodOptional<z.ZodEnum<typeof import("..").Comparison>>;
248
265
  }, z.core.$strip>, z.ZodObject<{
249
266
  type: z.ZodLiteral<import("..").PinchConditionType.MIN_OPPONENT_SCORE>;
250
267
  score: z.ZodNumber;
268
+ comparison: z.ZodOptional<z.ZodEnum<typeof import("..").Comparison>>;
251
269
  }, z.core.$strip>, z.ZodObject<{
252
270
  type: z.ZodLiteral<import("..").PinchConditionType.PHASE>;
253
271
  phase: z.ZodEnum<typeof import("..").MatchPhase>;
@@ -4,4 +4,5 @@ export * from './team';
4
4
  export * from './event';
5
5
  export * from './player';
6
6
  export * from './competition';
7
+ export * from './notification';
7
8
  export * from './utils';
@@ -4,4 +4,5 @@ export * from './team';
4
4
  export * from './event';
5
5
  export * from './player';
6
6
  export * from './competition';
7
+ export * from './notification';
7
8
  export * from './utils';
@@ -0,0 +1,41 @@
1
+ {
2
+ "matchResult": {
3
+ "title": "Match result",
4
+ "wonNamed": "You won against {{opponent}}",
5
+ "wonUnnamed": "You won your match",
6
+ "lostNamed": "You lost against {{opponent}}",
7
+ "lostUnnamed": "You lost your match"
8
+ },
9
+ "injury": {
10
+ "title": "Player injured",
11
+ "body": "{{name}} has been injured during the last match. Check the infirmary for their recovery time."
12
+ },
13
+ "injuryRecovered": {
14
+ "title": "Player recovered",
15
+ "body": "{{name}} has recovered from their injury and is ready to play!"
16
+ },
17
+ "seasonEnd": {
18
+ "title": "Season complete",
19
+ "body": "Your final league position is ready."
20
+ },
21
+ "grant": {
22
+ "title": "Recruit received",
23
+ "body": "A new recruit reward is available."
24
+ },
25
+ "promotion": {
26
+ "title": "Promotion",
27
+ "body": "Your team has been promoted to a higher division."
28
+ },
29
+ "dailyLogin": {
30
+ "title": "Daily reward claimed",
31
+ "body": "Your daily login reward was added."
32
+ },
33
+ "welcome": {
34
+ "title": "Welcome to World Aces",
35
+ "body": "Your welcome recruit is ready."
36
+ },
37
+ "generic": {
38
+ "title": "World Aces",
39
+ "body": "You have a new notification."
40
+ }
41
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "matchResult": {
3
+ "title": "Resultado del partido",
4
+ "wonNamed": "Ganaste contra {{opponent}}",
5
+ "wonUnnamed": "Ganaste tu partido",
6
+ "lostNamed": "Perdiste contra {{opponent}}",
7
+ "lostUnnamed": "Perdiste tu partido"
8
+ },
9
+ "injury": {
10
+ "title": "Jugador lesionado",
11
+ "body": "{{name}} se ha lesionado durante el último partido. Consulta la enfermería para ver su tiempo de recuperación."
12
+ },
13
+ "injuryRecovered": {
14
+ "title": "Jugador recuperado",
15
+ "body": "¡{{name}} se ha recuperado de su lesión y está listo para jugar!"
16
+ },
17
+ "seasonEnd": {
18
+ "title": "Temporada completada",
19
+ "body": "Tu posición final en la liga está lista."
20
+ },
21
+ "grant": {
22
+ "title": "Reclutamiento recibido",
23
+ "body": "Hay una nueva recompensa de reclutamiento disponible."
24
+ },
25
+ "promotion": {
26
+ "title": "Ascenso",
27
+ "body": "Tu equipo ha ascendido a una división superior."
28
+ },
29
+ "dailyLogin": {
30
+ "title": "Recompensa diaria reclamada",
31
+ "body": "Tu recompensa por inicio de sesión diario se ha añadido."
32
+ },
33
+ "welcome": {
34
+ "title": "Te damos la bienvenida a World Aces",
35
+ "body": "Tu reclutamiento de bienvenida ya está disponible."
36
+ },
37
+ "generic": {
38
+ "title": "World Aces",
39
+ "body": "Tienes una nueva notificación."
40
+ }
41
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "matchResult": {
3
+ "title": "Résultat du match",
4
+ "wonNamed": "Vous avez gagné contre {{opponent}}",
5
+ "wonUnnamed": "Vous avez gagné votre match",
6
+ "lostNamed": "Vous avez perdu contre {{opponent}}",
7
+ "lostUnnamed": "Vous avez perdu votre match"
8
+ },
9
+ "injury": {
10
+ "title": "Joueur blessé",
11
+ "body": "{{name}} s'est blessé lors du dernier match. Consultez l'infirmerie pour connaître son temps de récupération."
12
+ },
13
+ "injuryRecovered": {
14
+ "title": "Joueur rétabli",
15
+ "body": "{{name}} est remis de sa blessure et est prêt à jouer !"
16
+ },
17
+ "seasonEnd": {
18
+ "title": "Saison terminée",
19
+ "body": "Votre classement final dans la ligue est disponible."
20
+ },
21
+ "grant": {
22
+ "title": "Recrutement reçu",
23
+ "body": "Une nouvelle récompense de recrutement est disponible."
24
+ },
25
+ "promotion": {
26
+ "title": "Promotion",
27
+ "body": "Votre équipe a été promue dans une division supérieure."
28
+ },
29
+ "dailyLogin": {
30
+ "title": "Récompense quotidienne récupérée",
31
+ "body": "Votre récompense de connexion quotidienne a été ajoutée."
32
+ },
33
+ "welcome": {
34
+ "title": "Bienvenue sur World Aces",
35
+ "body": "Votre recrutement de bienvenue est prêt."
36
+ },
37
+ "generic": {
38
+ "title": "World Aces",
39
+ "body": "Vous avez une nouvelle notification."
40
+ }
41
+ }
@@ -0,0 +1,3 @@
1
+ import { type NotificationLocale } from '../notification-type';
2
+ export type CatalogNode = Record<string, unknown>;
3
+ export declare const NOTIFICATION_CATALOG: Record<NotificationLocale, CatalogNode>;
@@ -0,0 +1,16 @@
1
+ import en from './en.json';
2
+ import es from './es.json';
3
+ import fr from './fr.json';
4
+ import it from './it.json';
5
+ import pl from './pl.json';
6
+ import ptBR from './pt-BR.json';
7
+ import tr from './tr.json';
8
+ export const NOTIFICATION_CATALOG = {
9
+ en,
10
+ es,
11
+ fr,
12
+ it,
13
+ pl,
14
+ 'pt-BR': ptBR,
15
+ tr
16
+ };
@@ -0,0 +1,41 @@
1
+ {
2
+ "matchResult": {
3
+ "title": "Risultato della partita",
4
+ "wonNamed": "Hai vinto contro {{opponent}}",
5
+ "wonUnnamed": "Hai vinto la partita",
6
+ "lostNamed": "Hai perso contro {{opponent}}",
7
+ "lostUnnamed": "Hai perso la partita"
8
+ },
9
+ "injury": {
10
+ "title": "Giocatore infortunato",
11
+ "body": "{{name}} si è infortunato durante l'ultima partita. Controlla l'infermeria per conoscere i suoi tempi di recupero."
12
+ },
13
+ "injuryRecovered": {
14
+ "title": "Giocatore recuperato",
15
+ "body": "{{name}} si è ripreso dall'infortunio ed è pronto a giocare!"
16
+ },
17
+ "seasonEnd": {
18
+ "title": "Stagione completata",
19
+ "body": "La tua posizione finale in campionato è pronta."
20
+ },
21
+ "grant": {
22
+ "title": "Reclutamento ricevuto",
23
+ "body": "È disponibile una nuova ricompensa di reclutamento."
24
+ },
25
+ "promotion": {
26
+ "title": "Promozione",
27
+ "body": "La tua squadra è stata promossa in una divisione superiore."
28
+ },
29
+ "dailyLogin": {
30
+ "title": "Ricompensa giornaliera riscattata",
31
+ "body": "La tua ricompensa di accesso giornaliero è stata aggiunta."
32
+ },
33
+ "welcome": {
34
+ "title": "Benvenuto in World Aces",
35
+ "body": "La tua recluta di benvenuto è pronta."
36
+ },
37
+ "generic": {
38
+ "title": "World Aces",
39
+ "body": "Hai una nuova notifica."
40
+ }
41
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "matchResult": {
3
+ "title": "Wynik meczu",
4
+ "wonNamed": "Wygrana z {{opponent}}",
5
+ "wonUnnamed": "Wygrana w meczu",
6
+ "lostNamed": "Porażka z {{opponent}}",
7
+ "lostUnnamed": "Porażka w meczu"
8
+ },
9
+ "injury": {
10
+ "title": "Zawodnik kontuzjowany",
11
+ "body": "{{name}} doznał kontuzji w ostatnim meczu. Sprawdź ambulatorium, aby poznać czas powrotu do zdrowia."
12
+ },
13
+ "injuryRecovered": {
14
+ "title": "Zawodnik wyzdrowiał",
15
+ "body": "{{name}} wrócił do zdrowia po kontuzji i jest gotowy do gry!"
16
+ },
17
+ "seasonEnd": {
18
+ "title": "Sezon zakończony",
19
+ "body": "Twoja końcowa pozycja w lidze jest gotowa."
20
+ },
21
+ "grant": {
22
+ "title": "Otrzymano rekrutację",
23
+ "body": "Dostępna jest nowa nagroda rekrutacyjna."
24
+ },
25
+ "promotion": {
26
+ "title": "Awans",
27
+ "body": "Twoja drużyna awansowała do wyższej dywizji."
28
+ },
29
+ "dailyLogin": {
30
+ "title": "Odebrano codzienną nagrodę",
31
+ "body": "Twoja codzienna nagroda za logowanie została dodana."
32
+ },
33
+ "welcome": {
34
+ "title": "Witaj w World Aces",
35
+ "body": "Twoja powitalna rekrutacja jest gotowa."
36
+ },
37
+ "generic": {
38
+ "title": "World Aces",
39
+ "body": "Masz nowe powiadomienie."
40
+ }
41
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "matchResult": {
3
+ "title": "Resultado da partida",
4
+ "wonNamed": "Você venceu {{opponent}}",
5
+ "wonUnnamed": "Você venceu sua partida",
6
+ "lostNamed": "Você perdeu para {{opponent}}",
7
+ "lostUnnamed": "Você perdeu sua partida"
8
+ },
9
+ "injury": {
10
+ "title": "Jogador lesionado",
11
+ "body": "{{name}} se lesionou durante a última partida. Confira a enfermaria para ver o tempo de recuperação."
12
+ },
13
+ "injuryRecovered": {
14
+ "title": "Jogador recuperado",
15
+ "body": "{{name}} se recuperou da lesão e está pronto para jogar!"
16
+ },
17
+ "seasonEnd": {
18
+ "title": "Temporada concluída",
19
+ "body": "Sua colocação final na liga já está disponível."
20
+ },
21
+ "grant": {
22
+ "title": "Recrutamento recebido",
23
+ "body": "Uma nova recompensa de recrutamento está disponível."
24
+ },
25
+ "promotion": {
26
+ "title": "Promoção",
27
+ "body": "Seu time foi promovido para uma divisão superior."
28
+ },
29
+ "dailyLogin": {
30
+ "title": "Recompensa diária coletada",
31
+ "body": "Sua recompensa de login diário foi adicionada."
32
+ },
33
+ "welcome": {
34
+ "title": "Boas-vindas ao World Aces",
35
+ "body": "Seu recrutamento de boas-vindas está pronto."
36
+ },
37
+ "generic": {
38
+ "title": "World Aces",
39
+ "body": "Você tem uma nova notificação."
40
+ }
41
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "matchResult": {
3
+ "title": "Maç sonucu",
4
+ "wonNamed": "{{opponent}} karşısında kazandın",
5
+ "wonUnnamed": "Maçını kazandın",
6
+ "lostNamed": "{{opponent}} karşısında kaybettin",
7
+ "lostUnnamed": "Maçını kaybettin"
8
+ },
9
+ "injury": {
10
+ "title": "Oyuncu sakatlandı",
11
+ "body": "{{name}} son maçta sakatlandı. İyileşme süresini görmek için reviri kontrol edin."
12
+ },
13
+ "injuryRecovered": {
14
+ "title": "Oyuncu iyileşti",
15
+ "body": "{{name}} sakatlığını atlattı ve oynamaya hazır!"
16
+ },
17
+ "seasonEnd": {
18
+ "title": "Sezon tamamlandı",
19
+ "body": "Ligdeki nihai sıralaman hazır."
20
+ },
21
+ "grant": {
22
+ "title": "Transfer alındı",
23
+ "body": "Yeni bir transfer ödülü mevcut."
24
+ },
25
+ "promotion": {
26
+ "title": "Terfi",
27
+ "body": "Takımın bir üst gruba terfi etti."
28
+ },
29
+ "dailyLogin": {
30
+ "title": "Günlük ödül alındı",
31
+ "body": "Günlük giriş ödülün eklendi."
32
+ },
33
+ "welcome": {
34
+ "title": "World Aces'e hoş geldin",
35
+ "body": "Hoş geldin transferin hazır."
36
+ },
37
+ "generic": {
38
+ "title": "World Aces",
39
+ "body": "Yeni bir bildirimin var."
40
+ }
41
+ }
@@ -0,0 +1,5 @@
1
+ export * from './notification-type';
2
+ export * from './payloads';
3
+ export * from './registry';
4
+ export * from './render';
5
+ export { NOTIFICATION_CATALOG, type CatalogNode } from './catalog';
@@ -0,0 +1,5 @@
1
+ export * from './notification-type';
2
+ export * from './payloads';
3
+ export * from './registry';
4
+ export * from './render';
5
+ export { NOTIFICATION_CATALOG } from './catalog';
@@ -0,0 +1,6 @@
1
+ export declare const NOTIFICATION_TYPES: readonly ["MATCH_RESULT", "INJURY", "INJURY_RECOVERED", "SEASON_END", "GRANT_RECEIVED", "PROMOTION", "DAILY_LOGIN", "WELCOME", "COMPENSATION"];
2
+ export type NotificationType = typeof NOTIFICATION_TYPES[number];
3
+ export declare const NOTIFICATION_LOCALES: readonly ["en", "es", "fr", "it", "pl", "pt-BR", "tr"];
4
+ export type NotificationLocale = typeof NOTIFICATION_LOCALES[number];
5
+ export declare const DEFAULT_NOTIFICATION_LOCALE: NotificationLocale;
6
+ export declare function normalizeNotificationLocale(locale: string | null | undefined): NotificationLocale;
@@ -0,0 +1,26 @@
1
+ // Single source of truth for the set of notification types and the locales the catalog ships.
2
+ // Adding a notification type: add it here, add a NOTIFICATION_REGISTRY entry, and add its catalog
3
+ // strings to every locale file. The completeness test (notification.test.ts) fails the build if any
4
+ // of those three is missing. See README.md in this folder.
5
+ export const NOTIFICATION_TYPES = [
6
+ 'MATCH_RESULT',
7
+ 'INJURY',
8
+ 'INJURY_RECOVERED',
9
+ 'SEASON_END',
10
+ 'GRANT_RECEIVED',
11
+ 'PROMOTION',
12
+ 'DAILY_LOGIN',
13
+ 'WELCOME',
14
+ 'COMPENSATION'
15
+ ];
16
+ // The 7 supported locales. Must stay in sync with the API VALID_LOCALES set and the UI src/locales/*.json.
17
+ export const NOTIFICATION_LOCALES = ['en', 'es', 'fr', 'it', 'pl', 'pt-BR', 'tr'];
18
+ export const DEFAULT_NOTIFICATION_LOCALE = 'en';
19
+ // Coalesce an arbitrary/untrusted locale string (e.g. UserSettings.locale, which is nullable) to a
20
+ // supported locale, defaulting to English. Push senders MUST call this before renderPush.
21
+ export function normalizeNotificationLocale(locale) {
22
+ if (locale != null && NOTIFICATION_LOCALES.includes(locale)) {
23
+ return locale;
24
+ }
25
+ return DEFAULT_NOTIFICATION_LOCALE;
26
+ }
@@ -0,0 +1,126 @@
1
+ import { describe, it, expect } from '@jest/globals';
2
+ import { NOTIFICATION_TYPES, NOTIFICATION_LOCALES } from './notification-type';
3
+ import { NOTIFICATION_REGISTRY } from './registry';
4
+ import { NOTIFICATION_CATALOG } from './catalog';
5
+ import { renderPush, notificationPath, interpolate, translateNotification } from './render';
6
+ function readNested(node, dotKey) {
7
+ let current = node;
8
+ for (const part of dotKey.split('.')) {
9
+ if (typeof current !== 'object' || current == null)
10
+ return undefined;
11
+ current = current[part];
12
+ }
13
+ return typeof current === 'string' ? current : undefined;
14
+ }
15
+ function placeholders(value) {
16
+ return (value.match(/\{\{(\w+)\}\}/g) ?? []).sort((a, b) => a.localeCompare(b));
17
+ }
18
+ function flattenStrings(node, prefix = '') {
19
+ const out = [];
20
+ if (typeof node !== 'object' || node == null)
21
+ return out;
22
+ for (const [key, value] of Object.entries(node)) {
23
+ if (typeof value === 'string')
24
+ out.push([prefix + key, value]);
25
+ else if (typeof value === 'object' && value != null)
26
+ out.push(...flattenStrings(value, `${prefix}${key}.`));
27
+ }
28
+ return out;
29
+ }
30
+ describe('notification registry + catalog completeness', () => {
31
+ it('has exactly one descriptor per declared type', () => {
32
+ expect(Object.keys(NOTIFICATION_REGISTRY).sort((a, b) => a.localeCompare(b)))
33
+ .toEqual([...NOTIFICATION_TYPES].sort((a, b) => a.localeCompare(b)));
34
+ });
35
+ it('resolves every descriptor catalog key to a non-empty string in all 7 locales', () => {
36
+ for (const type of NOTIFICATION_TYPES) {
37
+ for (const key of NOTIFICATION_REGISTRY[type].catalogKeys) {
38
+ for (const locale of NOTIFICATION_LOCALES) {
39
+ const value = readNested(NOTIFICATION_CATALOG[locale], key);
40
+ expect([locale, key, (value ?? '').length > 0]).toEqual([locale, key, true]);
41
+ }
42
+ }
43
+ }
44
+ });
45
+ it('ships an identical key set across all 7 locales (no drift)', () => {
46
+ const enKeys = flattenStrings(NOTIFICATION_CATALOG.en).map(([key]) => key).sort((a, b) => a.localeCompare(b));
47
+ for (const locale of NOTIFICATION_LOCALES) {
48
+ const keys = flattenStrings(NOTIFICATION_CATALOG[locale]).map(([key]) => key).sort((a, b) => a.localeCompare(b));
49
+ expect([locale, keys]).toEqual([locale, enKeys]);
50
+ }
51
+ });
52
+ it('keeps placeholders identical across locales', () => {
53
+ for (const [key, enValue] of flattenStrings(NOTIFICATION_CATALOG.en)) {
54
+ const expected = placeholders(enValue);
55
+ for (const locale of NOTIFICATION_LOCALES) {
56
+ const value = readNested(NOTIFICATION_CATALOG[locale], key);
57
+ if (value == null)
58
+ continue;
59
+ expect([locale, key, placeholders(value)]).toEqual([locale, key, expected]);
60
+ }
61
+ }
62
+ });
63
+ it('contains no em or en dashes', () => {
64
+ for (const locale of NOTIFICATION_LOCALES) {
65
+ for (const [key, value] of flattenStrings(NOTIFICATION_CATALOG[locale])) {
66
+ expect([locale, key, value.includes('—') || value.includes('–')]).toEqual([locale, key, false]);
67
+ }
68
+ }
69
+ });
70
+ });
71
+ describe('renderPush', () => {
72
+ it('renders a localized MATCH_RESULT win with a named opponent', () => {
73
+ const payload = { matchId: 'm1', result: 'WIN', side: 'HOME', awayTeam: { name: 'Rivals' } };
74
+ const en = renderPush('MATCH_RESULT', payload, 'en');
75
+ expect(en?.title).toBe('Match result');
76
+ expect(en?.body).toBe('You won against Rivals');
77
+ expect(en?.data.path).toBe('/match/m1');
78
+ expect(renderPush('MATCH_RESULT', payload, 'es')?.body).toBe('Ganaste contra Rivals');
79
+ });
80
+ it('uses the unnamed variant and root path when the opponent is unknown', () => {
81
+ const en = renderPush('MATCH_RESULT', { result: 'LOSS' }, 'en');
82
+ expect(en?.body).toBe('You lost your match');
83
+ expect(en?.data.path).toBe('/');
84
+ });
85
+ it('interpolates the injured player name and links to the infirmary', () => {
86
+ const en = renderPush('INJURY', { playerNickname: 'Ace', teamId: 't1' }, 'en');
87
+ expect(en?.title).toBe('Player injured');
88
+ expect(en?.body).toContain('Ace');
89
+ expect(en?.data.path).toBe('/infirmary');
90
+ });
91
+ it('uses a generic subject when the injury payload has no player name', () => {
92
+ const en = renderPush('INJURY', { teamId: 't1' }, 'en');
93
+ expect(en?.body.startsWith('A player')).toBe(true);
94
+ });
95
+ it('returns undefined for in-app-only types (COMPENSATION)', () => {
96
+ expect(renderPush('COMPENSATION', { currency: 100 }, 'en')).toBeUndefined();
97
+ });
98
+ it('falls back to generic copy and root path for an unknown type', () => {
99
+ const en = renderPush('SOMETHING_NEW', {}, 'en');
100
+ expect(en?.title).toBe('World Aces');
101
+ expect(en?.body).toBe('You have a new notification.');
102
+ expect(en?.data.path).toBe('/');
103
+ });
104
+ it('serializes the payload into the FCM data block', () => {
105
+ const en = renderPush('GRANT_RECEIVED', { pullType: 'MYTHIC' }, 'en');
106
+ expect(en?.data.type).toBe('GRANT_RECEIVED');
107
+ expect(JSON.parse(en?.data.payload ?? '{}')).toEqual({ pullType: 'MYTHIC' });
108
+ });
109
+ });
110
+ describe('translateNotification + interpolate + notificationPath', () => {
111
+ it('falls back to English then to the raw key', () => {
112
+ expect(translateNotification('fr', 'promotion.title')).toBe('Promotion');
113
+ expect(translateNotification('en', 'does.not.exist')).toBe('does.not.exist');
114
+ });
115
+ it('replaces placeholders and drops unknown ones', () => {
116
+ expect(interpolate('Hi {{name}}!', { name: 'X' })).toBe('Hi X!');
117
+ expect(interpolate('Hi {{name}}!', {})).toBe('Hi !');
118
+ });
119
+ it('derives the deep-link path per type', () => {
120
+ expect(notificationPath('SEASON_END', { leagueId: 'l1' })).toBe('/league/l1');
121
+ expect(notificationPath('SEASON_END', {})).toBe('/league');
122
+ expect(notificationPath('PROMOTION', { teamId: 't1' })).toBe('/team/t1');
123
+ expect(notificationPath('GRANT_RECEIVED', {})).toBe('/recruit');
124
+ expect(notificationPath('COMPENSATION', {})).toBe('/');
125
+ });
126
+ });