volleyballsimtypes 0.0.524 → 0.0.526

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 (75) hide show
  1. package/dist/cjs/src/api/index.d.ts +17 -1
  2. package/dist/cjs/src/data/init-models.js +14 -0
  3. package/dist/cjs/src/data/models/competition.d.ts +1 -1
  4. package/dist/cjs/src/data/models/competition.js +1 -1
  5. package/dist/cjs/src/data/models/index.d.ts +5 -0
  6. package/dist/cjs/src/data/models/index.js +5 -0
  7. package/dist/cjs/src/data/models/jersey-badge.d.ts +1 -1
  8. package/dist/cjs/src/data/models/jersey-badge.js +1 -1
  9. package/dist/cjs/src/data/models/national-team-claim.d.ts +21 -0
  10. package/dist/cjs/src/data/models/national-team-claim.js +44 -0
  11. package/dist/cjs/src/data/models/national-team.d.ts +16 -0
  12. package/dist/cjs/src/data/models/national-team.js +31 -0
  13. package/dist/cjs/src/data/models/wnl-callup.d.ts +21 -0
  14. package/dist/cjs/src/data/models/wnl-callup.js +45 -0
  15. package/dist/cjs/src/data/models/wnl-season.d.ts +17 -0
  16. package/dist/cjs/src/data/models/wnl-season.js +34 -0
  17. package/dist/cjs/src/data/models/wnl-squad.d.ts +27 -0
  18. package/dist/cjs/src/data/models/wnl-squad.js +59 -0
  19. package/dist/cjs/src/service/match/index.d.ts +1 -0
  20. package/dist/cjs/src/service/match/index.js +8 -0
  21. package/dist/cjs/src/service/match/match-generator.d.ts +5 -1
  22. package/dist/cjs/src/service/match/match-generator.js +6 -4
  23. package/dist/cjs/src/service/notification/catalog/en.json +42 -0
  24. package/dist/cjs/src/service/notification/catalog/es.json +42 -0
  25. package/dist/cjs/src/service/notification/catalog/fr.json +42 -0
  26. package/dist/cjs/src/service/notification/catalog/it.json +42 -0
  27. package/dist/cjs/src/service/notification/catalog/pl.json +42 -0
  28. package/dist/cjs/src/service/notification/catalog/pt-BR.json +42 -0
  29. package/dist/cjs/src/service/notification/catalog/tr.json +42 -0
  30. package/dist/cjs/src/service/notification/notification-type.d.ts +1 -1
  31. package/dist/cjs/src/service/notification/notification-type.js +11 -1
  32. package/dist/cjs/src/service/notification/notification.test.js +18 -0
  33. package/dist/cjs/src/service/notification/payloads.d.ts +13 -1
  34. package/dist/cjs/src/service/notification/registry.d.ts +6 -3
  35. package/dist/cjs/src/service/notification/registry.js +134 -20
  36. package/dist/cjs/src/service/premium/premium-tier.d.ts +2 -0
  37. package/dist/cjs/src/service/premium/premium-tier.js +8 -1
  38. package/dist/esm/src/api/index.d.ts +17 -1
  39. package/dist/esm/src/data/init-models.js +15 -1
  40. package/dist/esm/src/data/models/competition.d.ts +1 -1
  41. package/dist/esm/src/data/models/competition.js +1 -1
  42. package/dist/esm/src/data/models/index.d.ts +5 -0
  43. package/dist/esm/src/data/models/index.js +5 -0
  44. package/dist/esm/src/data/models/jersey-badge.d.ts +1 -1
  45. package/dist/esm/src/data/models/jersey-badge.js +1 -1
  46. package/dist/esm/src/data/models/national-team-claim.d.ts +21 -0
  47. package/dist/esm/src/data/models/national-team-claim.js +40 -0
  48. package/dist/esm/src/data/models/national-team.d.ts +16 -0
  49. package/dist/esm/src/data/models/national-team.js +27 -0
  50. package/dist/esm/src/data/models/wnl-callup.d.ts +21 -0
  51. package/dist/esm/src/data/models/wnl-callup.js +41 -0
  52. package/dist/esm/src/data/models/wnl-season.d.ts +17 -0
  53. package/dist/esm/src/data/models/wnl-season.js +30 -0
  54. package/dist/esm/src/data/models/wnl-squad.d.ts +27 -0
  55. package/dist/esm/src/data/models/wnl-squad.js +55 -0
  56. package/dist/esm/src/service/match/index.d.ts +1 -0
  57. package/dist/esm/src/service/match/index.js +3 -0
  58. package/dist/esm/src/service/match/match-generator.d.ts +5 -1
  59. package/dist/esm/src/service/match/match-generator.js +6 -4
  60. package/dist/esm/src/service/notification/catalog/en.json +42 -0
  61. package/dist/esm/src/service/notification/catalog/es.json +42 -0
  62. package/dist/esm/src/service/notification/catalog/fr.json +42 -0
  63. package/dist/esm/src/service/notification/catalog/it.json +42 -0
  64. package/dist/esm/src/service/notification/catalog/pl.json +42 -0
  65. package/dist/esm/src/service/notification/catalog/pt-BR.json +42 -0
  66. package/dist/esm/src/service/notification/catalog/tr.json +42 -0
  67. package/dist/esm/src/service/notification/notification-type.d.ts +1 -1
  68. package/dist/esm/src/service/notification/notification-type.js +11 -1
  69. package/dist/esm/src/service/notification/notification.test.js +19 -1
  70. package/dist/esm/src/service/notification/payloads.d.ts +13 -1
  71. package/dist/esm/src/service/notification/registry.d.ts +6 -3
  72. package/dist/esm/src/service/notification/registry.js +132 -20
  73. package/dist/esm/src/service/premium/premium-tier.d.ts +2 -0
  74. package/dist/esm/src/service/premium/premium-tier.js +6 -0
  75. package/package.json +1 -1
