@ttt-productions/q-core 0.1.0 → 0.1.1

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 (60) hide show
  1. package/dist/constants/business.js.map +1 -1
  2. package/dist/constants/sports.js.map +1 -1
  3. package/dist/constants/storage-keys.js.map +1 -1
  4. package/dist/doc-schemas/catalogs.js.map +1 -1
  5. package/dist/doc-schemas/game-location.js.map +1 -1
  6. package/dist/doc-schemas/game-stats.js.map +1 -1
  7. package/dist/doc-schemas/league-team.js.map +1 -1
  8. package/dist/doc-schemas/pending-media.js.map +1 -1
  9. package/dist/doc-schemas/registry.js.map +1 -1
  10. package/dist/doc-schemas/sport-game.js.map +1 -1
  11. package/dist/doc-schemas/system.js.map +1 -1
  12. package/dist/doc-schemas/user-profile.js.map +1 -1
  13. package/dist/doc-schemas/waiver-template.js.map +1 -1
  14. package/dist/doc-schemas/waivers-and-hardware.js.map +1 -1
  15. package/dist/ids/index.js.map +1 -1
  16. package/dist/ids/minted-ids.js.map +1 -1
  17. package/dist/ids/stats-operation-ids.js.map +1 -1
  18. package/dist/index.js.map +1 -1
  19. package/dist/media/file-origins.js.map +1 -1
  20. package/dist/paths/collection-refs.js.map +1 -1
  21. package/dist/paths/collections.js.map +1 -1
  22. package/dist/paths/path-builders.js.map +1 -1
  23. package/dist/paths/utils.js.map +1 -1
  24. package/dist/schemas/account-settings.js.map +1 -1
  25. package/dist/schemas/atoms.js.map +1 -1
  26. package/dist/schemas/awards.js.map +1 -1
  27. package/dist/schemas/hardware-loan-contracts.js.map +1 -1
  28. package/dist/schemas/schedules.js.map +1 -1
  29. package/dist/schemas/scoring.js.map +1 -1
  30. package/dist/schemas/stats.js.map +1 -1
  31. package/dist/schemas/team-joining.js.map +1 -1
  32. package/dist/schemas/teams.js.map +1 -1
  33. package/dist/schemas/uploads.js.map +1 -1
  34. package/dist/schemas/users.js.map +1 -1
  35. package/dist/sports/basketball-scoring.js.map +1 -1
  36. package/dist/sports/basketball.js.map +1 -1
  37. package/dist/sports/bulk-entry.js.map +1 -1
  38. package/dist/sports/contribution.js.map +1 -1
  39. package/dist/sports/curling-scoring.js.map +1 -1
  40. package/dist/sports/finalized-record.js.map +1 -1
  41. package/dist/sports/football-scoring.js.map +1 -1
  42. package/dist/sports/index.js.map +1 -1
  43. package/dist/sports/rankings.js.map +1 -1
  44. package/dist/sports/rollup-check.js.map +1 -1
  45. package/dist/sports/scoring.js.map +1 -1
  46. package/dist/sports/store-driver.js.map +1 -1
  47. package/dist/types/documents.js.map +1 -1
  48. package/dist/types/notifications.js.map +1 -1
  49. package/dist/types/picture-owners.js.map +1 -1
  50. package/dist/types/sports.js.map +1 -1
  51. package/dist/types/stats.js.map +1 -1
  52. package/dist/types/unions.js.map +1 -1
  53. package/dist/utils/account-status.js.map +1 -1
  54. package/dist/utils/app-config.js.map +1 -1
  55. package/dist/utils/app-routes.js.map +1 -1
  56. package/dist/utils/index.js.map +1 -1
  57. package/dist/utils/league-calendar.js.map +1 -1
  58. package/dist/utils/league-mode.js.map +1 -1
  59. package/dist/utils/team-member-actions.js.map +1 -1
  60. package/package.json +90 -90
