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
@@ -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';
@@ -20,4 +20,5 @@ __exportStar(require("./team"), exports);
20
20
  __exportStar(require("./event"), exports);
21
21
  __exportStar(require("./player"), exports);
22
22
  __exportStar(require("./competition"), exports);
23
+ __exportStar(require("./notification"), exports);
23
24
  __exportStar(require("./utils"), exports);
@@ -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,22 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.NOTIFICATION_CATALOG = void 0;
7
+ const en_json_1 = __importDefault(require("./en.json"));
8
+ const es_json_1 = __importDefault(require("./es.json"));
9
+ const fr_json_1 = __importDefault(require("./fr.json"));
10
+ const it_json_1 = __importDefault(require("./it.json"));
11
+ const pl_json_1 = __importDefault(require("./pl.json"));
12
+ const pt_BR_json_1 = __importDefault(require("./pt-BR.json"));
13
+ const tr_json_1 = __importDefault(require("./tr.json"));
14
+ exports.NOTIFICATION_CATALOG = {
15
+ en: en_json_1.default,
16
+ es: es_json_1.default,
17
+ fr: fr_json_1.default,
18
+ it: it_json_1.default,
19
+ pl: pl_json_1.default,
20
+ 'pt-BR': pt_BR_json_1.default,
21
+ tr: tr_json_1.default
22
+ };
@@ -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,23 @@
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
+ exports.NOTIFICATION_CATALOG = void 0;
18
+ __exportStar(require("./notification-type"), exports);
19
+ __exportStar(require("./payloads"), exports);
20
+ __exportStar(require("./registry"), exports);
21
+ __exportStar(require("./render"), exports);
22
+ var catalog_1 = require("./catalog");
23
+ Object.defineProperty(exports, "NOTIFICATION_CATALOG", { enumerable: true, get: function () { return catalog_1.NOTIFICATION_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,30 @@
1
+ "use strict";
2
+ // Single source of truth for the set of notification types and the locales the catalog ships.
3
+ // Adding a notification type: add it here, add a NOTIFICATION_REGISTRY entry, and add its catalog
4
+ // strings to every locale file. The completeness test (notification.test.ts) fails the build if any
5
+ // of those three is missing. See README.md in this folder.
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.DEFAULT_NOTIFICATION_LOCALE = exports.NOTIFICATION_LOCALES = exports.NOTIFICATION_TYPES = void 0;
8
+ exports.normalizeNotificationLocale = normalizeNotificationLocale;
9
+ exports.NOTIFICATION_TYPES = [
10
+ 'MATCH_RESULT',
11
+ 'INJURY',
12
+ 'INJURY_RECOVERED',
13
+ 'SEASON_END',
14
+ 'GRANT_RECEIVED',
15
+ 'PROMOTION',
16
+ 'DAILY_LOGIN',
17
+ 'WELCOME',
18
+ 'COMPENSATION'
19
+ ];
20
+ // The 7 supported locales. Must stay in sync with the API VALID_LOCALES set and the UI src/locales/*.json.
21
+ exports.NOTIFICATION_LOCALES = ['en', 'es', 'fr', 'it', 'pl', 'pt-BR', 'tr'];
22
+ exports.DEFAULT_NOTIFICATION_LOCALE = 'en';
23
+ // Coalesce an arbitrary/untrusted locale string (e.g. UserSettings.locale, which is nullable) to a
24
+ // supported locale, defaulting to English. Push senders MUST call this before renderPush.
25
+ function normalizeNotificationLocale(locale) {
26
+ if (locale != null && exports.NOTIFICATION_LOCALES.includes(locale)) {
27
+ return locale;
28
+ }
29
+ return exports.DEFAULT_NOTIFICATION_LOCALE;
30
+ }
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const globals_1 = require("@jest/globals");
4
+ const notification_type_1 = require("./notification-type");
5
+ const registry_1 = require("./registry");
6
+ const catalog_1 = require("./catalog");
7
+ const render_1 = require("./render");
8
+ function readNested(node, dotKey) {
9
+ let current = node;
10
+ for (const part of dotKey.split('.')) {
11
+ if (typeof current !== 'object' || current == null)
12
+ return undefined;
13
+ current = current[part];
14
+ }
15
+ return typeof current === 'string' ? current : undefined;
16
+ }
17
+ function placeholders(value) {
18
+ return (value.match(/\{\{(\w+)\}\}/g) ?? []).sort((a, b) => a.localeCompare(b));
19
+ }
20
+ function flattenStrings(node, prefix = '') {
21
+ const out = [];
22
+ if (typeof node !== 'object' || node == null)
23
+ return out;
24
+ for (const [key, value] of Object.entries(node)) {
25
+ if (typeof value === 'string')
26
+ out.push([prefix + key, value]);
27
+ else if (typeof value === 'object' && value != null)
28
+ out.push(...flattenStrings(value, `${prefix}${key}.`));
29
+ }
30
+ return out;
31
+ }
32
+ (0, globals_1.describe)('notification registry + catalog completeness', () => {
33
+ (0, globals_1.it)('has exactly one descriptor per declared type', () => {
34
+ (0, globals_1.expect)(Object.keys(registry_1.NOTIFICATION_REGISTRY).sort((a, b) => a.localeCompare(b)))
35
+ .toEqual([...notification_type_1.NOTIFICATION_TYPES].sort((a, b) => a.localeCompare(b)));
36
+ });
37
+ (0, globals_1.it)('resolves every descriptor catalog key to a non-empty string in all 7 locales', () => {
38
+ for (const type of notification_type_1.NOTIFICATION_TYPES) {
39
+ for (const key of registry_1.NOTIFICATION_REGISTRY[type].catalogKeys) {
40
+ for (const locale of notification_type_1.NOTIFICATION_LOCALES) {
41
+ const value = readNested(catalog_1.NOTIFICATION_CATALOG[locale], key);
42
+ (0, globals_1.expect)([locale, key, (value ?? '').length > 0]).toEqual([locale, key, true]);
43
+ }
44
+ }
45
+ }
46
+ });
47
+ (0, globals_1.it)('ships an identical key set across all 7 locales (no drift)', () => {
48
+ const enKeys = flattenStrings(catalog_1.NOTIFICATION_CATALOG.en).map(([key]) => key).sort((a, b) => a.localeCompare(b));
49
+ for (const locale of notification_type_1.NOTIFICATION_LOCALES) {
50
+ const keys = flattenStrings(catalog_1.NOTIFICATION_CATALOG[locale]).map(([key]) => key).sort((a, b) => a.localeCompare(b));
51
+ (0, globals_1.expect)([locale, keys]).toEqual([locale, enKeys]);
52
+ }
53
+ });
54
+ (0, globals_1.it)('keeps placeholders identical across locales', () => {
55
+ for (const [key, enValue] of flattenStrings(catalog_1.NOTIFICATION_CATALOG.en)) {
56
+ const expected = placeholders(enValue);
57
+ for (const locale of notification_type_1.NOTIFICATION_LOCALES) {
58
+ const value = readNested(catalog_1.NOTIFICATION_CATALOG[locale], key);
59
+ if (value == null)
60
+ continue;
61
+ (0, globals_1.expect)([locale, key, placeholders(value)]).toEqual([locale, key, expected]);
62
+ }
63
+ }
64
+ });
65
+ (0, globals_1.it)('contains no em or en dashes', () => {
66
+ for (const locale of notification_type_1.NOTIFICATION_LOCALES) {
67
+ for (const [key, value] of flattenStrings(catalog_1.NOTIFICATION_CATALOG[locale])) {
68
+ (0, globals_1.expect)([locale, key, value.includes('—') || value.includes('–')]).toEqual([locale, key, false]);
69
+ }
70
+ }
71
+ });
72
+ });
73
+ (0, globals_1.describe)('renderPush', () => {
74
+ (0, globals_1.it)('renders a localized MATCH_RESULT win with a named opponent', () => {
75
+ const payload = { matchId: 'm1', result: 'WIN', side: 'HOME', awayTeam: { name: 'Rivals' } };
76
+ const en = (0, render_1.renderPush)('MATCH_RESULT', payload, 'en');
77
+ (0, globals_1.expect)(en?.title).toBe('Match result');
78
+ (0, globals_1.expect)(en?.body).toBe('You won against Rivals');
79
+ (0, globals_1.expect)(en?.data.path).toBe('/match/m1');
80
+ (0, globals_1.expect)((0, render_1.renderPush)('MATCH_RESULT', payload, 'es')?.body).toBe('Ganaste contra Rivals');
81
+ });
82
+ (0, globals_1.it)('uses the unnamed variant and root path when the opponent is unknown', () => {
83
+ const en = (0, render_1.renderPush)('MATCH_RESULT', { result: 'LOSS' }, 'en');
84
+ (0, globals_1.expect)(en?.body).toBe('You lost your match');
85
+ (0, globals_1.expect)(en?.data.path).toBe('/');
86
+ });
87
+ (0, globals_1.it)('interpolates the injured player name and links to the infirmary', () => {
88
+ const en = (0, render_1.renderPush)('INJURY', { playerNickname: 'Ace', teamId: 't1' }, 'en');
89
+ (0, globals_1.expect)(en?.title).toBe('Player injured');
90
+ (0, globals_1.expect)(en?.body).toContain('Ace');
91
+ (0, globals_1.expect)(en?.data.path).toBe('/infirmary');
92
+ });
93
+ (0, globals_1.it)('uses a generic subject when the injury payload has no player name', () => {
94
+ const en = (0, render_1.renderPush)('INJURY', { teamId: 't1' }, 'en');
95
+ (0, globals_1.expect)(en?.body.startsWith('A player')).toBe(true);
96
+ });
97
+ (0, globals_1.it)('returns undefined for in-app-only types (COMPENSATION)', () => {
98
+ (0, globals_1.expect)((0, render_1.renderPush)('COMPENSATION', { currency: 100 }, 'en')).toBeUndefined();
99
+ });
100
+ (0, globals_1.it)('falls back to generic copy and root path for an unknown type', () => {
101
+ const en = (0, render_1.renderPush)('SOMETHING_NEW', {}, 'en');
102
+ (0, globals_1.expect)(en?.title).toBe('World Aces');
103
+ (0, globals_1.expect)(en?.body).toBe('You have a new notification.');
104
+ (0, globals_1.expect)(en?.data.path).toBe('/');
105
+ });
106
+ (0, globals_1.it)('serializes the payload into the FCM data block', () => {
107
+ const en = (0, render_1.renderPush)('GRANT_RECEIVED', { pullType: 'MYTHIC' }, 'en');
108
+ (0, globals_1.expect)(en?.data.type).toBe('GRANT_RECEIVED');
109
+ (0, globals_1.expect)(JSON.parse(en?.data.payload ?? '{}')).toEqual({ pullType: 'MYTHIC' });
110
+ });
111
+ });
112
+ (0, globals_1.describe)('translateNotification + interpolate + notificationPath', () => {
113
+ (0, globals_1.it)('falls back to English then to the raw key', () => {
114
+ (0, globals_1.expect)((0, render_1.translateNotification)('fr', 'promotion.title')).toBe('Promotion');
115
+ (0, globals_1.expect)((0, render_1.translateNotification)('en', 'does.not.exist')).toBe('does.not.exist');
116
+ });
117
+ (0, globals_1.it)('replaces placeholders and drops unknown ones', () => {
118
+ (0, globals_1.expect)((0, render_1.interpolate)('Hi {{name}}!', { name: 'X' })).toBe('Hi X!');
119
+ (0, globals_1.expect)((0, render_1.interpolate)('Hi {{name}}!', {})).toBe('Hi !');
120
+ });
121
+ (0, globals_1.it)('derives the deep-link path per type', () => {
122
+ (0, globals_1.expect)((0, render_1.notificationPath)('SEASON_END', { leagueId: 'l1' })).toBe('/league/l1');
123
+ (0, globals_1.expect)((0, render_1.notificationPath)('SEASON_END', {})).toBe('/league');
124
+ (0, globals_1.expect)((0, render_1.notificationPath)('PROMOTION', { teamId: 't1' })).toBe('/team/t1');
125
+ (0, globals_1.expect)((0, render_1.notificationPath)('GRANT_RECEIVED', {})).toBe('/recruit');
126
+ (0, globals_1.expect)((0, render_1.notificationPath)('COMPENSATION', {})).toBe('/');
127
+ });
128
+ });
@@ -0,0 +1,34 @@
1
+ export interface MatchResultTeam {
2
+ id?: string;
3
+ name?: string;
4
+ }
5
+ export interface MatchResultPayload {
6
+ matchId?: string;
7
+ homeTeam?: MatchResultTeam;
8
+ awayTeam?: MatchResultTeam;
9
+ result?: 'WIN' | 'LOSS';
10
+ side?: 'HOME' | 'AWAY';
11
+ }
12
+ export interface InjuryPayload {
13
+ playerId?: string;
14
+ playerName?: string;
15
+ playerNickname?: string;
16
+ teamId?: string;
17
+ matchId?: string;
18
+ }
19
+ export interface InjuryRecoveredPayload {
20
+ playerId?: string;
21
+ playerName?: string;
22
+ playerNickname?: string;
23
+ teamId?: string;
24
+ }
25
+ export interface SeasonEndPayload {
26
+ competitionId?: string;
27
+ leagueId?: string;
28
+ position?: number;
29
+ }
30
+ export interface PromotionPayload {
31
+ teamId?: string;
32
+ amount?: number;
33
+ }
34
+ export type NotificationPayload = MatchResultPayload | InjuryPayload | InjuryRecoveredPayload | SeasonEndPayload | PromotionPayload | Record<string, unknown>;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ // Typed payload shapes per notification type, for call-site safety in the Sim/API. The DB `Notification.payload`
3
+ // column is jsonb (free-form), so the registry reads payloads defensively (Record<string, unknown>); these
4
+ // interfaces document the intended shape and can be used by emitters when constructing the payload.
5
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,13 @@
1
+ import { type NotificationType } from './notification-type';
2
+ export type CatalogKeyResolver = string | ((payload: Record<string, unknown>) => string);
3
+ export interface NotificationDescriptor {
4
+ emitsPush: boolean;
5
+ deepLink?: (payload: Record<string, unknown>) => string;
6
+ pushTitleKey?: CatalogKeyResolver;
7
+ pushBodyKey?: CatalogKeyResolver;
8
+ inAppMessageKey?: CatalogKeyResolver;
9
+ richRender?: NotificationType;
10
+ params?: (payload: Record<string, unknown>) => Record<string, string>;
11
+ catalogKeys: string[];
12
+ }
13
+ export declare const NOTIFICATION_REGISTRY: Record<NotificationType, NotificationDescriptor>;