@@ -62,5 +62,47 @@
62
62
  "regionRelocation": {
63
63
  "title": "Votre équipe déménage",
64
64
  "body": "Votre équipe a été choisie pour être transférée vers la nouvelle région : Kyorin. Rien ne change concernant l'équipe que vous gérez, vos joueurs ni quoi que ce soit d'autre."
65
+ },
66
+ "wnlCallupOpen": {
67
+ "title": "Convocation nationale ouverte",
68
+ "body": "La phase de convocation nationale a commencé ! Vous avez jusqu'au {{date}} pour sélectionner votre équipe et participer à la prochaine édition de la WNL."
69
+ },
70
+ "wnlCallupDeadline": {
71
+ "title": "La convocation se termine aujourd'hui",
72
+ "body": "La date limite de la convocation nationale est aujourd'hui. Assurez-vous d'avoir sélectionné les joueurs qui participeront à la prochaine édition de la WNL."
73
+ },
74
+ "wnlCallupClosed": {
75
+ "title": "Convocation nationale terminée",
76
+ "body": "La phase de convocation nationale est terminée. Vous pouvez modifier votre composition pour préparer la WNL qui commence lundi prochain !"
77
+ },
78
+ "wnlLeagueStart": {
79
+ "title": "La ligue WNL a commencé",
80
+ "body": "La phase de ligue de la WNL a commencé !"
81
+ },
82
+ "wnlFinalsStart": {
83
+ "title": "Les finales de la WNL ont commencé",
84
+ "body": "Les finales de la WNL ont commencé !"
85
+ },
86
+ "wnlChampion": {
87
+ "title": "Champions de la WNL",
88
+ "body": "L'édition de la WNL est terminée. {{team}} sont les champions de la WNL !",
89
+ "bodyUnnamed": "L'édition de la WNL est terminée. Les champions sont couronnés !"
90
+ },
91
+ "wnlPlayerCalledUp": {
92
+ "title": "Joueurs convoqués",
93
+ "body": "Un ou plusieurs de vos joueurs ont été convoqués par leur équipe nationale !"
94
+ },
95
+ "nationalTrialGranted": {
96
+ "title": "Équipe nationale débloquée",
97
+ "body": "Vous avez un accès National gratuit jusqu'au {{date}}. Réclamez une équipe nationale et dirigez-la dans la WNL."
98
+ },
99
+ "nationalAccessExpiring": {
100
+ "title": "Votre accès à l'équipe nationale se termine",
101
+ "bodyDays": "Votre équipe nationale sera libérée dans {{days}} jours sauf si vous souscrivez au palier National sur Patreon.",
102
+ "bodyLastDay": "Votre équipe nationale sera libérée demain sauf si vous souscrivez au palier National sur Patreon."
103
+ },
104
+ "nationalTeamRevoked": {
105
+ "title": "Équipe nationale libérée",
106
+ "body": "Votre équipe nationale a été libérée et quelqu'un d'autre peut désormais la réclamer. Souscrivez au palier National sur Patreon pour en réclamer une à nouveau."
65
107
  }
66
108
  }
@@ -62,5 +62,47 @@
62
62
  "regionRelocation": {
63
63
  "title": "La tua squadra si trasferisce",
64
64
  "body": "La tua squadra è stata scelta per essere spostata nella nuova regione: Kyorin. Non cambia nulla riguardo alla squadra che gestisci, ai tuoi giocatori o a qualsiasi altra cosa."
65
+ },
66
+ "wnlCallupOpen": {
67
+ "title": "Convocazione nazionale aperta",
68
+ "body": "La fase di convocazione nazionale è iniziata! Hai tempo fino al {{date}} per selezionare la tua squadra e partecipare alla prossima edizione della WNL."
69
+ },
70
+ "wnlCallupDeadline": {
71
+ "title": "La convocazione scade oggi",
72
+ "body": "Il termine della convocazione nazionale è oggi. Assicurati di aver selezionato i giocatori che parteciperanno alla prossima edizione della WNL."
73
+ },
74
+ "wnlCallupClosed": {
75
+ "title": "Convocazione nazionale chiusa",
76
+ "body": "La fase di convocazione nazionale è terminata. Puoi modificare la tua formazione per preparare la WNL che inizia lunedì prossimo!"
77
+ },
78
+ "wnlLeagueStart": {
79
+ "title": "La lega WNL è iniziata",
80
+ "body": "La fase di lega della WNL è iniziata!"
81
+ },
82
+ "wnlFinalsStart": {
83
+ "title": "Le finali della WNL sono iniziate",
84
+ "body": "Le finali della WNL sono iniziate!"
85
+ },
86
+ "wnlChampion": {
87
+ "title": "Campioni della WNL",
88
+ "body": "L'edizione della WNL è terminata. {{team}} sono i campioni della WNL!",
89
+ "bodyUnnamed": "L'edizione della WNL è terminata. I campioni sono stati incoronati!"
90
+ },
91
+ "wnlPlayerCalledUp": {
92
+ "title": "Giocatori convocati",
93
+ "body": "Uno o più dei tuoi giocatori sono stati convocati dalla loro nazionale!"
94
+ },
95
+ "nationalTrialGranted": {
96
+ "title": "Nazionale sbloccata",
97
+ "body": "Hai accesso National gratuito fino al {{date}}. Reclama una nazionale e guidala nella WNL."
98
+ },
99
+ "nationalAccessExpiring": {
100
+ "title": "Il tuo accesso alla nazionale sta per finire",
101
+ "bodyDays": "La tua nazionale sarà liberata tra {{days}} giorni a meno che tu non sottoscriva il livello National su Patreon.",
102
+ "bodyLastDay": "La tua nazionale sarà liberata domani a meno che tu non sottoscriva il livello National su Patreon."
103
+ },
104
+ "nationalTeamRevoked": {
105
+ "title": "Nazionale liberata",
106
+ "body": "La tua nazionale è stata liberata e ora può essere reclamata da qualcun altro. Sottoscrivi il livello National su Patreon per reclamarne di nuovo una."
65
107
  }
66
108
  }