@@ -1 +1 @@
1
- {"version":3,"file":"business.js","sourceRoot":"","sources":["../../src/constants/business.ts"],"names":[],"mappings":"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","sourcesContent":["// Business limits and policy values both app roots enforce. A form reads these for its\r\n// `maxLength` / zod bound and the callable's input schema reads the same constant (ENG-005).\r\n\r\n// Display names, team names, and ghost player names share one length policy.\r\nexport const MIN_DISPLAY_NAME_LENGTH = 3;\r\nexport const MAX_DISPLAY_NAME_LENGTH = 50;\r\n// The shortest password an account takes: Firebase Auth's own floor.\r\nexport const PASSWORD_MIN_LENGTH = 6;\r\nexport const MIN_TEAM_NAME_LENGTH = 3;\r\nexport const MAX_TEAM_NAME_LENGTH = 50;\r\n\r\nexport const MIN_TEAM_JOIN_REQUEST_MESSAGE_LENGTH = 10;\r\nexport const MAX_TEAM_JOIN_REQUEST_MESSAGE_LENGTH = 500;\r\nexport const MAX_TEAM_INVITATION_MESSAGE_LENGTH = 500;\r\n\r\nexport const MIN_LEAGUE_TITLE_LENGTH = 3;\r\nexport const MAX_LEAGUE_TITLE_LENGTH = 100;\r\nexport const MIN_LEAGUE_DESCRIPTION_LENGTH = 10;\r\nexport const MAX_LEAGUE_DESCRIPTION_LENGTH = 5000;\r\n// How many teams in a pool reach the playoffs. The cap only bounds the input: no league comes near\r\n// it, and a cutoff above a pool's team count simply admits every team.\r\nexport const MAX_PLAYOFF_CUTOFF = 100;\r\n// The equipment a referee can be loaned. The catalog is this constant: adding an item is a code\r\n// change, and a contract records each item's id and name as it was loaned.\r\nexport const HARDWARE_CATALOG = [\r\n { id: 'ipad', name: 'iPad' },\r\n { id: 'ipad-charger', name: 'iPad Charger' },\r\n] as const;\r\nexport type HardwareCatalogItemId = (typeof HARDWARE_CATALOG)[number]['id'];\r\nexport const HARDWARE_CATALOG_ITEM_IDS = HARDWARE_CATALOG.map((item) => item.id) as [\r\n HardwareCatalogItemId,\r\n ...HardwareCatalogItemId[],\r\n];\r\n\r\n// An award's name in the catalog.\r\nexport const MIN_AWARD_NAME_LENGTH = 3;\r\nexport const MAX_AWARD_NAME_LENGTH = 60;\r\n\r\n// The most people one award grant may reach once its teams and leagues are resolved to their active\r\n// members: every grant of one call commits in one transaction, beside its audit events.\r\nexport const MAX_AWARD_GRANT_RECIPIENTS = 200;\r\n// The most users, teams, or leagues one award grant call may name in each list.\r\nexport const MAX_AWARD_GRANT_TARGETS = 20;\r\n\r\n// The reason an admin gives for a repair action — a backfill, a recalculation, a reopen — recorded on\r\n// its audit event (BACKEND-212).\r\nexport const MIN_REPAIR_REASON_LENGTH = 3;\r\nexport const MAX_REPAIR_REASON_LENGTH = 500;\r\n\r\n// The most uids one `updateAdminList` call may grant or revoke per list.\r\nexport const MAX_ADMIN_LIST_CHANGES = 20;\r\n\r\n// A team may be created in a league until this many days after the league's start date.\r\nexport const TEAM_CREATION_CUTOFF_DAYS = 14;\r\n\r\n// Contact phone numbers are stored as typed, bounded only in length.\r\nexport const MAX_CONTACT_PHONE_LENGTH = 30;\r\n\r\n// The failure a roster obligation records when a reconciliation run fails, cut to this length.\r\nexport const MAX_ROSTER_SYNC_FAILURE_MESSAGE_LENGTH = 500;\r\n\r\n// `_appConfig/app` operator messages.\r\nexport const MAX_MAINTENANCE_MESSAGE_LENGTH = 2000;\r\nexport const MAX_ANNOUNCEMENT_MESSAGE_LENGTH = 300;\r\n// `_appConfig/app.appVersion` — the version a live client compares itself against.\r\nexport const MAX_APP_VERSION_LENGTH = 64;\r\n\r\n// `_appConfig/landingPage` copy.\r\nexport const MAX_LANDING_HERO_TITLE_LENGTH = 200;\r\nexport const MAX_LANDING_ABOUT_TEXT_LENGTH = 5000;\r\n\r\n// Waiver template wording. A signed waiver carries its own copy of the title and text, so the text\r\n// cap also bounds every signature document.\r\nexport const MAX_WAIVER_TEMPLATE_TITLE_LENGTH = 120;\r\nexport const MAX_WAIVER_TEMPLATE_TEXT_LENGTH = 20000;\r\n\r\n// Game locations: the venue, its address, the courts or sheets inside it, and the directions memo.\r\nexport const MAX_GAME_LOCATION_NAME_LENGTH = 100;\r\nexport const MAX_GAME_LOCATION_STREET_ADDRESS_LENGTH = 200;\r\nexport const MAX_GAME_LOCATION_SUB_LOCATIONS = 50;\r\nexport const MAX_GAME_LOCATION_SUB_LOCATION_LENGTH = 60;\r\nexport const MAX_GAME_LOCATION_MEMO_LENGTH = 500;\r\n\r\n// A week of the schedule: its title, and the games one save creates with it — each seeded with its\r\n// teams' rosters in the same transaction, so a larger week is saved in parts.\r\nexport const MAX_SCHEDULE_WEEK_TITLE_LENGTH = 100;\r\nexport const MAX_SCHEDULE_WEEK_GAMES = 20;\r\n\r\n// The timezone a game's `dayKey` (its local calendar date) is computed in.\r\nexport const LEAGUE_TIME_ZONE = 'America/New_York';\r\n\r\n// A scorer's note on one person in a game (\"red shirt\", \"#9\").\r\nexport const MAX_GAME_MEMO_LENGTH = 30;\r\n\r\n"]}
1
+ {"version":3,"file":"business.js","sourceRoot":"","sources":["../../src/constants/business.ts"],"names":[],"mappings":"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","sourcesContent":["// Business limits and policy values both app roots enforce. A form reads these for its\n// `maxLength` / zod bound and the callable's input schema reads the same constant (ENG-005).\n\n// Display names, team names, and ghost player names share one length policy.\nexport const MIN_DISPLAY_NAME_LENGTH = 3;\nexport const MAX_DISPLAY_NAME_LENGTH = 50;\n// The shortest password an account takes: Firebase Auth's own floor.\nexport const PASSWORD_MIN_LENGTH = 6;\nexport const MIN_TEAM_NAME_LENGTH = 3;\nexport const MAX_TEAM_NAME_LENGTH = 50;\n\nexport const MIN_TEAM_JOIN_REQUEST_MESSAGE_LENGTH = 10;\nexport const MAX_TEAM_JOIN_REQUEST_MESSAGE_LENGTH = 500;\nexport const MAX_TEAM_INVITATION_MESSAGE_LENGTH = 500;\n\nexport const MIN_LEAGUE_TITLE_LENGTH = 3;\nexport const MAX_LEAGUE_TITLE_LENGTH = 100;\nexport const MIN_LEAGUE_DESCRIPTION_LENGTH = 10;\nexport const MAX_LEAGUE_DESCRIPTION_LENGTH = 5000;\n// How many teams in a pool reach the playoffs. The cap only bounds the input: no league comes near\n// it, and a cutoff above a pool's team count simply admits every team.\nexport const MAX_PLAYOFF_CUTOFF = 100;\n// The equipment a referee can be loaned. The catalog is this constant: adding an item is a code\n// change, and a contract records each item's id and name as it was loaned.\nexport const HARDWARE_CATALOG = [\n { id: 'ipad', name: 'iPad' },\n { id: 'ipad-charger', name: 'iPad Charger' },\n] as const;\nexport type HardwareCatalogItemId = (typeof HARDWARE_CATALOG)[number]['id'];\nexport const HARDWARE_CATALOG_ITEM_IDS = HARDWARE_CATALOG.map((item) => item.id) as [\n HardwareCatalogItemId,\n ...HardwareCatalogItemId[],\n];\n\n// An award's name in the catalog.\nexport const MIN_AWARD_NAME_LENGTH = 3;\nexport const MAX_AWARD_NAME_LENGTH = 60;\n\n// The most people one award grant may reach once its teams and leagues are resolved to their active\n// members: every grant of one call commits in one transaction, beside its audit events.\nexport const MAX_AWARD_GRANT_RECIPIENTS = 200;\n// The most users, teams, or leagues one award grant call may name in each list.\nexport const MAX_AWARD_GRANT_TARGETS = 20;\n\n// The reason an admin gives for a repair action — a backfill, a recalculation, a reopen — recorded on\n// its audit event (BACKEND-212).\nexport const MIN_REPAIR_REASON_LENGTH = 3;\nexport const MAX_REPAIR_REASON_LENGTH = 500;\n\n// The most uids one `updateAdminList` call may grant or revoke per list.\nexport const MAX_ADMIN_LIST_CHANGES = 20;\n\n// A team may be created in a league until this many days after the league's start date.\nexport const TEAM_CREATION_CUTOFF_DAYS = 14;\n\n// Contact phone numbers are stored as typed, bounded only in length.\nexport const MAX_CONTACT_PHONE_LENGTH = 30;\n\n// The failure a roster obligation records when a reconciliation run fails, cut to this length.\nexport const MAX_ROSTER_SYNC_FAILURE_MESSAGE_LENGTH = 500;\n\n// `_appConfig/app` operator messages.\nexport const MAX_MAINTENANCE_MESSAGE_LENGTH = 2000;\nexport const MAX_ANNOUNCEMENT_MESSAGE_LENGTH = 300;\n// `_appConfig/app.appVersion` — the version a live client compares itself against.\nexport const MAX_APP_VERSION_LENGTH = 64;\n\n// `_appConfig/landingPage` copy.\nexport const MAX_LANDING_HERO_TITLE_LENGTH = 200;\nexport const MAX_LANDING_ABOUT_TEXT_LENGTH = 5000;\n\n// Waiver template wording. A signed waiver carries its own copy of the title and text, so the text\n// cap also bounds every signature document.\nexport const MAX_WAIVER_TEMPLATE_TITLE_LENGTH = 120;\nexport const MAX_WAIVER_TEMPLATE_TEXT_LENGTH = 20000;\n\n// Game locations: the venue, its address, the courts or sheets inside it, and the directions memo.\nexport const MAX_GAME_LOCATION_NAME_LENGTH = 100;\nexport const MAX_GAME_LOCATION_STREET_ADDRESS_LENGTH = 200;\nexport const MAX_GAME_LOCATION_SUB_LOCATIONS = 50;\nexport const MAX_GAME_LOCATION_SUB_LOCATION_LENGTH = 60;\nexport const MAX_GAME_LOCATION_MEMO_LENGTH = 500;\n\n// A week of the schedule: its title, and the games one save creates with it — each seeded with its\n// teams' rosters in the same transaction, so a larger week is saved in parts.\nexport const MAX_SCHEDULE_WEEK_TITLE_LENGTH = 100;\nexport const MAX_SCHEDULE_WEEK_GAMES = 20;\n\n// The timezone a game's `dayKey` (its local calendar date) is computed in.\nexport const LEAGUE_TIME_ZONE = 'America/New_York';\n\n// A scorer's note on one person in a game (\"red shirt\", \"#9\").\nexport const MAX_GAME_MEMO_LENGTH = 30;\n\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"sports.js","sourceRoot":"","sources":["../../src/constants/sports.ts"],"names":[],"mappings":"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","sourcesContent":["// Sport rule values that are persisted or validated on both sides of the boundary. What a sport\r\n// DOES with them — applying an event, recomputing a score — belongs to its adapter (SPORT-003).\r\n\r\nimport {\r\n BASKETBALL_ASSIST_EVENT_TYPE,\r\n BASKETBALL_BULK_EDIT_EVENT_TYPE,\r\n BASKETBALL_ONE_POINT_EVENT_TYPE,\r\n BASKETBALL_REBOUND_EVENT_TYPE,\r\n BASKETBALL_THREE_POINT_EVENT_TYPE,\r\n BASKETBALL_TIMEOUT_EVENT_TYPE,\r\n BASKETBALL_TWO_POINT_EVENT_TYPE,\r\n FOOTBALL_BULK_EDIT_EVENT_TYPE,\r\n FOOTBALL_ONE_POINT_EVENT_TYPE,\r\n FOOTBALL_SIX_POINT_EVENT_TYPE,\r\n FOOTBALL_TWO_POINT_EVENT_TYPE,\r\n} from '../types/sports.js';\r\nimport type { BasketballPlayerStatKey, BasketballStatEventType, FootballStatEventType } from '../types/sports.js';\r\nimport type {\r\n FinalizationNotReadyReason,\r\n GameRepairRefusal,\r\n StatEventRefusalCode,\r\n StatsIntegrityRefusal,\r\n} from '../types/stats.js';\r\n\r\n// Points a basketball stat event adds to its team's score. BULK_EDIT carries its own `value`.\r\nexport const BASKETBALL_POINTS_BY_EVENT_TYPE = {\r\n [BASKETBALL_ONE_POINT_EVENT_TYPE]: 1,\r\n [BASKETBALL_TWO_POINT_EVENT_TYPE]: 2,\r\n [BASKETBALL_THREE_POINT_EVENT_TYPE]: 3,\r\n [BASKETBALL_REBOUND_EVENT_TYPE]: 0,\r\n [BASKETBALL_ASSIST_EVENT_TYPE]: 0,\r\n [BASKETBALL_TIMEOUT_EVENT_TYPE]: 0,\r\n [BASKETBALL_BULK_EDIT_EVENT_TYPE]: 0,\r\n} as const satisfies Record<BasketballStatEventType, number>;\r\n\r\n// Basketball fantasy points (FP): each player counter times its weight. Every weight is a multiple\r\n// of one half, so FP summed across games equals FP of the summed counters exactly.\r\nexport const BASKETBALL_FANTASY_POINT_WEIGHTS = {\r\n points: 1,\r\n rebounds: 1.5,\r\n assists: 2,\r\n} as const satisfies Record<BasketballPlayerStatKey, number>;\r\n\r\n// A curling game is this many rounds; `roundScores` has exactly this many slots.\r\nexport const CURLING_ROUND_COUNT = 14;\r\n\r\n// A scored curling round is worth 1..MAX; a blank end is 0.\r\nexport const MAX_CURLING_ROUND_POINTS = 4;\r\n\r\n// The most events one scoring write may carry: each event is a document created in the write's one\r\n// transaction, and a Firestore transaction commits at most 500 writes.\r\nexport const MAX_STAT_EVENTS_PER_WRITE = 200;\r\n\r\n// The most lines and fields one Correct a game call may set; a game holds far fewer.\r\nexport const MAX_GAME_STATS_CORRECTION_TARGETS = 200;\r\n\r\n// The most destination changes one rebuild call lists in its answer; the audit events record all.\r\nexport const MAX_STATS_REPAIR_LISTED_CHANGES = 500;\r\n\r\n// The most findings one stats check keeps on its report; every finding is still counted by kind.\r\nexport const MAX_STATS_VALIDATION_DISCREPANCIES = 500;\r\n\r\n// The most events one page of a completed game's finalized record holds.\r\nexport const STAT_HISTORY_PAGE_SIZE = 1000;\r\n\r\n// Points a football stat event adds to its team's score. FOOTBALL_BULK_EDIT carries its own `value`.\r\nexport const FOOTBALL_POINTS_BY_EVENT_TYPE = {\r\n [FOOTBALL_SIX_POINT_EVENT_TYPE]: 6,\r\n [FOOTBALL_TWO_POINT_EVENT_TYPE]: 2,\r\n [FOOTBALL_ONE_POINT_EVENT_TYPE]: 1,\r\n [FOOTBALL_BULK_EDIT_EVENT_TYPE]: 0,\r\n} as const satisfies Record<FootballStatEventType, number>;\r\n\r\n// A game write's answer — a scoring event, a bulk-entry form, absence, the possession arrow, a ghost\r\n// added from the game — when the request names a team the game does not hold.\r\nexport const TEAM_NOT_IN_GAME_MESSAGE = 'That team is not playing in this game.';\r\n\r\n// A game write's answer — a bulk-entry row, absence, a memo — when the request names a person the\r\n// game's roster does not hold.\r\nexport const NOT_ON_GAME_ROSTER_MESSAGE = \"That player is not on this game's roster.\";\r\n\r\n// What the scorekeeper reads beside an event a scoring write refused.\r\nexport const STAT_EVENT_REFUSAL_COPY = {\r\n eventTypeNotInSport: \"That stat does not belong to this game's sport.\",\r\n teamNotInGame: TEAM_NOT_IN_GAME_MESSAGE,\r\n playerNotRostered: \"That player is not on this game's roster for that team.\",\r\n assisterNotRostered: \"The assisting player is not on this game's roster for that team.\",\r\n assistNotAllowed: 'Only a 2- or 3-point basket can be assisted, and never by the player who scored it.',\r\n playerNotAllowed: \"That stat is the team's; it cannot name a player.\",\r\n roundNotNext: 'That round is not the next one to record.',\r\n roundsComplete: 'Every round is already recorded.',\r\n bulkValueInvalid: 'That entry is not a real result: stats are whole numbers and never go below zero.',\r\n bulkStale: 'The numbers changed while you were editing. Check the flagged values and save again.',\r\n eventIdConflict: 'A different stat was already saved under this one. Discard it and enter it again.',\r\n voidTargetNotSportEvent: 'There is no recorded stat there to undo.',\r\n correctionNotAccepted: 'Corrections are made from Fix stats, not from the scoring screen.',\r\n undoBelowZero: 'Undoing that play would leave a stat below zero, because a later entry already lowered it.',\r\n} as const satisfies Record<StatEventRefusalCode, string>;\r\n\r\n// The operation every correction the game copy appends is recorded under, and the reason it carries.\r\n// The copy carries each recorded result exactly: where a game's recorded lines differ from what its\r\n// recorded plays replay to, one correction per field closes the difference, and these name it.\r\nexport const STATS_MIGRATION_OPERATION_ID = 'migration';\r\nexport const STATS_MIGRATION_CORRECTION_REASON =\r\n 'The recorded result differs from what its recorded plays add up to; this correction keeps the recorded result.';\r\n\r\n// What an admin reads when Fix stats refuses a game repair.\r\nexport const GAME_REPAIR_REFUSAL_COPY = {\r\n eventUnreadable: \"One of this game's recorded stats can no longer be read. Fix the event in the console, then repair the game.\",\r\n sourcesDisagree:\r\n \"This game's record, its stored numbers, and its recorded stats all disagree, so nothing says what was applied. Fix the game in the console first.\",\r\n scoreAmbiguous:\r\n \"The team's score was edited in two places to different values. Enter the score you want as a correction instead.\",\r\n fieldNotCorrectable:\r\n \"That number cannot be corrected for this sport. Restore the game from its recorded stats, or reopen it and score it again.\",\r\n lineNotRostered: \"That player is not on this game's roster.\",\r\n teamNotInGame: \"That team is not playing in this game.\",\r\n gameTooLarge: 'This repair is too large to save at once.',\r\n} as const satisfies Record<GameRepairRefusal, string>;\r\n\r\n// What the caller reads when a stats path refuses a game whose numbers changed outside the app.\r\nexport const STATS_INTEGRITY_REFUSAL_COPY = {\r\n statsOutOfBand:\r\n \"This game's stored numbers no longer match the stats recorded for it. An admin must reconcile the game in Fix stats first.\",\r\n finalRecordMissing:\r\n \"This finalized game's record of its stats is missing. An admin must repair the game in Fix stats first.\",\r\n aggregateBelowZero:\r\n 'A league, career, or hall-of-fame total holds less than this game added to it. An admin must rebuild the totals in Fix stats first.',\r\n} as const satisfies Record<StatsIntegrityRefusal, string>;\r\n\r\n// What the finish control shows while a game may not be finalized, and what finalize refuses with.\r\nexport const FINALIZATION_NOT_READY_COPY = {\r\n roundsIncomplete: 'Every round must be recorded before the game can be finalized.',\r\n} as const satisfies Record<FinalizationNotReadyReason, string>;\r\n\r\n// The hall of fame's minimum-games filter: each choice keeps the careers with at least that many\r\n// games played. None is chosen until the viewer picks one, so the hall of fame opens on everyone.\r\nexport const HALL_OF_FAME_MINIMUM_GAMES_CHOICES = [25, 50, 100] as const;\r\nexport type HallOfFameMinimumGames = (typeof HALL_OF_FAME_MINIMUM_GAMES_CHOICES)[number];\r\n"]}
1
+ {"version":3,"file":"sports.js","sourceRoot":"","sources":["../../src/constants/sports.ts"],"names":[],"mappings":"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","sourcesContent":["// Sport rule values that are persisted or validated on both sides of the boundary. What a sport\n// DOES with them — applying an event, recomputing a score — belongs to its adapter (SPORT-003).\n\nimport {\n BASKETBALL_ASSIST_EVENT_TYPE,\n BASKETBALL_BULK_EDIT_EVENT_TYPE,\n BASKETBALL_ONE_POINT_EVENT_TYPE,\n BASKETBALL_REBOUND_EVENT_TYPE,\n BASKETBALL_THREE_POINT_EVENT_TYPE,\n BASKETBALL_TIMEOUT_EVENT_TYPE,\n BASKETBALL_TWO_POINT_EVENT_TYPE,\n FOOTBALL_BULK_EDIT_EVENT_TYPE,\n FOOTBALL_ONE_POINT_EVENT_TYPE,\n FOOTBALL_SIX_POINT_EVENT_TYPE,\n FOOTBALL_TWO_POINT_EVENT_TYPE,\n} from '../types/sports.js';\nimport type { BasketballPlayerStatKey, BasketballStatEventType, FootballStatEventType } from '../types/sports.js';\nimport type {\n FinalizationNotReadyReason,\n GameRepairRefusal,\n StatEventRefusalCode,\n StatsIntegrityRefusal,\n} from '../types/stats.js';\n\n// Points a basketball stat event adds to its team's score. BULK_EDIT carries its own `value`.\nexport const BASKETBALL_POINTS_BY_EVENT_TYPE = {\n [BASKETBALL_ONE_POINT_EVENT_TYPE]: 1,\n [BASKETBALL_TWO_POINT_EVENT_TYPE]: 2,\n [BASKETBALL_THREE_POINT_EVENT_TYPE]: 3,\n [BASKETBALL_REBOUND_EVENT_TYPE]: 0,\n [BASKETBALL_ASSIST_EVENT_TYPE]: 0,\n [BASKETBALL_TIMEOUT_EVENT_TYPE]: 0,\n [BASKETBALL_BULK_EDIT_EVENT_TYPE]: 0,\n} as const satisfies Record<BasketballStatEventType, number>;\n\n// Basketball fantasy points (FP): each player counter times its weight. Every weight is a multiple\n// of one half, so FP summed across games equals FP of the summed counters exactly.\nexport const BASKETBALL_FANTASY_POINT_WEIGHTS = {\n points: 1,\n rebounds: 1.5,\n assists: 2,\n} as const satisfies Record<BasketballPlayerStatKey, number>;\n\n// A curling game is this many rounds; `roundScores` has exactly this many slots.\nexport const CURLING_ROUND_COUNT = 14;\n\n// A scored curling round is worth 1..MAX; a blank end is 0.\nexport const MAX_CURLING_ROUND_POINTS = 4;\n\n// The most events one scoring write may carry: each event is a document created in the write's one\n// transaction, and a Firestore transaction commits at most 500 writes.\nexport const MAX_STAT_EVENTS_PER_WRITE = 200;\n\n// The most lines and fields one Correct a game call may set; a game holds far fewer.\nexport const MAX_GAME_STATS_CORRECTION_TARGETS = 200;\n\n// The most destination changes one rebuild call lists in its answer; the audit events record all.\nexport const MAX_STATS_REPAIR_LISTED_CHANGES = 500;\n\n// The most findings one stats check keeps on its report; every finding is still counted by kind.\nexport const MAX_STATS_VALIDATION_DISCREPANCIES = 500;\n\n// The most events one page of a completed game's finalized record holds.\nexport const STAT_HISTORY_PAGE_SIZE = 1000;\n\n// Points a football stat event adds to its team's score. FOOTBALL_BULK_EDIT carries its own `value`.\nexport const FOOTBALL_POINTS_BY_EVENT_TYPE = {\n [FOOTBALL_SIX_POINT_EVENT_TYPE]: 6,\n [FOOTBALL_TWO_POINT_EVENT_TYPE]: 2,\n [FOOTBALL_ONE_POINT_EVENT_TYPE]: 1,\n [FOOTBALL_BULK_EDIT_EVENT_TYPE]: 0,\n} as const satisfies Record<FootballStatEventType, number>;\n\n// A game write's answer — a scoring event, a bulk-entry form, absence, the possession arrow, a ghost\n// added from the game — when the request names a team the game does not hold.\nexport const TEAM_NOT_IN_GAME_MESSAGE = 'That team is not playing in this game.';\n\n// A game write's answer — a bulk-entry row, absence, a memo — when the request names a person the\n// game's roster does not hold.\nexport const NOT_ON_GAME_ROSTER_MESSAGE = \"That player is not on this game's roster.\";\n\n// What the scorekeeper reads beside an event a scoring write refused.\nexport const STAT_EVENT_REFUSAL_COPY = {\n eventTypeNotInSport: \"That stat does not belong to this game's sport.\",\n teamNotInGame: TEAM_NOT_IN_GAME_MESSAGE,\n playerNotRostered: \"That player is not on this game's roster for that team.\",\n assisterNotRostered: \"The assisting player is not on this game's roster for that team.\",\n assistNotAllowed: 'Only a 2- or 3-point basket can be assisted, and never by the player who scored it.',\n playerNotAllowed: \"That stat is the team's; it cannot name a player.\",\n roundNotNext: 'That round is not the next one to record.',\n roundsComplete: 'Every round is already recorded.',\n bulkValueInvalid: 'That entry is not a real result: stats are whole numbers and never go below zero.',\n bulkStale: 'The numbers changed while you were editing. Check the flagged values and save again.',\n eventIdConflict: 'A different stat was already saved under this one. Discard it and enter it again.',\n voidTargetNotSportEvent: 'There is no recorded stat there to undo.',\n correctionNotAccepted: 'Corrections are made from Fix stats, not from the scoring screen.',\n undoBelowZero: 'Undoing that play would leave a stat below zero, because a later entry already lowered it.',\n} as const satisfies Record<StatEventRefusalCode, string>;\n\n// The operation every correction the game copy appends is recorded under, and the reason it carries.\n// The copy carries each recorded result exactly: where a game's recorded lines differ from what its\n// recorded plays replay to, one correction per field closes the difference, and these name it.\nexport const STATS_MIGRATION_OPERATION_ID = 'migration';\nexport const STATS_MIGRATION_CORRECTION_REASON =\n 'The recorded result differs from what its recorded plays add up to; this correction keeps the recorded result.';\n\n// What an admin reads when Fix stats refuses a game repair.\nexport const GAME_REPAIR_REFUSAL_COPY = {\n eventUnreadable: \"One of this game's recorded stats can no longer be read. Fix the event in the console, then repair the game.\",\n sourcesDisagree:\n \"This game's record, its stored numbers, and its recorded stats all disagree, so nothing says what was applied. Fix the game in the console first.\",\n scoreAmbiguous:\n \"The team's score was edited in two places to different values. Enter the score you want as a correction instead.\",\n fieldNotCorrectable:\n \"That number cannot be corrected for this sport. Restore the game from its recorded stats, or reopen it and score it again.\",\n lineNotRostered: \"That player is not on this game's roster.\",\n teamNotInGame: \"That team is not playing in this game.\",\n gameTooLarge: 'This repair is too large to save at once.',\n} as const satisfies Record<GameRepairRefusal, string>;\n\n// What the caller reads when a stats path refuses a game whose numbers changed outside the app.\nexport const STATS_INTEGRITY_REFUSAL_COPY = {\n statsOutOfBand:\n \"This game's stored numbers no longer match the stats recorded for it. An admin must reconcile the game in Fix stats first.\",\n finalRecordMissing:\n \"This finalized game's record of its stats is missing. An admin must repair the game in Fix stats first.\",\n aggregateBelowZero:\n 'A league, career, or hall-of-fame total holds less than this game added to it. An admin must rebuild the totals in Fix stats first.',\n} as const satisfies Record<StatsIntegrityRefusal, string>;\n\n// What the finish control shows while a game may not be finalized, and what finalize refuses with.\nexport const FINALIZATION_NOT_READY_COPY = {\n roundsIncomplete: 'Every round must be recorded before the game can be finalized.',\n} as const satisfies Record<FinalizationNotReadyReason, string>;\n\n// The hall of fame's minimum-games filter: each choice keeps the careers with at least that many\n// games played. None is chosen until the viewer picks one, so the hall of fame opens on everyone.\nexport const HALL_OF_FAME_MINIMUM_GAMES_CHOICES = [25, 50, 100] as const;\nexport type HallOfFameMinimumGames = (typeof HALL_OF_FAME_MINIMUM_GAMES_CHOICES)[number];\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"storage-keys.js","sourceRoot":"","sources":["../../src/constants/storage-keys.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,mGAAmG;AACnG,mGAAmG;AAKnG;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,OAAO,CAAC;AAEzC,wFAAwF;AACxF,MAAM,CAAC,MAAM,0BAA0B,GAAG,yBAAyB,CAAC;AAEpE,yEAAyE;AACzE,MAAM,CAAC,MAAM,2BAA2B,GAAG,gCAAgC,CAAC;AAE5E;;;GAGG;AACH,MAAM,CAAC,MAAM,qCAAqC,GAAG,oCAAoC,CAAC;AAE1F;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,sBAAsB,CAAC;AAErD;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,mCAAmC,CAAC;AAE3E,uGAAuG;AACvG,MAAM,CAAC,MAAM,mCAAmC,GAAG,kCAAkC,CAAC;AAEtF,kFAAkF;AAClF,MAAM,CAAC,MAAM,oCAAoC,GAAG,yCAAyC,CAAC;AAE9F,yGAAyG;AACzG,MAAM,CAAC,MAAM,mCAAmC,GAAG,2BAA2B,CAAC;AAE/E;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAC,SAA2B,EAAE,QAAkB;IACtF,OAAO,GAAG,mCAAmC,IAAI,SAAS,IAAI,QAAQ,EAAE,CAAC;AAC3E,CAAC;AAED,6EAA6E;AAC7E,MAAM,CAAC,MAAM,6BAA6B,GAAG,kCAAkC,CAAC;AAEhF,iGAAiG;AACjG,MAAM,CAAC,MAAM,6BAA6B,GAAG,qBAAqB,CAAC;AAEnE;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAW,EAAE,MAAc;IAC7D,OAAO,GAAG,6BAA6B,IAAI,GAAG,IAAI,MAAM,EAAE,CAAC;AAC7D,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,yBAAyB,CAAC","sourcesContent":["// Browser-storage keys, each declared once with the same-tab event its writer dispatches: a\r\n// localStorage write fires the native `storage` event in every other tab but never in the tab that\r\n// wrote it. Every value is persisted on viewers' devices, so changing one forgets what they saved.\r\n\r\nimport type { ColumnCatalogKey } from '../sports/column-catalog.js';\r\nimport type { SportKey } from '../types/sports.js';\r\n\r\n/**\r\n * localStorage key holding the device's theme. It is `next-themes`' default key, the one viewers'\r\n * saved themes are stored under.\r\n */\r\nexport const THEME_STORAGE_KEY = 'theme';\r\n\r\n/** localStorage key: `'true'` when the viewer has turned motion down on this device. */\r\nexport const REDUCED_MOTION_STORAGE_KEY = 'q-sports-reduced-motion';\r\n\r\n/** Same-tab event dispatched when REDUCED_MOTION_STORAGE_KEY changes. */\r\nexport const REDUCED_MOTION_CHANGE_EVENT = 'q-sports-reduced-motion-change';\r\n\r\n/**\r\n * localStorage key holding the differences between this device's settings and the account's that\r\n * the viewer chose to keep, so the compare prompt does not ask about them again while they hold.\r\n */\r\nexport const VIEWER_SETTINGS_DISMISSAL_STORAGE_KEY = 'q-sports-viewer-settings-dismissed';\r\n\r\n/**\r\n * localStorage key holding the `_appConfig/app` version this device last loaded. Written only once\r\n * a load has confirmed it, so a release that never landed is retried next session.\r\n */\r\nexport const LS_VERSION_KEY = 'q-sports-app-version';\r\n\r\n/**\r\n * sessionStorage key naming the version the version gate last reloaded for. It is what tells the\r\n * load after that reload apart from a mismatch still to act on, so a stuck release cannot hold a\r\n * tab in a reload loop; a new tab starts without it and tries again.\r\n */\r\nexport const SS_RELOAD_ATTEMPTED_KEY = 'q-sports-version-reload-attempted';\r\n\r\n/** localStorage key: `'true'` while the admin sidebar is collapsed to its icon rail on this device. */\r\nexport const ADMIN_SIDEBAR_COLLAPSED_STORAGE_KEY = 'q-sports-admin-sidebar-collapsed';\r\n\r\n/** Same-tab event dispatched when ADMIN_SIDEBAR_COLLAPSED_STORAGE_KEY changes. */\r\nexport const ADMIN_SIDEBAR_COLLAPSED_CHANGE_EVENT = 'q-sports-admin-sidebar-collapsed-change';\r\n\r\n/** The prefix every stats table View-state key starts with; `statsTableViewStorageKey` adds the rest. */\r\nexport const STATS_TABLE_VIEW_STORAGE_KEY_PREFIX = 'q-sports-stats-table-view';\r\n\r\n/**\r\n * localStorage key holding one stats table's View state on this device — its preset or Custom layout,\r\n * its sort, and its switches — for one table type of one sport: `{prefix}:{tableType}:{sportKey}`.\r\n */\r\nexport function statsTableViewStorageKey(tableType: ColumnCatalogKey, sportKey: SportKey): string {\r\n return `${STATS_TABLE_VIEW_STORAGE_KEY_PREFIX}:${tableType}:${sportKey}`;\r\n}\r\n\r\n/** Same-tab event dispatched when any stats table View-state key changes. */\r\nexport const STATS_TABLE_VIEW_CHANGE_EVENT = 'q-sports-stats-table-view-change';\r\n\r\n/** The prefix every scorer's stat-queue key starts with; `statQueueStorageKey` adds the rest. */\r\nexport const STAT_QUEUE_STORAGE_KEY_PREFIX = 'q-sports-stat-queue';\r\n\r\n/**\r\n * localStorage key holding one scorer's unsent and refused stat events for one game on this device —\r\n * `{prefix}:{uid}:{gameId}` — so a reload loses nothing, and another account signed in on the same\r\n * browser never sends or sees them.\r\n */\r\nexport function statQueueStorageKey(uid: string, gameId: string): string {\r\n return `${STAT_QUEUE_STORAGE_KEY_PREFIX}:${uid}:${gameId}`;\r\n}\r\n\r\n/**\r\n * localStorage key, and the first-party cookie's name, holding this browser's analytics consent:\r\n * `'granted'` or `'denied'`; absent until the visitor chooses.\r\n */\r\nexport const COOKIE_CONSENT_STORAGE_KEY = 'q-sports-cookie-consent';\r\n"]}
1
+ {"version":3,"file":"storage-keys.js","sourceRoot":"","sources":["../../src/constants/storage-keys.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,mGAAmG;AACnG,mGAAmG;AAKnG;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,OAAO,CAAC;AAEzC,wFAAwF;AACxF,MAAM,CAAC,MAAM,0BAA0B,GAAG,yBAAyB,CAAC;AAEpE,yEAAyE;AACzE,MAAM,CAAC,MAAM,2BAA2B,GAAG,gCAAgC,CAAC;AAE5E;;;GAGG;AACH,MAAM,CAAC,MAAM,qCAAqC,GAAG,oCAAoC,CAAC;AAE1F;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,sBAAsB,CAAC;AAErD;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,mCAAmC,CAAC;AAE3E,uGAAuG;AACvG,MAAM,CAAC,MAAM,mCAAmC,GAAG,kCAAkC,CAAC;AAEtF,kFAAkF;AAClF,MAAM,CAAC,MAAM,oCAAoC,GAAG,yCAAyC,CAAC;AAE9F,yGAAyG;AACzG,MAAM,CAAC,MAAM,mCAAmC,GAAG,2BAA2B,CAAC;AAE/E;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAC,SAA2B,EAAE,QAAkB;IACtF,OAAO,GAAG,mCAAmC,IAAI,SAAS,IAAI,QAAQ,EAAE,CAAC;AAC3E,CAAC;AAED,6EAA6E;AAC7E,MAAM,CAAC,MAAM,6BAA6B,GAAG,kCAAkC,CAAC;AAEhF,iGAAiG;AACjG,MAAM,CAAC,MAAM,6BAA6B,GAAG,qBAAqB,CAAC;AAEnE;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAW,EAAE,MAAc;IAC7D,OAAO,GAAG,6BAA6B,IAAI,GAAG,IAAI,MAAM,EAAE,CAAC;AAC7D,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,yBAAyB,CAAC","sourcesContent":["// Browser-storage keys, each declared once with the same-tab event its writer dispatches: a\n// localStorage write fires the native `storage` event in every other tab but never in the tab that\n// wrote it. Every value is persisted on viewers' devices, so changing one forgets what they saved.\n\nimport type { ColumnCatalogKey } from '../sports/column-catalog.js';\nimport type { SportKey } from '../types/sports.js';\n\n/**\n * localStorage key holding the device's theme. It is `next-themes`' default key, the one viewers'\n * saved themes are stored under.\n */\nexport const THEME_STORAGE_KEY = 'theme';\n\n/** localStorage key: `'true'` when the viewer has turned motion down on this device. */\nexport const REDUCED_MOTION_STORAGE_KEY = 'q-sports-reduced-motion';\n\n/** Same-tab event dispatched when REDUCED_MOTION_STORAGE_KEY changes. */\nexport const REDUCED_MOTION_CHANGE_EVENT = 'q-sports-reduced-motion-change';\n\n/**\n * localStorage key holding the differences between this device's settings and the account's that\n * the viewer chose to keep, so the compare prompt does not ask about them again while they hold.\n */\nexport const VIEWER_SETTINGS_DISMISSAL_STORAGE_KEY = 'q-sports-viewer-settings-dismissed';\n\n/**\n * localStorage key holding the `_appConfig/app` version this device last loaded. Written only once\n * a load has confirmed it, so a release that never landed is retried next session.\n */\nexport const LS_VERSION_KEY = 'q-sports-app-version';\n\n/**\n * sessionStorage key naming the version the version gate last reloaded for. It is what tells the\n * load after that reload apart from a mismatch still to act on, so a stuck release cannot hold a\n * tab in a reload loop; a new tab starts without it and tries again.\n */\nexport const SS_RELOAD_ATTEMPTED_KEY = 'q-sports-version-reload-attempted';\n\n/** localStorage key: `'true'` while the admin sidebar is collapsed to its icon rail on this device. */\nexport const ADMIN_SIDEBAR_COLLAPSED_STORAGE_KEY = 'q-sports-admin-sidebar-collapsed';\n\n/** Same-tab event dispatched when ADMIN_SIDEBAR_COLLAPSED_STORAGE_KEY changes. */\nexport const ADMIN_SIDEBAR_COLLAPSED_CHANGE_EVENT = 'q-sports-admin-sidebar-collapsed-change';\n\n/** The prefix every stats table View-state key starts with; `statsTableViewStorageKey` adds the rest. */\nexport const STATS_TABLE_VIEW_STORAGE_KEY_PREFIX = 'q-sports-stats-table-view';\n\n/**\n * localStorage key holding one stats table's View state on this device — its preset or Custom layout,\n * its sort, and its switches — for one table type of one sport: `{prefix}:{tableType}:{sportKey}`.\n */\nexport function statsTableViewStorageKey(tableType: ColumnCatalogKey, sportKey: SportKey): string {\n return `${STATS_TABLE_VIEW_STORAGE_KEY_PREFIX}:${tableType}:${sportKey}`;\n}\n\n/** Same-tab event dispatched when any stats table View-state key changes. */\nexport const STATS_TABLE_VIEW_CHANGE_EVENT = 'q-sports-stats-table-view-change';\n\n/** The prefix every scorer's stat-queue key starts with; `statQueueStorageKey` adds the rest. */\nexport const STAT_QUEUE_STORAGE_KEY_PREFIX = 'q-sports-stat-queue';\n\n/**\n * localStorage key holding one scorer's unsent and refused stat events for one game on this device —\n * `{prefix}:{uid}:{gameId}` — so a reload loses nothing, and another account signed in on the same\n * browser never sends or sees them.\n */\nexport function statQueueStorageKey(uid: string, gameId: string): string {\n return `${STAT_QUEUE_STORAGE_KEY_PREFIX}:${uid}:${gameId}`;\n}\n\n/**\n * localStorage key, and the first-party cookie's name, holding this browser's analytics consent:\n * `'granted'` or `'denied'`; absent until the visitor chooses.\n */\nexport const COOKIE_CONSENT_STORAGE_KEY = 'q-sports-cookie-consent';\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"catalogs.js","sourceRoot":"","sources":["../../src/doc-schemas/catalogs.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,sDAAsD;AAEtD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,uBAAuB,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClG,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,oGAAoG;AACpG,kCAAkC;AAClC,MAAM,CAAC,MAAM,aAAa,GAAG,uBAAuB,CAAC;AAErD,8EAA8E;AAC9E,MAAM,CAAC,MAAM,eAAe,GAAG,eAAe,CAAC,uBAAuB,CAAC,CAAC;AAExE,mGAAmG;AACnG,qGAAqG;AACrG,sGAAsG;AACtG,EAAE;AACF,gGAAgG;AAChG,kGAAkG;AAClG,gFAAgF;AAChF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,YAAY,CAAC;IAClD,IAAI,EAAE,eAAe;IACrB,cAAc,EAAE,eAAe,CAAC,QAAQ,EAAE;IAC1C,WAAW,EAAE,WAAW;IACxB,SAAS,EAAE,aAAa;IACxB,SAAS,EAAE,aAAa;CACzB,CAAC,CAAC","sourcesContent":["// The award catalog. Each award is its own document, keeping the id granted awards already\r\n// reference. The venue catalog is ./game-location.ts.\r\n\r\nimport { z } from 'zod';\r\nimport { AWARD_NAME_INPUT_FORMAT } from '../input-formats/index.js';\r\nimport { countSchema, documentIdSegmentSchema, epochMsSchema, textFieldSchema } from './atoms.js';\r\nimport { pictureIdSchema } from './gallery.js';\r\n\r\n// An award's id, which every grant names and so never changes: an award created by its badge upload\r\n// takes that upload's staging id.\r\nexport const awardIdSchema = documentIdSegmentSchema;\r\n\r\n// An award's name, as the catalog stores it and the creating upload sends it.\r\nexport const awardNameSchema = textFieldSchema(AWARD_NAME_INPUT_FORMAT);\r\n\r\n// `awardDefinitions/{awardId}` — one award in the catalog. It is created by the publication of its\r\n// first badge, so an award created that way always has one; an award with no badge yet gets one from\r\n// the next badge upload for it. A badge is replaced only by a newer upload, never removed on its own.\r\n//\r\n// `holderCount` is how many accounts hold the award now. Every grant and revoke moves it in the\r\n// transaction that writes the grant, so a deletion — refused while anyone holds the award — and a\r\n// concurrent grant conflict on this document instead of racing past each other.\r\nexport const AwardDefinitionSchema = z.strictObject({\r\n name: awardNameSchema,\r\n badgePictureId: pictureIdSchema.optional(),\r\n holderCount: countSchema,\r\n createdAt: epochMsSchema,\r\n updatedAt: epochMsSchema,\r\n});\r\nexport type AwardDefinition = z.infer<typeof AwardDefinitionSchema>;\r\n"]}
1
+ {"version":3,"file":"catalogs.js","sourceRoot":"","sources":["../../src/doc-schemas/catalogs.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,sDAAsD;AAEtD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,uBAAuB,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClG,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,oGAAoG;AACpG,kCAAkC;AAClC,MAAM,CAAC,MAAM,aAAa,GAAG,uBAAuB,CAAC;AAErD,8EAA8E;AAC9E,MAAM,CAAC,MAAM,eAAe,GAAG,eAAe,CAAC,uBAAuB,CAAC,CAAC;AAExE,mGAAmG;AACnG,qGAAqG;AACrG,sGAAsG;AACtG,EAAE;AACF,gGAAgG;AAChG,kGAAkG;AAClG,gFAAgF;AAChF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,YAAY,CAAC;IAClD,IAAI,EAAE,eAAe;IACrB,cAAc,EAAE,eAAe,CAAC,QAAQ,EAAE;IAC1C,WAAW,EAAE,WAAW;IACxB,SAAS,EAAE,aAAa;IACxB,SAAS,EAAE,aAAa;CACzB,CAAC,CAAC","sourcesContent":["// The award catalog. Each award is its own document, keeping the id granted awards already\n// reference. The venue catalog is ./game-location.ts.\n\nimport { z } from 'zod';\nimport { AWARD_NAME_INPUT_FORMAT } from '../input-formats/index.js';\nimport { countSchema, documentIdSegmentSchema, epochMsSchema, textFieldSchema } from './atoms.js';\nimport { pictureIdSchema } from './gallery.js';\n\n// An award's id, which every grant names and so never changes: an award created by its badge upload\n// takes that upload's staging id.\nexport const awardIdSchema = documentIdSegmentSchema;\n\n// An award's name, as the catalog stores it and the creating upload sends it.\nexport const awardNameSchema = textFieldSchema(AWARD_NAME_INPUT_FORMAT);\n\n// `awardDefinitions/{awardId}` — one award in the catalog. It is created by the publication of its\n// first badge, so an award created that way always has one; an award with no badge yet gets one from\n// the next badge upload for it. A badge is replaced only by a newer upload, never removed on its own.\n//\n// `holderCount` is how many accounts hold the award now. Every grant and revoke moves it in the\n// transaction that writes the grant, so a deletion — refused while anyone holds the award — and a\n// concurrent grant conflict on this document instead of racing past each other.\nexport const AwardDefinitionSchema = z.strictObject({\n name: awardNameSchema,\n badgePictureId: pictureIdSchema.optional(),\n holderCount: countSchema,\n createdAt: epochMsSchema,\n updatedAt: epochMsSchema,\n});\nexport type AwardDefinition = z.infer<typeof AwardDefinitionSchema>;\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"game-location.js","sourceRoot":"","sources":["../../src/doc-schemas/game-location.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,iGAAiG;AACjG,oBAAoB;AAEpB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,MAAM,CAAC,MAAM,oBAAoB,GAAG,uBAAuB,CAAC;AAE5D,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE;IACzB,6CAA6C;IAC7C,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACjC,sDAAsD;IACtD,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAClC,8FAA8F;IAC9F,6CAA6C;IAC7C,SAAS,EAAE,eAAe,CAAC,QAAQ,EAAE;IACrC,SAAS,EAAE,aAAa;IACxB,SAAS,EAAE,aAAa;CACzB,CAAC,CAAC","sourcesContent":["// `gameLocations/{gameLocationId}` — one venue games are played at. Games name it by id\r\n// (`gameLocationId`) beside their own free-text label, so a game still reads correctly after its\r\n// venue is deleted.\r\n\r\nimport { z } from 'zod';\r\nimport { documentIdSegmentSchema, epochMsSchema } from './atoms.js';\r\nimport { pictureIdSchema } from './gallery.js';\r\n\r\nexport const gameLocationIdSchema = documentIdSegmentSchema;\r\n\r\nexport const GameLocationSchema = z.object({\r\n name: z.string().min(1),\r\n streetAddress: z.string(),\r\n // Courts, sheets, or rooms within the venue.\r\n subLocations: z.array(z.string()),\r\n // Directions for players; absent when there are none.\r\n memo: z.string().min(1).optional(),\r\n // The venue's picture. Only the picture pipeline writes it (ARCH-001); the location callables\r\n // write the fields above and never this one.\r\n pictureId: pictureIdSchema.optional(),\r\n createdAt: epochMsSchema,\r\n updatedAt: epochMsSchema,\r\n});\r\nexport type GameLocation = z.infer<typeof GameLocationSchema>;\r\n"]}
1
+ {"version":3,"file":"game-location.js","sourceRoot":"","sources":["../../src/doc-schemas/game-location.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,iGAAiG;AACjG,oBAAoB;AAEpB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,MAAM,CAAC,MAAM,oBAAoB,GAAG,uBAAuB,CAAC;AAE5D,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE;IACzB,6CAA6C;IAC7C,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACjC,sDAAsD;IACtD,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAClC,8FAA8F;IAC9F,6CAA6C;IAC7C,SAAS,EAAE,eAAe,CAAC,QAAQ,EAAE;IACrC,SAAS,EAAE,aAAa;IACxB,SAAS,EAAE,aAAa;CACzB,CAAC,CAAC","sourcesContent":["// `gameLocations/{gameLocationId}` — one venue games are played at. Games name it by id\n// (`gameLocationId`) beside their own free-text label, so a game still reads correctly after its\n// venue is deleted.\n\nimport { z } from 'zod';\nimport { documentIdSegmentSchema, epochMsSchema } from './atoms.js';\nimport { pictureIdSchema } from './gallery.js';\n\nexport const gameLocationIdSchema = documentIdSegmentSchema;\n\nexport const GameLocationSchema = z.object({\n name: z.string().min(1),\n streetAddress: z.string(),\n // Courts, sheets, or rooms within the venue.\n subLocations: z.array(z.string()),\n // Directions for players; absent when there are none.\n memo: z.string().min(1).optional(),\n // The venue's picture. Only the picture pipeline writes it (ARCH-001); the location callables\n // write the fields above and never this one.\n pictureId: pictureIdSchema.optional(),\n createdAt: epochMsSchema,\n updatedAt: epochMsSchema,\n});\nexport type GameLocation = z.infer<typeof GameLocationSchema>;\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"game-stats.js","sourceRoot":"","sources":["../../src/doc-schemas/game-stats.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,6CAA6C;AAC7C,EAAE;AACF,+FAA+F;AAC/F,mGAAmG;AACnG,kGAAkG;AAClG,6EAA6E;AAC7E,EAAE;AACF,oGAAoG;AACpG,kGAAkG;AAClG,oFAAoF;AAEpF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AACvF,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAClF,OAAO,EACL,0CAA0C,EAC1C,+BAA+B,EAC/B,+BAA+B,EAC/B,2BAA2B,EAC3B,uCAAuC,EACvC,yBAAyB,EACzB,6BAA6B,EAC7B,8BAA8B,EAC9B,8BAA8B,EAC9B,sBAAsB,EACtB,6BAA6B,EAC7B,8BAA8B,EAC9B,uBAAuB,GACxB,MAAM,oBAAoB,CAAC;AAO5B,OAAO,EACL,0BAA0B,EAC1B,2BAA2B,EAC3B,yBAAyB,EACzB,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EACL,2BAA2B,EAC3B,WAAW,EACX,aAAa,EACb,wBAAwB,EACxB,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,EACjB,sBAAsB,EACtB,YAAY,EACZ,YAAY,GACb,MAAM,YAAY,CAAC;AAEpB,gGAAgG;AAChG,wFAAwF;AACxF,gGAAgG;AAEhG,kGAAkG;AAClG,2DAA2D;AAC3D,SAAS,YAAY,CAA2B,IAAkB;IAChE,OAAO,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC,CAAkC,CAAC;AACpG,CAAC;AAED,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,YAAY,CAAC,YAAY,CAAC,yBAAyB,CAAC,CAAC,CAAC;AAGjG,+FAA+F;AAC/F,8FAA8F;AAC9F,kGAAkG;AAClG,mCAAmC;AACnC,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC,YAAY,CAAC;IACxD,GAAG,YAAY,CAAC,2BAA2B,CAAC;IAC5C,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE;CACxC,CAAC,CAAC;AAGH,sGAAsG;AACtG,2CAA2C;AAC3C,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,YAAY,CAAC;IACnD,GAAG,YAAY,CAAC,sBAAsB,CAAC;IACvC,WAAW,EAAE,CAAC;SACX,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC,QAAQ,EAAE,CAAC;SACvE,MAAM,CAAC,mBAAmB,CAAC;CAC/B,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,YAAY,CAAC,YAAY,CAAC,uBAAuB,CAAC,CAAC,CAAC;AAG7F,gGAAgG;AAChG,6DAA6D;AAC7D,gGAAgG;AAEhG,8FAA8F;AAC9F,oGAAoG;AACpG,qGAAqG;AACrG,yEAAyE;AACzE,MAAM,UAAU,eAAe,CAC7B,QAAkB,EAClB,UAAsB;IAEtB,OAAO,CAAC,CAAC,YAAY,CAAC;QACpB,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC,YAAY,EAAE,QAAQ,CAAC;QAC3C,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,wBAAwB,EAAE,UAAU,CAAC;KAC5D,CAAC,CAAC;AACL,CAAC;AAED,gGAAgG;AAChG,SAAS;AACT,gGAAgG;AAEhG,gGAAgG;AAChG,iGAAiG;AACjG,MAAM,aAAa,GAAG;IACpB,OAAO,EAAE,iBAAiB;IAC1B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACrC,UAAU,EAAE,aAAa;IACzB,UAAU,EAAE,aAAa,CAAC,QAAQ,EAAE;IACpC,gBAAgB,EAAE,YAAY,CAAC,QAAQ,EAAE;IACzC,mBAAmB,EAAE,CAAC,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC,QAAQ,EAAE;IACjE,sFAAsF;IACtF,gBAAgB,EAAE,YAAY,CAAC,QAAQ,EAAE;CAC1C,CAAC;AAEF,oGAAoG;AACpG,MAAM,iBAAiB,GAAG;IACxB,MAAM,EAAE,YAAY;IACpB,YAAY,EAAE,YAAY;IAC1B,mBAAmB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC3C,CAAC;AAEF,aAAa;AAEb,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,CAAC,YAAY,CAAC;IAC1D,GAAG,aAAa;IAChB,GAAG,iBAAiB;IACpB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,+BAA+B,CAAC;CACjD,CAAC,CAAC;AAEH,qFAAqF;AACrF,MAAM,CAAC,MAAM,mCAAmC,GAAG,CAAC;KACjD,YAAY,CAAC;IACZ,GAAG,aAAa;IAChB,GAAG,iBAAiB;IACpB,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,0CAA0C,CAAC;IACxD,gBAAgB,EAAE,YAAY,CAAC,QAAQ,EAAE;IACzC,6BAA6B,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACrD,CAAC;KACD,MAAM,CACL,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,6BAA6B,KAAK,SAAS,IAAI,KAAK,CAAC,gBAAgB,KAAK,SAAS,EACpG,0CAA0C,CAC3C,CAAC;AAEJ,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAAC,CAAC,YAAY,CAAC;IAC7D,GAAG,aAAa;IAChB,GAAG,iBAAiB;IACpB,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,uCAAuC,CAAC;CACtD,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,CAAC,YAAY,CAAC;IACzD,GAAG,aAAa;IAChB,MAAM,EAAE,YAAY;IACpB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,6BAA6B,CAAC;CAC/C,CAAC,CAAC;AAEH,gGAAgG;AAChG,iGAAiG;AACjG,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,CAAC,YAAY,CAAC;IAC1D,GAAG,aAAa;IAChB,GAAG,iBAAiB;IACpB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,+BAA+B,CAAC;IAChD,IAAI,EAAE,2BAA2B;IACjC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;IACvB,aAAa,EAAE,WAAW,CAAC,QAAQ,EAAE;CACtC,CAAC,CAAC;AAEH,mEAAmE;AAEnE,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;AAElF,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,CAAC,YAAY,CAAC;IACzD,GAAG,aAAa;IAChB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,8BAA8B,CAAC;IAC/C,MAAM,EAAE,YAAY;IACpB,WAAW,EAAE,wBAAwB;IACrC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,wBAAwB,CAAC;CAC9D,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC,YAAY,CAAC;IACvD,GAAG,aAAa;IAChB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,8BAA8B,CAAC;IAC/C,WAAW,EAAE,wBAAwB;CACtC,CAAC,CAAC;AAEH,mCAAmC;AAEnC,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,YAAY,CAAC;IACrD,GAAG,aAAa;IAChB,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,8BAA8B,CAAC;IAC5C,MAAM,EAAE,YAAY;CACrB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC,YAAY,CAAC;IACxD,GAAG,aAAa;IAChB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,6BAA6B,CAAC;IAC9C,MAAM,EAAE,YAAY;IACpB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;IACvB,aAAa,EAAE,WAAW;CAC3B,CAAC,CAAC;AAEH,0DAA0D;AAE1D,kGAAkG;AAClG,qEAAqE;AACrE,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC;KACjC,YAAY,CAAC;IACZ,GAAG,aAAa;IAChB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC;IACrC,aAAa,EAAE,iBAAiB;CACjC,CAAC;KACD,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,KAAK,eAAe,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE;IACzE,OAAO,EAAE,gDAAgD;IACzD,IAAI,EAAE,CAAC,SAAS,CAAC;CAClB,CAAC,CAAC;AAEL,gGAAgG;AAChG,kGAAkG;AAClG,6CAA6C;AAC7C,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC;KACvC,YAAY,CAAC;IACZ,GAAG,aAAa;IAChB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,0BAA0B,CAAC;IAC3C,MAAM,EAAE,YAAY;IACpB,YAAY,EAAE,YAAY,CAAC,QAAQ,EAAE;IACrC,IAAI,EAAE,oBAAoB;IAC1B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;IACvB,gBAAgB,EAAE,CAAC,CAAC,IAAI,CAAC,2BAA2B,CAAC;IACrD,WAAW,EAAE,sBAAsB;IACnC,MAAM,EAAE,kBAAkB;IAC1B,4FAA4F;IAC5F,iFAAiF;IACjF,sBAAsB,EAAE,YAAY,CAAC,QAAQ,EAAE;CAChD,CAAC;KACD,MAAM,CACL,CAAC,KAAK,EAAE,EAAE,CACR,KAAK,CAAC,OAAO;IACb,qBAAqB,CACnB,KAAK,CAAC,WAAW,EACjB,KAAK,CAAC,YAAY,KAAK,SAAS;QAC9B,CAAC,CAAC,KAAK,CAAC,MAAM;QACd,CAAC,CAAC,uBAAuB,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,sBAAsB,IAAI,KAAK,CAAC,YAAY,CAAC,EAC7F,KAAK,CAAC,IAAI,CACX,EACH,EAAE,OAAO,EAAE,yEAAyE,EAAE,IAAI,EAAE,CAAC,SAAS,CAAC,EAAE,CAC1G,CAAC;AAEJ,oGAAoG;AACpG,aAAa;AACb,SAAS,kBAAkB,CAAC,KAK3B;IACC,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,gBAAgB,EAAE,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACpF,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC;IAClE,IAAI,OAAO,KAAK,CAAC,IAAI,OAAO,KAAK,KAAK,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC5D,MAAM,aAAa,GAAI,kBAAwC,CAAC,QAAQ,CAAC,KAAK,CAAC,mBAAmB,IAAI,EAAE,CAAC,CAAC;IAC1G,OAAO,aAAa,KAAK,CAAC,KAAK,CAAC,gBAAgB,KAAK,SAAS,CAAC,CAAC;AAClE,CAAC;AAED,mGAAmG;AACnG,mGAAmG;AACnG,iEAAiE;AACjE,SAAS,2BAA2B,CAAC,KAOpC;IACC,MAAM,QAAQ,GAAG,KAAK,CAAC,mBAAmB,KAAK,SAAS,IAAI,KAAK,CAAC,6BAA6B,KAAK,SAAS,CAAC;IAC9G,MAAM,QAAQ,GAAG,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,gBAAgB,EAAE,KAAK,CAAC,mBAAmB,EAAE,KAAK,CAAC,gBAAgB,CAAC,CAAC,IAAI,CACjH,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,SAAS,CAC7B,CAAC;IACF,OAAO,CAAC,CAAC,QAAQ,IAAI,QAAQ,CAAC,CAAC;AACjC,CAAC;AAED,yFAAyF;AACzF,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC;KACjC,kBAAkB,CAAC,MAAM,EAAE;IAC1B,6BAA6B;IAC7B,mCAAmC;IACnC,gCAAgC;IAChC,4BAA4B;IAC5B,6BAA6B;IAC7B,4BAA4B;IAC5B,0BAA0B;IAC1B,wBAAwB;IACxB,2BAA2B;IAC3B,mBAAmB;IACnB,yBAAyB;CAC1B,CAAC;KACD,MAAM,CAAC,kBAAkB,EAAE,kFAAkF,CAAC;KAC9G,MAAM,CAAC,2BAA2B,EAAE,6EAA6E,CAAC,CAAC","sourcesContent":["// The game tier of the stats model: the stat event documents, and the rollup the game document\r\n// carries (SPORT-001, SPORT-101, SPORT-102).\r\n//\r\n// Each stat event is its own document, `sportGames/{gameId}/gameStatEvents/{eventId}`, and the\r\n// event documents are the source of truth: at every commit the game's `stats` equals their replay,\r\n// in `sequence` order, from the sport's empty stats. The store is append-only — an undo appends a\r\n// `STAT_VOID` and a correction a `STAT_CORRECTION`; no event is ever edited.\r\n//\r\n// An event names people by id; names resolve from `publicUsers` at read time (ARCH-103, SPORT-104).\r\n// `displayNameSnapshot` and `assistedByDisplayNameSnapshot` are carried only by copied events, as\r\n// the audit record of the name the stat was entered under — never read as identity.\r\n\r\nimport { z } from 'zod';\r\nimport { CURLING_ROUND_COUNT, MAX_CURLING_ROUND_POINTS } from '../constants/sports.js';\r\nimport { buildGameRosterPlayerId } from '../ids/compound-ids.js';\r\nimport { buildStatCorrectionId, buildStatVoidId } from '../ids/stat-event-ids.js';\r\nimport {\r\n BASKETBALL_ASSISTABLE_SHOT_EVENT_TYPE_KEYS,\r\n BASKETBALL_BULK_EDIT_EVENT_TYPE,\r\n BASKETBALL_ONE_POINT_EVENT_TYPE,\r\n BASKETBALL_PLAYER_STAT_KEYS,\r\n BASKETBALL_PLAYER_TALLY_EVENT_TYPE_KEYS,\r\n BASKETBALL_TEAM_STAT_KEYS,\r\n BASKETBALL_TIMEOUT_EVENT_TYPE,\r\n CURLING_ROUND_BLANK_EVENT_TYPE,\r\n CURLING_ROUND_SCORE_EVENT_TYPE,\r\n CURLING_TEAM_STAT_KEYS,\r\n FOOTBALL_BULK_EDIT_EVENT_TYPE,\r\n FOOTBALL_SCORE_EVENT_TYPE_KEYS,\r\n FOOTBALL_TEAM_STAT_KEYS,\r\n} from '../types/sports.js';\r\nimport type {\r\n BasketballStatEventType,\r\n CurlingStatEventType,\r\n FootballStatEventType,\r\n StatCounterKey,\r\n} from '../types/sports.js';\r\nimport {\r\n STAT_CORRECTION_EVENT_TYPE,\r\n STAT_CORRECTION_SOURCE_KEYS,\r\n STAT_EVENT_AUTHORITY_KEYS,\r\n STAT_VOID_EVENT_TYPE,\r\n} from '../types/stats.js';\r\nimport { CAPTAIN_TEAM_ROLES } from '../types/unions.js';\r\nimport {\r\n basketballBulkStatKeySchema,\r\n countSchema,\r\n epochMsSchema,\r\n gameRosterPlayerIdSchema,\r\n repairReasonSchema,\r\n statCounterKeySchema,\r\n statEventIdSchema,\r\n statsOperationIdSchema,\r\n teamIdSchema,\r\n userIdSchema,\r\n} from './atoms.js';\r\n\r\n// ---------------------------------------------------------------------------------------------\r\n// Lines — one per team of the game, one per roster entry holding a non-zero player line\r\n// ---------------------------------------------------------------------------------------------\r\n\r\n// A line's counters: one field per key of the sport's declared stat-key set (SPORT-007), so a key\r\n// added to the set is a field of every line built from it.\r\nfunction counterShape<K extends StatCounterKey>(keys: readonly K[]): Record<K, typeof countSchema> {\r\n return Object.fromEntries(keys.map((key) => [key, countSchema])) as Record<K, typeof countSchema>;\r\n}\r\n\r\nexport const BasketballTeamStatsSchema = z.strictObject(counterShape(BASKETBALL_TEAM_STAT_KEYS));\r\nexport type BasketballTeamStats = z.infer<typeof BasketballTeamStatsSchema>;\r\n\r\n// `fantasyPoints` is `basketballFantasyPoints` of the line's own counters, on every basketball\r\n// player line — this game line and the totals lines built from it — stored so every table and\r\n// aggregate reads it rather than deriving it. FP is linear in the counters, so a totals line's FP\r\n// equals the sum of its games' FP.\r\nexport const BasketballPlayerStatsSchema = z.strictObject({\r\n ...counterShape(BASKETBALL_PLAYER_STAT_KEYS),\r\n fantasyPoints: z.number().nonnegative(),\r\n});\r\nexport type BasketballPlayerStats = z.infer<typeof BasketballPlayerStatsSchema>;\r\n\r\n// `roundScores[n]` is end n + 1: `null` until the end is recorded, `0` on both teams for a blank end.\r\n// `score` is the sum of the recorded ends.\r\nexport const CurlingTeamStatsSchema = z.strictObject({\r\n ...counterShape(CURLING_TEAM_STAT_KEYS),\r\n roundScores: z\r\n .array(z.number().int().min(0).max(MAX_CURLING_ROUND_POINTS).nullable())\r\n .length(CURLING_ROUND_COUNT),\r\n});\r\nexport type CurlingTeamStats = z.infer<typeof CurlingTeamStatsSchema>;\r\n\r\nexport const FootballTeamStatsSchema = z.strictObject(counterShape(FOOTBALL_TEAM_STAT_KEYS));\r\nexport type FootballTeamStats = z.infer<typeof FootballTeamStatsSchema>;\r\n\r\n// ---------------------------------------------------------------------------------------------\r\n// The payload — exactly two keys for every sport (SPORT-001)\r\n// ---------------------------------------------------------------------------------------------\r\n\r\n// The one declaration of the game `stats` payload. Each sport's is built through it from that\r\n// sport's game lines in `SPORT_LINE_SCHEMAS`, as `SPORT_GAME_STATS_SCHEMAS`. `playerStats` is keyed\r\n// by roster entry, never by person, so a person on both teams of a game holds a line on each side. A\r\n// sport with no player lines passes `z.never()`, so its map stays empty.\r\nexport function gameStatsSchema<TeamLine extends z.ZodType, PlayerLine extends z.ZodType>(\r\n teamLine: TeamLine,\r\n playerLine: PlayerLine,\r\n) {\r\n return z.strictObject({\r\n teamStats: z.record(teamIdSchema, teamLine),\r\n playerStats: z.record(gameRosterPlayerIdSchema, playerLine),\r\n });\r\n}\r\n\r\n// ---------------------------------------------------------------------------------------------\r\n// Events\r\n// ---------------------------------------------------------------------------------------------\r\n\r\n// Every event: its id, its place in the replay, and when it was entered. The recorder facts are\r\n// derived by the writing core, never taken from the payload (ARCH-106); a copied event has none.\r\nconst statEventBase = {\r\n eventId: statEventIdSchema,\r\n sequence: z.number().int().positive(),\r\n recordedAt: epochMsSchema,\r\n acceptedAt: epochMsSchema.optional(),\r\n recordedByUserId: userIdSchema.optional(),\r\n recordedByAuthority: z.enum(STAT_EVENT_AUTHORITY_KEYS).optional(),\r\n // Only for the two team roles: the team whose authority the event was recorded under.\r\n recordedByTeamId: teamIdSchema.optional(),\r\n};\r\n\r\n// A player-attributed event names the roster entry `{teamId}_{playerUserId}` whose line it changes.\r\nconst playerEventFields = {\r\n teamId: teamIdSchema,\r\n playerUserId: userIdSchema,\r\n displayNameSnapshot: z.string().optional(),\r\n};\r\n\r\n// Basketball\r\n\r\nexport const BasketballOnePointEventSchema = z.strictObject({\r\n ...statEventBase,\r\n ...playerEventFields,\r\n type: z.literal(BASKETBALL_ONE_POINT_EVENT_TYPE),\r\n});\r\n\r\n// An assisted shot names its assister on the same event, which credits both players.\r\nexport const BasketballAssistableShotEventSchema = z\r\n .strictObject({\r\n ...statEventBase,\r\n ...playerEventFields,\r\n type: z.enum(BASKETBALL_ASSISTABLE_SHOT_EVENT_TYPE_KEYS),\r\n assistedByUserId: userIdSchema.optional(),\r\n assistedByDisplayNameSnapshot: z.string().optional(),\r\n })\r\n .refine(\r\n (event) => event.assistedByDisplayNameSnapshot === undefined || event.assistedByUserId !== undefined,\r\n 'An assister snapshot needs the assister.',\r\n );\r\n\r\nexport const BasketballPlayerTallyEventSchema = z.strictObject({\r\n ...statEventBase,\r\n ...playerEventFields,\r\n type: z.enum(BASKETBALL_PLAYER_TALLY_EVENT_TYPE_KEYS),\r\n});\r\n\r\nexport const BasketballTimeoutEventSchema = z.strictObject({\r\n ...statEventBase,\r\n teamId: teamIdSchema,\r\n type: z.literal(BASKETBALL_TIMEOUT_EVENT_TYPE),\r\n});\r\n\r\n// A scorer's edit: `value` is the signed change to `stat`, entered against `previousValue`, the\r\n// value the form showed. A copied bulk edit has no `previousValue`; nothing validates one again.\r\nexport const BasketballBulkEditEventSchema = z.strictObject({\r\n ...statEventBase,\r\n ...playerEventFields,\r\n type: z.literal(BASKETBALL_BULK_EDIT_EVENT_TYPE),\r\n stat: basketballBulkStatKeySchema,\r\n value: z.number().int(),\r\n previousValue: countSchema.optional(),\r\n});\r\n\r\n// Curling — an event names the end it records; it names no player.\r\n\r\nconst curlingRoundNumberSchema = z.number().int().min(1).max(CURLING_ROUND_COUNT);\r\n\r\nexport const CurlingRoundScoreEventSchema = z.strictObject({\r\n ...statEventBase,\r\n type: z.literal(CURLING_ROUND_SCORE_EVENT_TYPE),\r\n teamId: teamIdSchema,\r\n roundNumber: curlingRoundNumberSchema,\r\n points: z.number().int().min(1).max(MAX_CURLING_ROUND_POINTS),\r\n});\r\n\r\nexport const CurlingBlankEndEventSchema = z.strictObject({\r\n ...statEventBase,\r\n type: z.literal(CURLING_ROUND_BLANK_EVENT_TYPE),\r\n roundNumber: curlingRoundNumberSchema,\r\n});\r\n\r\n// Football — team-attributed only.\r\n\r\nexport const FootballScoreEventSchema = z.strictObject({\r\n ...statEventBase,\r\n type: z.enum(FOOTBALL_SCORE_EVENT_TYPE_KEYS),\r\n teamId: teamIdSchema,\r\n});\r\n\r\nexport const FootballBulkEditEventSchema = z.strictObject({\r\n ...statEventBase,\r\n type: z.literal(FOOTBALL_BULK_EDIT_EVENT_TYPE),\r\n teamId: teamIdSchema,\r\n value: z.number().int(),\r\n previousValue: countSchema,\r\n});\r\n\r\n// The store-level events, members of every sport's union.\r\n\r\n// Undoes exactly the event it targets. Its id is derived from the target, so a target has at most\r\n// one void and a second undo of the same event is the same document.\r\nexport const StatVoidEventSchema = z\r\n .strictObject({\r\n ...statEventBase,\r\n type: z.literal(STAT_VOID_EVENT_TYPE),\r\n targetEventId: statEventIdSchema,\r\n })\r\n .refine((event) => event.eventId === buildStatVoidId(event.targetEventId), {\r\n message: 'A void is stored at the id its target derives.',\r\n path: ['eventId'],\r\n });\r\n\r\n// Adds `value` to one correctable field of one line — the player line `{teamId}_{playerUserId}`\r\n// when it names a player, the team line of `teamId` when it does not. It never cascades: a player\r\n// correction does not move the team's score.\r\nexport const StatCorrectionEventSchema = z\r\n .strictObject({\r\n ...statEventBase,\r\n type: z.literal(STAT_CORRECTION_EVENT_TYPE),\r\n teamId: teamIdSchema,\r\n playerUserId: userIdSchema.optional(),\r\n stat: statCounterKeySchema,\r\n value: z.number().int(),\r\n correctionSource: z.enum(STAT_CORRECTION_SOURCE_KEYS),\r\n operationId: statsOperationIdSchema,\r\n reason: repairReasonSchema,\r\n // The ghost a link re-keyed this correction's player line from. The id was derived from the\r\n // ghost's line when the correction was written, and a document id never changes.\r\n rekeyedFromGhostUserId: userIdSchema.optional(),\r\n })\r\n .refine(\r\n (event) =>\r\n event.eventId ===\r\n buildStatCorrectionId(\r\n event.operationId,\r\n event.playerUserId === undefined\r\n ? event.teamId\r\n : buildGameRosterPlayerId(event.teamId, event.rekeyedFromGhostUserId ?? event.playerUserId),\r\n event.stat,\r\n ),\r\n { message: 'A correction is stored at the id its operation, line, and field derive.', path: ['eventId'] },\r\n );\r\n\r\n// Recorder facts are all present or all absent, and a team is named exactly when the authority is a\r\n// team role.\r\nfunction recorderFactsAgree(event: {\r\n acceptedAt?: number;\r\n recordedByUserId?: string;\r\n recordedByAuthority?: (typeof STAT_EVENT_AUTHORITY_KEYS)[number];\r\n recordedByTeamId?: string;\r\n}): boolean {\r\n const facts = [event.acceptedAt, event.recordedByUserId, event.recordedByAuthority];\r\n const present = facts.filter((fact) => fact !== undefined).length;\r\n if (present !== 0 && present !== facts.length) return false;\r\n const teamAuthority = (CAPTAIN_TEAM_ROLES as readonly string[]).includes(event.recordedByAuthority ?? '');\r\n return teamAuthority === (event.recordedByTeamId !== undefined);\r\n}\r\n\r\n// A name snapshot is the audit record a copied event keeps of the name its stat was entered under.\r\n// Only the writing core stamps recorder facts, and it writes ids only, so an event carrying both a\r\n// snapshot and a recorder fact is refused (ARCH-103, SPORT-104).\r\nfunction snapshotsOnlyOnCopiedEvents(event: {\r\n acceptedAt?: number;\r\n recordedByUserId?: string;\r\n recordedByAuthority?: string;\r\n recordedByTeamId?: string;\r\n displayNameSnapshot?: string;\r\n assistedByDisplayNameSnapshot?: string;\r\n}): boolean {\r\n const snapshot = event.displayNameSnapshot !== undefined || event.assistedByDisplayNameSnapshot !== undefined;\r\n const recorded = [event.acceptedAt, event.recordedByUserId, event.recordedByAuthority, event.recordedByTeamId].some(\r\n (fact) => fact !== undefined,\r\n );\r\n return !(snapshot && recorded);\r\n}\r\n\r\n// Event types are disjoint across sports, so `type` alone discriminates the whole union.\r\nexport const GameStatEventSchema = z\r\n .discriminatedUnion('type', [\r\n BasketballOnePointEventSchema,\r\n BasketballAssistableShotEventSchema,\r\n BasketballPlayerTallyEventSchema,\r\n BasketballTimeoutEventSchema,\r\n BasketballBulkEditEventSchema,\r\n CurlingRoundScoreEventSchema,\r\n CurlingBlankEndEventSchema,\r\n FootballScoreEventSchema,\r\n FootballBulkEditEventSchema,\r\n StatVoidEventSchema,\r\n StatCorrectionEventSchema,\r\n ])\r\n .refine(recorderFactsAgree, 'Recorder facts are all present, or all absent, with a team only for a team role.')\r\n .refine(snapshotsOnlyOnCopiedEvents, 'A name snapshot belongs to a copied event, which carries no recorder facts.');\r\nexport type GameStatEvent = z.infer<typeof GameStatEventSchema>;\r\nexport type GameStatEventType = GameStatEvent['type'];\r\n\r\nexport type BasketballStatEvent = Extract<GameStatEvent, { type: BasketballStatEventType }>;\r\nexport type CurlingRoundScoreEvent = z.infer<typeof CurlingRoundScoreEventSchema>;\r\nexport type CurlingBlankEndEvent = z.infer<typeof CurlingBlankEndEventSchema>;\r\nexport type CurlingStatEvent = Extract<GameStatEvent, { type: CurlingStatEventType }>;\r\nexport type FootballStatEvent = Extract<GameStatEvent, { type: FootballStatEventType }>;\r\nexport type StatVoidEvent = z.infer<typeof StatVoidEventSchema>;\r\nexport type StatCorrectionEvent = z.infer<typeof StatCorrectionEventSchema>;\r\n"]}
1
+ {"version":3,"file":"game-stats.js","sourceRoot":"","sources":["../../src/doc-schemas/game-stats.ts"],"names":[],"mappings":"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","sourcesContent":["// The game tier of the stats model: the stat event documents, and the rollup the game document\n// carries (SPORT-001, SPORT-101, SPORT-102).\n//\n// Each stat event is its own document, `sportGames/{gameId}/gameStatEvents/{eventId}`, and the\n// event documents are the source of truth: at every commit the game's `stats` equals their replay,\n// in `sequence` order, from the sport's empty stats. The store is append-only — an undo appends a\n// `STAT_VOID` and a correction a `STAT_CORRECTION`; no event is ever edited.\n//\n// An event names people by id; names resolve from `publicUsers` at read time (ARCH-103, SPORT-104).\n// `displayNameSnapshot` and `assistedByDisplayNameSnapshot` are carried only by copied events, as\n// the audit record of the name the stat was entered under — never read as identity.\n\nimport { z } from 'zod';\nimport { CURLING_ROUND_COUNT, MAX_CURLING_ROUND_POINTS } from '../constants/sports.js';\nimport { buildGameRosterPlayerId } from '../ids/compound-ids.js';\nimport { buildStatCorrectionId, buildStatVoidId } from '../ids/stat-event-ids.js';\nimport {\n BASKETBALL_ASSISTABLE_SHOT_EVENT_TYPE_KEYS,\n BASKETBALL_BULK_EDIT_EVENT_TYPE,\n BASKETBALL_ONE_POINT_EVENT_TYPE,\n BASKETBALL_PLAYER_STAT_KEYS,\n BASKETBALL_PLAYER_TALLY_EVENT_TYPE_KEYS,\n BASKETBALL_TEAM_STAT_KEYS,\n BASKETBALL_TIMEOUT_EVENT_TYPE,\n CURLING_ROUND_BLANK_EVENT_TYPE,\n CURLING_ROUND_SCORE_EVENT_TYPE,\n CURLING_TEAM_STAT_KEYS,\n FOOTBALL_BULK_EDIT_EVENT_TYPE,\n FOOTBALL_SCORE_EVENT_TYPE_KEYS,\n FOOTBALL_TEAM_STAT_KEYS,\n} from '../types/sports.js';\nimport type {\n BasketballStatEventType,\n CurlingStatEventType,\n FootballStatEventType,\n StatCounterKey,\n} from '../types/sports.js';\nimport {\n STAT_CORRECTION_EVENT_TYPE,\n STAT_CORRECTION_SOURCE_KEYS,\n STAT_EVENT_AUTHORITY_KEYS,\n STAT_VOID_EVENT_TYPE,\n} from '../types/stats.js';\nimport { CAPTAIN_TEAM_ROLES } from '../types/unions.js';\nimport {\n basketballBulkStatKeySchema,\n countSchema,\n epochMsSchema,\n gameRosterPlayerIdSchema,\n repairReasonSchema,\n statCounterKeySchema,\n statEventIdSchema,\n statsOperationIdSchema,\n teamIdSchema,\n userIdSchema,\n} from './atoms.js';\n\n// ---------------------------------------------------------------------------------------------\n// Lines — one per team of the game, one per roster entry holding a non-zero player line\n// ---------------------------------------------------------------------------------------------\n\n// A line's counters: one field per key of the sport's declared stat-key set (SPORT-007), so a key\n// added to the set is a field of every line built from it.\nfunction counterShape<K extends StatCounterKey>(keys: readonly K[]): Record<K, typeof countSchema> {\n return Object.fromEntries(keys.map((key) => [key, countSchema])) as Record<K, typeof countSchema>;\n}\n\nexport const BasketballTeamStatsSchema = z.strictObject(counterShape(BASKETBALL_TEAM_STAT_KEYS));\nexport type BasketballTeamStats = z.infer<typeof BasketballTeamStatsSchema>;\n\n// `fantasyPoints` is `basketballFantasyPoints` of the line's own counters, on every basketball\n// player line — this game line and the totals lines built from it — stored so every table and\n// aggregate reads it rather than deriving it. FP is linear in the counters, so a totals line's FP\n// equals the sum of its games' FP.\nexport const BasketballPlayerStatsSchema = z.strictObject({\n ...counterShape(BASKETBALL_PLAYER_STAT_KEYS),\n fantasyPoints: z.number().nonnegative(),\n});\nexport type BasketballPlayerStats = z.infer<typeof BasketballPlayerStatsSchema>;\n\n// `roundScores[n]` is end n + 1: `null` until the end is recorded, `0` on both teams for a blank end.\n// `score` is the sum of the recorded ends.\nexport const CurlingTeamStatsSchema = z.strictObject({\n ...counterShape(CURLING_TEAM_STAT_KEYS),\n roundScores: z\n .array(z.number().int().min(0).max(MAX_CURLING_ROUND_POINTS).nullable())\n .length(CURLING_ROUND_COUNT),\n});\nexport type CurlingTeamStats = z.infer<typeof CurlingTeamStatsSchema>;\n\nexport const FootballTeamStatsSchema = z.strictObject(counterShape(FOOTBALL_TEAM_STAT_KEYS));\nexport type FootballTeamStats = z.infer<typeof FootballTeamStatsSchema>;\n\n// ---------------------------------------------------------------------------------------------\n// The payload — exactly two keys for every sport (SPORT-001)\n// ---------------------------------------------------------------------------------------------\n\n// The one declaration of the game `stats` payload. Each sport's is built through it from that\n// sport's game lines in `SPORT_LINE_SCHEMAS`, as `SPORT_GAME_STATS_SCHEMAS`. `playerStats` is keyed\n// by roster entry, never by person, so a person on both teams of a game holds a line on each side. A\n// sport with no player lines passes `z.never()`, so its map stays empty.\nexport function gameStatsSchema<TeamLine extends z.ZodType, PlayerLine extends z.ZodType>(\n teamLine: TeamLine,\n playerLine: PlayerLine,\n) {\n return z.strictObject({\n teamStats: z.record(teamIdSchema, teamLine),\n playerStats: z.record(gameRosterPlayerIdSchema, playerLine),\n });\n}\n\n// ---------------------------------------------------------------------------------------------\n// Events\n// ---------------------------------------------------------------------------------------------\n\n// Every event: its id, its place in the replay, and when it was entered. The recorder facts are\n// derived by the writing core, never taken from the payload (ARCH-106); a copied event has none.\nconst statEventBase = {\n eventId: statEventIdSchema,\n sequence: z.number().int().positive(),\n recordedAt: epochMsSchema,\n acceptedAt: epochMsSchema.optional(),\n recordedByUserId: userIdSchema.optional(),\n recordedByAuthority: z.enum(STAT_EVENT_AUTHORITY_KEYS).optional(),\n // Only for the two team roles: the team whose authority the event was recorded under.\n recordedByTeamId: teamIdSchema.optional(),\n};\n\n// A player-attributed event names the roster entry `{teamId}_{playerUserId}` whose line it changes.\nconst playerEventFields = {\n teamId: teamIdSchema,\n playerUserId: userIdSchema,\n displayNameSnapshot: z.string().optional(),\n};\n\n// Basketball\n\nexport const BasketballOnePointEventSchema = z.strictObject({\n ...statEventBase,\n ...playerEventFields,\n type: z.literal(BASKETBALL_ONE_POINT_EVENT_TYPE),\n});\n\n// An assisted shot names its assister on the same event, which credits both players.\nexport const BasketballAssistableShotEventSchema = z\n .strictObject({\n ...statEventBase,\n ...playerEventFields,\n type: z.enum(BASKETBALL_ASSISTABLE_SHOT_EVENT_TYPE_KEYS),\n assistedByUserId: userIdSchema.optional(),\n assistedByDisplayNameSnapshot: z.string().optional(),\n })\n .refine(\n (event) => event.assistedByDisplayNameSnapshot === undefined || event.assistedByUserId !== undefined,\n 'An assister snapshot needs the assister.',\n );\n\nexport const BasketballPlayerTallyEventSchema = z.strictObject({\n ...statEventBase,\n ...playerEventFields,\n type: z.enum(BASKETBALL_PLAYER_TALLY_EVENT_TYPE_KEYS),\n});\n\nexport const BasketballTimeoutEventSchema = z.strictObject({\n ...statEventBase,\n teamId: teamIdSchema,\n type: z.literal(BASKETBALL_TIMEOUT_EVENT_TYPE),\n});\n\n// A scorer's edit: `value` is the signed change to `stat`, entered against `previousValue`, the\n// value the form showed. A copied bulk edit has no `previousValue`; nothing validates one again.\nexport const BasketballBulkEditEventSchema = z.strictObject({\n ...statEventBase,\n ...playerEventFields,\n type: z.literal(BASKETBALL_BULK_EDIT_EVENT_TYPE),\n stat: basketballBulkStatKeySchema,\n value: z.number().int(),\n previousValue: countSchema.optional(),\n});\n\n// Curling — an event names the end it records; it names no player.\n\nconst curlingRoundNumberSchema = z.number().int().min(1).max(CURLING_ROUND_COUNT);\n\nexport const CurlingRoundScoreEventSchema = z.strictObject({\n ...statEventBase,\n type: z.literal(CURLING_ROUND_SCORE_EVENT_TYPE),\n teamId: teamIdSchema,\n roundNumber: curlingRoundNumberSchema,\n points: z.number().int().min(1).max(MAX_CURLING_ROUND_POINTS),\n});\n\nexport const CurlingBlankEndEventSchema = z.strictObject({\n ...statEventBase,\n type: z.literal(CURLING_ROUND_BLANK_EVENT_TYPE),\n roundNumber: curlingRoundNumberSchema,\n});\n\n// Football — team-attributed only.\n\nexport const FootballScoreEventSchema = z.strictObject({\n ...statEventBase,\n type: z.enum(FOOTBALL_SCORE_EVENT_TYPE_KEYS),\n teamId: teamIdSchema,\n});\n\nexport const FootballBulkEditEventSchema = z.strictObject({\n ...statEventBase,\n type: z.literal(FOOTBALL_BULK_EDIT_EVENT_TYPE),\n teamId: teamIdSchema,\n value: z.number().int(),\n previousValue: countSchema,\n});\n\n// The store-level events, members of every sport's union.\n\n// Undoes exactly the event it targets. Its id is derived from the target, so a target has at most\n// one void and a second undo of the same event is the same document.\nexport const StatVoidEventSchema = z\n .strictObject({\n ...statEventBase,\n type: z.literal(STAT_VOID_EVENT_TYPE),\n targetEventId: statEventIdSchema,\n })\n .refine((event) => event.eventId === buildStatVoidId(event.targetEventId), {\n message: 'A void is stored at the id its target derives.',\n path: ['eventId'],\n });\n\n// Adds `value` to one correctable field of one line — the player line `{teamId}_{playerUserId}`\n// when it names a player, the team line of `teamId` when it does not. It never cascades: a player\n// correction does not move the team's score.\nexport const StatCorrectionEventSchema = z\n .strictObject({\n ...statEventBase,\n type: z.literal(STAT_CORRECTION_EVENT_TYPE),\n teamId: teamIdSchema,\n playerUserId: userIdSchema.optional(),\n stat: statCounterKeySchema,\n value: z.number().int(),\n correctionSource: z.enum(STAT_CORRECTION_SOURCE_KEYS),\n operationId: statsOperationIdSchema,\n reason: repairReasonSchema,\n // The ghost a link re-keyed this correction's player line from. The id was derived from the\n // ghost's line when the correction was written, and a document id never changes.\n rekeyedFromGhostUserId: userIdSchema.optional(),\n })\n .refine(\n (event) =>\n event.eventId ===\n buildStatCorrectionId(\n event.operationId,\n event.playerUserId === undefined\n ? event.teamId\n : buildGameRosterPlayerId(event.teamId, event.rekeyedFromGhostUserId ?? event.playerUserId),\n event.stat,\n ),\n { message: 'A correction is stored at the id its operation, line, and field derive.', path: ['eventId'] },\n );\n\n// Recorder facts are all present or all absent, and a team is named exactly when the authority is a\n// team role.\nfunction recorderFactsAgree(event: {\n acceptedAt?: number;\n recordedByUserId?: string;\n recordedByAuthority?: (typeof STAT_EVENT_AUTHORITY_KEYS)[number];\n recordedByTeamId?: string;\n}): boolean {\n const facts = [event.acceptedAt, event.recordedByUserId, event.recordedByAuthority];\n const present = facts.filter((fact) => fact !== undefined).length;\n if (present !== 0 && present !== facts.length) return false;\n const teamAuthority = (CAPTAIN_TEAM_ROLES as readonly string[]).includes(event.recordedByAuthority ?? '');\n return teamAuthority === (event.recordedByTeamId !== undefined);\n}\n\n// A name snapshot is the audit record a copied event keeps of the name its stat was entered under.\n// Only the writing core stamps recorder facts, and it writes ids only, so an event carrying both a\n// snapshot and a recorder fact is refused (ARCH-103, SPORT-104).\nfunction snapshotsOnlyOnCopiedEvents(event: {\n acceptedAt?: number;\n recordedByUserId?: string;\n recordedByAuthority?: string;\n recordedByTeamId?: string;\n displayNameSnapshot?: string;\n assistedByDisplayNameSnapshot?: string;\n}): boolean {\n const snapshot = event.displayNameSnapshot !== undefined || event.assistedByDisplayNameSnapshot !== undefined;\n const recorded = [event.acceptedAt, event.recordedByUserId, event.recordedByAuthority, event.recordedByTeamId].some(\n (fact) => fact !== undefined,\n );\n return !(snapshot && recorded);\n}\n\n// Event types are disjoint across sports, so `type` alone discriminates the whole union.\nexport const GameStatEventSchema = z\n .discriminatedUnion('type', [\n BasketballOnePointEventSchema,\n BasketballAssistableShotEventSchema,\n BasketballPlayerTallyEventSchema,\n BasketballTimeoutEventSchema,\n BasketballBulkEditEventSchema,\n CurlingRoundScoreEventSchema,\n CurlingBlankEndEventSchema,\n FootballScoreEventSchema,\n FootballBulkEditEventSchema,\n StatVoidEventSchema,\n StatCorrectionEventSchema,\n ])\n .refine(recorderFactsAgree, 'Recorder facts are all present, or all absent, with a team only for a team role.')\n .refine(snapshotsOnlyOnCopiedEvents, 'A name snapshot belongs to a copied event, which carries no recorder facts.');\nexport type GameStatEvent = z.infer<typeof GameStatEventSchema>;\nexport type GameStatEventType = GameStatEvent['type'];\n\nexport type BasketballStatEvent = Extract<GameStatEvent, { type: BasketballStatEventType }>;\nexport type CurlingRoundScoreEvent = z.infer<typeof CurlingRoundScoreEventSchema>;\nexport type CurlingBlankEndEvent = z.infer<typeof CurlingBlankEndEventSchema>;\nexport type CurlingStatEvent = Extract<GameStatEvent, { type: CurlingStatEventType }>;\nexport type FootballStatEvent = Extract<GameStatEvent, { type: FootballStatEventType }>;\nexport type StatVoidEvent = z.infer<typeof StatVoidEventSchema>;\nexport type StatCorrectionEvent = z.infer<typeof StatCorrectionEventSchema>;\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"league-team.js","sourceRoot":"","sources":["../../src/doc-schemas/league-team.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,wEAAwE;AAExE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EACL,WAAW,EACX,aAAa,EACb,cAAc,EACd,cAAc,EACd,cAAc,EACd,eAAe,EACf,YAAY,GACb,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AACtF,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,IAAI,EAAE,cAAc;IACpB,QAAQ,EAAE,cAAc;IACxB,qDAAqD;IACrD,QAAQ,EAAE,cAAc,CAAC,QAAQ,EAAE;IACnC,+FAA+F;IAC/F,kEAAkE;IAClE,eAAe,EAAE,YAAY;IAC7B,uEAAuE;IACvE,WAAW,EAAE,qBAAqB,CAAC,QAAQ,EAAE;IAC7C,kGAAkG;IAClG,0FAA0F;IAC1F,iBAAiB,EAAE,CAAC,CAAC,MAAM,CAAC,YAAY,EAAE,uBAAuB,CAAC,CAAC,QAAQ,EAAE;IAC7E,gGAAgG;IAChG,uDAAuD;IACvD,QAAQ,EAAE,oBAAoB,CAAC,QAAQ,EAAE;IACzC,8FAA8F;IAC9F,mGAAmG;IACnG,iGAAiG;IACjG,qFAAqF;IACrF,kBAAkB,EAAE,WAAW,CAAC,QAAQ,EAAE;IAC1C,8FAA8F;IAC9F,6FAA6F;IAC7F,gGAAgG;IAChG,yFAAyF;IACzF,kGAAkG;IAClG,8BAA8B;IAC9B,YAAY,EAAE,WAAW,CAAC,QAAQ,EAAE;IACpC,gGAAgG;IAChG,mGAAmG;IACnG,kGAAkG;IAClG,qGAAqG;IACrG,sDAAsD;IACtD,gBAAgB,EAAE,WAAW,CAAC,QAAQ,EAAE;IACxC,SAAS,EAAE,aAAa;IACxB,SAAS,EAAE,aAAa;CACzB,CAAC,CAAC;AAGH,6FAA6F;AAC7F,qGAAqG;AACrG,0EAA0E;AAC1E,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,QAAQ,EAAE,cAAc;IACxB,6FAA6F;IAC7F,YAAY,EAAE,eAAe,CAAC,0BAA0B,CAAC;IACzD,SAAS,EAAE,aAAa;CACzB,CAAC,CAAC","sourcesContent":["// `leagueTeams/{teamId}` — one team in one league, with its derived season rollups. Who is on the\r\n// team, and who captains it, is `teamMemberships` — never a field here.\r\n\r\nimport { z } from 'zod';\r\nimport { CONTACT_PHONE_INPUT_FORMAT } from '../input-formats/index.js';\r\nimport {\r\n countSchema,\r\n epochMsSchema,\r\n leagueIdSchema,\r\n teamIconSchema,\r\n teamTypeSchema,\r\n textFieldSchema,\r\n userIdSchema,\r\n} from './atoms.js';\r\nimport { PlayerSeasonStatsSchema, TeamSeasonStatsSchema } from './aggregate-stats.js';\r\nimport { GallerySummarySchema } from './gallery.js';\r\n\r\nexport const LeagueTeamSchema = z.object({\r\n name: z.string(),\r\n icon: teamIconSchema,\r\n leagueId: leagueIdSchema,\r\n // Present only in a Competitive/Recreational league.\r\n teamType: teamTypeSchema.optional(),\r\n // The account that created the team — the new captain, or the admin who created it for a ghost\r\n // captain. A creation record, never a membership or an authority.\r\n createdByUserId: userIdSchema,\r\n // The team's standings line, absent until its first game is finalized.\r\n seasonStats: TeamSeasonStatsSchema.optional(),\r\n // The league player stats: one row per person the team's games credited, keyed by userId (real or\r\n // ghost). The team's league, the team, and the key are the membership the row belongs to.\r\n playerSeasonStats: z.record(userIdSchema, PlayerSeasonStatsSchema).optional(),\r\n // The team's picture gallery summary — absent is an empty gallery. Only the picture core writes\r\n // it; the team callables and the copy action never do.\r\n pictures: GallerySummarySchema.optional(),\r\n // Advanced by every membership change on the team after its creation; absent until the first.\r\n // Only its changing matters: game creation and a game's team replacement read this document in the\r\n // transaction that seeds a roster from the team's memberships, so a membership change landing at\r\n // the same moment conflicts with them instead of missing the new game (BACKEND-116).\r\n membershipRevision: countSchema.optional(),\r\n // Advanced by every invitation and join request a door creates for the team; absent until the\r\n // first. Only its changing matters: a pending invitation or request between one team and one\r\n // person is unique, and a membership on the team closes both, but a transaction does not lock a\r\n // query against an insert — so each creation writes this document, which every competing\r\n // creation and every membership door also writes, and two racers conflict instead of both passing\r\n // their checks (BACKEND-116).\r\n joinRevision: countSchema.optional(),\r\n // Advanced by every game creation and every game edit that brings the team in; absent until the\r\n // first. Only its changing matters: a team's deletion re-reads the team's games in the transaction\r\n // that deletes the team document, and a query does not lock against an insert — so a game created\r\n // for the team writes this document, which the deletion also writes, and the two conflict instead of\r\n // leaving a game naming a deleted team (BACKEND-116).\r\n scheduleRevision: countSchema.optional(),\r\n createdAt: epochMsSchema,\r\n updatedAt: epochMsSchema,\r\n});\r\nexport type LeagueTeam = z.infer<typeof LeagueTeamSchema>;\r\n\r\n// `leagueTeamContacts/{teamId}` — how the league reaches the team. Admin read only: the team\r\n// document is public, so the phone lives here, never on it (BACKEND-105). `leagueId` is what lists a\r\n// league's contacts in one query. Written with the team by team creation.\r\nexport const LeagueTeamContactSchema = z.object({\r\n leagueId: leagueIdSchema,\r\n // The number as the contact callables kept it, held to the same declaration they parse with.\r\n contactPhone: textFieldSchema(CONTACT_PHONE_INPUT_FORMAT),\r\n updatedAt: epochMsSchema,\r\n});\r\nexport type LeagueTeamContact = z.infer<typeof LeagueTeamContactSchema>;\r\n"]}
1
+ {"version":3,"file":"league-team.js","sourceRoot":"","sources":["../../src/doc-schemas/league-team.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,wEAAwE;AAExE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EACL,WAAW,EACX,aAAa,EACb,cAAc,EACd,cAAc,EACd,cAAc,EACd,eAAe,EACf,YAAY,GACb,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AACtF,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,IAAI,EAAE,cAAc;IACpB,QAAQ,EAAE,cAAc;IACxB,qDAAqD;IACrD,QAAQ,EAAE,cAAc,CAAC,QAAQ,EAAE;IACnC,+FAA+F;IAC/F,kEAAkE;IAClE,eAAe,EAAE,YAAY;IAC7B,uEAAuE;IACvE,WAAW,EAAE,qBAAqB,CAAC,QAAQ,EAAE;IAC7C,kGAAkG;IAClG,0FAA0F;IAC1F,iBAAiB,EAAE,CAAC,CAAC,MAAM,CAAC,YAAY,EAAE,uBAAuB,CAAC,CAAC,QAAQ,EAAE;IAC7E,gGAAgG;IAChG,uDAAuD;IACvD,QAAQ,EAAE,oBAAoB,CAAC,QAAQ,EAAE;IACzC,8FAA8F;IAC9F,mGAAmG;IACnG,iGAAiG;IACjG,qFAAqF;IACrF,kBAAkB,EAAE,WAAW,CAAC,QAAQ,EAAE;IAC1C,8FAA8F;IAC9F,6FAA6F;IAC7F,gGAAgG;IAChG,yFAAyF;IACzF,kGAAkG;IAClG,8BAA8B;IAC9B,YAAY,EAAE,WAAW,CAAC,QAAQ,EAAE;IACpC,gGAAgG;IAChG,mGAAmG;IACnG,kGAAkG;IAClG,qGAAqG;IACrG,sDAAsD;IACtD,gBAAgB,EAAE,WAAW,CAAC,QAAQ,EAAE;IACxC,SAAS,EAAE,aAAa;IACxB,SAAS,EAAE,aAAa;CACzB,CAAC,CAAC;AAGH,6FAA6F;AAC7F,qGAAqG;AACrG,0EAA0E;AAC1E,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,QAAQ,EAAE,cAAc;IACxB,6FAA6F;IAC7F,YAAY,EAAE,eAAe,CAAC,0BAA0B,CAAC;IACzD,SAAS,EAAE,aAAa;CACzB,CAAC,CAAC","sourcesContent":["// `leagueTeams/{teamId}` — one team in one league, with its derived season rollups. Who is on the\n// team, and who captains it, is `teamMemberships` — never a field here.\n\nimport { z } from 'zod';\nimport { CONTACT_PHONE_INPUT_FORMAT } from '../input-formats/index.js';\nimport {\n countSchema,\n epochMsSchema,\n leagueIdSchema,\n teamIconSchema,\n teamTypeSchema,\n textFieldSchema,\n userIdSchema,\n} from './atoms.js';\nimport { PlayerSeasonStatsSchema, TeamSeasonStatsSchema } from './aggregate-stats.js';\nimport { GallerySummarySchema } from './gallery.js';\n\nexport const LeagueTeamSchema = z.object({\n name: z.string(),\n icon: teamIconSchema,\n leagueId: leagueIdSchema,\n // Present only in a Competitive/Recreational league.\n teamType: teamTypeSchema.optional(),\n // The account that created the team — the new captain, or the admin who created it for a ghost\n // captain. A creation record, never a membership or an authority.\n createdByUserId: userIdSchema,\n // The team's standings line, absent until its first game is finalized.\n seasonStats: TeamSeasonStatsSchema.optional(),\n // The league player stats: one row per person the team's games credited, keyed by userId (real or\n // ghost). The team's league, the team, and the key are the membership the row belongs to.\n playerSeasonStats: z.record(userIdSchema, PlayerSeasonStatsSchema).optional(),\n // The team's picture gallery summary — absent is an empty gallery. Only the picture core writes\n // it; the team callables and the copy action never do.\n pictures: GallerySummarySchema.optional(),\n // Advanced by every membership change on the team after its creation; absent until the first.\n // Only its changing matters: game creation and a game's team replacement read this document in the\n // transaction that seeds a roster from the team's memberships, so a membership change landing at\n // the same moment conflicts with them instead of missing the new game (BACKEND-116).\n membershipRevision: countSchema.optional(),\n // Advanced by every invitation and join request a door creates for the team; absent until the\n // first. Only its changing matters: a pending invitation or request between one team and one\n // person is unique, and a membership on the team closes both, but a transaction does not lock a\n // query against an insert — so each creation writes this document, which every competing\n // creation and every membership door also writes, and two racers conflict instead of both passing\n // their checks (BACKEND-116).\n joinRevision: countSchema.optional(),\n // Advanced by every game creation and every game edit that brings the team in; absent until the\n // first. Only its changing matters: a team's deletion re-reads the team's games in the transaction\n // that deletes the team document, and a query does not lock against an insert — so a game created\n // for the team writes this document, which the deletion also writes, and the two conflict instead of\n // leaving a game naming a deleted team (BACKEND-116).\n scheduleRevision: countSchema.optional(),\n createdAt: epochMsSchema,\n updatedAt: epochMsSchema,\n});\nexport type LeagueTeam = z.infer<typeof LeagueTeamSchema>;\n\n// `leagueTeamContacts/{teamId}` — how the league reaches the team. Admin read only: the team\n// document is public, so the phone lives here, never on it (BACKEND-105). `leagueId` is what lists a\n// league's contacts in one query. Written with the team by team creation.\nexport const LeagueTeamContactSchema = z.object({\n leagueId: leagueIdSchema,\n // The number as the contact callables kept it, held to the same declaration they parse with.\n contactPhone: textFieldSchema(CONTACT_PHONE_INPUT_FORMAT),\n updatedAt: epochMsSchema,\n});\nexport type LeagueTeamContact = z.infer<typeof LeagueTeamContactSchema>;\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"pending-media.js","sourceRoot":"","sources":["../../src/doc-schemas/pending-media.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,iGAAiG;AACjG,gFAAgF;AAChF,EAAE;AACF,kGAAkG;AAClG,+FAA+F;AAC/F,8FAA8F;AAC9F,wFAAwF;AAExF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACjF,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAEtD,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;AACzD,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;AAE/D,oGAAoG;AACpG,oDAAoD;AACpD,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC;KACpC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9D,MAAM,EAAE,CAAC;AAGZ,uGAAuG;AACvG,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC;KACnC,MAAM,CAAC,EAAE,IAAI,EAAE,sBAAsB,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;KAC/D,MAAM,EAAE,CAAC;AAGZ,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC;KACtC,MAAM,CAAC;IACN,KAAK,EAAE,qBAAqB;IAC5B,8FAA8F;IAC9F,oFAAoF;IACpF,WAAW,EAAE,sBAAsB,CAAC,QAAQ,EAAE;IAC9C,6FAA6F;IAC7F,6DAA6D;IAC7D,YAAY,EAAE,eAAe,CAAC,QAAQ,EAAE;CACzC,CAAC;KACD,MAAM,EAAE;KACR,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,KAAK,SAAS,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,2BAA2B,EAAE;IAC1G,OAAO,EAAE,uCAAuC;IAChD,IAAI,EAAE,CAAC,cAAc,CAAC;CACvB,CAAC,CAAC;AAGL,MAAM,OAAO,GAAG,yBAAyB,CAAC;IACxC,gBAAgB;IAChB,iBAAiB,EAAE,iBAAiB;CACrC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,+BAA+B,GAAG,OAAO,CAAC,+BAA+B,CAAC;AACvF,MAAM,CAAC,MAAM,wBAAwB,GAAG,OAAO,CAAC,wBAAwB,CAAC;AACzE,MAAM,CAAC,MAAM,yBAAyB,GAAG,OAAO,CAAC,yBAAyB,CAAC;AAC3E,MAAM,CAAC,MAAM,4BAA4B,GAAG,OAAO,CAAC,4BAA4B,CAAC;AACjF,MAAM,CAAC,MAAM,2BAA2B,GAAG,OAAO,CAAC,2BAA2B,CAAC;AAC/E,MAAM,CAAC,MAAM,wBAAwB,GAAG,OAAO,CAAC,wBAAwB,CAAC;AACzE,MAAM,CAAC,MAAM,0BAA0B,GAAG,OAAO,CAAC,0BAA0B,CAAC;AAC7E,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC","sourcesContent":["// `pendingMedia/{pendingMediaId}` — one accepted picture upload: the durable hand-off from the\r\n// accepting callable to the processing trigger. Its id is the upload's staging id, which is also\r\n// the picture's id; rows are never deleted, so no two uploads ever share an id.\r\n//\r\n// The shape is media-schemas' generic pending-media lifecycle composed with Q-Sports' origins and\r\n// domain events. Every field is written by the server: `originalFileName` and `clientContext`,\r\n// which the generic shape requires, hold the staging id and the origin, so no client-authored\r\n// metadata reaches the row. `targetInfo` is the server-normalized `PendingMediaTarget`.\r\n\r\nimport { z } from 'zod';\r\nimport { createPendingMediaSchemas } from '@ttt-productions/media-schemas';\r\nimport { DomainEventSchema } from '../domain-events/domain-events.js';\r\nimport { FILE_ORIGIN_KEYS, PICTURE_ORIGIN_KEYS } from '../media/file-origins.js';\r\nimport { AWARD_DEFINITION_OWNER_KIND } from '../types/picture-owners.js';\r\nimport { awardNameSchema } from './catalogs.js';\r\nimport { pictureOwnerKindSchema } from './gallery.js';\r\n\r\nexport const fileOriginSchema = z.enum(FILE_ORIGIN_KEYS);\r\nexport const pictureOriginSchema = z.enum(PICTURE_ORIGIN_KEYS);\r\n\r\n// A stored object, by bucket and path — never a URL. A retained signature is recorded this way, and\r\n// so is the source a carried picture was read from.\r\nexport const StorageObjectRefSchema = z\r\n .object({ bucket: z.string().min(1), path: z.string().min(1) })\r\n .strict();\r\nexport type StorageObjectRef = z.infer<typeof StorageObjectRefSchema>;\r\n\r\n// The document a picture belongs to. A landing page picture's owner id is `SPECIAL_DOCS.LANDING_PAGE`.\r\nexport const PictureOwnerRefSchema = z\r\n .object({ kind: pictureOwnerKindSchema, id: z.string().min(1) })\r\n .strict();\r\nexport type PictureOwnerRef = z.infer<typeof PictureOwnerRefSchema>;\r\n\r\nexport const PendingMediaTargetSchema = z\r\n .object({\r\n owner: PictureOwnerRefSchema,\r\n // The object a carried picture was read from. Written only by the migration console's picture\r\n // carry, never from a callable's input; a carried picture fills only an empty slot.\r\n carriedFrom: StorageObjectRefSchema.optional(),\r\n // A badge upload that creates its award: the award's name, which the publication writes with\r\n // the award. The owner's id is then the upload's staging id.\r\n newAwardName: awardNameSchema.optional(),\r\n })\r\n .strict()\r\n .refine((target) => target.newAwardName === undefined || target.owner.kind === AWARD_DEFINITION_OWNER_KIND, {\r\n message: 'Only a badge upload creates an award.',\r\n path: ['newAwardName'],\r\n });\r\nexport type PendingMediaTarget = z.infer<typeof PendingMediaTargetSchema>;\r\n\r\nconst schemas = createPendingMediaSchemas({\r\n fileOriginSchema,\r\n domainEventSchema: DomainEventSchema,\r\n});\r\n\r\nexport const PendingMediaErrorCategorySchema = schemas.PendingMediaErrorCategorySchema;\r\nexport const PendingMediaResultSchema = schemas.PendingMediaResultSchema;\r\nexport const PendingMediaPendingSchema = schemas.PendingMediaPendingSchema;\r\nexport const PendingMediaProcessingSchema = schemas.PendingMediaProcessingSchema;\r\nexport const PendingMediaCompletedSchema = schemas.PendingMediaCompletedSchema;\r\nexport const PendingMediaFailedSchema = schemas.PendingMediaFailedSchema;\r\nexport const PendingMediaRejectedSchema = schemas.PendingMediaRejectedSchema;\r\nexport const PendingMediaSchema = schemas.PendingMediaSchema;\r\n\r\nexport type PendingMediaErrorCategory = z.infer<typeof PendingMediaErrorCategorySchema>;\r\nexport type PendingMediaResult = z.infer<typeof PendingMediaResultSchema>;\r\nexport type PendingMediaPending = z.infer<typeof PendingMediaPendingSchema>;\r\nexport type PendingMediaProcessing = z.infer<typeof PendingMediaProcessingSchema>;\r\nexport type PendingMediaCompleted = z.infer<typeof PendingMediaCompletedSchema>;\r\nexport type PendingMediaFailed = z.infer<typeof PendingMediaFailedSchema>;\r\nexport type PendingMediaRejected = z.infer<typeof PendingMediaRejectedSchema>;\r\nexport type PendingMedia = z.infer<typeof PendingMediaSchema>;\r\n"]}
1
+ {"version":3,"file":"pending-media.js","sourceRoot":"","sources":["../../src/doc-schemas/pending-media.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,iGAAiG;AACjG,gFAAgF;AAChF,EAAE;AACF,kGAAkG;AAClG,+FAA+F;AAC/F,8FAA8F;AAC9F,wFAAwF;AAExF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACjF,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAEtD,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;AACzD,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;AAE/D,oGAAoG;AACpG,oDAAoD;AACpD,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC;KACpC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9D,MAAM,EAAE,CAAC;AAGZ,uGAAuG;AACvG,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC;KACnC,MAAM,CAAC,EAAE,IAAI,EAAE,sBAAsB,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;KAC/D,MAAM,EAAE,CAAC;AAGZ,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC;KACtC,MAAM,CAAC;IACN,KAAK,EAAE,qBAAqB;IAC5B,8FAA8F;IAC9F,oFAAoF;IACpF,WAAW,EAAE,sBAAsB,CAAC,QAAQ,EAAE;IAC9C,6FAA6F;IAC7F,6DAA6D;IAC7D,YAAY,EAAE,eAAe,CAAC,QAAQ,EAAE;CACzC,CAAC;KACD,MAAM,EAAE;KACR,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,KAAK,SAAS,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,2BAA2B,EAAE;IAC1G,OAAO,EAAE,uCAAuC;IAChD,IAAI,EAAE,CAAC,cAAc,CAAC;CACvB,CAAC,CAAC;AAGL,MAAM,OAAO,GAAG,yBAAyB,CAAC;IACxC,gBAAgB;IAChB,iBAAiB,EAAE,iBAAiB;CACrC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,+BAA+B,GAAG,OAAO,CAAC,+BAA+B,CAAC;AACvF,MAAM,CAAC,MAAM,wBAAwB,GAAG,OAAO,CAAC,wBAAwB,CAAC;AACzE,MAAM,CAAC,MAAM,yBAAyB,GAAG,OAAO,CAAC,yBAAyB,CAAC;AAC3E,MAAM,CAAC,MAAM,4BAA4B,GAAG,OAAO,CAAC,4BAA4B,CAAC;AACjF,MAAM,CAAC,MAAM,2BAA2B,GAAG,OAAO,CAAC,2BAA2B,CAAC;AAC/E,MAAM,CAAC,MAAM,wBAAwB,GAAG,OAAO,CAAC,wBAAwB,CAAC;AACzE,MAAM,CAAC,MAAM,0BAA0B,GAAG,OAAO,CAAC,0BAA0B,CAAC;AAC7E,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC","sourcesContent":["// `pendingMedia/{pendingMediaId}` — one accepted picture upload: the durable hand-off from the\n// accepting callable to the processing trigger. Its id is the upload's staging id, which is also\n// the picture's id; rows are never deleted, so no two uploads ever share an id.\n//\n// The shape is media-schemas' generic pending-media lifecycle composed with Q-Sports' origins and\n// domain events. Every field is written by the server: `originalFileName` and `clientContext`,\n// which the generic shape requires, hold the staging id and the origin, so no client-authored\n// metadata reaches the row. `targetInfo` is the server-normalized `PendingMediaTarget`.\n\nimport { z } from 'zod';\nimport { createPendingMediaSchemas } from '@ttt-productions/media-schemas';\nimport { DomainEventSchema } from '../domain-events/domain-events.js';\nimport { FILE_ORIGIN_KEYS, PICTURE_ORIGIN_KEYS } from '../media/file-origins.js';\nimport { AWARD_DEFINITION_OWNER_KIND } from '../types/picture-owners.js';\nimport { awardNameSchema } from './catalogs.js';\nimport { pictureOwnerKindSchema } from './gallery.js';\n\nexport const fileOriginSchema = z.enum(FILE_ORIGIN_KEYS);\nexport const pictureOriginSchema = z.enum(PICTURE_ORIGIN_KEYS);\n\n// A stored object, by bucket and path — never a URL. A retained signature is recorded this way, and\n// so is the source a carried picture was read from.\nexport const StorageObjectRefSchema = z\n .object({ bucket: z.string().min(1), path: z.string().min(1) })\n .strict();\nexport type StorageObjectRef = z.infer<typeof StorageObjectRefSchema>;\n\n// The document a picture belongs to. A landing page picture's owner id is `SPECIAL_DOCS.LANDING_PAGE`.\nexport const PictureOwnerRefSchema = z\n .object({ kind: pictureOwnerKindSchema, id: z.string().min(1) })\n .strict();\nexport type PictureOwnerRef = z.infer<typeof PictureOwnerRefSchema>;\n\nexport const PendingMediaTargetSchema = z\n .object({\n owner: PictureOwnerRefSchema,\n // The object a carried picture was read from. Written only by the migration console's picture\n // carry, never from a callable's input; a carried picture fills only an empty slot.\n carriedFrom: StorageObjectRefSchema.optional(),\n // A badge upload that creates its award: the award's name, which the publication writes with\n // the award. The owner's id is then the upload's staging id.\n newAwardName: awardNameSchema.optional(),\n })\n .strict()\n .refine((target) => target.newAwardName === undefined || target.owner.kind === AWARD_DEFINITION_OWNER_KIND, {\n message: 'Only a badge upload creates an award.',\n path: ['newAwardName'],\n });\nexport type PendingMediaTarget = z.infer<typeof PendingMediaTargetSchema>;\n\nconst schemas = createPendingMediaSchemas({\n fileOriginSchema,\n domainEventSchema: DomainEventSchema,\n});\n\nexport const PendingMediaErrorCategorySchema = schemas.PendingMediaErrorCategorySchema;\nexport const PendingMediaResultSchema = schemas.PendingMediaResultSchema;\nexport const PendingMediaPendingSchema = schemas.PendingMediaPendingSchema;\nexport const PendingMediaProcessingSchema = schemas.PendingMediaProcessingSchema;\nexport const PendingMediaCompletedSchema = schemas.PendingMediaCompletedSchema;\nexport const PendingMediaFailedSchema = schemas.PendingMediaFailedSchema;\nexport const PendingMediaRejectedSchema = schemas.PendingMediaRejectedSchema;\nexport const PendingMediaSchema = schemas.PendingMediaSchema;\n\nexport type PendingMediaErrorCategory = z.infer<typeof PendingMediaErrorCategorySchema>;\nexport type PendingMediaResult = z.infer<typeof PendingMediaResultSchema>;\nexport type PendingMediaPending = z.infer<typeof PendingMediaPendingSchema>;\nexport type PendingMediaProcessing = z.infer<typeof PendingMediaProcessingSchema>;\nexport type PendingMediaCompleted = z.infer<typeof PendingMediaCompletedSchema>;\nexport type PendingMediaFailed = z.infer<typeof PendingMediaFailedSchema>;\nexport type PendingMediaRejected = z.infer<typeof PendingMediaRejectedSchema>;\nexport type PendingMedia = z.infer<typeof PendingMediaSchema>;\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/doc-schemas/registry.ts"],"names":[],"mappings":"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","sourcesContent":["// Collection → schema registry: every Firestore path template bound to the zod schema its\r\n// documents must satisfy. Every document a migration copy action writes is validated against the\r\n// schema bound here.\r\n//\r\n// A key is the literal path template — `{placeholder}` for an id, the literal segment for a fixed\r\n// document id. __tests__/doc-schemas-registry.test.ts fails when a collection constant has neither a\r\n// binding here nor an explicit PENDING_COLLECTIONS entry.\r\n\r\nimport type { z } from 'zod';\r\nimport { StatusReconcileQueueEntrySchema } from './status-reconcile-queue.js';\r\nimport {\r\n ActiveAdminNotificationSchema,\r\n AdminNotificationHistorySchema,\r\n NotificationArchiveAllJobSchema,\r\n NotificationDeliverySchema,\r\n} from './admin-notifications.js';\r\nimport { AwardDefinitionSchema } from './catalogs.js';\r\nimport { GameLocationSchema } from './game-location.js';\r\nimport { GalleryPictureSchema } from './gallery.js';\r\nimport { PendingMediaSchema } from './pending-media.js';\r\nimport { PictureRetirementSchema } from './picture-retirement.js';\r\nimport { GameRosterPlayerSchema, SportGameSchema } from './sport-game.js';\r\nimport { SportHallOfFameSchema } from './aggregate-stats.js';\r\nimport { GameStatHistoryPageSchema } from './game-stat-history.js';\r\nimport { GameStatEventSchema } from './game-stats.js';\r\nimport { StatsOperationSchema } from './stats-operation.js';\r\nimport { LeagueTeamContactSchema, LeagueTeamSchema } from './league-team.js';\r\nimport { MembershipRosterSyncSchema } from './membership-roster-sync.js';\r\nimport { MigrationActionRunSchema, MigrationConsoleSchema } from './migration-console.js';\r\nimport { AuditEventSchema } from './audit-event.js';\r\nimport { PublicUserSchema } from './publicUser.js';\r\nimport { RegistrationStartSchema } from './registration-start.js';\r\nimport { ReservedDisplayNameSchema } from './reserved-display-name.js';\r\nimport { SportLeagueSchema } from './sport-league.js';\r\nimport { SweepStateSchema } from './sweep-state.js';\r\nimport {\r\n AppConfigSchema,\r\n LandingPageConfigSchema,\r\n AdminListSchema,\r\n StatsBeaconSchema,\r\n StatsValidationReportSchema,\r\n} from './system.js';\r\nimport {\r\n LeagueFreeAgentSchema,\r\n TeamJoinRequestSchema,\r\n TeamInvitationSchema,\r\n TeamMembershipSchema,\r\n} from './team-membership.js';\r\nimport { HistoricalStatsSchema, UserPrivateDataSchema, UserProfileSchema } from './user-profile.js';\r\nimport { HardwareLoanContractSchema, SignedWaiverSchema } from './waivers-and-hardware.js';\r\nimport { WaiverTemplateSchema, WaiverTemplateVersionSchema } from './waiver-template.js';\r\n\r\nexport const COLLECTION_SCHEMAS = {\r\n 'userProfiles/{userId}': UserProfileSchema,\r\n 'userProfiles/{userId}/privateData/{privateDataId}': UserPrivateDataSchema,\r\n 'userProfiles/{userId}/historicalStats/{sportKey}': HistoricalStatsSchema,\r\n 'publicUsers/{userId}': PublicUserSchema,\r\n 'reservedDisplayNames/{reservedDisplayNameKey}': ReservedDisplayNameSchema,\r\n 'statusReconcileQueue/{userId}': StatusReconcileQueueEntrySchema,\r\n 'registrationStarts/{userId}': RegistrationStartSchema,\r\n\r\n 'sportLeagues/{leagueId}': SportLeagueSchema,\r\n 'leagueTeams/{teamId}': LeagueTeamSchema,\r\n 'leagueTeamContacts/{teamId}': LeagueTeamContactSchema,\r\n 'teamMemberships/{teamMembershipId}': TeamMembershipSchema,\r\n 'membershipRosterSyncs/{teamMembershipId}': MembershipRosterSyncSchema,\r\n 'teamInvitations/{inviteId}': TeamInvitationSchema,\r\n 'teamJoinRequests/{requestId}': TeamJoinRequestSchema,\r\n 'leagueFreeAgents/{leagueFreeAgentId}': LeagueFreeAgentSchema,\r\n\r\n 'sportGames/{gameId}': SportGameSchema,\r\n 'sportGames/{gameId}/gameRosterPlayers/{gameRosterPlayerId}': GameRosterPlayerSchema,\r\n 'sportGames/{gameId}/gameStatEvents/{eventId}': GameStatEventSchema,\r\n 'sportGames/{gameId}/gameStatHistory/{pageId}': GameStatHistoryPageSchema,\r\n 'gameLocations/{gameLocationId}': GameLocationSchema,\r\n\r\n 'waiverTemplates/{waiverTemplateId}': WaiverTemplateSchema,\r\n 'waiverTemplates/{waiverTemplateId}/waiverTemplateVersions/{versionId}': WaiverTemplateVersionSchema,\r\n 'signedWaivers/{signedWaiverId}': SignedWaiverSchema,\r\n 'hardwareLoanContracts/{contractId}': HardwareLoanContractSchema,\r\n\r\n 'awardDefinitions/{awardId}': AwardDefinitionSchema,\r\n 'sportHallOfFame/{sportKey}': SportHallOfFameSchema,\r\n 'statsValidationReports/{reportId}': StatsValidationReportSchema,\r\n 'statsOperations/{operationId}': StatsOperationSchema,\r\n\r\n 'galleryPictures/{pictureId}': GalleryPictureSchema,\r\n 'pendingMedia/{pendingMediaId}': PendingMediaSchema,\r\n 'pictureRetirements/{retirementId}': PictureRetirementSchema,\r\n\r\n 'auditEvents/{auditEventId}': AuditEventSchema,\r\n\r\n 'activeAdminNotifications/{notificationId}': ActiveAdminNotificationSchema,\r\n 'adminNotificationHistory/{historyId}': AdminNotificationHistorySchema,\r\n 'notificationDeliveries/{deliveryId}': NotificationDeliverySchema,\r\n 'notificationArchiveAllJobs/{jobId}': NotificationArchiveAllJobSchema,\r\n\r\n '_appConfig/landingPage': LandingPageConfigSchema,\r\n '_appConfig/app': AppConfigSchema,\r\n '_appConfig/statsBeacon': StatsBeaconSchema,\r\n '_systemData/adminList': AdminListSchema,\r\n '_serverData/migrationConsole': MigrationConsoleSchema,\r\n 'migrationActionRuns/{actionId}': MigrationActionRunSchema,\r\n 'sweepState/{sweepName}': SweepStateSchema,\r\n} as const satisfies Record<string, z.ZodTypeAny>;\r\n\r\nexport type RegisteredCollectionPath = keyof typeof COLLECTION_SCHEMAS;\r\n\r\n// The body field that equals the document id, where a schema declares one. A drift check injects\r\n// `doc.id` under this key before validating, so a doc-id field is never a false mismatch.\r\nexport const COLLECTION_DOC_ID_FIELDS = {\r\n 'publicUsers/{userId}': 'uid',\r\n 'registrationStarts/{userId}': 'userId',\r\n 'sportGames/{gameId}/gameStatEvents/{eventId}': 'eventId',\r\n 'statsValidationReports/{reportId}': 'reportId',\r\n 'auditEvents/{auditEventId}': 'id',\r\n 'pendingMedia/{pendingMediaId}': 'id',\r\n 'adminNotificationHistory/{historyId}': 'archiveOccurrenceId',\r\n 'notificationDeliveries/{deliveryId}': 'deliveryId',\r\n 'notificationArchiveAllJobs/{jobId}': 'jobId',\r\n 'migrationActionRuns/{actionId}': 'actionId',\r\n} as const satisfies Partial<Record<RegisteredCollectionPath, string>>;\r\n\r\n// Collections declared in ../paths with no bound schema yet, each with the reason.\r\nexport const PENDING_COLLECTIONS: readonly string[] = [];\r\n"]}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/doc-schemas/registry.ts"],"names":[],"mappings":"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","sourcesContent":["// Collection → schema registry: every Firestore path template bound to the zod schema its\n// documents must satisfy. Every document a migration copy action writes is validated against the\n// schema bound here.\n//\n// A key is the literal path template — `{placeholder}` for an id, the literal segment for a fixed\n// document id. __tests__/doc-schemas-registry.test.ts fails when a collection constant has neither a\n// binding here nor an explicit PENDING_COLLECTIONS entry.\n\nimport type { z } from 'zod';\nimport { StatusReconcileQueueEntrySchema } from './status-reconcile-queue.js';\nimport {\n ActiveAdminNotificationSchema,\n AdminNotificationHistorySchema,\n NotificationArchiveAllJobSchema,\n NotificationDeliverySchema,\n} from './admin-notifications.js';\nimport { AwardDefinitionSchema } from './catalogs.js';\nimport { GameLocationSchema } from './game-location.js';\nimport { GalleryPictureSchema } from './gallery.js';\nimport { PendingMediaSchema } from './pending-media.js';\nimport { PictureRetirementSchema } from './picture-retirement.js';\nimport { GameRosterPlayerSchema, SportGameSchema } from './sport-game.js';\nimport { SportHallOfFameSchema } from './aggregate-stats.js';\nimport { GameStatHistoryPageSchema } from './game-stat-history.js';\nimport { GameStatEventSchema } from './game-stats.js';\nimport { StatsOperationSchema } from './stats-operation.js';\nimport { LeagueTeamContactSchema, LeagueTeamSchema } from './league-team.js';\nimport { MembershipRosterSyncSchema } from './membership-roster-sync.js';\nimport { MigrationActionRunSchema, MigrationConsoleSchema } from './migration-console.js';\nimport { AuditEventSchema } from './audit-event.js';\nimport { PublicUserSchema } from './publicUser.js';\nimport { RegistrationStartSchema } from './registration-start.js';\nimport { ReservedDisplayNameSchema } from './reserved-display-name.js';\nimport { SportLeagueSchema } from './sport-league.js';\nimport { SweepStateSchema } from './sweep-state.js';\nimport {\n AppConfigSchema,\n LandingPageConfigSchema,\n AdminListSchema,\n StatsBeaconSchema,\n StatsValidationReportSchema,\n} from './system.js';\nimport {\n LeagueFreeAgentSchema,\n TeamJoinRequestSchema,\n TeamInvitationSchema,\n TeamMembershipSchema,\n} from './team-membership.js';\nimport { HistoricalStatsSchema, UserPrivateDataSchema, UserProfileSchema } from './user-profile.js';\nimport { HardwareLoanContractSchema, SignedWaiverSchema } from './waivers-and-hardware.js';\nimport { WaiverTemplateSchema, WaiverTemplateVersionSchema } from './waiver-template.js';\n\nexport const COLLECTION_SCHEMAS = {\n 'userProfiles/{userId}': UserProfileSchema,\n 'userProfiles/{userId}/privateData/{privateDataId}': UserPrivateDataSchema,\n 'userProfiles/{userId}/historicalStats/{sportKey}': HistoricalStatsSchema,\n 'publicUsers/{userId}': PublicUserSchema,\n 'reservedDisplayNames/{reservedDisplayNameKey}': ReservedDisplayNameSchema,\n 'statusReconcileQueue/{userId}': StatusReconcileQueueEntrySchema,\n 'registrationStarts/{userId}': RegistrationStartSchema,\n\n 'sportLeagues/{leagueId}': SportLeagueSchema,\n 'leagueTeams/{teamId}': LeagueTeamSchema,\n 'leagueTeamContacts/{teamId}': LeagueTeamContactSchema,\n 'teamMemberships/{teamMembershipId}': TeamMembershipSchema,\n 'membershipRosterSyncs/{teamMembershipId}': MembershipRosterSyncSchema,\n 'teamInvitations/{inviteId}': TeamInvitationSchema,\n 'teamJoinRequests/{requestId}': TeamJoinRequestSchema,\n 'leagueFreeAgents/{leagueFreeAgentId}': LeagueFreeAgentSchema,\n\n 'sportGames/{gameId}': SportGameSchema,\n 'sportGames/{gameId}/gameRosterPlayers/{gameRosterPlayerId}': GameRosterPlayerSchema,\n 'sportGames/{gameId}/gameStatEvents/{eventId}': GameStatEventSchema,\n 'sportGames/{gameId}/gameStatHistory/{pageId}': GameStatHistoryPageSchema,\n 'gameLocations/{gameLocationId}': GameLocationSchema,\n\n 'waiverTemplates/{waiverTemplateId}': WaiverTemplateSchema,\n 'waiverTemplates/{waiverTemplateId}/waiverTemplateVersions/{versionId}': WaiverTemplateVersionSchema,\n 'signedWaivers/{signedWaiverId}': SignedWaiverSchema,\n 'hardwareLoanContracts/{contractId}': HardwareLoanContractSchema,\n\n 'awardDefinitions/{awardId}': AwardDefinitionSchema,\n 'sportHallOfFame/{sportKey}': SportHallOfFameSchema,\n 'statsValidationReports/{reportId}': StatsValidationReportSchema,\n 'statsOperations/{operationId}': StatsOperationSchema,\n\n 'galleryPictures/{pictureId}': GalleryPictureSchema,\n 'pendingMedia/{pendingMediaId}': PendingMediaSchema,\n 'pictureRetirements/{retirementId}': PictureRetirementSchema,\n\n 'auditEvents/{auditEventId}': AuditEventSchema,\n\n 'activeAdminNotifications/{notificationId}': ActiveAdminNotificationSchema,\n 'adminNotificationHistory/{historyId}': AdminNotificationHistorySchema,\n 'notificationDeliveries/{deliveryId}': NotificationDeliverySchema,\n 'notificationArchiveAllJobs/{jobId}': NotificationArchiveAllJobSchema,\n\n '_appConfig/landingPage': LandingPageConfigSchema,\n '_appConfig/app': AppConfigSchema,\n '_appConfig/statsBeacon': StatsBeaconSchema,\n '_systemData/adminList': AdminListSchema,\n '_serverData/migrationConsole': MigrationConsoleSchema,\n 'migrationActionRuns/{actionId}': MigrationActionRunSchema,\n 'sweepState/{sweepName}': SweepStateSchema,\n} as const satisfies Record<string, z.ZodTypeAny>;\n\nexport type RegisteredCollectionPath = keyof typeof COLLECTION_SCHEMAS;\n\n// The body field that equals the document id, where a schema declares one. A drift check injects\n// `doc.id` under this key before validating, so a doc-id field is never a false mismatch.\nexport const COLLECTION_DOC_ID_FIELDS = {\n 'publicUsers/{userId}': 'uid',\n 'registrationStarts/{userId}': 'userId',\n 'sportGames/{gameId}/gameStatEvents/{eventId}': 'eventId',\n 'statsValidationReports/{reportId}': 'reportId',\n 'auditEvents/{auditEventId}': 'id',\n 'pendingMedia/{pendingMediaId}': 'id',\n 'adminNotificationHistory/{historyId}': 'archiveOccurrenceId',\n 'notificationDeliveries/{deliveryId}': 'deliveryId',\n 'notificationArchiveAllJobs/{jobId}': 'jobId',\n 'migrationActionRuns/{actionId}': 'actionId',\n} as const satisfies Partial<Record<RegisteredCollectionPath, string>>;\n\n// Collections declared in ../paths with no bound schema yet, each with the reason.\nexport const PENDING_COLLECTIONS: readonly string[] = [];\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"sport-game.js","sourceRoot":"","sources":["../../src/doc-schemas/sport-game.ts"],"names":[],"mappings":"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","sourcesContent":["// `sportGames/{gameId}` — one scheduled or played game — and its `gameRosterPlayers` subcollection.\r\n// The game is discriminated on `leagueType`, which selects its `stats` shape (SPORT-002).\r\n\r\nimport { z } from 'zod';\r\nimport { parseGameRosterPlayerId } from '../ids/compound-ids.js';\r\nimport type { LeagueType } from '../types/sports.js';\r\nimport { SPORT_LINE_SCHEMAS } from './aggregate-stats.js';\r\nimport { gameStatsSchema } from './game-stats.js';\r\nimport {\r\n calendarDateSchema,\r\n countSchema,\r\n epochMsSchema,\r\n gameRosterPlayerIdSchema,\r\n gameStatusSchema,\r\n leagueIdSchema,\r\n scheduleWeekIdSchema,\r\n statsOperationIdSchema,\r\n teamIdSchema,\r\n userIdSchema,\r\n} from './atoms.js';\r\nimport { gameLocationIdSchema } from './game-location.js';\r\n\r\n// Each sport's game `stats`: the two-key payload (SPORT-001) over that sport's game lines, taken\r\n// from `SPORT_LINE_SCHEMAS` — the one pairing of a sport with its lines. Spelled per sport so each\r\n// payload keeps its exact type; `satisfies` fails the build when a sport is missing.\r\nexport const SPORT_GAME_STATS_SCHEMAS = {\r\n Basketball: gameStatsSchema(SPORT_LINE_SCHEMAS.Basketball.gameTeam, SPORT_LINE_SCHEMAS.Basketball.gamePlayer),\r\n Curling: gameStatsSchema(SPORT_LINE_SCHEMAS.Curling.gameTeam, SPORT_LINE_SCHEMAS.Curling.gamePlayer),\r\n Football: gameStatsSchema(SPORT_LINE_SCHEMAS.Football.gameTeam, SPORT_LINE_SCHEMAS.Football.gamePlayer),\r\n} as const satisfies Record<LeagueType, z.ZodType>;\r\n\r\nexport type BasketballGameStats = z.infer<typeof SPORT_GAME_STATS_SCHEMAS.Basketball>;\r\nexport type CurlingGameStats = z.infer<typeof SPORT_GAME_STATS_SCHEMAS.Curling>;\r\nexport type FootballGameStats = z.infer<typeof SPORT_GAME_STATS_SCHEMAS.Football>;\r\n\r\n// Which sport's payload a game holds is decided by its `leagueType`, never by the payload's shape\r\n// (SPORT-002).\r\nexport type GameStats = z.infer<(typeof SPORT_GAME_STATS_SCHEMAS)[LeagueType]>;\r\n\r\n// One of a game's two team slots. `teamId` is a leagueTeams id, or BYE_TEAM_ID for a bye week.\r\nexport const GameTeamSchema = z.object({\r\n teamId: teamIdSchema,\r\n});\r\nexport type GameTeam = z.infer<typeof GameTeamSchema>;\r\n\r\nconst sportGameBase = {\r\n leagueId: leagueIdSchema,\r\n // The week this game belongs to; weekly schedules are reconstructed by grouping on `weekId`.\r\n weekId: scheduleWeekIdSchema,\r\n weekTitle: z.string(),\r\n weekLocation: z.string(),\r\n weekStartDate: epochMsSchema,\r\n startTime: epochMsSchema,\r\n // The game's local calendar date in LEAGUE_TIME_ZONE.\r\n dayKey: calendarDateSchema,\r\n // The venue as shown, e.g. \"Main Gym - Court 2\". Free text; `gameLocationId` is the reference.\r\n locationLabel: z.string(),\r\n gameLocationId: gameLocationIdSchema.optional(),\r\n subLocation: z.string().optional(),\r\n status: gameStatusSchema,\r\n // Positional: teams[0] is team one, teams[1] is team two.\r\n teams: z.tuple([GameTeamSchema, GameTeamSchema]),\r\n // The real (non-bye) team ids, for `array-contains` queries.\r\n teamIds: z.array(teamIdSchema).min(1).max(2),\r\n // Each team's score, mirrored from its `stats.teamStats` line by the same write that changes it\r\n // (SPORT-107), so a schedule or list reads a score without the stats.\r\n score: z.record(teamIdSchema, countSchema),\r\n // The number of this game's stat event documents, which is also the last `sequence` assigned.\r\n statEventSequence: countSchema,\r\n // A scorer's note per person, keyed by userId (real or ghost).\r\n memos: z.record(z.string(), z.string()).optional(),\r\n // Absence is per roster entry: a person on both teams can be absent on one side only.\r\n absentRosterPlayerIds: z.array(gameRosterPlayerIdSchema),\r\n // The roster entries finalize credited no game played — the adapter's decision, stored so every\r\n // later path reverses what was applied rather than deciding again (SPORT-204). Present only while\r\n // the game is completed.\r\n nonScoringRosterPlayerIds: z.array(gameRosterPlayerIdSchema).optional(),\r\n // Set while a multi-page stats operation holds the game; every other writer of its stats, roster,\r\n // absence, memos, teams, or existence refuses it until the operation clears it.\r\n statsOperationId: statsOperationIdSchema.optional(),\r\n finalizedAt: epochMsSchema.optional(),\r\n // When reopen last opened the finalized game again; set by reopen and never cleared. Its roster is\r\n // then the record of who played it, so a membership change no longer reaches it — only a scorer\r\n // adding a ghost to this game does.\r\n reopenedAt: epochMsSchema.optional(),\r\n createdAt: epochMsSchema,\r\n updatedAt: epochMsSchema,\r\n};\r\n\r\n// The rollup holds a line for each of the game's teams and no other, the score mirror names the same\r\n// teams, and every player line is keyed to one of them.\r\nfunction rollupMatchesTeams(game: {\r\n teamIds: string[];\r\n score: Record<string, number>;\r\n stats: { teamStats: Record<string, unknown>; playerStats: Record<string, unknown> };\r\n}): boolean {\r\n const teams = new Set(game.teamIds);\r\n const namesEveryTeam = (keys: string[]) => keys.length === teams.size && keys.every((key) => teams.has(key));\r\n return (\r\n namesEveryTeam(Object.keys(game.stats.teamStats)) &&\r\n namesEveryTeam(Object.keys(game.score)) &&\r\n Object.keys(game.stats.playerStats).every((key) => teams.has(parseGameRosterPlayerId(key)?.teamId ?? ''))\r\n );\r\n}\r\nconst ROLLUP_MISMATCH = \"The stats rollup and score mirror hold exactly the game's teams.\";\r\n\r\nexport const BasketballGameSchema = z\r\n .object({\r\n ...sportGameBase,\r\n leagueType: z.literal('Basketball' satisfies LeagueType),\r\n stats: SPORT_GAME_STATS_SCHEMAS.Basketball,\r\n // Which team holds the possession arrow.\r\n jumpBallTeamId: teamIdSchema.optional(),\r\n })\r\n .refine(rollupMatchesTeams, ROLLUP_MISMATCH);\r\nexport type BasketballGame = z.infer<typeof BasketballGameSchema>;\r\n\r\nexport const CurlingGameSchema = z\r\n .object({\r\n ...sportGameBase,\r\n leagueType: z.literal('Curling' satisfies LeagueType),\r\n stats: SPORT_GAME_STATS_SCHEMAS.Curling,\r\n })\r\n .refine(rollupMatchesTeams, ROLLUP_MISMATCH);\r\nexport type CurlingGame = z.infer<typeof CurlingGameSchema>;\r\n\r\nexport const FootballGameSchema = z\r\n .object({\r\n ...sportGameBase,\r\n leagueType: z.literal('Football' satisfies LeagueType),\r\n stats: SPORT_GAME_STATS_SCHEMAS.Football,\r\n })\r\n .refine(rollupMatchesTeams, ROLLUP_MISMATCH);\r\nexport type FootballGame = z.infer<typeof FootballGameSchema>;\r\n\r\nexport const SportGameSchema = z.discriminatedUnion('leagueType', [\r\n BasketballGameSchema,\r\n CurlingGameSchema,\r\n FootballGameSchema,\r\n]);\r\nexport type SportGame = z.infer<typeof SportGameSchema>;\r\n\r\n// `sportGames/{gameId}/gameRosterPlayers/{teamId}_{userId}` — one entry per team per person, the id\r\n// from `buildGameRosterPlayerId`. The roster is the participation authority finalize iterates.\r\nexport const GameRosterPlayerSchema = z.object({\r\n userId: userIdSchema,\r\n teamId: teamIdSchema,\r\n isGhost: z.boolean(),\r\n syncedAt: epochMsSchema,\r\n});\r\nexport type GameRosterPlayer = z.infer<typeof GameRosterPlayerSchema>;\r\n"]}
1
+ {"version":3,"file":"sport-game.js","sourceRoot":"","sources":["../../src/doc-schemas/sport-game.ts"],"names":[],"mappings":"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","sourcesContent":["// `sportGames/{gameId}` — one scheduled or played game — and its `gameRosterPlayers` subcollection.\n// The game is discriminated on `leagueType`, which selects its `stats` shape (SPORT-002).\n\nimport { z } from 'zod';\nimport { parseGameRosterPlayerId } from '../ids/compound-ids.js';\nimport type { LeagueType } from '../types/sports.js';\nimport { SPORT_LINE_SCHEMAS } from './aggregate-stats.js';\nimport { gameStatsSchema } from './game-stats.js';\nimport {\n calendarDateSchema,\n countSchema,\n epochMsSchema,\n gameRosterPlayerIdSchema,\n gameStatusSchema,\n leagueIdSchema,\n scheduleWeekIdSchema,\n statsOperationIdSchema,\n teamIdSchema,\n userIdSchema,\n} from './atoms.js';\nimport { gameLocationIdSchema } from './game-location.js';\n\n// Each sport's game `stats`: the two-key payload (SPORT-001) over that sport's game lines, taken\n// from `SPORT_LINE_SCHEMAS` — the one pairing of a sport with its lines. Spelled per sport so each\n// payload keeps its exact type; `satisfies` fails the build when a sport is missing.\nexport const SPORT_GAME_STATS_SCHEMAS = {\n Basketball: gameStatsSchema(SPORT_LINE_SCHEMAS.Basketball.gameTeam, SPORT_LINE_SCHEMAS.Basketball.gamePlayer),\n Curling: gameStatsSchema(SPORT_LINE_SCHEMAS.Curling.gameTeam, SPORT_LINE_SCHEMAS.Curling.gamePlayer),\n Football: gameStatsSchema(SPORT_LINE_SCHEMAS.Football.gameTeam, SPORT_LINE_SCHEMAS.Football.gamePlayer),\n} as const satisfies Record<LeagueType, z.ZodType>;\n\nexport type BasketballGameStats = z.infer<typeof SPORT_GAME_STATS_SCHEMAS.Basketball>;\nexport type CurlingGameStats = z.infer<typeof SPORT_GAME_STATS_SCHEMAS.Curling>;\nexport type FootballGameStats = z.infer<typeof SPORT_GAME_STATS_SCHEMAS.Football>;\n\n// Which sport's payload a game holds is decided by its `leagueType`, never by the payload's shape\n// (SPORT-002).\nexport type GameStats = z.infer<(typeof SPORT_GAME_STATS_SCHEMAS)[LeagueType]>;\n\n// One of a game's two team slots. `teamId` is a leagueTeams id, or BYE_TEAM_ID for a bye week.\nexport const GameTeamSchema = z.object({\n teamId: teamIdSchema,\n});\nexport type GameTeam = z.infer<typeof GameTeamSchema>;\n\nconst sportGameBase = {\n leagueId: leagueIdSchema,\n // The week this game belongs to; weekly schedules are reconstructed by grouping on `weekId`.\n weekId: scheduleWeekIdSchema,\n weekTitle: z.string(),\n weekLocation: z.string(),\n weekStartDate: epochMsSchema,\n startTime: epochMsSchema,\n // The game's local calendar date in LEAGUE_TIME_ZONE.\n dayKey: calendarDateSchema,\n // The venue as shown, e.g. \"Main Gym - Court 2\". Free text; `gameLocationId` is the reference.\n locationLabel: z.string(),\n gameLocationId: gameLocationIdSchema.optional(),\n subLocation: z.string().optional(),\n status: gameStatusSchema,\n // Positional: teams[0] is team one, teams[1] is team two.\n teams: z.tuple([GameTeamSchema, GameTeamSchema]),\n // The real (non-bye) team ids, for `array-contains` queries.\n teamIds: z.array(teamIdSchema).min(1).max(2),\n // Each team's score, mirrored from its `stats.teamStats` line by the same write that changes it\n // (SPORT-107), so a schedule or list reads a score without the stats.\n score: z.record(teamIdSchema, countSchema),\n // The number of this game's stat event documents, which is also the last `sequence` assigned.\n statEventSequence: countSchema,\n // A scorer's note per person, keyed by userId (real or ghost).\n memos: z.record(z.string(), z.string()).optional(),\n // Absence is per roster entry: a person on both teams can be absent on one side only.\n absentRosterPlayerIds: z.array(gameRosterPlayerIdSchema),\n // The roster entries finalize credited no game played — the adapter's decision, stored so every\n // later path reverses what was applied rather than deciding again (SPORT-204). Present only while\n // the game is completed.\n nonScoringRosterPlayerIds: z.array(gameRosterPlayerIdSchema).optional(),\n // Set while a multi-page stats operation holds the game; every other writer of its stats, roster,\n // absence, memos, teams, or existence refuses it until the operation clears it.\n statsOperationId: statsOperationIdSchema.optional(),\n finalizedAt: epochMsSchema.optional(),\n // When reopen last opened the finalized game again; set by reopen and never cleared. Its roster is\n // then the record of who played it, so a membership change no longer reaches it — only a scorer\n // adding a ghost to this game does.\n reopenedAt: epochMsSchema.optional(),\n createdAt: epochMsSchema,\n updatedAt: epochMsSchema,\n};\n\n// The rollup holds a line for each of the game's teams and no other, the score mirror names the same\n// teams, and every player line is keyed to one of them.\nfunction rollupMatchesTeams(game: {\n teamIds: string[];\n score: Record<string, number>;\n stats: { teamStats: Record<string, unknown>; playerStats: Record<string, unknown> };\n}): boolean {\n const teams = new Set(game.teamIds);\n const namesEveryTeam = (keys: string[]) => keys.length === teams.size && keys.every((key) => teams.has(key));\n return (\n namesEveryTeam(Object.keys(game.stats.teamStats)) &&\n namesEveryTeam(Object.keys(game.score)) &&\n Object.keys(game.stats.playerStats).every((key) => teams.has(parseGameRosterPlayerId(key)?.teamId ?? ''))\n );\n}\nconst ROLLUP_MISMATCH = \"The stats rollup and score mirror hold exactly the game's teams.\";\n\nexport const BasketballGameSchema = z\n .object({\n ...sportGameBase,\n leagueType: z.literal('Basketball' satisfies LeagueType),\n stats: SPORT_GAME_STATS_SCHEMAS.Basketball,\n // Which team holds the possession arrow.\n jumpBallTeamId: teamIdSchema.optional(),\n })\n .refine(rollupMatchesTeams, ROLLUP_MISMATCH);\nexport type BasketballGame = z.infer<typeof BasketballGameSchema>;\n\nexport const CurlingGameSchema = z\n .object({\n ...sportGameBase,\n leagueType: z.literal('Curling' satisfies LeagueType),\n stats: SPORT_GAME_STATS_SCHEMAS.Curling,\n })\n .refine(rollupMatchesTeams, ROLLUP_MISMATCH);\nexport type CurlingGame = z.infer<typeof CurlingGameSchema>;\n\nexport const FootballGameSchema = z\n .object({\n ...sportGameBase,\n leagueType: z.literal('Football' satisfies LeagueType),\n stats: SPORT_GAME_STATS_SCHEMAS.Football,\n })\n .refine(rollupMatchesTeams, ROLLUP_MISMATCH);\nexport type FootballGame = z.infer<typeof FootballGameSchema>;\n\nexport const SportGameSchema = z.discriminatedUnion('leagueType', [\n BasketballGameSchema,\n CurlingGameSchema,\n FootballGameSchema,\n]);\nexport type SportGame = z.infer<typeof SportGameSchema>;\n\n// `sportGames/{gameId}/gameRosterPlayers/{teamId}_{userId}` — one entry per team per person, the id\n// from `buildGameRosterPlayerId`. The roster is the participation authority finalize iterates.\nexport const GameRosterPlayerSchema = z.object({\n userId: userIdSchema,\n teamId: teamIdSchema,\n isGhost: z.boolean(),\n syncedAt: epochMsSchema,\n});\nexport type GameRosterPlayer = z.infer<typeof GameRosterPlayerSchema>;\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"system.js","sourceRoot":"","sources":["../../src/doc-schemas/system.ts"],"names":[],"mappings":"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","sourcesContent":["// Fixed-ID system documents in the underscore buckets, plus the derived stat documents. Every one\r\n// is callable-only for writes.\r\n\r\nimport { z } from 'zod';\r\nimport {\r\n MAX_ANNOUNCEMENT_MESSAGE_LENGTH,\r\n MAX_APP_VERSION_LENGTH,\r\n MAX_LANDING_ABOUT_TEXT_LENGTH,\r\n MAX_LANDING_HERO_TITLE_LENGTH,\r\n MAX_MAINTENANCE_MESSAGE_LENGTH,\r\n} from '../constants/business.js';\r\nimport { MAX_STATS_VALIDATION_DISCREPANCIES } from '../constants/sports.js';\r\nimport { GallerySummarySchema } from './gallery.js';\r\nimport {\r\n countSchema,\r\n epochMsSchema,\r\n gameIdSchema,\r\n leagueIdSchema,\r\n statsBeaconActionSchema,\r\n statsDiscrepancyKindSchema,\r\n statsOperationIdSchema,\r\n teamIdSchema,\r\n userIdSchema,\r\n} from './atoms.js';\r\n\r\n// `_appConfig/app` — the app-wide operational levers, set through the audited updateAppConfig\r\n// callable. `appVersion` is what a live client compares itself against to force a refresh. It is\r\n// written whole through `mergeAppConfigUpdate` and read one lever at a time through\r\n// `readAppConfigLever` (../utils/app-config.ts). Every text lever carries the same cap as its\r\n// updateAppConfig input field, so no writer can store a value the input would refuse.\r\nexport const AppConfigSchema = z.object({\r\n // `''` is valid here — no version published yet; only the update input requires a value.\r\n appVersion: z.string().max(MAX_APP_VERSION_LENGTH),\r\n maintenanceMode: z.boolean(),\r\n maintenanceMessage: z.string().max(MAX_MAINTENANCE_MESSAGE_LENGTH).optional(),\r\n registrationEnabled: z.boolean(),\r\n // Empty means no banner — there is no separate on/off flag. Non-empty is the band above every\r\n // page.\r\n announcementMessage: z.string().max(MAX_ANNOUNCEMENT_MESSAGE_LENGTH).optional(),\r\n});\r\nexport type AppConfig = z.infer<typeof AppConfigSchema>;\r\n\r\n// `_appConfig/landingPage` — admin-authored landing page copy, written by adminUpdateLandingPage,\r\n// plus the landing gallery's summary, which only the picture core writes.\r\nexport const LandingPageConfigSchema = z.object({\r\n heroTitle: z.string().max(MAX_LANDING_HERO_TITLE_LENGTH),\r\n aboutCompany: z.string().max(MAX_LANDING_ABOUT_TEXT_LENGTH),\r\n pictures: GallerySummarySchema.optional(),\r\n updatedAt: epochMsSchema,\r\n});\r\nexport type LandingPageConfig = z.infer<typeof LandingPageConfigSchema>;\r\n\r\n// `_appConfig/statsBeacon` — the signal watching clients refresh their stats on, replaced whole by\r\n// every write. The scope ids say what changed; an action that spans more than one game or league\r\n// omits them, and because every write replaces the document, an omitted id never survives from an\r\n// earlier write.\r\nexport const StatsBeaconSchema = z.object({\r\n updatedAt: epochMsSchema,\r\n action: statsBeaconActionSchema,\r\n leagueId: leagueIdSchema.optional(),\r\n gameId: gameIdSchema.optional(),\r\n teamIds: z.array(teamIdSchema).optional(),\r\n});\r\nexport type StatsBeacon = z.infer<typeof StatsBeaconSchema>;\r\n\r\n// `_systemData/adminList` — the admin and referee rosters. The source of truth the `admin` and\r\n// `referee` custom claims are minted from, written only by the audited updateAdminList callable;\r\n// the first admin of a fresh environment is seeded in the console.\r\nexport const AdminListSchema = z.object({\r\n admins: z.array(userIdSchema),\r\n referees: z.array(userIdSchema),\r\n});\r\nexport type AdminList = z.infer<typeof AdminListSchema>;\r\n\r\n// `statsValidationReports/{reportId}` — one stats check: every place a game's stored numbers, or a\r\n// derived aggregate, disagree with what the game's events and the completed games say they should\r\n// be. Its id is the check's operation id, which the admin's browser mints, so two checks never share\r\n// a report. Written only by the check, page by page; `complete` turns true with its last page.\r\nexport const STATS_DISCREPANCY_VALUE_SCHEMA = z.union([\r\n z.number(),\r\n z.string(),\r\n z.array(z.number().nullable()),\r\n z.record(z.string(), z.number()),\r\n z.null(),\r\n]);\r\n\r\nexport const StatsDiscrepancySchema = z.strictObject({\r\n kind: statsDiscrepancyKindSchema,\r\n gameId: gameIdSchema.optional(),\r\n // The slash path of the document the finding is about.\r\n path: z.string().min(1),\r\n // The line or entry within it — a team id, a roster-entry id, a user id — when there is one.\r\n key: z.string().min(1).optional(),\r\n field: z.string().min(1).optional(),\r\n expected: STATS_DISCREPANCY_VALUE_SCHEMA.optional(),\r\n actual: STATS_DISCREPANCY_VALUE_SCHEMA.optional(),\r\n});\r\nexport type StatsDiscrepancy = z.infer<typeof StatsDiscrepancySchema>;\r\n\r\nexport const StatsValidationScopeSchema = z\r\n .strictObject({ gameId: gameIdSchema.optional(), leagueId: leagueIdSchema.optional() })\r\n .refine((scope) => scope.gameId === undefined || scope.leagueId === undefined, 'One game or one league, not both.');\r\nexport type StatsValidationScope = z.infer<typeof StatsValidationScopeSchema>;\r\n\r\nexport const StatsValidationSummarySchema = z.strictObject({\r\n totalGamesChecked: countSchema,\r\n totalUsersChecked: countSchema,\r\n totalTeamsChecked: countSchema,\r\n totalLeaguesChecked: countSchema,\r\n discrepanciesFound: countSchema,\r\n});\r\nexport type StatsValidationSummary = z.infer<typeof StatsValidationSummarySchema>;\r\n\r\nexport const StatsValidationReportSchema = z.strictObject({\r\n reportId: z.string().min(1),\r\n operationId: statsOperationIdSchema,\r\n ranAt: epochMsSchema,\r\n ranByUserId: userIdSchema,\r\n scope: StatsValidationScopeSchema,\r\n complete: z.boolean(),\r\n // Game-tier findings first, then aggregate ones, at most MAX_STATS_VALIDATION_DISCREPANCIES.\r\n discrepancies: z.array(StatsDiscrepancySchema).max(MAX_STATS_VALIDATION_DISCREPANCIES),\r\n // Every finding, kept or not, counted by kind.\r\n discrepancyCounts: z.partialRecord(statsDiscrepancyKindSchema, countSchema),\r\n summary: StatsValidationSummarySchema,\r\n});\r\nexport type StatsValidationReport = z.infer<typeof StatsValidationReportSchema>;\r\n"]}
1
+ {"version":3,"file":"system.js","sourceRoot":"","sources":["../../src/doc-schemas/system.ts"],"names":[],"mappings":"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","sourcesContent":["// Fixed-ID system documents in the underscore buckets, plus the derived stat documents. Every one\n// is callable-only for writes.\n\nimport { z } from 'zod';\nimport {\n MAX_ANNOUNCEMENT_MESSAGE_LENGTH,\n MAX_APP_VERSION_LENGTH,\n MAX_LANDING_ABOUT_TEXT_LENGTH,\n MAX_LANDING_HERO_TITLE_LENGTH,\n MAX_MAINTENANCE_MESSAGE_LENGTH,\n} from '../constants/business.js';\nimport { MAX_STATS_VALIDATION_DISCREPANCIES } from '../constants/sports.js';\nimport { GallerySummarySchema } from './gallery.js';\nimport {\n countSchema,\n epochMsSchema,\n gameIdSchema,\n leagueIdSchema,\n statsBeaconActionSchema,\n statsDiscrepancyKindSchema,\n statsOperationIdSchema,\n teamIdSchema,\n userIdSchema,\n} from './atoms.js';\n\n// `_appConfig/app` — the app-wide operational levers, set through the audited updateAppConfig\n// callable. `appVersion` is what a live client compares itself against to force a refresh. It is\n// written whole through `mergeAppConfigUpdate` and read one lever at a time through\n// `readAppConfigLever` (../utils/app-config.ts). Every text lever carries the same cap as its\n// updateAppConfig input field, so no writer can store a value the input would refuse.\nexport const AppConfigSchema = z.object({\n // `''` is valid here — no version published yet; only the update input requires a value.\n appVersion: z.string().max(MAX_APP_VERSION_LENGTH),\n maintenanceMode: z.boolean(),\n maintenanceMessage: z.string().max(MAX_MAINTENANCE_MESSAGE_LENGTH).optional(),\n registrationEnabled: z.boolean(),\n // Empty means no banner — there is no separate on/off flag. Non-empty is the band above every\n // page.\n announcementMessage: z.string().max(MAX_ANNOUNCEMENT_MESSAGE_LENGTH).optional(),\n});\nexport type AppConfig = z.infer<typeof AppConfigSchema>;\n\n// `_appConfig/landingPage` — admin-authored landing page copy, written by adminUpdateLandingPage,\n// plus the landing gallery's summary, which only the picture core writes.\nexport const LandingPageConfigSchema = z.object({\n heroTitle: z.string().max(MAX_LANDING_HERO_TITLE_LENGTH),\n aboutCompany: z.string().max(MAX_LANDING_ABOUT_TEXT_LENGTH),\n pictures: GallerySummarySchema.optional(),\n updatedAt: epochMsSchema,\n});\nexport type LandingPageConfig = z.infer<typeof LandingPageConfigSchema>;\n\n// `_appConfig/statsBeacon` — the signal watching clients refresh their stats on, replaced whole by\n// every write. The scope ids say what changed; an action that spans more than one game or league\n// omits them, and because every write replaces the document, an omitted id never survives from an\n// earlier write.\nexport const StatsBeaconSchema = z.object({\n updatedAt: epochMsSchema,\n action: statsBeaconActionSchema,\n leagueId: leagueIdSchema.optional(),\n gameId: gameIdSchema.optional(),\n teamIds: z.array(teamIdSchema).optional(),\n});\nexport type StatsBeacon = z.infer<typeof StatsBeaconSchema>;\n\n// `_systemData/adminList` — the admin and referee rosters. The source of truth the `admin` and\n// `referee` custom claims are minted from, written only by the audited updateAdminList callable;\n// the first admin of a fresh environment is seeded in the console.\nexport const AdminListSchema = z.object({\n admins: z.array(userIdSchema),\n referees: z.array(userIdSchema),\n});\nexport type AdminList = z.infer<typeof AdminListSchema>;\n\n// `statsValidationReports/{reportId}` — one stats check: every place a game's stored numbers, or a\n// derived aggregate, disagree with what the game's events and the completed games say they should\n// be. Its id is the check's operation id, which the admin's browser mints, so two checks never share\n// a report. Written only by the check, page by page; `complete` turns true with its last page.\nexport const STATS_DISCREPANCY_VALUE_SCHEMA = z.union([\n z.number(),\n z.string(),\n z.array(z.number().nullable()),\n z.record(z.string(), z.number()),\n z.null(),\n]);\n\nexport const StatsDiscrepancySchema = z.strictObject({\n kind: statsDiscrepancyKindSchema,\n gameId: gameIdSchema.optional(),\n // The slash path of the document the finding is about.\n path: z.string().min(1),\n // The line or entry within it — a team id, a roster-entry id, a user id — when there is one.\n key: z.string().min(1).optional(),\n field: z.string().min(1).optional(),\n expected: STATS_DISCREPANCY_VALUE_SCHEMA.optional(),\n actual: STATS_DISCREPANCY_VALUE_SCHEMA.optional(),\n});\nexport type StatsDiscrepancy = z.infer<typeof StatsDiscrepancySchema>;\n\nexport const StatsValidationScopeSchema = z\n .strictObject({ gameId: gameIdSchema.optional(), leagueId: leagueIdSchema.optional() })\n .refine((scope) => scope.gameId === undefined || scope.leagueId === undefined, 'One game or one league, not both.');\nexport type StatsValidationScope = z.infer<typeof StatsValidationScopeSchema>;\n\nexport const StatsValidationSummarySchema = z.strictObject({\n totalGamesChecked: countSchema,\n totalUsersChecked: countSchema,\n totalTeamsChecked: countSchema,\n totalLeaguesChecked: countSchema,\n discrepanciesFound: countSchema,\n});\nexport type StatsValidationSummary = z.infer<typeof StatsValidationSummarySchema>;\n\nexport const StatsValidationReportSchema = z.strictObject({\n reportId: z.string().min(1),\n operationId: statsOperationIdSchema,\n ranAt: epochMsSchema,\n ranByUserId: userIdSchema,\n scope: StatsValidationScopeSchema,\n complete: z.boolean(),\n // Game-tier findings first, then aggregate ones, at most MAX_STATS_VALIDATION_DISCREPANCIES.\n discrepancies: z.array(StatsDiscrepancySchema).max(MAX_STATS_VALIDATION_DISCREPANCIES),\n // Every finding, kept or not, counted by kind.\n discrepancyCounts: z.partialRecord(statsDiscrepancyKindSchema, countSchema),\n summary: StatsValidationSummarySchema,\n});\nexport type StatsValidationReport = z.infer<typeof StatsValidationReportSchema>;\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"user-profile.js","sourceRoot":"","sources":["../../src/doc-schemas/user-profile.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,6FAA6F;AAC7F,kGAAkG;AAClG,uDAAuD;AAEvD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,4BAA4B,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC7F,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9E,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,kGAAkG;AAClG,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AAEnD,6FAA6F;AAC7F,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,OAAO,EAAE,aAAa;IACtB,SAAS,EAAE,aAAa;CACzB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,qBAAqB,EAAE,CAAC,CAAC,MAAM,EAAE;IACjC,oFAAoF;IACpF,UAAU,EAAE,gBAAgB;IAC5B,+FAA+F;IAC/F,yBAAyB;IACzB,aAAa,EAAE,uBAAuB,CAAC,QAAQ,EAAE;IACjD,aAAa,EAAE,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC,QAAQ,EAAE;IACrD,mEAAmE;IACnE,cAAc,EAAE,aAAa,CAAC,QAAQ,EAAE;IACxC,gBAAgB,EAAE,aAAa,CAAC,QAAQ,EAAE;IAC1C,SAAS,EAAE,aAAa;IACxB,SAAS,EAAE,aAAa;CACzB,CAAC,CAAC;AAGH,mGAAmG;AACnG,gBAAgB;AAChB,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,6FAA6F;IAC7F,6FAA6F;IAC7F,qBAAqB,EAAE,aAAa,CAAC,QAAQ,EAAE;IAC/C,6DAA6D;IAC7D,mBAAmB,EAAE,eAAe,CAAC,0BAA0B,CAAC,CAAC,QAAQ,EAAE;IAC3E,0FAA0F;IAC1F,gEAAgE;IAChE,KAAK,EAAE,eAAe,CAAC,QAAQ,EAAE;IACjC,aAAa,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IACrC,SAAS,EAAE,aAAa;CACzB,CAAC,CAAC;AAGH,mGAAmG;AACnG,sGAAsG;AACtG,+FAA+F;AAC/F,yCAAyC;AACzC,MAAM,CAAC,MAAM,qBAAqB,GAAG,4BAA4B,CAAC,IAAI,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;AAGhG,sGAAsG;AACtG,8BAA8B;AAC9B,MAAM,qBAAqB,GAAwC;IACjE,WAAW,EAAE,IAAI;IACjB,MAAM,EAAE,IAAI;IACZ,QAAQ,EAAE,IAAI;IACd,OAAO,EAAE,IAAI;CACd,CAAC;AAEF,8DAA8D;AAC9D,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC,IAAI,CAAC,qBAAqB,CAAuC,CAAC","sourcesContent":["// `userProfiles/{userId}` — the account's profile, one per Firebase Auth uid. Ghosts have none.\r\n// It is publicly readable (a profile page renders signed out), so it holds nothing personal:\r\n// email and contact details live in its owner-only `privateData/{userId}` document (BACKEND-105).\r\n// Plus its `historicalStats/{sportKey}` subcollection.\r\n\r\nimport { z } from 'zod';\r\nimport { THEME_NAMES } from '@ttt-productions/theme-core';\r\nimport { CONTACT_PHONE_INPUT_FORMAT } from '../input-formats/index.js';\r\nimport { BasketballPlayerTotalsSchema, UserLifetimeStatsSchema } from './aggregate-stats.js';\r\nimport { epochMsSchema, textFieldSchema, userStatusSchema } from './atoms.js';\r\nimport { awardIdSchema } from './catalogs.js';\r\n\r\n// A saved theme, typed by theme-core's theme set — the one declaration its provider renders from.\r\nexport const themeNameSchema = z.enum(THEME_NAMES);\r\n\r\n// An award granted to this user. Name and imagery resolve from `awardDefinitions/{awardId}`.\r\nexport const GrantedAwardSchema = z.object({\r\n awardId: awardIdSchema,\r\n grantedAt: epochMsSchema,\r\n});\r\nexport type GrantedAward = z.infer<typeof GrantedAwardSchema>;\r\n\r\nexport const UserProfileSchema = z.object({\r\n displayName: z.string(),\r\n displayName_lowercase: z.string(),\r\n // Written `active` at registration and by the profile copy; nothing else writes it.\r\n userStatus: userStatusSchema,\r\n // Per sport, the career line; career games played is the sum of their games played, derived at\r\n // read and never stored.\r\n lifetimeStats: UserLifetimeStatsSchema.optional(),\r\n grantedAwards: z.array(GrantedAwardSchema).optional(),\r\n // The awards the account shows beside its name, each one it holds.\r\n primaryAwardId: awardIdSchema.optional(),\r\n secondaryAwardId: awardIdSchema.optional(),\r\n createdAt: epochMsSchema,\r\n updatedAt: epochMsSchema,\r\n});\r\nexport type UserProfile = z.infer<typeof UserProfileSchema>;\r\n\r\n// `userProfiles/{userId}/privateData/{userId}` — readable by its owner and by admins; written only\r\n// by callables.\r\nexport const UserPrivateDataSchema = z.object({\r\n email: z.string(),\r\n // When this login's registration started (`createRegistrationStart`); `registerUser` refuses\r\n // without it. Absent on an account copied from V1, which registered before the step existed.\r\n registrationStartedAt: epochMsSchema.optional(),\r\n // Pre-fills the contact phone when this user creates a team.\r\n defaultContactPhone: textFieldSchema(CONTACT_PHONE_INPUT_FORMAT).optional(),\r\n // The account's copy of the viewer's settings, synced with the device's. Absent means the\r\n // account has saved nothing and the device's own choice stands.\r\n theme: themeNameSchema.optional(),\r\n reducedMotion: z.boolean().optional(),\r\n updatedAt: epochMsSchema,\r\n});\r\nexport type UserPrivateData = z.infer<typeof UserPrivateDataSchema>;\r\n\r\n// `userProfiles/{userId}/historicalStats/{sportKey}` — pre-Q-Sports career totals an admin enters,\r\n// folded into lifetime and hall-of-fame recalculation. Absolute totals, not increments. Basketball is\r\n// the sport with a baseline, so it is the basketball totals line without FP, which the rebuild\r\n// derives when it folds the baseline in.\r\nexport const HistoricalStatsSchema = BasketballPlayerTotalsSchema.omit({ fantasyPoints: true });\r\nexport type HistoricalStats = z.infer<typeof HistoricalStatsSchema>;\n\n// Every field of a baseline, keyed so the record cannot miss one or name one the schema lacks, in the\n// order an admin enters them.\nconst HISTORICAL_STAT_ENTRY: Record<keyof HistoricalStats, true> = {\n gamesPlayed: true,\n points: true,\n rebounds: true,\n assists: true,\n};\n\n/** A baseline's totals, in the order an admin enters them. */\nexport const HISTORICAL_STAT_KEYS = Object.keys(HISTORICAL_STAT_ENTRY) as readonly (keyof HistoricalStats)[];\r\n"]}
1
+ {"version":3,"file":"user-profile.js","sourceRoot":"","sources":["../../src/doc-schemas/user-profile.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,6FAA6F;AAC7F,kGAAkG;AAClG,uDAAuD;AAEvD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,4BAA4B,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC7F,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9E,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,kGAAkG;AAClG,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AAEnD,6FAA6F;AAC7F,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,OAAO,EAAE,aAAa;IACtB,SAAS,EAAE,aAAa;CACzB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,qBAAqB,EAAE,CAAC,CAAC,MAAM,EAAE;IACjC,oFAAoF;IACpF,UAAU,EAAE,gBAAgB;IAC5B,+FAA+F;IAC/F,yBAAyB;IACzB,aAAa,EAAE,uBAAuB,CAAC,QAAQ,EAAE;IACjD,aAAa,EAAE,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC,QAAQ,EAAE;IACrD,mEAAmE;IACnE,cAAc,EAAE,aAAa,CAAC,QAAQ,EAAE;IACxC,gBAAgB,EAAE,aAAa,CAAC,QAAQ,EAAE;IAC1C,SAAS,EAAE,aAAa;IACxB,SAAS,EAAE,aAAa;CACzB,CAAC,CAAC;AAGH,mGAAmG;AACnG,gBAAgB;AAChB,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,6FAA6F;IAC7F,6FAA6F;IAC7F,qBAAqB,EAAE,aAAa,CAAC,QAAQ,EAAE;IAC/C,6DAA6D;IAC7D,mBAAmB,EAAE,eAAe,CAAC,0BAA0B,CAAC,CAAC,QAAQ,EAAE;IAC3E,0FAA0F;IAC1F,gEAAgE;IAChE,KAAK,EAAE,eAAe,CAAC,QAAQ,EAAE;IACjC,aAAa,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IACrC,SAAS,EAAE,aAAa;CACzB,CAAC,CAAC;AAGH,mGAAmG;AACnG,sGAAsG;AACtG,+FAA+F;AAC/F,yCAAyC;AACzC,MAAM,CAAC,MAAM,qBAAqB,GAAG,4BAA4B,CAAC,IAAI,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;AAGhG,sGAAsG;AACtG,8BAA8B;AAC9B,MAAM,qBAAqB,GAAwC;IACjE,WAAW,EAAE,IAAI;IACjB,MAAM,EAAE,IAAI;IACZ,QAAQ,EAAE,IAAI;IACd,OAAO,EAAE,IAAI;CACd,CAAC;AAEF,8DAA8D;AAC9D,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC,IAAI,CAAC,qBAAqB,CAAuC,CAAC","sourcesContent":["// `userProfiles/{userId}` — the account's profile, one per Firebase Auth uid. Ghosts have none.\n// It is publicly readable (a profile page renders signed out), so it holds nothing personal:\n// email and contact details live in its owner-only `privateData/{userId}` document (BACKEND-105).\n// Plus its `historicalStats/{sportKey}` subcollection.\n\nimport { z } from 'zod';\nimport { THEME_NAMES } from '@ttt-productions/theme-core';\nimport { CONTACT_PHONE_INPUT_FORMAT } from '../input-formats/index.js';\nimport { BasketballPlayerTotalsSchema, UserLifetimeStatsSchema } from './aggregate-stats.js';\nimport { epochMsSchema, textFieldSchema, userStatusSchema } from './atoms.js';\nimport { awardIdSchema } from './catalogs.js';\n\n// A saved theme, typed by theme-core's theme set — the one declaration its provider renders from.\nexport const themeNameSchema = z.enum(THEME_NAMES);\n\n// An award granted to this user. Name and imagery resolve from `awardDefinitions/{awardId}`.\nexport const GrantedAwardSchema = z.object({\n awardId: awardIdSchema,\n grantedAt: epochMsSchema,\n});\nexport type GrantedAward = z.infer<typeof GrantedAwardSchema>;\n\nexport const UserProfileSchema = z.object({\n displayName: z.string(),\n displayName_lowercase: z.string(),\n // Written `active` at registration and by the profile copy; nothing else writes it.\n userStatus: userStatusSchema,\n // Per sport, the career line; career games played is the sum of their games played, derived at\n // read and never stored.\n lifetimeStats: UserLifetimeStatsSchema.optional(),\n grantedAwards: z.array(GrantedAwardSchema).optional(),\n // The awards the account shows beside its name, each one it holds.\n primaryAwardId: awardIdSchema.optional(),\n secondaryAwardId: awardIdSchema.optional(),\n createdAt: epochMsSchema,\n updatedAt: epochMsSchema,\n});\nexport type UserProfile = z.infer<typeof UserProfileSchema>;\n\n// `userProfiles/{userId}/privateData/{userId}` — readable by its owner and by admins; written only\n// by callables.\nexport const UserPrivateDataSchema = z.object({\n email: z.string(),\n // When this login's registration started (`createRegistrationStart`); `registerUser` refuses\n // without it. Absent on an account copied from V1, which registered before the step existed.\n registrationStartedAt: epochMsSchema.optional(),\n // Pre-fills the contact phone when this user creates a team.\n defaultContactPhone: textFieldSchema(CONTACT_PHONE_INPUT_FORMAT).optional(),\n // The account's copy of the viewer's settings, synced with the device's. Absent means the\n // account has saved nothing and the device's own choice stands.\n theme: themeNameSchema.optional(),\n reducedMotion: z.boolean().optional(),\n updatedAt: epochMsSchema,\n});\nexport type UserPrivateData = z.infer<typeof UserPrivateDataSchema>;\n\n// `userProfiles/{userId}/historicalStats/{sportKey}` — pre-Q-Sports career totals an admin enters,\n// folded into lifetime and hall-of-fame recalculation. Absolute totals, not increments. Basketball is\n// the sport with a baseline, so it is the basketball totals line without FP, which the rebuild\n// derives when it folds the baseline in.\nexport const HistoricalStatsSchema = BasketballPlayerTotalsSchema.omit({ fantasyPoints: true });\nexport type HistoricalStats = z.infer<typeof HistoricalStatsSchema>;\n\n// Every field of a baseline, keyed so the record cannot miss one or name one the schema lacks, in the\n// order an admin enters them.\nconst HISTORICAL_STAT_ENTRY: Record<keyof HistoricalStats, true> = {\n gamesPlayed: true,\n points: true,\n rebounds: true,\n assists: true,\n};\n\n/** A baseline's totals, in the order an admin enters them. */\nexport const HISTORICAL_STAT_KEYS = Object.keys(HISTORICAL_STAT_ENTRY) as readonly (keyof HistoricalStats)[];\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"waiver-template.js","sourceRoot":"","sources":["../../src/doc-schemas/waiver-template.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,EAAE;AACF,mGAAmG;AACnG,+FAA+F;AAC/F,+FAA+F;AAC/F,sDAAsD;AAEtD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,uBAAuB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAElF,MAAM,CAAC,MAAM,sBAAsB,GAAG,uBAAuB,CAAC;AAC9D,MAAM,CAAC,MAAM,iCAAiC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAEzE,4FAA4F;AAC5F,gCAAgC;AAChC,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,OAAO,EAAE,iCAAiC;IAC1C,SAAS,EAAE,aAAa;IACxB,SAAS,EAAE,aAAa;IACxB,+FAA+F;IAC/F,6FAA6F;IAC7F,sDAAsD;IACtD,SAAS,EAAE,aAAa,CAAC,QAAQ,EAAE;IACnC,kGAAkG;IAClG,kGAAkG;IAClG,kEAAkE;IAClE,YAAY,EAAE,aAAa,CAAC,QAAQ,EAAE;CACvC,CAAC,CAAC;AAGH,mGAAmG;AACnG,kCAAkC;AAClC,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC,MAAM,CAAC;IAClD,OAAO,EAAE,iCAAiC;IAC1C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,uDAAuD;IACvD,iBAAiB,EAAE,YAAY,CAAC,QAAQ,EAAE;IAC1C,WAAW,EAAE,aAAa;CAC3B,CAAC,CAAC","sourcesContent":["// Waiver templates: the wording an admin authors for a league's players to sign.\r\n//\r\n// The wording is versioned. Every save that changes it publishes a new version, and each published\r\n// version is its own immutable document — so the exact wording in force when any signature was\r\n// captured still exists however the template has changed since. A signature also keeps its own\r\n// copy of what the signer saw (`SignedWaiverSchema`).\r\n\r\nimport { z } from 'zod';\r\nimport { documentIdSegmentSchema, epochMsSchema, userIdSchema } from './atoms.js';\r\n\r\nexport const waiverTemplateIdSchema = documentIdSegmentSchema;\r\nexport const waiverTemplateVersionNumberSchema = z.number().int().min(1);\r\n\r\n// `waiverTemplates/{waiverTemplateId}` — the template as it reads now: its latest published\r\n// version's wording and number.\r\nexport const WaiverTemplateSchema = z.object({\r\n title: z.string(),\r\n waiverText: z.string(),\r\n version: waiverTemplateVersionNumberSchema,\r\n createdAt: epochMsSchema,\r\n updatedAt: epochMsSchema,\r\n // Set when an admin deletes a template a signature or a closed league still names, or one with\r\n // more versions than a delete removes: it is kept for them and offered to no league again. A\r\n // template nothing names is deleted outright instead.\r\n retiredAt: epochMsSchema.optional(),\r\n // When a league last came to require the template. The league save that chooses it writes this in\r\n // its own transaction so a delete's read of this document conflicts with that save; it is its own\r\n // field so `updatedAt` keeps meaning the template itself changed.\r\n lastChosenAt: epochMsSchema.optional(),\r\n});\r\nexport type WaiverTemplate = z.infer<typeof WaiverTemplateSchema>;\r\n\r\n// `waiverTemplates/{waiverTemplateId}/waiverTemplateVersions/{versionId}` — one published version,\r\n// created once and never changed.\r\nexport const WaiverTemplateVersionSchema = z.object({\r\n version: waiverTemplateVersionNumberSchema,\r\n title: z.string(),\r\n waiverText: z.string(),\r\n // Absent on a version whose author was never recorded.\r\n publishedByUserId: userIdSchema.optional(),\r\n publishedAt: epochMsSchema,\r\n});\r\nexport type WaiverTemplateVersion = z.infer<typeof WaiverTemplateVersionSchema>;\r\n"]}
1
+ {"version":3,"file":"waiver-template.js","sourceRoot":"","sources":["../../src/doc-schemas/waiver-template.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,EAAE;AACF,mGAAmG;AACnG,+FAA+F;AAC/F,+FAA+F;AAC/F,sDAAsD;AAEtD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,uBAAuB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAElF,MAAM,CAAC,MAAM,sBAAsB,GAAG,uBAAuB,CAAC;AAC9D,MAAM,CAAC,MAAM,iCAAiC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAEzE,4FAA4F;AAC5F,gCAAgC;AAChC,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,OAAO,EAAE,iCAAiC;IAC1C,SAAS,EAAE,aAAa;IACxB,SAAS,EAAE,aAAa;IACxB,+FAA+F;IAC/F,6FAA6F;IAC7F,sDAAsD;IACtD,SAAS,EAAE,aAAa,CAAC,QAAQ,EAAE;IACnC,kGAAkG;IAClG,kGAAkG;IAClG,kEAAkE;IAClE,YAAY,EAAE,aAAa,CAAC,QAAQ,EAAE;CACvC,CAAC,CAAC;AAGH,mGAAmG;AACnG,kCAAkC;AAClC,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC,MAAM,CAAC;IAClD,OAAO,EAAE,iCAAiC;IAC1C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,uDAAuD;IACvD,iBAAiB,EAAE,YAAY,CAAC,QAAQ,EAAE;IAC1C,WAAW,EAAE,aAAa;CAC3B,CAAC,CAAC","sourcesContent":["// Waiver templates: the wording an admin authors for a league's players to sign.\n//\n// The wording is versioned. Every save that changes it publishes a new version, and each published\n// version is its own immutable document — so the exact wording in force when any signature was\n// captured still exists however the template has changed since. A signature also keeps its own\n// copy of what the signer saw (`SignedWaiverSchema`).\n\nimport { z } from 'zod';\nimport { documentIdSegmentSchema, epochMsSchema, userIdSchema } from './atoms.js';\n\nexport const waiverTemplateIdSchema = documentIdSegmentSchema;\nexport const waiverTemplateVersionNumberSchema = z.number().int().min(1);\n\n// `waiverTemplates/{waiverTemplateId}` — the template as it reads now: its latest published\n// version's wording and number.\nexport const WaiverTemplateSchema = z.object({\n title: z.string(),\n waiverText: z.string(),\n version: waiverTemplateVersionNumberSchema,\n createdAt: epochMsSchema,\n updatedAt: epochMsSchema,\n // Set when an admin deletes a template a signature or a closed league still names, or one with\n // more versions than a delete removes: it is kept for them and offered to no league again. A\n // template nothing names is deleted outright instead.\n retiredAt: epochMsSchema.optional(),\n // When a league last came to require the template. The league save that chooses it writes this in\n // its own transaction so a delete's read of this document conflicts with that save; it is its own\n // field so `updatedAt` keeps meaning the template itself changed.\n lastChosenAt: epochMsSchema.optional(),\n});\nexport type WaiverTemplate = z.infer<typeof WaiverTemplateSchema>;\n\n// `waiverTemplates/{waiverTemplateId}/waiverTemplateVersions/{versionId}` — one published version,\n// created once and never changed.\nexport const WaiverTemplateVersionSchema = z.object({\n version: waiverTemplateVersionNumberSchema,\n title: z.string(),\n waiverText: z.string(),\n // Absent on a version whose author was never recorded.\n publishedByUserId: userIdSchema.optional(),\n publishedAt: epochMsSchema,\n});\nexport type WaiverTemplateVersion = z.infer<typeof WaiverTemplateVersionSchema>;\n"]}