@@ -62,5 +62,47 @@
62
62
  "regionRelocation": {
63
63
  "title": "Twoja drużyna się przenosi",
64
64
  "body": "Twoja drużyna została wybrana do przeniesienia do nowego regionu: Kyorin. Nic się nie zmienia, jeśli chodzi o zarządzaną przez ciebie drużynę, twoich zawodników ani cokolwiek innego."
65
+ },
66
+ "wnlCallupOpen": {
67
+ "title": "Powołania do reprezentacji otwarte",
68
+ "body": "Faza powołań do reprezentacji rozpoczęła się! Masz czas do {{date}}, aby wybrać swoją drużynę do udziału w następnej edycji WNL."
69
+ },
70
+ "wnlCallupDeadline": {
71
+ "title": "Termin powołań mija dzisiaj",
72
+ "body": "Termin powołań do reprezentacji mija dzisiaj. Upewnij się, że wybrałeś zawodników, którzy wezmą udział w następnej edycji WNL."
73
+ },
74
+ "wnlCallupClosed": {
75
+ "title": "Powołania do reprezentacji zamknięte",
76
+ "body": "Faza powołań do reprezentacji zakończyła się. Możesz edytować swój skład, aby przygotować się do WNL, która zaczyna się w najbliższy poniedziałek!"
77
+ },
78
+ "wnlLeagueStart": {
79
+ "title": "Liga WNL rozpoczęta",
80
+ "body": "Faza ligowa WNL rozpoczęła się!"
81
+ },
82
+ "wnlFinalsStart": {
83
+ "title": "Finały WNL rozpoczęte",
84
+ "body": "Finały WNL rozpoczęły się!"
85
+ },
86
+ "wnlChampion": {
87
+ "title": "Mistrzowie WNL",
88
+ "body": "Edycja WNL zakończyła się. {{team}} to mistrzowie WNL!",
89
+ "bodyUnnamed": "Edycja WNL zakończyła się. Mistrzowie zostali wyłonieni!"
90
+ },
91
+ "wnlPlayerCalledUp": {
92
+ "title": "Zawodnicy powołani",
93
+ "body": "Co najmniej jeden z twoich zawodników został powołany do swojej reprezentacji!"
94
+ },
95
+ "nationalTrialGranted": {
96
+ "title": "Reprezentacja odblokowana",
97
+ "body": "Masz darmowy dostęp National do {{date}}. Przejmij reprezentację i poprowadź ją w WNL."
98
+ },
99
+ "nationalAccessExpiring": {
100
+ "title": "Twój dostęp do reprezentacji się kończy",
101
+ "bodyDays": "Twoja reprezentacja zostanie zwolniona za {{days}} dni, chyba że wykupisz poziom National na Patreonie.",
102
+ "bodyLastDay": "Twoja reprezentacja zostanie zwolniona jutro, chyba że wykupisz poziom National na Patreonie."
103
+ },
104
+ "nationalTeamRevoked": {
105
+ "title": "Reprezentacja zwolniona",
106
+ "body": "Twoja reprezentacja została zwolniona i może ją teraz przejąć ktoś inny. Wykup poziom National na Patreonie, aby ponownie przejąć reprezentację."
65
107
  }
66
108
  }
@@ -62,5 +62,47 @@
62
62
  "regionRelocation": {
63
63
  "title": "Seu time está mudando",
64
64
  "body": "Seu time foi escolhido para ser movido para a nova região: Kyorin. Nada muda em relação ao time que você gerencia, seus jogadores ou qualquer outra coisa."
65
+ },
66
+ "wnlCallupOpen": {
67
+ "title": "Convocação nacional aberta",
68
+ "body": "A fase de convocação nacional começou! Você tem até {{date}} para selecionar seu time e participar da próxima edição da WNL."
69
+ },
70
+ "wnlCallupDeadline": {
71
+ "title": "A convocação termina hoje",
72
+ "body": "O prazo da convocação nacional termina hoje. Verifique se você selecionou os jogadores que participarão da próxima edição da WNL."
73
+ },
74
+ "wnlCallupClosed": {
75
+ "title": "Convocação nacional encerrada",
76
+ "body": "A fase de convocação nacional terminou. Você pode editar sua escalação para se preparar para a WNL que começa na próxima segunda-feira!"
77
+ },
78
+ "wnlLeagueStart": {
79
+ "title": "A liga WNL começou",
80
+ "body": "A fase de liga da WNL começou!"
81
+ },
82
+ "wnlFinalsStart": {
83
+ "title": "As finais da WNL começaram",
84
+ "body": "As finais da WNL começaram!"
85
+ },
86
+ "wnlChampion": {
87
+ "title": "Campeões da WNL",
88
+ "body": "A edição da WNL terminou. {{team}} são os campeões da WNL!",
89
+ "bodyUnnamed": "A edição da WNL terminou. Os campeões foram coroados!"
90
+ },
91
+ "wnlPlayerCalledUp": {
92
+ "title": "Jogadores convocados",
93
+ "body": "Um ou mais dos seus jogadores foram convocados pela sua seleção nacional!"
94
+ },
95
+ "nationalTrialGranted": {
96
+ "title": "Seleção nacional desbloqueada",
97
+ "body": "Você tem acesso National gratuito até {{date}}. Reivindique uma seleção nacional e comande-a na WNL."
98
+ },
99
+ "nationalAccessExpiring": {
100
+ "title": "Seu acesso à seleção nacional está acabando",
101
+ "bodyDays": "Sua seleção nacional será liberada em {{days}} dias, a menos que você assine o nível National no Patreon.",
102
+ "bodyLastDay": "Sua seleção nacional será liberada amanhã, a menos que você assine o nível National no Patreon."
103
+ },
104
+ "nationalTeamRevoked": {
105
+ "title": "Seleção nacional liberada",
106
+ "body": "Sua seleção nacional foi liberada e agora está disponível para outra pessoa reivindicar. Assine o nível National no Patreon para reivindicar uma novamente."
65
107
  }
66
108
  }
@@ -62,5 +62,47 @@
62
62
  "regionRelocation": {
63
63
  "title": "Takımın taşınıyor",
64
64
  "body": "Takımın yeni bölgeye, Kyorin'e taşınmak üzere seçildi. Yönettiğin takım, oyuncuların ya da başka hiçbir şey açısından hiçbir şey değişmiyor."
65
+ },
66
+ "wnlCallupOpen": {
67
+ "title": "Milli takım kadro çağrısı açıldı",
68
+ "body": "Milli takım kadro çağrısı başladı! Bir sonraki WNL sezonuna katılacak takımını seçmek için {{date}} tarihine kadar vaktin var."
69
+ },
70
+ "wnlCallupDeadline": {
71
+ "title": "Kadro çağrısı bugün sona eriyor",
72
+ "body": "Milli takım kadro çağrısının son günü bugün. Bir sonraki WNL sezonunda oynayacak oyuncuları seçtiğinden emin ol."
73
+ },
74
+ "wnlCallupClosed": {
75
+ "title": "Kadro çağrısı kapandı",
76
+ "body": "Milli takım kadro çağrısı sona erdi. Önümüzdeki pazartesi başlayacak WNL için dizilişini düzenleyebilirsin!"
77
+ },
78
+ "wnlLeagueStart": {
79
+ "title": "WNL ligi başladı",
80
+ "body": "WNL lig aşaması başladı!"
81
+ },
82
+ "wnlFinalsStart": {
83
+ "title": "WNL finalleri başladı",
84
+ "body": "WNL finalleri başladı!"
85
+ },
86
+ "wnlChampion": {
87
+ "title": "WNL şampiyonları",
88
+ "body": "WNL sezonu sona erdi. {{team}} WNL şampiyonu oldu!",
89
+ "bodyUnnamed": "WNL sezonu sona erdi. Şampiyonlar belli oldu!"
90
+ },
91
+ "wnlPlayerCalledUp": {
92
+ "title": "Oyuncular milli takıma çağrıldı",
93
+ "body": "Bir veya daha fazla oyuncun milli takımına çağrıldı!"
94
+ },
95
+ "nationalTrialGranted": {
96
+ "title": "Milli takım kilidi açıldı",
97
+ "body": "{{date}} tarihine kadar ücretsiz National erişimin var. Bir milli takım seç ve WNL'de yönet."
98
+ },
99
+ "nationalAccessExpiring": {
100
+ "title": "Milli takım erişimin sona eriyor",
101
+ "bodyDays": "Patreon'da National seviyesine abone olmazsan milli takımın {{days}} gün içinde serbest bırakılacak.",
102
+ "bodyLastDay": "Patreon'da National seviyesine abone olmazsan milli takımın yarın serbest bırakılacak."
103
+ },
104
+ "nationalTeamRevoked": {
105
+ "title": "Milli takım serbest bırakıldı",
106
+ "body": "Milli takımın serbest bırakıldı ve artık başka biri tarafından seçilebilir. Yeniden bir milli takım seçmek için Patreon'da National seviyesine abone ol."
65
107
  }
66
108
  }
@@ -1,4 +1,4 @@
1
- export declare const NOTIFICATION_TYPES: readonly ["MATCH_RESULT", "INJURY", "INJURY_RECOVERED", "SEASON_END", "GRANT_RECEIVED", "PROMOTION", "DAILY_LOGIN", "WELCOME", "COMPENSATION", "EVENT_REGISTRATION_OPEN", "EVENT_REGISTRATION_CLOSING", "EVENT_STARTED", "EVENT_FINAL_ROUND", "EVENT_FINISHED", "REGION_RELOCATION"];
1
+ export declare const NOTIFICATION_TYPES: readonly ["MATCH_RESULT", "INJURY", "INJURY_RECOVERED", "SEASON_END", "GRANT_RECEIVED", "PROMOTION", "DAILY_LOGIN", "WELCOME", "COMPENSATION", "EVENT_REGISTRATION_OPEN", "EVENT_REGISTRATION_CLOSING", "EVENT_STARTED", "EVENT_FINAL_ROUND", "EVENT_FINISHED", "REGION_RELOCATION", "WNL_CALLUP_OPEN", "WNL_CALLUP_DEADLINE", "WNL_CALLUP_CLOSED", "WNL_LEAGUE_START", "WNL_FINALS_START", "WNL_CHAMPION", "WNL_PLAYER_CALLED_UP", "NATIONAL_TRIAL_GRANTED", "NATIONAL_ACCESS_EXPIRING", "NATIONAL_TEAM_REVOKED"];
2
2
  export type NotificationType = typeof NOTIFICATION_TYPES[number];
3
3
  export declare const NOTIFICATION_LOCALES: readonly ["en", "es", "fr", "it", "pl", "pt-BR", "tr"];
4
4
  export type NotificationLocale = typeof NOTIFICATION_LOCALES[number];
@@ -22,7 +22,17 @@ exports.NOTIFICATION_TYPES = [
22
22
  'EVENT_STARTED',
23
23
  'EVENT_FINAL_ROUND',
24
24
  'EVENT_FINISHED',
25
- 'REGION_RELOCATION'
25
+ 'REGION_RELOCATION',
26
+ 'WNL_CALLUP_OPEN',
27
+ 'WNL_CALLUP_DEADLINE',
28
+ 'WNL_CALLUP_CLOSED',
29
+ 'WNL_LEAGUE_START',
30
+ 'WNL_FINALS_START',
31
+ 'WNL_CHAMPION',
32
+ 'WNL_PLAYER_CALLED_UP',
33
+ 'NATIONAL_TRIAL_GRANTED',
34
+ 'NATIONAL_ACCESS_EXPIRING',
35
+ 'NATIONAL_TEAM_REVOKED'
26
36
  ];
27
37
  // The 7 supported locales. Must stay in sync with the API VALID_LOCALES set and the UI src/locales/*.json.
28
38
  exports.NOTIFICATION_LOCALES = ['en', 'es', 'fr', 'it', 'pl', 'pt-BR', 'tr'];
@@ -145,4 +145,22 @@ function flattenStrings(node, prefix = '') {
145
145
  // disabling one type does not affect another
146
146
  (0, globals_1.expect)((0, notification_type_1.pushEnabledFor)({ MATCH_RESULT: false }, 'INJURY')).toBe(true);
147
147
  });
148
+ (0, globals_1.it)('groups the WNL types under the single WNL preference key', () => {
149
+ // Every WNL type resolves to the shared toggle; ungrouped types resolve to themselves.
150
+ for (const type of notification_type_1.NOTIFICATION_TYPES.filter((t) => t.startsWith('WNL_'))) {
151
+ (0, globals_1.expect)((0, registry_1.pushPrefKeyFor)(type)).toBe('WNL');
152
+ }
153
+ (0, globals_1.expect)((0, registry_1.pushPrefKeyFor)('MATCH_RESULT')).toBe('MATCH_RESULT');
154
+ (0, globals_1.expect)((0, registry_1.pushPrefKeyFor)('UNKNOWN_TYPE')).toBe('UNKNOWN_TYPE');
155
+ // The one WNL toggle gates every WNL type's push; individual WNL type keys are never consulted.
156
+ (0, globals_1.expect)((0, registry_1.pushEnabledForType)({ WNL: false }, 'WNL_CALLUP_OPEN')).toBe(false);
157
+ (0, globals_1.expect)((0, registry_1.pushEnabledForType)({ WNL: false }, 'WNL_CHAMPION')).toBe(false);
158
+ (0, globals_1.expect)((0, registry_1.pushEnabledForType)({}, 'WNL_CHAMPION')).toBe(true);
159
+ (0, globals_1.expect)((0, registry_1.pushEnabledForType)({ WNL_CHAMPION: false }, 'WNL_CHAMPION')).toBe(true);
160
+ (0, globals_1.expect)((0, registry_1.pushEnabledForType)({ MATCH_RESULT: false }, 'MATCH_RESULT')).toBe(false);
161
+ // The toggleable key set collapses the seven WNL types into one 'WNL' entry and keeps the rest per-type.
162
+ (0, globals_1.expect)(registry_1.PUSH_PREF_KEYS.filter((k) => k === 'WNL')).toHaveLength(1);
163
+ (0, globals_1.expect)(registry_1.PUSH_PREF_KEYS).not.toContain('WNL_CALLUP_OPEN');
164
+ (0, globals_1.expect)(registry_1.PUSH_PREF_KEYS).toContain('MATCH_RESULT');
165
+ });
148
166
  });
@@ -31,4 +31,16 @@ export interface PromotionPayload {
31
31
  teamId?: string;
32
32
  amount?: number;
33
33
  }
34
- export type NotificationPayload = MatchResultPayload | InjuryPayload | InjuryRecoveredPayload | SeasonEndPayload | PromotionPayload | Record<string, unknown>;
34
+ export interface WnlCallupOpenPayload {
35
+ deadline?: string;
36
+ }
37
+ export interface WnlChampionPayload {
38
+ team?: string;
39
+ }
40
+ export interface NationalTrialGrantedPayload {
41
+ endsOn?: string;
42
+ }
43
+ export interface NationalAccessExpiringPayload {
44
+ expiresAt?: string;
45
+ }
46
+ export type NotificationPayload = MatchResultPayload | InjuryPayload | InjuryRecoveredPayload | SeasonEndPayload | PromotionPayload | WnlCallupOpenPayload | WnlChampionPayload | NationalTrialGrantedPayload | NationalAccessExpiringPayload | Record<string, unknown>;
@@ -1,14 +1,17 @@
1
- import { type NotificationType } from './notification-type';
1
+ import { type NotificationPreferences, type NotificationType } from './notification-type';
2
2
  export type CatalogKeyResolver = string | ((payload: Record<string, unknown>) => string);
3
3
  export interface NotificationDescriptor {
4
4
  emitsPush: boolean;
5
+ prefKey?: string;
5
6
  deepLink?: (payload: Record<string, unknown>) => string;
6
7
  pushTitleKey?: CatalogKeyResolver;
7
8
  pushBodyKey?: CatalogKeyResolver;
8
- inAppMessageKey?: CatalogKeyResolver;
9
- richRender?: NotificationType;
10
9
  params?: (payload: Record<string, unknown>) => Record<string, string>;
11
10
  catalogKeys: string[];
12
11
  }
12
+ export declare const NATIONAL_ACCESS_PREF_KEY = "NATIONAL_ACCESS";
13
13
  export declare const NOTIFICATION_REGISTRY: Record<NotificationType, NotificationDescriptor>;
14
14
  export declare const PUSH_NOTIFICATION_TYPES: NotificationType[];
15
+ export declare function pushPrefKeyFor(type: string): string;
16
+ export declare function pushEnabledForType(prefs: NotificationPreferences | null | undefined, type: string): boolean;
17
+ export declare const PUSH_PREF_KEYS: string[];
@@ -1,10 +1,28 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PUSH_NOTIFICATION_TYPES = exports.NOTIFICATION_REGISTRY = void 0;
3
+ exports.PUSH_PREF_KEYS = exports.PUSH_NOTIFICATION_TYPES = exports.NOTIFICATION_REGISTRY = exports.NATIONAL_ACCESS_PREF_KEY = void 0;
4
+ exports.pushPrefKeyFor = pushPrefKeyFor;
5
+ exports.pushEnabledForType = pushEnabledForType;
4
6
  const notification_type_1 = require("./notification-type");
5
7
  function readString(value) {
6
8
  return typeof value === 'string' && value !== '' ? value : null;
7
9
  }
10
+ // The preference key the three national-team access types share. Deliberately NOT the 'WNL' group: muting routine
11
+ // call-up chatter must never also mute "you are about to lose your national team" (owner 2026-08-29).
12
+ exports.NATIONAL_ACCESS_PREF_KEY = 'NATIONAL_ACCESS';
13
+ // Whole days from now until an ISO instant, floored at 0. NATIONAL_ACCESS_EXPIRING stores the ABSOLUTE deadline
14
+ // rather than a day count because the in-app bell renders a stored payload at READ time: a stored "3" would still
15
+ // read "3 days" two days later. Push resolves the number here at SEND time and the bell resolves it again at render,
16
+ // so neither surface can go stale.
17
+ function daysUntilIso(value) {
18
+ const iso = readString(value);
19
+ if (iso == null)
20
+ return 0;
21
+ const ms = new Date(iso).getTime() - Date.now();
22
+ if (!Number.isFinite(ms) || ms <= 0)
23
+ return 0;
24
+ return Math.ceil(ms / 86400000);
25
+ }
8
26
  function teamPath(payload) {
9
27
  const teamId = readString(payload.teamId);
10
28
  return teamId != null ? `/team/${encodeURIComponent(teamId)}` : '/';
@@ -40,7 +58,6 @@ exports.NOTIFICATION_REGISTRY = {
40
58
  return `matchResult.${outcome}${named}`;
41
59
  },
42
60
  params: (p) => ({ opponent: matchOpponent(p) ?? '' }),
43
- richRender: 'MATCH_RESULT',
44
61
  catalogKeys: ['matchResult.title', 'matchResult.wonNamed', 'matchResult.wonUnnamed', 'matchResult.lostNamed', 'matchResult.lostUnnamed']
45
62
  },
46
63
  INJURY: {
@@ -48,7 +65,6 @@ exports.NOTIFICATION_REGISTRY = {
48
65
  deepLink: () => '/infirmary',
49
66
  pushTitleKey: 'injury.title',
50
67
  pushBodyKey: 'injury.body',
51
- inAppMessageKey: 'injury.body',
52
68
  params: (p) => ({ name: injuredName(p) }),
53
69
  catalogKeys: ['injury.title', 'injury.body']
54
70
  },
@@ -57,7 +73,6 @@ exports.NOTIFICATION_REGISTRY = {
57
73
  deepLink: teamPath,
58
74
  pushTitleKey: 'injuryRecovered.title',
59
75
  pushBodyKey: 'injuryRecovered.body',
60
- inAppMessageKey: 'injuryRecovered.body',
61
76
  params: (p) => ({ name: injuredName(p) }),
62
77
  catalogKeys: ['injuryRecovered.title', 'injuryRecovered.body']
63
78
  },
@@ -69,7 +84,6 @@ exports.NOTIFICATION_REGISTRY = {
69
84
  },
70
85
  pushTitleKey: 'seasonEnd.title',
71
86
  pushBodyKey: 'seasonEnd.body',
72
- inAppMessageKey: 'seasonEnd.body',
73
87
  catalogKeys: ['seasonEnd.title', 'seasonEnd.body']
74
88
  },
75
89
  GRANT_RECEIVED: {
@@ -77,7 +91,6 @@ exports.NOTIFICATION_REGISTRY = {
77
91
  deepLink: () => '/recruit',
78
92
  pushTitleKey: 'grant.title',
79
93
  pushBodyKey: 'grant.body',
80
- inAppMessageKey: 'grant.body',
81
94
  catalogKeys: ['grant.title', 'grant.body']
82
95
  },
83
96
  PROMOTION: {
@@ -85,7 +98,6 @@ exports.NOTIFICATION_REGISTRY = {
85
98
  deepLink: teamPath,
86
99
  pushTitleKey: 'promotion.title',
87
100
  pushBodyKey: 'promotion.body',
88
- inAppMessageKey: 'promotion.body',
89
101
  catalogKeys: ['promotion.title', 'promotion.body']
90
102
  },
91
103
  // In-game events. All five deep-link to the Events section, which is where every phase of an event lives.
@@ -96,7 +108,6 @@ exports.NOTIFICATION_REGISTRY = {
96
108
  deepLink: () => '/events',
97
109
  pushTitleKey: 'eventRegistrationOpen.title',
98
110
  pushBodyKey: 'eventRegistrationOpen.body',
99
- inAppMessageKey: 'eventRegistrationOpen.body',
100
111
  catalogKeys: ['eventRegistrationOpen.title', 'eventRegistrationOpen.body']
101
112
  },
102
113
  EVENT_REGISTRATION_CLOSING: {
@@ -104,7 +115,6 @@ exports.NOTIFICATION_REGISTRY = {
104
115
  deepLink: () => '/events',
105
116
  pushTitleKey: 'eventRegistrationClosing.title',
106
117
  pushBodyKey: 'eventRegistrationClosing.body',
107
- inAppMessageKey: 'eventRegistrationClosing.body',
108
118
  catalogKeys: ['eventRegistrationClosing.title', 'eventRegistrationClosing.body']
109
119
  },
110
120
  EVENT_STARTED: {
@@ -112,7 +122,6 @@ exports.NOTIFICATION_REGISTRY = {
112
122
  deepLink: () => '/events',
113
123
  pushTitleKey: 'eventStarted.title',
114
124
  pushBodyKey: 'eventStarted.body',
115
- inAppMessageKey: 'eventStarted.body',
116
125
  catalogKeys: ['eventStarted.title', 'eventStarted.body']
117
126
  },
118
127
  EVENT_FINAL_ROUND: {
@@ -120,7 +129,6 @@ exports.NOTIFICATION_REGISTRY = {
120
129
  deepLink: () => '/events',
121
130
  pushTitleKey: 'eventFinalRound.title',
122
131
  pushBodyKey: 'eventFinalRound.body',
123
- inAppMessageKey: 'eventFinalRound.body',
124
132
  catalogKeys: ['eventFinalRound.title', 'eventFinalRound.body']
125
133
  },
126
134
  EVENT_FINISHED: {
@@ -131,9 +139,6 @@ exports.NOTIFICATION_REGISTRY = {
131
139
  pushBodyKey: (p) => readString(p.position) != null || typeof p.position === 'number'
132
140
  ? 'eventFinished.placed'
133
141
  : 'eventFinished.body',
134
- inAppMessageKey: (p) => readString(p.position) != null || typeof p.position === 'number'
135
- ? 'eventFinished.placed'
136
- : 'eventFinished.body',
137
142
  params: (p) => ({
138
143
  position: typeof p.position === 'number' ? String(p.position) : (readString(p.position) ?? ''),
139
144
  entrants: typeof p.entrants === 'number' ? String(p.entrants) : (readString(p.entrants) ?? '')
@@ -146,7 +151,6 @@ exports.NOTIFICATION_REGISTRY = {
146
151
  deepLink: () => '/daily-login',
147
152
  pushTitleKey: 'dailyLogin.title',
148
153
  pushBodyKey: 'dailyLogin.body',
149
- inAppMessageKey: 'dailyLogin.body',
150
154
  catalogKeys: ['dailyLogin.title', 'dailyLogin.body']
151
155
  },
152
156
  WELCOME: {
@@ -154,13 +158,11 @@ exports.NOTIFICATION_REGISTRY = {
154
158
  deepLink: () => '/recruit',
155
159
  pushTitleKey: 'welcome.title',
156
160
  pushBodyKey: 'welcome.body',
157
- richRender: 'WELCOME',
158
161
  catalogKeys: ['welcome.title', 'welcome.body']
159
162
  },
160
163
  // In-app only (no push); the UI keeps a bespoke renderer (amount formatting) and its own `compensation` label.
161
164
  COMPENSATION: {
162
165
  emitsPush: false,
163
- richRender: 'COMPENSATION',
164
166
  catalogKeys: []
165
167
  },
166
168
  // One-off relocation warning: sent to users whose team is being moved to Kyorin. Informational; opens their team.
@@ -169,11 +171,123 @@ exports.NOTIFICATION_REGISTRY = {
169
171
  deepLink: teamPath,
170
172
  pushTitleKey: 'regionRelocation.title',
171
173
  pushBodyKey: 'regionRelocation.body',
172
- inAppMessageKey: 'regionRelocation.body',
173
174
  catalogKeys: ['regionRelocation.title', 'regionRelocation.body']
175
+ },
176
+ // World Nations League (owner 2026-08-28). All seven share the single 'WNL' preference toggle. The three callup
177
+ // types go to national team managers, LEAGUE_START/FINALS_START/CHAMPION are broadcast to every active user, and
178
+ // PLAYER_CALLED_UP goes to club owners with at least one player in a finalized squad.
179
+ WNL_CALLUP_OPEN: {
180
+ emitsPush: true,
181
+ prefKey: 'WNL',
182
+ deepLink: () => '/wnl/callup',
183
+ pushTitleKey: 'wnlCallupOpen.title',
184
+ pushBodyKey: 'wnlCallupOpen.body',
185
+ // The deadline arrives pre-formatted (locale-neutral 'YYYY-MM-DD HH:mm UTC') because the stored payload is
186
+ // shared by the push and the in-app bell and cannot be re-localised per render.
187
+ params: (p) => ({ date: readString(p.deadline) ?? '' }),
188
+ catalogKeys: ['wnlCallupOpen.title', 'wnlCallupOpen.body']
189
+ },
190
+ WNL_CALLUP_DEADLINE: {
191
+ emitsPush: true,
192
+ prefKey: 'WNL',
193
+ deepLink: () => '/wnl/callup',
194
+ pushTitleKey: 'wnlCallupDeadline.title',
195
+ pushBodyKey: 'wnlCallupDeadline.body',
196
+ catalogKeys: ['wnlCallupDeadline.title', 'wnlCallupDeadline.body']
197
+ },
198
+ WNL_CALLUP_CLOSED: {
199
+ emitsPush: true,
200
+ prefKey: 'WNL',
201
+ deepLink: () => '/wnl/manage/lineup',
202
+ pushTitleKey: 'wnlCallupClosed.title',
203
+ pushBodyKey: 'wnlCallupClosed.body',
204
+ catalogKeys: ['wnlCallupClosed.title', 'wnlCallupClosed.body']
205
+ },
206
+ WNL_LEAGUE_START: {
207
+ emitsPush: true,
208
+ prefKey: 'WNL',
209
+ deepLink: () => '/wnl',
210
+ pushTitleKey: 'wnlLeagueStart.title',
211
+ pushBodyKey: 'wnlLeagueStart.body',
212
+ catalogKeys: ['wnlLeagueStart.title', 'wnlLeagueStart.body']
213
+ },
214
+ WNL_FINALS_START: {
215
+ emitsPush: true,
216
+ prefKey: 'WNL',
217
+ deepLink: () => '/wnl/finals',
218
+ pushTitleKey: 'wnlFinalsStart.title',
219
+ pushBodyKey: 'wnlFinalsStart.body',
220
+ catalogKeys: ['wnlFinalsStart.title', 'wnlFinalsStart.body']
221
+ },
222
+ WNL_CHAMPION: {
223
+ emitsPush: true,
224
+ prefKey: 'WNL',
225
+ deepLink: () => '/wnl/finals',
226
+ pushTitleKey: 'wnlChampion.title',
227
+ // A missing team name never renders a subjectless sentence (mirrors the MATCH_RESULT named/unnamed split).
228
+ pushBodyKey: (p) => readString(p.team) != null ? 'wnlChampion.body' : 'wnlChampion.bodyUnnamed',
229
+ params: (p) => ({ team: readString(p.team) ?? '' }),
230
+ catalogKeys: ['wnlChampion.title', 'wnlChampion.body', 'wnlChampion.bodyUnnamed']
231
+ },
232
+ WNL_PLAYER_CALLED_UP: {
233
+ emitsPush: true,
234
+ prefKey: 'WNL',
235
+ deepLink: () => '/wnl/teams',
236
+ pushTitleKey: 'wnlPlayerCalledUp.title',
237
+ pushBodyKey: 'wnlPlayerCalledUp.body',
238
+ catalogKeys: ['wnlPlayerCalledUp.title', 'wnlPlayerCalledUp.body']
239
+ },
240
+ // National-team ACCESS lifecycle (owner 2026-08-29): the free Tier-2 trial is granted, is about to end, and the
241
+ // team is released. These share their OWN toggle rather than the 'WNL' group so that muting call-up chatter
242
+ // cannot silence the warning that a team is about to be lost. They are also intentionally not named WNL_*: the
243
+ // completeness test asserts every WNL_-prefixed type resolves to the 'WNL' key.
244
+ NATIONAL_TRIAL_GRANTED: {
245
+ emitsPush: true,
246
+ prefKey: exports.NATIONAL_ACCESS_PREF_KEY,
247
+ deepLink: () => '/wnl/claim',
248
+ pushTitleKey: 'nationalTrialGranted.title',
249
+ pushBodyKey: 'nationalTrialGranted.body',
250
+ // The end date arrives pre-formatted (locale-neutral 'YYYY-MM-DD'), same reason as wnlCallupOpen: one stored
251
+ // payload serves both the push and the bell and cannot be re-localised per render.
252
+ params: (p) => ({ date: readString(p.endsOn) ?? '' }),
253
+ catalogKeys: ['nationalTrialGranted.title', 'nationalTrialGranted.body']
254
+ },
255
+ NATIONAL_ACCESS_EXPIRING: {
256
+ emitsPush: true,
257
+ prefKey: exports.NATIONAL_ACCESS_PREF_KEY,
258
+ // Settings is where the Patreon card lives, so the action the copy asks for is one tap away.
259
+ deepLink: () => '/settings',
260
+ pushTitleKey: 'nationalAccessExpiring.title',
261
+ // The catalog has no pluralisation (interpolate is a bare {{param}} replace), so the final day is its own key
262
+ // instead of rendering "1 days" (mirrors the wnlChampion named/unnamed split).
263
+ pushBodyKey: (p) => daysUntilIso(p.expiresAt) <= 1 ? 'nationalAccessExpiring.bodyLastDay' : 'nationalAccessExpiring.bodyDays',
264
+ params: (p) => ({ days: String(daysUntilIso(p.expiresAt)) }),
265
+ catalogKeys: ['nationalAccessExpiring.title', 'nationalAccessExpiring.bodyDays', 'nationalAccessExpiring.bodyLastDay']
266
+ },
267
+ NATIONAL_TEAM_REVOKED: {
268
+ emitsPush: true,
269
+ prefKey: exports.NATIONAL_ACCESS_PREF_KEY,
270
+ deepLink: () => '/wnl/teams',
271
+ pushTitleKey: 'nationalTeamRevoked.title',
272
+ pushBodyKey: 'nationalTeamRevoked.body',
273
+ catalogKeys: ['nationalTeamRevoked.title', 'nationalTeamRevoked.body']
174
274
  }
175
275
  };
176
276
  // The notification types that emit a mobile push (everything except COMPENSATION). Source of truth = each
177
- // descriptor's emitsPush, so this cannot drift. This is the user-toggleable set surfaced in the mobile app's
178
- // notification settings and validated by the API before persisting a preference.
277
+ // descriptor's emitsPush, so this cannot drift.
179
278
  exports.PUSH_NOTIFICATION_TYPES = notification_type_1.NOTIFICATION_TYPES.filter((type) => exports.NOTIFICATION_REGISTRY[type].emitsPush);
279
+ // The preference-map key a type's push toggle lives under: the descriptor's prefKey (group toggle) or the type
280
+ // itself. Unknown types resolve to themselves, mirroring pushEnabledFor's loose-string tolerance.
281
+ function pushPrefKeyFor(type) {
282
+ const descriptor = exports.NOTIFICATION_REGISTRY[type];
283
+ return descriptor?.prefKey ?? type;
284
+ }
285
+ // Group-aware push gate: like pushEnabledFor, but resolves the type's preference GROUP first, so muting the one
286
+ // 'WNL' toggle suppresses every WNL type's push. Senders (Sim/API insertNotification) use THIS, not pushEnabledFor.
287
+ function pushEnabledForType(prefs, type) {
288
+ return (0, notification_type_1.pushEnabledFor)(prefs, pushPrefKeyFor(type));
289
+ }
290
+ // The user-toggleable keys surfaced in the mobile app's notification settings and validated by the API before
291
+ // persisting a preference: one entry per DISTINCT pref key (individual types plus each group toggle once),
292
+ // in first-appearance order.
293
+ exports.PUSH_PREF_KEYS = [...new Set(exports.PUSH_NOTIFICATION_TYPES.map((type) => pushPrefKeyFor(type)))];
@@ -8,3 +8,5 @@ export declare const PREMIUM_TIERS: PremiumTierDef[];
8
8
  export declare const MAX_PREMIUM_TIER: number;
9
9
  export declare function premiumTierDef(tier: number): PremiumTierDef | undefined;
10
10
  export declare function hasTier(effectiveTier: number, required: number): boolean;
11
+ export declare const TIER_NATIONAL = 2;
12
+ export declare function canManageNationalTeam(effectiveTier: number): boolean;
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MAX_PREMIUM_TIER = exports.PREMIUM_TIERS = exports.NO_PREMIUM = void 0;
3
+ exports.TIER_NATIONAL = exports.MAX_PREMIUM_TIER = exports.PREMIUM_TIERS = exports.NO_PREMIUM = void 0;
4
4
  exports.premiumTierDef = premiumTierDef;
5
5
  exports.hasTier = hasTier;
6
+ exports.canManageNationalTeam = canManageNationalTeam;
6
7
  // Hierarchical premium tiers (owner 2026-08-17): tier N grants everything below it, so a feature gate is just
7
8
  // `effectiveTier >= required`. Tiers are NUMERIC and code-defined here (the DB stores only the number), so adding a
8
9
  // tier needs no migration. Names/keys are display + gating helpers. Keep PREMIUM_TIERS ascending by tier. See
@@ -22,3 +23,9 @@ function premiumTierDef(tier) {
22
23
  function hasTier(effectiveTier, required) {
23
24
  return effectiveTier >= required;
24
25
  }
26
+ // Tier 2 (NATIONAL) unlocks World Cup / national-team management (claiming and managing a national team, the WNL
27
+ // callup, lineup and kit). Gate every national-team write on this.
28
+ exports.TIER_NATIONAL = 2;
29
+ function canManageNationalTeam(effectiveTier) {
30
+ return hasTier(effectiveTier, exports.TIER_NATIONAL);
31
+ }