@ttt-productions/q-core 0.1.0 → 0.1.2

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 (178) hide show
  1. package/dist/constants/business.d.ts +12 -0
  2. package/dist/constants/business.d.ts.map +1 -1
  3. package/dist/constants/business.js +24 -0
  4. package/dist/constants/business.js.map +1 -1
  5. package/dist/constants/media.d.ts +1 -0
  6. package/dist/constants/media.d.ts.map +1 -1
  7. package/dist/constants/media.js +5 -0
  8. package/dist/constants/media.js.map +1 -1
  9. package/dist/constants/sports.js.map +1 -1
  10. package/dist/constants/storage-keys.js.map +1 -1
  11. package/dist/doc-schemas/admin-notifications.d.ts +1 -0
  12. package/dist/doc-schemas/admin-notifications.d.ts.map +1 -1
  13. package/dist/doc-schemas/admin-notifications.js +4 -0
  14. package/dist/doc-schemas/admin-notifications.js.map +1 -1
  15. package/dist/doc-schemas/atoms.d.ts +2 -0
  16. package/dist/doc-schemas/atoms.d.ts.map +1 -1
  17. package/dist/doc-schemas/audit-event.d.ts +2 -0
  18. package/dist/doc-schemas/audit-event.d.ts.map +1 -1
  19. package/dist/doc-schemas/catalogs.js.map +1 -1
  20. package/dist/doc-schemas/gallery.d.ts +1 -0
  21. package/dist/doc-schemas/gallery.d.ts.map +1 -1
  22. package/dist/doc-schemas/gallery.js +3 -0
  23. package/dist/doc-schemas/gallery.js.map +1 -1
  24. package/dist/doc-schemas/game-location.js.map +1 -1
  25. package/dist/doc-schemas/game-stats.js.map +1 -1
  26. package/dist/doc-schemas/league-team.js.map +1 -1
  27. package/dist/doc-schemas/migration-console.d.ts +2 -0
  28. package/dist/doc-schemas/migration-console.d.ts.map +1 -1
  29. package/dist/doc-schemas/migration-console.js +5 -0
  30. package/dist/doc-schemas/migration-console.js.map +1 -1
  31. package/dist/doc-schemas/pending-media.d.ts +1 -0
  32. package/dist/doc-schemas/pending-media.d.ts.map +1 -1
  33. package/dist/doc-schemas/pending-media.js +8 -0
  34. package/dist/doc-schemas/pending-media.js.map +1 -1
  35. package/dist/doc-schemas/registry.d.ts +12 -0
  36. package/dist/doc-schemas/registry.d.ts.map +1 -1
  37. package/dist/doc-schemas/registry.js.map +1 -1
  38. package/dist/doc-schemas/sport-game.js.map +1 -1
  39. package/dist/doc-schemas/stats-operation.d.ts.map +1 -1
  40. package/dist/doc-schemas/stats-operation.js +6 -11
  41. package/dist/doc-schemas/stats-operation.js.map +1 -1
  42. package/dist/doc-schemas/system.d.ts +7 -0
  43. package/dist/doc-schemas/system.d.ts.map +1 -1
  44. package/dist/doc-schemas/system.js +4 -1
  45. package/dist/doc-schemas/system.js.map +1 -1
  46. package/dist/doc-schemas/user-profile.d.ts +1 -0
  47. package/dist/doc-schemas/user-profile.d.ts.map +1 -1
  48. package/dist/doc-schemas/user-profile.js +5 -0
  49. package/dist/doc-schemas/user-profile.js.map +1 -1
  50. package/dist/doc-schemas/waiver-template.d.ts +1 -0
  51. package/dist/doc-schemas/waiver-template.d.ts.map +1 -1
  52. package/dist/doc-schemas/waiver-template.js +10 -1
  53. package/dist/doc-schemas/waiver-template.js.map +1 -1
  54. package/dist/doc-schemas/waivers-and-hardware.d.ts +1 -0
  55. package/dist/doc-schemas/waivers-and-hardware.d.ts.map +1 -1
  56. package/dist/doc-schemas/waivers-and-hardware.js +17 -9
  57. package/dist/doc-schemas/waivers-and-hardware.js.map +1 -1
  58. package/dist/ids/index.js.map +1 -1
  59. package/dist/ids/minted-ids.d.ts +1 -0
  60. package/dist/ids/minted-ids.d.ts.map +1 -1
  61. package/dist/ids/minted-ids.js +6 -0
  62. package/dist/ids/minted-ids.js.map +1 -1
  63. package/dist/ids/stats-operation-ids.js.map +1 -1
  64. package/dist/index.js.map +1 -1
  65. package/dist/input-formats/index.d.ts +2 -2
  66. package/dist/input-formats/index.d.ts.map +1 -1
  67. package/dist/input-formats/index.js +2 -2
  68. package/dist/input-formats/index.js.map +1 -1
  69. package/dist/media/file-origins.js.map +1 -1
  70. package/dist/notifications/archive-all.d.ts +2 -0
  71. package/dist/notifications/archive-all.d.ts.map +1 -0
  72. package/dist/notifications/archive-all.js +6 -0
  73. package/dist/notifications/archive-all.js.map +1 -0
  74. package/dist/notifications/index.d.ts +1 -0
  75. package/dist/notifications/index.d.ts.map +1 -1
  76. package/dist/notifications/index.js +1 -0
  77. package/dist/notifications/index.js.map +1 -1
  78. package/dist/paths/collection-refs.js.map +1 -1
  79. package/dist/paths/collections.js.map +1 -1
  80. package/dist/paths/path-builders.js.map +1 -1
  81. package/dist/paths/utils.js.map +1 -1
  82. package/dist/schemas/account-settings.js.map +1 -1
  83. package/dist/schemas/admin-notifications.d.ts +1 -0
  84. package/dist/schemas/admin-notifications.d.ts.map +1 -1
  85. package/dist/schemas/admin-notifications.js +4 -2
  86. package/dist/schemas/admin-notifications.js.map +1 -1
  87. package/dist/schemas/app-config.d.ts +2 -2
  88. package/dist/schemas/app-config.d.ts.map +1 -1
  89. package/dist/schemas/app-config.js +9 -5
  90. package/dist/schemas/app-config.js.map +1 -1
  91. package/dist/schemas/atoms.js.map +1 -1
  92. package/dist/schemas/awards.d.ts +16 -4
  93. package/dist/schemas/awards.d.ts.map +1 -1
  94. package/dist/schemas/awards.js +10 -6
  95. package/dist/schemas/awards.js.map +1 -1
  96. package/dist/schemas/game-locations.d.ts +4 -4
  97. package/dist/schemas/game-locations.d.ts.map +1 -1
  98. package/dist/schemas/game-locations.js +11 -3
  99. package/dist/schemas/game-locations.js.map +1 -1
  100. package/dist/schemas/hardware-loan-contracts.js.map +1 -1
  101. package/dist/schemas/migration-console.d.ts +1 -0
  102. package/dist/schemas/migration-console.d.ts.map +1 -1
  103. package/dist/schemas/pictures.d.ts +3 -0
  104. package/dist/schemas/pictures.d.ts.map +1 -1
  105. package/dist/schemas/schedules.js.map +1 -1
  106. package/dist/schemas/scoring.js.map +1 -1
  107. package/dist/schemas/stats.d.ts +2 -0
  108. package/dist/schemas/stats.d.ts.map +1 -1
  109. package/dist/schemas/stats.js.map +1 -1
  110. package/dist/schemas/team-joining.js.map +1 -1
  111. package/dist/schemas/teams.d.ts +3 -2
  112. package/dist/schemas/teams.d.ts.map +1 -1
  113. package/dist/schemas/teams.js +19 -9
  114. package/dist/schemas/teams.js.map +1 -1
  115. package/dist/schemas/uploads.js.map +1 -1
  116. package/dist/schemas/users.d.ts +7 -3
  117. package/dist/schemas/users.d.ts.map +1 -1
  118. package/dist/schemas/users.js +10 -3
  119. package/dist/schemas/users.js.map +1 -1
  120. package/dist/schemas/waiver-templates.d.ts +1 -0
  121. package/dist/schemas/waiver-templates.d.ts.map +1 -1
  122. package/dist/schemas/waiver-templates.js +4 -1
  123. package/dist/schemas/waiver-templates.js.map +1 -1
  124. package/dist/sports/basketball-scoring.js.map +1 -1
  125. package/dist/sports/basketball.js.map +1 -1
  126. package/dist/sports/bulk-entry.js.map +1 -1
  127. package/dist/sports/contribution.js.map +1 -1
  128. package/dist/sports/curling-scoring.js.map +1 -1
  129. package/dist/sports/finalized-record.js.map +1 -1
  130. package/dist/sports/football-scoring.js.map +1 -1
  131. package/dist/sports/index.d.ts +1 -0
  132. package/dist/sports/index.d.ts.map +1 -1
  133. package/dist/sports/index.js +1 -0
  134. package/dist/sports/index.js.map +1 -1
  135. package/dist/sports/ledger-continuity.d.ts +13 -0
  136. package/dist/sports/ledger-continuity.d.ts.map +1 -0
  137. package/dist/sports/ledger-continuity.js +19 -0
  138. package/dist/sports/ledger-continuity.js.map +1 -0
  139. package/dist/sports/rankings.js.map +1 -1
  140. package/dist/sports/rollup-check.js.map +1 -1
  141. package/dist/sports/scoring.js.map +1 -1
  142. package/dist/sports/store-driver.js.map +1 -1
  143. package/dist/types/audit.d.ts +1 -1
  144. package/dist/types/audit.d.ts.map +1 -1
  145. package/dist/types/audit.js +5 -2
  146. package/dist/types/audit.js.map +1 -1
  147. package/dist/types/documents.js.map +1 -1
  148. package/dist/types/notifications.js.map +1 -1
  149. package/dist/types/picture-owners.js.map +1 -1
  150. package/dist/types/sports.js.map +1 -1
  151. package/dist/types/stats.js.map +1 -1
  152. package/dist/types/unions.d.ts +4 -2
  153. package/dist/types/unions.d.ts.map +1 -1
  154. package/dist/types/unions.js +6 -0
  155. package/dist/types/unions.js.map +1 -1
  156. package/dist/upload-variables/index.d.ts +3 -0
  157. package/dist/upload-variables/index.d.ts.map +1 -0
  158. package/dist/upload-variables/index.js +3 -0
  159. package/dist/upload-variables/index.js.map +1 -0
  160. package/dist/upload-variables/on-progress.d.ts +4 -0
  161. package/dist/upload-variables/on-progress.d.ts.map +1 -0
  162. package/dist/upload-variables/on-progress.js +10 -0
  163. package/dist/upload-variables/on-progress.js.map +1 -0
  164. package/dist/upload-variables/picture-upload-variables.d.ts +32 -0
  165. package/dist/upload-variables/picture-upload-variables.d.ts.map +1 -0
  166. package/dist/upload-variables/picture-upload-variables.js +21 -0
  167. package/dist/upload-variables/picture-upload-variables.js.map +1 -0
  168. package/dist/utils/account-status.js.map +1 -1
  169. package/dist/utils/app-config.d.ts +17 -3
  170. package/dist/utils/app-config.d.ts.map +1 -1
  171. package/dist/utils/app-config.js +21 -14
  172. package/dist/utils/app-config.js.map +1 -1
  173. package/dist/utils/app-routes.js.map +1 -1
  174. package/dist/utils/index.js.map +1 -1
  175. package/dist/utils/league-calendar.js.map +1 -1
  176. package/dist/utils/league-mode.js.map +1 -1
  177. package/dist/utils/team-member-actions.js.map +1 -1
  178. package/package.json +94 -90
@@ -1 +1 @@
1
- {"version":3,"file":"unions.js","sourceRoot":"","sources":["../../src/types/unions.ts"],"names":[],"mappings":"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","sourcesContent":["// Canonical unions (ARCH-102): an `as const` key array with the type derived from it. Zod enums,\r\n// type positions, query filters, and option lists all derive from these arrays — never a quoted\r\n// member.\r\n\r\n// The two global roles. Each is a roster on `_systemData/adminList` and a boolean custom claim of\r\n// the same name; the roster is the authority, the claim a fast hint.\r\nexport const SYSTEM_ROLE_KEYS = ['admin', 'referee'] as const;\r\nexport type SystemRole = (typeof SYSTEM_ROLE_KEYS)[number];\r\n\r\n// Account status on the private profile. Only an exact `active` passes the server auth assertion.\r\nexport const USER_STATUS_KEYS = ['active', 'banned'] as const;\r\nexport type UserStatus = (typeof USER_STATUS_KEYS)[number];\r\n/** The one status that lets an account act. */\r\nexport const ACTIVE_USER_STATUS = 'active' satisfies UserStatus;\r\n/** The status an admin's ban sets: the account keeps its data and cannot sign in. */\r\nexport const BANNED_USER_STATUS = 'banned' satisfies UserStatus;\r\n\r\n// Which post-commit Auth effect queued an account on `statusReconcileQueue/{userId}`:\r\n// `accountStatus` — a status change's disabled flag, status claim, and token revocation, queued in\r\n// the status transaction itself; `roleClaims` — the `admin` / `referee` claims after a failed roster\r\n// claim write.\r\nexport const STATUS_RECONCILE_QUEUE_AUTH_EFFECTS = ['accountStatus', 'roleClaims'] as const;\r\nexport type StatusReconcileQueueAuthEffect = (typeof STATUS_RECONCILE_QUEUE_AUTH_EFFECTS)[number];\r\nexport const ACCOUNT_STATUS_AUTH_EFFECT = 'accountStatus' as const satisfies StatusReconcileQueueAuthEffect;\r\nexport const ROLE_CLAIMS_EFFECT = 'roleClaims' as const satisfies StatusReconcileQueueAuthEffect;\r\n\r\nexport const TEAM_ROLE_KEYS = ['captain', 'coCaptain', 'player'] as const;\r\nexport type TeamRole = (typeof TEAM_ROLE_KEYS)[number];\r\n\r\n// The roles that carry captain authority. `firestore.rules` restates this list, because a rules file\r\n// cannot import it.\r\nexport const CAPTAIN_TEAM_ROLES = ['captain', 'coCaptain'] as const satisfies readonly TeamRole[];\r\n\r\n// The one role a team holds at most one of: team creation, the admin captain change, and ghost\r\n// linking write it, and nothing else does.\r\nexport const TEAM_CAPTAIN_ROLE = 'captain' as const satisfies TeamRole;\r\n\r\n// The two roles below the captain. A co-captain holds every captain power except managing\r\n// co-captains; a player holds none.\r\nexport const TEAM_CO_CAPTAIN_ROLE = 'coCaptain' as const satisfies TeamRole;\r\nexport const TEAM_PLAYER_ROLE = 'player' as const satisfies TeamRole;\r\n\r\n// The roles a member's role change moves a membership between — promoting a player to co-captain,\r\n// or demoting a co-captain to player. The captain's role moves only through the admin's captain\r\n// change, so it is never one of them.\r\nexport const MEMBER_ASSIGNABLE_TEAM_ROLES = [TEAM_CO_CAPTAIN_ROLE, TEAM_PLAYER_ROLE] as const satisfies readonly TeamRole[];\r\nexport type MemberAssignableTeamRole = (typeof MEMBER_ASSIGNABLE_TEAM_ROLES)[number];\r\n\r\n// A ghost link that merges a ghost's membership into the account's keeps one membership with the\r\n// higher of the two roles: `TEAM_ROLE_KEYS` runs from the highest role down.\r\nexport function higherTeamRole(left: TeamRole, right: TeamRole): TeamRole {\r\n return TEAM_ROLE_KEYS.indexOf(left) <= TEAM_ROLE_KEYS.indexOf(right) ? left : right;\r\n}\r\n\r\nexport const TEAM_MEMBERSHIP_STATUS_KEYS = ['active'] as const;\r\nexport type TeamMembershipStatus = (typeof TEAM_MEMBERSHIP_STATUS_KEYS)[number];\r\n\r\n// The status a membership grants authority in. Every membership-based check reads it with the role\r\n// (BACKEND-006), so a status added later grants nothing until a check names it.\r\nexport const ACTIVE_TEAM_MEMBERSHIP_STATUS = 'active' as const satisfies TeamMembershipStatus;\r\n\r\n// How a membership came to exist; absent on a membership whose provenance was never recorded.\r\nexport const TEAM_JOIN_METHOD_KEYS = [\r\n 'teamCreation',\r\n 'invite',\r\n 'joinRequest',\r\n 'freeAgent',\r\n 'ghostAdd',\r\n] as const;\r\nexport type TeamJoinMethod = (typeof TEAM_JOIN_METHOD_KEYS)[number];\r\n\r\nexport const LEAGUE_STATUS_KEYS = ['pending', 'upcoming', 'active', 'closed'] as const;\r\nexport type LeagueStatus = (typeof LEAGUE_STATUS_KEYS)[number];\r\n\r\n// Every league is created unpublished; the admin moves it forward from here.\r\nexport const LEAGUE_STATUS_ON_CREATE = 'pending' as const satisfies LeagueStatus;\r\n\r\n// A finished season. Its settings stay editable; its status and mode never change again.\r\nexport const CLOSED_LEAGUE_STATUS = 'closed' as const satisfies LeagueStatus;\r\n\r\n// The two published statuses before a league closes: its teams are forming, then it is in play.\r\nexport const UPCOMING_LEAGUE_STATUS = 'upcoming' as const satisfies LeagueStatus;\r\nexport const ACTIVE_LEAGUE_STATUS = 'active' as const satisfies LeagueStatus;\r\n\r\n// The statuses a league may move to from each status. Movement is forward only, and `closed` is\r\n// final; the status callable refuses any other move.\r\nexport const LEAGUE_STATUS_TRANSITIONS: Readonly<Record<LeagueStatus, readonly LeagueStatus[]>> = {\r\n pending: ['upcoming', 'closed'],\r\n upcoming: ['active', 'closed'],\r\n active: ['closed'],\r\n closed: [],\r\n};\r\n\r\n// Every status but closed: a league still being set up, published, or played. Whatever such a\r\n// league is configured with — its waiver template among them — is still in use.\r\nexport const OPEN_LEAGUE_STATUS_KEYS: readonly Exclude<LeagueStatus, typeof CLOSED_LEAGUE_STATUS>[] =\r\n LEAGUE_STATUS_KEYS.filter(\r\n (status): status is Exclude<LeagueStatus, typeof CLOSED_LEAGUE_STATUS> => status !== CLOSED_LEAGUE_STATUS,\r\n );\r\n\r\n// A league players are part of now or next: published and not yet closed. The admin dashboard's\r\n// active free-agent count covers only these leagues.\r\nexport const CURRENT_LEAGUE_STATUS_KEYS = [\r\n UPCOMING_LEAGUE_STATUS,\r\n ACTIVE_LEAGUE_STATUS,\r\n] as const satisfies readonly LeagueStatus[];\r\n\r\nexport const LEAGUE_MODE_KEYS = ['Regular', 'Competitive/Recreational'] as const;\r\nexport type LeagueMode = (typeof LEAGUE_MODE_KEYS)[number];\r\n\r\n// Present only on teams in a Competitive/Recreational league. In any other mode a team has no type:\r\n// the field is absent, never null.\r\nexport const TEAM_TYPE_KEYS = ['Competitive', 'Recreational'] as const;\r\nexport type TeamType = (typeof TEAM_TYPE_KEYS)[number];\r\n\r\n// The one mode whose teams each carry a `teamType`.\r\nexport const TEAM_TYPED_LEAGUE_MODE = 'Competitive/Recreational' as const satisfies LeagueMode;\r\n\r\n// The type every team takes when its league switches into `TEAM_TYPED_LEAGUE_MODE`.\r\nexport const TEAM_TYPE_ON_MODE_SWITCH = 'Recreational' as const satisfies TeamType;\r\n\r\nexport const GAME_STATUS_KEYS = ['scheduled', 'inprogress', 'completed'] as const;\r\nexport type GameStatus = (typeof GAME_STATUS_KEYS)[number];\r\n\r\n// The one status a finished game holds: it was played, and its stats count. Every reader asking\r\n// whether a game was played compares against this rather than naming a status of its own.\r\nexport const COMPLETED_GAME_STATUS = 'completed' as const satisfies GameStatus;\r\n\r\n// The status a newly scheduled game is created in.\r\nexport const GAME_STATUS_ON_CREATE = 'scheduled' as const satisfies GameStatus;\r\n\r\n// The status reopen returns a finalized game to: open for scoring again, its record of who played\r\n// kept.\r\nexport const GAME_STATUS_ON_REOPEN = 'inprogress' as const satisfies GameStatus;\r\n\r\n// An invitation is pending until the invitee accepts (`approved`) or declines (`rejected`) it, or the\r\n// team withdraws it (`cancelled`). A membership on its team made any other way closes it `approved`.\r\nexport const TEAM_INVITATION_STATUS_KEYS = ['pending', 'approved', 'rejected', 'cancelled'] as const;\r\nexport type TeamInvitationStatus = (typeof TEAM_INVITATION_STATUS_KEYS)[number];\r\n\r\nexport const PENDING_TEAM_INVITATION_STATUS = 'pending' as const satisfies TeamInvitationStatus;\r\nexport const APPROVED_TEAM_INVITATION_STATUS = 'approved' as const satisfies TeamInvitationStatus;\r\nexport const REJECTED_TEAM_INVITATION_STATUS = 'rejected' as const satisfies TeamInvitationStatus;\r\nexport const CANCELLED_TEAM_INVITATION_STATUS = 'cancelled' as const satisfies TeamInvitationStatus;\r\n\r\n// A join request is pending until the team's leaders accept (`approved`) or reject (`rejected`) it,\r\n// or the requester withdraws it (`cancelled`). A membership on its team made any other way closes it\r\n// `approved`.\r\nexport const TEAM_JOIN_REQUEST_STATUS_KEYS = ['pending', 'approved', 'rejected', 'cancelled'] as const;\r\nexport type TeamJoinRequestStatus = (typeof TEAM_JOIN_REQUEST_STATUS_KEYS)[number];\r\n\r\nexport const PENDING_TEAM_JOIN_REQUEST_STATUS = 'pending' as const satisfies TeamJoinRequestStatus;\r\nexport const APPROVED_TEAM_JOIN_REQUEST_STATUS = 'approved' as const satisfies TeamJoinRequestStatus;\r\nexport const REJECTED_TEAM_JOIN_REQUEST_STATUS = 'rejected' as const satisfies TeamJoinRequestStatus;\r\nexport const CANCELLED_TEAM_JOIN_REQUEST_STATUS = 'cancelled' as const satisfies TeamJoinRequestStatus;\r\n\r\nexport const HARDWARE_LOAN_CONTRACT_STATUS_KEYS = ['pending', 'loaned', 'returned'] as const;\r\nexport type HardwareLoanContractStatus = (typeof HARDWARE_LOAN_CONTRACT_STATUS_KEYS)[number];\r\n// Why an award grant passed over an account its teams or leagues named: it has no profile, or it is\r\n// a ghost, which holds no account.\r\nexport const AWARD_GRANT_SKIP_REASON_KEYS = ['noProfile', 'ghost'] as const;\r\nexport type AwardGrantSkipReason = (typeof AWARD_GRANT_SKIP_REASON_KEYS)[number];\r\n\r\nexport const PENDING_HARDWARE_LOAN_CONTRACT_STATUS = 'pending' as const satisfies HardwareLoanContractStatus;\r\nexport const LOANED_HARDWARE_LOAN_CONTRACT_STATUS = 'loaned' as const satisfies HardwareLoanContractStatus;\r\nexport const RETURNED_HARDWARE_LOAN_CONTRACT_STATUS = 'returned' as const satisfies HardwareLoanContractStatus;\r\n\r\n// What deleting a waiver template did. A template nothing names is deleted with its versions; one a\r\n// signature or a closed league still names is retired — kept for them, offered to no new league.\r\nexport const WAIVER_TEMPLATE_DELETE_OUTCOME_KEYS = ['deleted', 'retired'] as const;\r\nexport type WaiverTemplateDeleteOutcome = (typeof WAIVER_TEMPLATE_DELETE_OUTCOME_KEYS)[number];\r\n\r\n// What last changed the stats, as announced on the stats beacon; `link` is a ghost link moving a\r\n// game's lines onto an account.\r\nexport const STATS_BEACON_ACTION_KEYS = ['finalize', 'reopen', 'recalculate', 'correct', 'historical-update', 'link'] as const;\r\nexport type StatsBeaconAction = (typeof STATS_BEACON_ACTION_KEYS)[number];\r\n\r\n// The actions that describe one game, so a beacon announcing one names the game, its league, and its\r\n// teams; every other action names no game, and a game id left beside it would point at a stale one.\r\nexport const GAME_SCOPED_STATS_BEACON_ACTION_KEYS = ['finalize', 'reopen'] as const satisfies readonly StatsBeaconAction[];\r\nexport type GameScopedStatsBeaconAction = (typeof GAME_SCOPED_STATS_BEACON_ACTION_KEYS)[number];\r\n\r\n// The statuses a game's roster still follows its teams' memberships in: listed explicitly, so a\r\n// status added later is closed until it is named here, never open by default.\r\nexport const OPEN_GAME_STATUS_KEYS = ['scheduled', 'inprogress'] as const satisfies readonly GameStatus[];\r\nexport type OpenGameStatus = (typeof OPEN_GAME_STATUS_KEYS)[number];\r\n\r\n// Whether a game is still open — not yet finalized — so a scorer may still change it. Both app roots\r\n// ask this one predicate.\r\nexport function isOpenGameStatus(status: GameStatus): status is OpenGameStatus {\r\n return (OPEN_GAME_STATUS_KEYS as readonly GameStatus[]).includes(status);\r\n}\r\n\r\n// Where one membership's roster obligation stands. `pending` waits for the reconciliation worker;\r\n// `failed` has spent its retries and waits for an admin's retry; `reconciled` owes nothing.\r\nexport const MEMBERSHIP_ROSTER_SYNC_STATUS_KEYS = ['pending', 'reconciled', 'failed'] as const;\r\nexport type MembershipRosterSyncStatus = (typeof MEMBERSHIP_ROSTER_SYNC_STATUS_KEYS)[number];\r\nexport const PENDING_ROSTER_SYNC_STATUS = 'pending' as const satisfies MembershipRosterSyncStatus;\r\nexport const RECONCILED_ROSTER_SYNC_STATUS = 'reconciled' as const satisfies MembershipRosterSyncStatus;\r\nexport const FAILED_ROSTER_SYNC_STATUS = 'failed' as const satisfies MembershipRosterSyncStatus;\r\n\r\n// The authority a ghost's waiver was signed under: the witness's captaincy on the ghost's team, or\r\n// the global role the admin entry confirmed.\r\nexport const WAIVER_WITNESS_AUTHORITY_KEYS = ['captain', 'coCaptain', 'admin', 'referee'] as const;\r\nexport type WaiverWitnessAuthority = (typeof WAIVER_WITNESS_AUTHORITY_KEYS)[number];\r\n\r\n// Why a signing was refused, named in its details so the signing dialog answers it: `waiverChanged`\r\n// when the wording the signer saw — the template id and version the dialog sent — is no longer the\r\n// league's, so the dialog loads the current wording to read again.\r\nexport const WAIVER_SIGNING_REFUSAL_KEYS = ['waiverChanged'] as const;\r\nexport type WaiverSigningRefusal = (typeof WAIVER_SIGNING_REFUSAL_KEYS)[number];\r\n\r\n// The icon a team picks at creation; the persisted value is the icon component's name.\r\nexport const TEAM_ICON_KEYS = [\r\n 'Shield',\r\n 'Swords',\r\n 'Skull',\r\n 'Ghost',\r\n 'Cat',\r\n 'Dog',\r\n 'Bird',\r\n 'Fish',\r\n 'Rabbit',\r\n 'Snail',\r\n 'Turtle',\r\n 'Cherry',\r\n 'Apple',\r\n 'Grape',\r\n 'Rocket',\r\n 'Plane',\r\n 'Ship',\r\n 'Train',\r\n 'Car',\r\n 'Bike',\r\n 'Bolt',\r\n 'Star',\r\n 'Sun',\r\n 'Moon',\r\n 'Cloud',\r\n 'Crown',\r\n 'Diamond',\r\n 'Gem',\r\n 'Anchor',\r\n // Appended — the set only grows, and no stored key is ever renamed or removed. Seasonal and events.\r\n 'TreePine',\r\n 'Trees',\r\n 'TreeDeciduous',\r\n 'TreePalm',\r\n 'Snowflake',\r\n 'MountainSnow',\r\n 'Gift',\r\n 'PartyPopper',\r\n 'CandyCane',\r\n 'Candy',\r\n 'Lollipop',\r\n 'Cake',\r\n 'Egg',\r\n 'Clover',\r\n 'Heart',\r\n 'Flower',\r\n 'Flower2',\r\n 'Rose',\r\n 'Leaf',\r\n 'Sprout',\r\n 'Wheat',\r\n 'Sunrise',\r\n 'Rainbow',\r\n 'Bell',\r\n // Awards and sport.\r\n 'Trophy',\r\n 'Medal',\r\n 'Award',\r\n 'Ribbon',\r\n 'Flag',\r\n 'Target',\r\n 'Crosshair',\r\n 'Goal',\r\n 'Dumbbell',\r\n 'Footprints',\r\n // Power and the elements.\r\n 'Zap',\r\n 'Flame',\r\n 'FlameKindling',\r\n 'Bomb',\r\n 'Atom',\r\n 'Orbit',\r\n 'Sparkles',\r\n 'WandSparkles',\r\n 'Mountain',\r\n 'Tornado',\r\n 'Wind',\r\n 'Droplet',\r\n 'CloudLightning',\r\n 'MoonStar',\r\n // Arms and strongholds.\r\n 'Sword',\r\n 'Axe',\r\n 'Hammer',\r\n 'Pickaxe',\r\n 'ShieldHalf',\r\n 'Castle',\r\n // Animals.\r\n 'Squirrel',\r\n 'Rat',\r\n 'Panda',\r\n 'Shrimp',\r\n 'Worm',\r\n 'Bug',\r\n 'Shell',\r\n 'PawPrint',\r\n 'Feather',\r\n 'Bone',\r\n 'Origami',\r\n // Food.\r\n 'Citrus',\r\n 'Banana',\r\n 'Carrot',\r\n 'Pizza',\r\n 'Cookie',\r\n 'Donut',\r\n 'Coffee',\r\n // Games and music.\r\n 'Dices',\r\n 'Puzzle',\r\n 'Gamepad2',\r\n 'Spade',\r\n 'Club',\r\n 'Music',\r\n 'Guitar',\r\n 'Hexagon',\r\n 'Triangle',\r\n // Travel.\r\n 'Sailboat',\r\n 'Tractor',\r\n 'Truck',\r\n] as const;\r\nexport type TeamIcon = (typeof TEAM_ICON_KEYS)[number];\r\n\r\n// A picture's processed sizes, smallest first: every picture kind declares all three, and a display\r\n// asks for the smallest one that covers its slot.\r\nexport const PICTURE_VARIANT_KEYS = ['sm', 'md', 'lg'] as const;\r\nexport type PictureVariantKey = (typeof PICTURE_VARIANT_KEYS)[number];\r\n\r\n// What a stats check found, game tier first — a game's own numbers are the cause of the aggregate\r\n// findings, so Fix stats repairs games before it rebuilds what they feed.\r\nexport const GAME_TIER_DISCREPANCY_KIND_KEYS = [\r\n 'invalidEvent',\r\n 'eventSequenceMismatch',\r\n 'rollupMismatch',\r\n 'scoreMirrorMismatch',\r\n 'unrosteredStatLine',\r\n 'fantasyPointsStale',\r\n 'finalRecordMissing',\r\n 'finalRecordMismatch',\r\n 'participationSnapshotMismatch',\r\n 'gameHeld',\r\n] as const;\r\nexport const AGGREGATE_TIER_DISCREPANCY_KIND_KEYS = [\r\n 'aggregateMismatch',\r\n 'aggregateEntryStale',\r\n 'aggregateEntryMissing',\r\n 'hallOfFameLifetimeMismatch',\r\n] as const;\r\nexport const STATS_DISCREPANCY_KIND_KEYS = [\r\n ...GAME_TIER_DISCREPANCY_KIND_KEYS,\r\n ...AGGREGATE_TIER_DISCREPANCY_KIND_KEYS,\r\n] as const;\r\nexport type StatsDiscrepancyKind = (typeof STATS_DISCREPANCY_KIND_KEYS)[number];\r\n"]}
1
+ {"version":3,"file":"unions.js","sourceRoot":"","sources":["../../src/types/unions.ts"],"names":[],"mappings":"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","sourcesContent":["// Canonical unions (ARCH-102): an `as const` key array with the type derived from it. Zod enums,\n// type positions, query filters, and option lists all derive from these arrays — never a quoted\n// member.\n\n// The two global roles. Each is a roster on `_systemData/adminList` and a boolean custom claim of\n// the same name; the roster is the authority, the claim a fast hint.\nexport const SYSTEM_ROLE_KEYS = ['admin', 'referee'] as const;\nexport type SystemRole = (typeof SYSTEM_ROLE_KEYS)[number];\n\n// Account status on the private profile. Only an exact `active` passes the server auth assertion.\nexport const USER_STATUS_KEYS = ['active', 'banned'] as const;\nexport type UserStatus = (typeof USER_STATUS_KEYS)[number];\n/** The one status that lets an account act. */\nexport const ACTIVE_USER_STATUS = 'active' satisfies UserStatus;\n/** The status an admin's ban sets: the account keeps its data and cannot sign in. */\nexport const BANNED_USER_STATUS = 'banned' satisfies UserStatus;\n\n// Which post-commit Auth effect queued an account on `statusReconcileQueue/{userId}`:\n// `accountStatus` — a status change's disabled flag, status claim, and token revocation, queued in\n// the status transaction itself; `roleClaims` — the `admin` / `referee` claims after a failed roster\n// claim write.\nexport const STATUS_RECONCILE_QUEUE_AUTH_EFFECTS = ['accountStatus', 'roleClaims'] as const;\nexport type StatusReconcileQueueAuthEffect = (typeof STATUS_RECONCILE_QUEUE_AUTH_EFFECTS)[number];\nexport const ACCOUNT_STATUS_AUTH_EFFECT = 'accountStatus' as const satisfies StatusReconcileQueueAuthEffect;\nexport const ROLE_CLAIMS_EFFECT = 'roleClaims' as const satisfies StatusReconcileQueueAuthEffect;\n\nexport const TEAM_ROLE_KEYS = ['captain', 'coCaptain', 'player'] as const;\nexport type TeamRole = (typeof TEAM_ROLE_KEYS)[number];\n\n// The roles that carry captain authority. `firestore.rules` restates this list, because a rules file\n// cannot import it.\nexport const CAPTAIN_TEAM_ROLES = ['captain', 'coCaptain'] as const satisfies readonly TeamRole[];\n\n// The one role a team holds at most one of: team creation, the admin captain change, and ghost\n// linking write it, and nothing else does.\nexport const TEAM_CAPTAIN_ROLE = 'captain' as const satisfies TeamRole;\n\n// The two roles below the captain. A co-captain holds every captain power except managing\n// co-captains; a player holds none.\nexport const TEAM_CO_CAPTAIN_ROLE = 'coCaptain' as const satisfies TeamRole;\nexport const TEAM_PLAYER_ROLE = 'player' as const satisfies TeamRole;\n\n// The roles a member's role change moves a membership between — promoting a player to co-captain,\n// or demoting a co-captain to player. The captain's role moves only through the admin's captain\n// change, so it is never one of them.\nexport const MEMBER_ASSIGNABLE_TEAM_ROLES = [TEAM_CO_CAPTAIN_ROLE, TEAM_PLAYER_ROLE] as const satisfies readonly TeamRole[];\nexport type MemberAssignableTeamRole = (typeof MEMBER_ASSIGNABLE_TEAM_ROLES)[number];\n\n// A ghost link that merges a ghost's membership into the account's keeps one membership with the\n// higher of the two roles: `TEAM_ROLE_KEYS` runs from the highest role down.\nexport function higherTeamRole(left: TeamRole, right: TeamRole): TeamRole {\n return TEAM_ROLE_KEYS.indexOf(left) <= TEAM_ROLE_KEYS.indexOf(right) ? left : right;\n}\n\nexport const TEAM_MEMBERSHIP_STATUS_KEYS = ['active'] as const;\nexport type TeamMembershipStatus = (typeof TEAM_MEMBERSHIP_STATUS_KEYS)[number];\n\n// The status a membership grants authority in. Every membership-based check reads it with the role\n// (BACKEND-006), so a status added later grants nothing until a check names it.\nexport const ACTIVE_TEAM_MEMBERSHIP_STATUS = 'active' as const satisfies TeamMembershipStatus;\n\n// How a membership came to exist; absent on a membership whose provenance was never recorded.\nexport const TEAM_JOIN_METHOD_KEYS = [\n 'teamCreation',\n 'invite',\n 'joinRequest',\n 'freeAgent',\n 'ghostAdd',\n] as const;\nexport type TeamJoinMethod = (typeof TEAM_JOIN_METHOD_KEYS)[number];\n\nexport const LEAGUE_STATUS_KEYS = ['pending', 'upcoming', 'active', 'closed'] as const;\nexport type LeagueStatus = (typeof LEAGUE_STATUS_KEYS)[number];\n\n// Every league is created unpublished; the admin moves it forward from here.\nexport const LEAGUE_STATUS_ON_CREATE = 'pending' as const satisfies LeagueStatus;\n\n// A finished season. Its settings stay editable; its status and mode never change again.\nexport const CLOSED_LEAGUE_STATUS = 'closed' as const satisfies LeagueStatus;\n\n// The two published statuses before a league closes: its teams are forming, then it is in play.\nexport const UPCOMING_LEAGUE_STATUS = 'upcoming' as const satisfies LeagueStatus;\nexport const ACTIVE_LEAGUE_STATUS = 'active' as const satisfies LeagueStatus;\n\n// The statuses a league may move to from each status. Movement is forward only, and `closed` is\n// final; the status callable refuses any other move.\nexport const LEAGUE_STATUS_TRANSITIONS: Readonly<Record<LeagueStatus, readonly LeagueStatus[]>> = {\n pending: ['upcoming', 'closed'],\n upcoming: ['active', 'closed'],\n active: ['closed'],\n closed: [],\n};\n\n// Every status but closed: a league still being set up, published, or played. Whatever such a\n// league is configured with — its waiver template among them — is still in use.\nexport const OPEN_LEAGUE_STATUS_KEYS: readonly Exclude<LeagueStatus, typeof CLOSED_LEAGUE_STATUS>[] =\n LEAGUE_STATUS_KEYS.filter(\n (status): status is Exclude<LeagueStatus, typeof CLOSED_LEAGUE_STATUS> => status !== CLOSED_LEAGUE_STATUS,\n );\n\n// A league players are part of now or next: published and not yet closed. The admin dashboard's\n// active free-agent count covers only these leagues.\nexport const CURRENT_LEAGUE_STATUS_KEYS = [\n UPCOMING_LEAGUE_STATUS,\n ACTIVE_LEAGUE_STATUS,\n] as const satisfies readonly LeagueStatus[];\n\nexport const LEAGUE_MODE_KEYS = ['Regular', 'Competitive/Recreational'] as const;\nexport type LeagueMode = (typeof LEAGUE_MODE_KEYS)[number];\n\n// Present only on teams in a Competitive/Recreational league. In any other mode a team has no type:\n// the field is absent, never null.\nexport const TEAM_TYPE_KEYS = ['Competitive', 'Recreational'] as const;\nexport type TeamType = (typeof TEAM_TYPE_KEYS)[number];\n\n// The one mode whose teams each carry a `teamType`.\nexport const TEAM_TYPED_LEAGUE_MODE = 'Competitive/Recreational' as const satisfies LeagueMode;\n\n// The type every team takes when its league switches into `TEAM_TYPED_LEAGUE_MODE`.\nexport const TEAM_TYPE_ON_MODE_SWITCH = 'Recreational' as const satisfies TeamType;\n\nexport const GAME_STATUS_KEYS = ['scheduled', 'inprogress', 'completed'] as const;\nexport type GameStatus = (typeof GAME_STATUS_KEYS)[number];\n\n// The one status a finished game holds: it was played, and its stats count. Every reader asking\n// whether a game was played compares against this rather than naming a status of its own.\nexport const COMPLETED_GAME_STATUS = 'completed' as const satisfies GameStatus;\n\n// The status a newly scheduled game is created in.\nexport const GAME_STATUS_ON_CREATE = 'scheduled' as const satisfies GameStatus;\n\n// The status reopen returns a finalized game to: open for scoring again, its record of who played\n// kept.\nexport const GAME_STATUS_ON_REOPEN = 'inprogress' as const satisfies GameStatus;\n\n// An invitation is pending until the invitee accepts (`approved`) or declines (`rejected`) it, or the\n// team withdraws it (`cancelled`). A membership on its team made any other way closes it `approved`.\nexport const TEAM_INVITATION_STATUS_KEYS = ['pending', 'approved', 'rejected', 'cancelled'] as const;\nexport type TeamInvitationStatus = (typeof TEAM_INVITATION_STATUS_KEYS)[number];\n\nexport const PENDING_TEAM_INVITATION_STATUS = 'pending' as const satisfies TeamInvitationStatus;\nexport const APPROVED_TEAM_INVITATION_STATUS = 'approved' as const satisfies TeamInvitationStatus;\nexport const REJECTED_TEAM_INVITATION_STATUS = 'rejected' as const satisfies TeamInvitationStatus;\nexport const CANCELLED_TEAM_INVITATION_STATUS = 'cancelled' as const satisfies TeamInvitationStatus;\n\n// A join request is pending until the team's leaders accept (`approved`) or reject (`rejected`) it,\n// or the requester withdraws it (`cancelled`). A membership on its team made any other way closes it\n// `approved`.\nexport const TEAM_JOIN_REQUEST_STATUS_KEYS = ['pending', 'approved', 'rejected', 'cancelled'] as const;\nexport type TeamJoinRequestStatus = (typeof TEAM_JOIN_REQUEST_STATUS_KEYS)[number];\n\nexport const PENDING_TEAM_JOIN_REQUEST_STATUS = 'pending' as const satisfies TeamJoinRequestStatus;\nexport const APPROVED_TEAM_JOIN_REQUEST_STATUS = 'approved' as const satisfies TeamJoinRequestStatus;\nexport const REJECTED_TEAM_JOIN_REQUEST_STATUS = 'rejected' as const satisfies TeamJoinRequestStatus;\nexport const CANCELLED_TEAM_JOIN_REQUEST_STATUS = 'cancelled' as const satisfies TeamJoinRequestStatus;\n\nexport const HARDWARE_LOAN_CONTRACT_STATUS_KEYS = ['pending', 'loaned', 'returned'] as const;\nexport type HardwareLoanContractStatus = (typeof HARDWARE_LOAN_CONTRACT_STATUS_KEYS)[number];\n// Why an award grant passed over an account its teams or leagues named: it has no profile, or it is\n// a ghost, which holds no account.\nexport const AWARD_GRANT_SKIP_REASON_KEYS = ['noProfile', 'ghost'] as const;\nexport type AwardGrantSkipReason = (typeof AWARD_GRANT_SKIP_REASON_KEYS)[number];\n\n// The two badges an account shows beside its name.\nexport const FEATURED_AWARD_SLOT_KEYS = ['primary', 'secondary'] as const;\nexport type FeaturedAwardSlot = (typeof FEATURED_AWARD_SLOT_KEYS)[number];\n\nexport const PENDING_HARDWARE_LOAN_CONTRACT_STATUS = 'pending' as const satisfies HardwareLoanContractStatus;\nexport const LOANED_HARDWARE_LOAN_CONTRACT_STATUS = 'loaned' as const satisfies HardwareLoanContractStatus;\nexport const RETURNED_HARDWARE_LOAN_CONTRACT_STATUS = 'returned' as const satisfies HardwareLoanContractStatus;\n\n// What deleting a waiver template did. A template nothing names is deleted with its versions; one a\n// signature or a closed league still names is retired — kept for them, offered to no new league.\nexport const WAIVER_TEMPLATE_DELETE_OUTCOME_KEYS = ['deleted', 'retired'] as const;\nexport type WaiverTemplateDeleteOutcome = (typeof WAIVER_TEMPLATE_DELETE_OUTCOME_KEYS)[number];\n\n// What last changed the stats, as announced on the stats beacon; `link` is a ghost link moving a\n// game's lines onto an account.\nexport const STATS_BEACON_ACTION_KEYS = ['finalize', 'reopen', 'recalculate', 'correct', 'historical-update', 'link'] as const;\nexport type StatsBeaconAction = (typeof STATS_BEACON_ACTION_KEYS)[number];\n\n// The actions that describe one game, so a beacon announcing one names the game, its league, and its\n// teams; every other action names no game, and a game id left beside it would point at a stale one.\nexport const GAME_SCOPED_STATS_BEACON_ACTION_KEYS = ['finalize', 'reopen'] as const satisfies readonly StatsBeaconAction[];\nexport type GameScopedStatsBeaconAction = (typeof GAME_SCOPED_STATS_BEACON_ACTION_KEYS)[number];\n\n// The statuses a game's roster still follows its teams' memberships in: listed explicitly, so a\n// status added later is closed until it is named here, never open by default.\nexport const OPEN_GAME_STATUS_KEYS = ['scheduled', 'inprogress'] as const satisfies readonly GameStatus[];\nexport type OpenGameStatus = (typeof OPEN_GAME_STATUS_KEYS)[number];\n\n// Whether a game is still open — not yet finalized — so a scorer may still change it. Both app roots\n// ask this one predicate.\nexport function isOpenGameStatus(status: GameStatus): status is OpenGameStatus {\n return (OPEN_GAME_STATUS_KEYS as readonly GameStatus[]).includes(status);\n}\n\n// Where one membership's roster obligation stands. `pending` waits for the reconciliation worker;\n// `failed` has spent its retries and waits for an admin's retry; `reconciled` owes nothing.\nexport const MEMBERSHIP_ROSTER_SYNC_STATUS_KEYS = ['pending', 'reconciled', 'failed'] as const;\nexport type MembershipRosterSyncStatus = (typeof MEMBERSHIP_ROSTER_SYNC_STATUS_KEYS)[number];\nexport const PENDING_ROSTER_SYNC_STATUS = 'pending' as const satisfies MembershipRosterSyncStatus;\nexport const RECONCILED_ROSTER_SYNC_STATUS = 'reconciled' as const satisfies MembershipRosterSyncStatus;\nexport const FAILED_ROSTER_SYNC_STATUS = 'failed' as const satisfies MembershipRosterSyncStatus;\n\n// The authority a ghost's waiver was signed under: the witness's captaincy on the ghost's team, or\n// the global role the admin entry confirmed.\nexport const WAIVER_WITNESS_AUTHORITY_KEYS = ['captain', 'coCaptain', 'admin', 'referee'] as const;\nexport type WaiverWitnessAuthority = (typeof WAIVER_WITNESS_AUTHORITY_KEYS)[number];\n\n// Why a signing was refused, named in its details so the signing dialog answers it: `waiverChanged`\n// when the wording the signer saw — the template id and version the dialog sent — is no longer the\n// league's, so the dialog loads the current wording to read again.\nexport const WAIVER_SIGNING_REFUSAL_KEYS = ['waiverChanged'] as const;\nexport type WaiverSigningRefusal = (typeof WAIVER_SIGNING_REFUSAL_KEYS)[number];\n\n// The icon a team picks at creation; the persisted value is the icon component's name.\nexport const TEAM_ICON_KEYS = [\n 'Shield',\n 'Swords',\n 'Skull',\n 'Ghost',\n 'Cat',\n 'Dog',\n 'Bird',\n 'Fish',\n 'Rabbit',\n 'Snail',\n 'Turtle',\n 'Cherry',\n 'Apple',\n 'Grape',\n 'Rocket',\n 'Plane',\n 'Ship',\n 'Train',\n 'Car',\n 'Bike',\n 'Bolt',\n 'Star',\n 'Sun',\n 'Moon',\n 'Cloud',\n 'Crown',\n 'Diamond',\n 'Gem',\n 'Anchor',\n // Appended — the set only grows, and no stored key is ever renamed or removed. Seasonal and events.\n 'TreePine',\n 'Trees',\n 'TreeDeciduous',\n 'TreePalm',\n 'Snowflake',\n 'MountainSnow',\n 'Gift',\n 'PartyPopper',\n 'CandyCane',\n 'Candy',\n 'Lollipop',\n 'Cake',\n 'Egg',\n 'Clover',\n 'Heart',\n 'Flower',\n 'Flower2',\n 'Rose',\n 'Leaf',\n 'Sprout',\n 'Wheat',\n 'Sunrise',\n 'Rainbow',\n 'Bell',\n // Awards and sport.\n 'Trophy',\n 'Medal',\n 'Award',\n 'Ribbon',\n 'Flag',\n 'Target',\n 'Crosshair',\n 'Goal',\n 'Dumbbell',\n 'Footprints',\n // Power and the elements.\n 'Zap',\n 'Flame',\n 'FlameKindling',\n 'Bomb',\n 'Atom',\n 'Orbit',\n 'Sparkles',\n 'WandSparkles',\n 'Mountain',\n 'Tornado',\n 'Wind',\n 'Droplet',\n 'CloudLightning',\n 'MoonStar',\n // Arms and strongholds.\n 'Sword',\n 'Axe',\n 'Hammer',\n 'Pickaxe',\n 'ShieldHalf',\n 'Castle',\n // Animals.\n 'Squirrel',\n 'Rat',\n 'Panda',\n 'Shrimp',\n 'Worm',\n 'Bug',\n 'Shell',\n 'PawPrint',\n 'Feather',\n 'Bone',\n 'Origami',\n // Food.\n 'Citrus',\n 'Banana',\n 'Carrot',\n 'Pizza',\n 'Cookie',\n 'Donut',\n 'Coffee',\n // Games and music.\n 'Dices',\n 'Puzzle',\n 'Gamepad2',\n 'Spade',\n 'Club',\n 'Music',\n 'Guitar',\n 'Hexagon',\n 'Triangle',\n // Travel.\n 'Sailboat',\n 'Tractor',\n 'Truck',\n] as const;\nexport type TeamIcon = (typeof TEAM_ICON_KEYS)[number];\n\n// A picture's processed sizes, smallest first: every picture kind declares all three, and a display\n// asks for the smallest one that covers its slot.\nexport const PICTURE_VARIANT_KEYS = ['sm', 'md', 'lg'] as const;\nexport type PictureVariantKey = (typeof PICTURE_VARIANT_KEYS)[number];\n\n// What a stats check found, game tier first — a game's own numbers are the cause of the aggregate\n// findings, so Fix stats repairs games before it rebuilds what they feed.\nexport const GAME_TIER_DISCREPANCY_KIND_KEYS = [\n // A game whose document the check could not read: nothing else about it could be checked.\n 'gameUnreadable',\n 'invalidEvent',\n 'eventSequenceMismatch',\n // A game whose events do not hold exactly the sequences 1..n: a gap or a duplicate.\n 'eventSequenceGap',\n 'rollupMismatch',\n 'scoreMirrorMismatch',\n 'unrosteredStatLine',\n 'fantasyPointsStale',\n 'finalRecordMissing',\n 'finalRecordMismatch',\n 'participationSnapshotMismatch',\n 'gameHeld',\n] as const;\nexport const AGGREGATE_TIER_DISCREPANCY_KIND_KEYS = [\n 'aggregateMismatch',\n 'aggregateEntryStale',\n 'aggregateEntryMissing',\n 'hallOfFameLifetimeMismatch',\n] as const;\nexport const STATS_DISCREPANCY_KIND_KEYS = [\n ...GAME_TIER_DISCREPANCY_KIND_KEYS,\n ...AGGREGATE_TIER_DISCREPANCY_KIND_KEYS,\n] as const;\nexport type StatsDiscrepancyKind = (typeof STATS_DISCREPANCY_KIND_KEYS)[number];\n"]}
@@ -0,0 +1,3 @@
1
+ export * from './on-progress.js';
2
+ export * from './picture-upload-variables.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/upload-variables/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,+BAA+B,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from './on-progress.js';
2
+ export * from './picture-upload-variables.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/upload-variables/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,+BAA+B,CAAC","sourcesContent":["export * from './on-progress.js';\nexport * from './picture-upload-variables.js';\n"]}
@@ -0,0 +1,4 @@
1
+ import { z } from 'zod';
2
+ import type { UploadState } from '@ttt-productions/media-schemas';
3
+ export declare const onProgressSchema: z.ZodOptional<z.ZodCustom<(state: UploadState | null) => void, (state: UploadState | null) => void>>;
4
+ //# sourceMappingURL=on-progress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"on-progress.d.ts","sourceRoot":"","sources":["../../src/upload-variables/on-progress.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAOlE,eAAO,MAAM,gBAAgB,oCACX,WAAW,GAAG,IAAI,KAAK,IAAI,UAA3B,WAAW,GAAG,IAAI,KAAK,IAAI,EAChC,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { z } from 'zod';
2
+ // Shared schema for the optional upload-progress callback carried in upload
3
+ // mutation variables. Zod 4 removed embeddable `z.function()` schemas, so this
4
+ // validates "is a function" at parse time and preserves the callback's type
5
+ // (and identity — zod 3's function schema returned a validating wrapper; this
6
+ // returns the original function).
7
+ export const onProgressSchema = z
8
+ .custom((value) => typeof value === 'function')
9
+ .optional();
10
+ //# sourceMappingURL=on-progress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"on-progress.js","sourceRoot":"","sources":["../../src/upload-variables/on-progress.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,4EAA4E;AAC5E,+EAA+E;AAC/E,4EAA4E;AAC5E,8EAA8E;AAC9E,kCAAkC;AAClC,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC;KAC9B,MAAM,CAAsC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,UAAU,CAAC;KACnF,QAAQ,EAAE,CAAC","sourcesContent":["import { z } from 'zod';\nimport type { UploadState } from '@ttt-productions/media-schemas';\n\n// Shared schema for the optional upload-progress callback carried in upload\n// mutation variables. Zod 4 removed embeddable `z.function()` schemas, so this\n// validates \"is a function\" at parse time and preserves the callback's type\n// (and identity — zod 3's function schema returned a validating wrapper; this\n// returns the original function).\nexport const onProgressSchema = z\n .custom<(state: UploadState | null) => void>((value) => typeof value === 'function')\n .optional();\n"]}
@@ -0,0 +1,32 @@
1
+ import { z } from 'zod';
2
+ import type { PictureOrigin } from '../media/file-origins.js';
3
+ import { PICTURE_TARGETS } from '../schemas/uploads.js';
4
+ /** What a picture is uploaded for: its origin, and that origin's target — the owner, by id. */
5
+ export type PictureUploadTarget = {
6
+ [O in PictureOrigin]: {
7
+ origin: O;
8
+ targetInfo: z.infer<(typeof PICTURE_TARGETS)[O]['schema']>;
9
+ };
10
+ }[PictureOrigin];
11
+ export declare const PictureUploadTargetSchema: z.ZodType<PictureUploadTarget>;
12
+ export declare const PictureUploadVariablesSchema: z.ZodObject<{
13
+ target: z.ZodType<PictureUploadTarget, unknown, z.core.$ZodTypeInternals<PictureUploadTarget, unknown>>;
14
+ file: z.ZodCustom<File, File>;
15
+ claim: z.ZodOptional<z.ZodObject<{
16
+ kind: z.ZodEnum<{
17
+ file: "file";
18
+ image: "image";
19
+ video: "video";
20
+ audio: "audio";
21
+ }>;
22
+ source: z.ZodEnum<{
23
+ "file-picker": "file-picker";
24
+ "camera-capture": "camera-capture";
25
+ "media-recorder": "media-recorder";
26
+ }>;
27
+ }, z.core.$strict>>;
28
+ onProgress: z.ZodOptional<z.ZodCustom<(state: import("@ttt-productions/media-schemas").UploadState | null) => void, (state: import("@ttt-productions/media-schemas").UploadState | null) => void>>;
29
+ signal: z.ZodOptional<z.ZodCustom<AbortSignal, AbortSignal>>;
30
+ }, z.core.$strict>;
31
+ export type PictureUploadVariables = z.infer<typeof PictureUploadVariablesSchema>;
32
+ //# sourceMappingURL=picture-upload-variables.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"picture-upload-variables.d.ts","sourceRoot":"","sources":["../../src/upload-variables/picture-upload-variables.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGxD,+FAA+F;AAC/F,MAAM,MAAM,mBAAmB,GAAG;KAC/B,CAAC,IAAI,aAAa,GAAG;QAAE,MAAM,EAAE,CAAC,CAAC;QAAC,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAA;KAAE;CAChG,CAAC,aAAa,CAAC,CAAC;AAUjB,eAAO,MAAM,yBAAyB,EAGtB,CAAC,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAE/C,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;kBAQ9B,CAAC;AACZ,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { z } from 'zod';
2
+ import { ClientMediaClaimSchema } from '@ttt-productions/media-schemas';
3
+ import { PICTURE_ORIGIN_KEYS } from '../media/file-origins.js';
4
+ import { PICTURE_TARGETS } from '../schemas/uploads.js';
5
+ import { onProgressSchema } from './on-progress.js';
6
+ // One member per picture origin, each pairing the origin with its own target schema, built from
7
+ // `PICTURE_TARGETS` so a new origin joins it with no change here.
8
+ const pictureUploadTargetOptions = PICTURE_ORIGIN_KEYS.map((origin) => z.object({ origin: z.literal(origin), targetInfo: PICTURE_TARGETS[origin].schema }).strict());
9
+ // The mapped options are one object schema per origin; TypeScript cannot follow the origin-to-target
10
+ // pairing through the map, so the union is typed by `PictureUploadTarget`, which states it.
11
+ export const PictureUploadTargetSchema = z.discriminatedUnion('origin', pictureUploadTargetOptions);
12
+ export const PictureUploadVariablesSchema = z.object({
13
+ target: PictureUploadTargetSchema,
14
+ file: z.instanceof(File),
15
+ // Untrusted client claim of what the user's action implies (advisory; the
16
+ // server byte inspection is the only classification authority).
17
+ claim: ClientMediaClaimSchema.optional(),
18
+ onProgress: onProgressSchema,
19
+ signal: z.instanceof(AbortSignal).optional(),
20
+ }).strict();
21
+ //# sourceMappingURL=picture-upload-variables.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"picture-upload-variables.js","sourceRoot":"","sources":["../../src/upload-variables/picture-upload-variables.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAOpD,gGAAgG;AAChG,kEAAkE;AAClE,MAAM,0BAA0B,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CACpE,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAC7F,CAAC;AAEF,qGAAqG;AACrG,4FAA4F;AAC5F,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,kBAAkB,CAC3D,QAAQ,EACR,0BAA6H,CACjF,CAAC;AAE/C,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,CAAC,MAAM,CAAC;IACnD,MAAM,EAAE,yBAAyB;IACjC,IAAI,EAAE,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC;IACxB,0EAA0E;IAC1E,gEAAgE;IAChE,KAAK,EAAE,sBAAsB,CAAC,QAAQ,EAAE;IACxC,UAAU,EAAE,gBAAgB;IAC5B,MAAM,EAAE,CAAC,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,QAAQ,EAAE;CAC7C,CAAC,CAAC,MAAM,EAAE,CAAC","sourcesContent":["import { z } from 'zod';\nimport { ClientMediaClaimSchema } from '@ttt-productions/media-schemas';\nimport { PICTURE_ORIGIN_KEYS } from '../media/file-origins.js';\nimport type { PictureOrigin } from '../media/file-origins.js';\nimport { PICTURE_TARGETS } from '../schemas/uploads.js';\nimport { onProgressSchema } from './on-progress.js';\n\n/** What a picture is uploaded for: its origin, and that origin's target — the owner, by id. */\nexport type PictureUploadTarget = {\n [O in PictureOrigin]: { origin: O; targetInfo: z.infer<(typeof PICTURE_TARGETS)[O]['schema']> };\n}[PictureOrigin];\n\n// One member per picture origin, each pairing the origin with its own target schema, built from\n// `PICTURE_TARGETS` so a new origin joins it with no change here.\nconst pictureUploadTargetOptions = PICTURE_ORIGIN_KEYS.map((origin) =>\n z.object({ origin: z.literal(origin), targetInfo: PICTURE_TARGETS[origin].schema }).strict(),\n);\n\n// The mapped options are one object schema per origin; TypeScript cannot follow the origin-to-target\n// pairing through the map, so the union is typed by `PictureUploadTarget`, which states it.\nexport const PictureUploadTargetSchema = z.discriminatedUnion(\n 'origin',\n pictureUploadTargetOptions as [(typeof pictureUploadTargetOptions)[number], ...(typeof pictureUploadTargetOptions)[number][]],\n) as unknown as z.ZodType<PictureUploadTarget>;\n\nexport const PictureUploadVariablesSchema = z.object({\n target: PictureUploadTargetSchema,\n file: z.instanceof(File),\n // Untrusted client claim of what the user's action implies (advisory; the\n // server byte inspection is the only classification authority).\n claim: ClientMediaClaimSchema.optional(),\n onProgress: onProgressSchema,\n signal: z.instanceof(AbortSignal).optional(),\n}).strict();\nexport type PictureUploadVariables = z.infer<typeof PictureUploadVariablesSchema>;\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"account-status.js","sourceRoot":"","sources":["../../src/utils/account-status.ts"],"names":[],"mappings":"AAAA,uGAAuG;AACvG,yGAAyG;AACzG,oGAAoG;AACpG,+EAA+E;AAE/E,OAAO,EAAE,kBAAkB,EAAmB,MAAM,oBAAoB,CAAC;AAEzE,MAAM,UAAU,eAAe,CAAC,OAAsD;IACpF,OAAO,OAAO,EAAE,UAAU,KAAK,kBAAkB,CAAC;AACpD,CAAC","sourcesContent":["// Whether an account may act as a member: it has finished registration — its profile exists — and that\r\n// profile's status is exactly `active`. Fail closed: a missing profile, or any other status, is refused.\r\n// The callable auth assertion refuses every other account before a member callable runs, and a door\r\n// is offered only to an account this answers true for (ENG-006, FRONTEND-110).\r\n\r\nimport { ACTIVE_USER_STATUS, type UserStatus } from '../types/unions.js';\r\n\r\nexport function isActiveAccount(profile: { userStatus: UserStatus } | null | undefined): boolean {\r\n return profile?.userStatus === ACTIVE_USER_STATUS;\r\n}\r\n"]}
1
+ {"version":3,"file":"account-status.js","sourceRoot":"","sources":["../../src/utils/account-status.ts"],"names":[],"mappings":"AAAA,uGAAuG;AACvG,yGAAyG;AACzG,oGAAoG;AACpG,+EAA+E;AAE/E,OAAO,EAAE,kBAAkB,EAAmB,MAAM,oBAAoB,CAAC;AAEzE,MAAM,UAAU,eAAe,CAAC,OAAsD;IACpF,OAAO,OAAO,EAAE,UAAU,KAAK,kBAAkB,CAAC;AACpD,CAAC","sourcesContent":["// Whether an account may act as a member: it has finished registration — its profile exists — and that\n// profile's status is exactly `active`. Fail closed: a missing profile, or any other status, is refused.\n// The callable auth assertion refuses every other account before a member callable runs, and a door\n// is offered only to an account this answers true for (ENG-006, FRONTEND-110).\n\nimport { ACTIVE_USER_STATUS, type UserStatus } from '../types/unions.js';\n\nexport function isActiveAccount(profile: { userStatus: UserStatus } | null | undefined): boolean {\n return profile?.userStatus === ACTIVE_USER_STATUS;\n}\n"]}
@@ -38,16 +38,30 @@ export declare function isMaintenanceRefusalDetails(details: unknown): details i
38
38
  * outage is never mistaken for the designed refusal.
39
39
  */
40
40
  export declare function isMaintenanceRefusalError(err: unknown): boolean;
41
+ /** A stored lever its schema field refused, with the raw value the document held. */
42
+ export interface AppConfigLeverRepair {
43
+ readonly lever: AppConfigLeverKey;
44
+ readonly storedValue: unknown;
45
+ }
46
+ /** The document a writer writes, and every stored lever the merge had to repair to write it. */
47
+ export interface AppConfigMerge {
48
+ readonly next: AppConfig;
49
+ readonly repairs: readonly AppConfigLeverRepair[];
50
+ }
41
51
  /**
42
52
  * The complete `_appConfig/app` document to write after applying `update` to `existing` (the
43
53
  * document as read in the writer's transaction; `null` or `undefined` when it does not exist). The
44
54
  * update's fields win, `existing` supplies the rest, and any field still missing comes from
45
55
  * DEFAULT_APP_CONFIG. An `undefined` update field changes nothing. Pure.
46
56
  *
47
- * Throws when the result would not parse — the update itself, or a stored field the update does not
48
- * replace — so an invalid document is never written.
57
+ * A stored lever its schema refuses never blocks a save — a console typo must not disable the
58
+ * maintenance switch. It is dropped, so the update's value replaces it when the update sends that
59
+ * lever and its DEFAULT_APP_CONFIG value (what every reader already falls back to) otherwise, and it
60
+ * is reported in `repairs`. A valid stored lever is carried as it is.
61
+ *
62
+ * Throws when the update itself would not parse, so an invalid document is never written.
49
63
  */
50
- export declare function mergeAppConfigUpdate(existing: Readonly<Record<string, unknown>> | null | undefined, update: Readonly<Partial<AppConfig>>): AppConfig;
64
+ export declare function mergeAppConfigUpdate(existing: Readonly<Record<string, unknown>> | null | undefined, update: Readonly<Partial<AppConfig>>): AppConfigMerge;
51
65
  /**
52
66
  * One operator lever off an unvalidated `_appConfig/app` snapshot (`null` or `undefined` when the
53
67
  * document is missing or not yet read): the stored value when its schema field accepts it, otherwise
@@ -1 +1 @@
1
- {"version":3,"file":"app-config.d.ts","sourceRoot":"","sources":["../../src/utils/app-config.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAE1D,0FAA0F;AAC1F,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;AAE5D,sCAAsC;AACtC,MAAM,MAAM,iBAAiB,GAAG,MAAM,eAAe,CAAC;AAEtD,0DAA0D;AAC1D,eAAO,MAAM,qBAAqB,6GAAkC,CAAC;AAErE;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,EAAE,eAM/B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,2BAA2B,4GAC8D,CAAC;AAEvG;;;GAGG;AACH,eAAO,MAAM,wBAAwB,EAAG,aAAsB,CAAC;AAE/D,qEAAqE;AACrE,eAAO,MAAM,0BAA0B,EAAG,aAAsB,CAAC;AAEjE,sEAAsE;AACtE,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,MAAM,EAAE,OAAO,0BAA0B,CAAC;CACpD;AASD,2EAA2E;AAC3E,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,yBAAyB,CAGlG;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAQ/D;AAMD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,EAC9D,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,GACnC,SAAS,CAcX;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,iBAAiB,EAC5D,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,EAC9D,KAAK,EAAE,CAAC,GACP,eAAe,CAAC,CAAC,CAAC,CAKpB;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,EAC9D,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAGT;AAED,gGAAgG;AAChG,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,GAC7D,eAAe,CAMjB"}
1
+ {"version":3,"file":"app-config.d.ts","sourceRoot":"","sources":["../../src/utils/app-config.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAE1D,0FAA0F;AAC1F,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;AAE5D,sCAAsC;AACtC,MAAM,MAAM,iBAAiB,GAAG,MAAM,eAAe,CAAC;AAEtD,0DAA0D;AAC1D,eAAO,MAAM,qBAAqB,6GAAkC,CAAC;AAErE;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,EAAE,eAM/B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,2BAA2B,4GAC8D,CAAC;AAEvG;;;GAGG;AACH,eAAO,MAAM,wBAAwB,EAAG,aAAsB,CAAC;AAE/D,qEAAqE;AACrE,eAAO,MAAM,0BAA0B,EAAG,aAAsB,CAAC;AAEjE,sEAAsE;AACtE,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,MAAM,EAAE,OAAO,0BAA0B,CAAC;CACpD;AASD,2EAA2E;AAC3E,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,yBAAyB,CAGlG;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAQ/D;AAMD,qFAAqF;AACrF,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;CAC/B;AAED,gGAAgG;AAChG,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,SAAS,oBAAoB,EAAE,CAAC;CACnD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,EAC9D,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,GACnC,cAAc,CAiBhB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,iBAAiB,EAC5D,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,EAC9D,KAAK,EAAE,CAAC,GACP,eAAe,CAAC,CAAC,CAAC,CAKpB;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,EAC9D,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAGT;AAED,gGAAgG;AAChG,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,GAC7D,eAAe,CAMjB"}
@@ -1,12 +1,11 @@
1
1
  // The `_appConfig/app` singleton at rest, and the one rule each for writing it and reading it.
2
2
  //
3
3
  // WRITE. `mergeAppConfigUpdate(existing, update)`: the writer's update on top of the document it
4
- // read, any field that document lacks taken from DEFAULT_APP_CONFIG, the whole result validated.
5
- // A writer that runs first on a fresh environment therefore still writes a complete, valid
6
- // document rather than a partial one. A stored field that does not parse — a hand edit in the
7
- // console — is never written over by a save that does not send it: the merge refuses, and the
8
- // writer answers with the field, so the operator corrects it at the source. A save that sends the
9
- // field replaces it.
4
+ // read, any field that document lacks — and any lever it holds at a value its schema refuses —
5
+ // taken from DEFAULT_APP_CONFIG, the whole result validated. A writer that runs first on a fresh
6
+ // environment therefore still writes a complete, valid document rather than a partial one, and a
7
+ // hand edit in the console that broke one lever never blocks a save of another: the merge resets it
8
+ // and reports the repair with the value it replaced, which the writer records.
10
9
  //
11
10
  // READ. The stored document is untrusted — an operator can edit any field in the console into any
12
11
  // shape — and a config read must never take the product down by itself. `readAppConfigLever`
@@ -77,21 +76,29 @@ function definedFields(fields) {
77
76
  * update's fields win, `existing` supplies the rest, and any field still missing comes from
78
77
  * DEFAULT_APP_CONFIG. An `undefined` update field changes nothing. Pure.
79
78
  *
80
- * Throws when the result would not parse — the update itself, or a stored field the update does not
81
- * replace — so an invalid document is never written.
79
+ * A stored lever its schema refuses never blocks a save — a console typo must not disable the
80
+ * maintenance switch. It is dropped, so the update's value replaces it when the update sends that
81
+ * lever and its DEFAULT_APP_CONFIG value (what every reader already falls back to) otherwise, and it
82
+ * is reported in `repairs`. A valid stored lever is carried as it is.
83
+ *
84
+ * Throws when the update itself would not parse, so an invalid document is never written.
82
85
  */
83
86
  export function mergeAppConfigUpdate(existing, update) {
84
- const parsed = AppConfigSchema.safeParse({
85
- ...DEFAULT_APP_CONFIG,
86
- ...definedFields(existing ?? {}),
87
- ...definedFields(update),
88
- });
87
+ const stored = definedFields(existing ?? {});
88
+ const repairs = [];
89
+ for (const lever of APP_CONFIG_LEVER_KEYS) {
90
+ if (!(lever in stored) || AppConfigSchema.shape[lever].safeParse(stored[lever]).success)
91
+ continue;
92
+ repairs.push({ lever, storedValue: stored[lever] });
93
+ delete stored[lever];
94
+ }
95
+ const parsed = AppConfigSchema.safeParse({ ...DEFAULT_APP_CONFIG, ...stored, ...definedFields(update) });
89
96
  if (!parsed.success) {
90
97
  const issue = parsed.error.issues[0];
91
98
  const field = issue?.path.join('.') || 'document';
92
99
  throw new Error(`_appConfig/app would not match its schema after this update (${field}: ${issue?.message ?? 'invalid'}). Nothing should be written.`);
93
100
  }
94
- return parsed.data;
101
+ return { next: parsed.data, repairs };
95
102
  }
96
103
  /**
97
104
  * One operator lever off an unvalidated `_appConfig/app` snapshot (`null` or `undefined` when the
@@ -1 +1 @@
1
- {"version":3,"file":"app-config.js","sourceRoot":"","sources":["../../src/utils/app-config.ts"],"names":[],"mappings":"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","sourcesContent":["// The `_appConfig/app` singleton at rest, and the one rule each for writing it and reading it.\r\n//\r\n// WRITE. `mergeAppConfigUpdate(existing, update)`: the writer's update on top of the document it\r\n// read, any field that document lacks taken from DEFAULT_APP_CONFIG, the whole result validated.\r\n// A writer that runs first on a fresh environment therefore still writes a complete, valid\r\n// document rather than a partial one. A stored field that does not parse — a hand edit in the\r\n// console — is never written over by a save that does not send it: the merge refuses, and the\r\n// writer answers with the field, so the operator corrects it at the source. A save that sends the\r\n// field replaces it.\r\n//\r\n// READ. The stored document is untrusted — an operator can edit any field in the console into any\r\n// shape — and a config read must never take the product down by itself. `readAppConfigLever`\r\n// answers one lever at a time: the stored value when its schema field accepts it, otherwise the\r\n// lever's DEFAULT_APP_CONFIG value, so one bad field never costs the good ones. The server's cached\r\n// snapshot reader and the client's readers both read through it.\r\n\r\nimport { AppConfigSchema } from '../doc-schemas/system.js';\r\nimport type { AppConfig } from '../doc-schemas/system.js';\r\n\r\n/** Every operator lever of `_appConfig/app`, each at a value its schema field accepts. */\r\nexport type AppConfigLevers = Readonly<Required<AppConfig>>;\r\n\r\n/** The name of one operator lever. */\r\nexport type AppConfigLeverKey = keyof AppConfigLevers;\r\n\r\n/** Every lever name, derived from the document schema. */\r\nexport const APP_CONFIG_LEVER_KEYS = AppConfigSchema.keyof().options;\r\n\r\n/**\r\n * The document a fresh environment behaves as if it held, and the value each reader falls back to\r\n * for a missing or unreadable field: maintenance off, registration open, no banner.\r\n *\r\n * `appVersion` is `''` — no version has been published — so the version gate stays dormant until an\r\n * operator sets a real one through `updateAppConfig`, whose input refuses `''`.\r\n */\r\nexport const DEFAULT_APP_CONFIG: AppConfigLevers = Object.freeze({\r\n appVersion: '',\r\n maintenanceMode: false,\r\n maintenanceMessage: '',\r\n registrationEnabled: true,\r\n announcementMessage: '',\r\n});\r\n\r\n/**\r\n * The line shown while maintenance is on and the operator left `maintenanceMessage` blank — on the\r\n * maintenance screen and in the callable refusal alike.\r\n */\r\nexport const DEFAULT_MAINTENANCE_MESSAGE =\r\n 'Q-Sports is down for maintenance right now. We will be back shortly — thank you for your patience.';\r\n\r\n/**\r\n * The code the callable maintenance refusal is thrown with. `unavailable` is also what a real outage\r\n * produces, so the code alone never identifies the refusal — its details do.\r\n */\r\nexport const MAINTENANCE_REFUSAL_CODE = 'unavailable' as const;\r\n\r\n/** The `details.reason` the callable maintenance refusal carries. */\r\nexport const MAINTENANCE_REFUSAL_REASON = 'maintenance' as const;\r\n\r\n/** Structured details carried by the callable maintenance refusal. */\r\nexport interface MaintenanceRefusalDetails {\r\n readonly reason: typeof MAINTENANCE_REFUSAL_REASON;\r\n}\r\n\r\n// The refusal code as the server (`unavailable`) and the Firebase client SDK\r\n// (`functions/unavailable`) spell it.\r\nconst MAINTENANCE_REFUSAL_CODES: ReadonlySet<string> = new Set([\r\n MAINTENANCE_REFUSAL_CODE,\r\n `functions/${MAINTENANCE_REFUSAL_CODE}`,\r\n]);\r\n\r\n/** True when `details` is the maintenance refusal's structured details. */\r\nexport function isMaintenanceRefusalDetails(details: unknown): details is MaintenanceRefusalDetails {\r\n if (!details || typeof details !== 'object') return false;\r\n return (details as Record<string, unknown>).reason === MAINTENANCE_REFUSAL_REASON;\r\n}\r\n\r\n/**\r\n * True when `err` is the callable maintenance refusal — an `unavailable` error in either spelling\r\n * whose details carry `reason: 'maintenance'`. Works on the server-side error and on the client-side\r\n * callable error alike, and is decided by the code and the details, never the message, so a genuine\r\n * outage is never mistaken for the designed refusal.\r\n */\r\nexport function isMaintenanceRefusalError(err: unknown): boolean {\r\n if (!err || typeof err !== 'object') return false;\r\n const candidate = err as { code?: unknown; details?: unknown };\r\n return (\r\n typeof candidate.code === 'string' &&\r\n MAINTENANCE_REFUSAL_CODES.has(candidate.code) &&\r\n isMaintenanceRefusalDetails(candidate.details)\r\n );\r\n}\r\n\r\nfunction definedFields(fields: Readonly<Record<string, unknown>>): Record<string, unknown> {\r\n return Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined));\r\n}\r\n\r\n/**\r\n * The complete `_appConfig/app` document to write after applying `update` to `existing` (the\r\n * document as read in the writer's transaction; `null` or `undefined` when it does not exist). The\r\n * update's fields win, `existing` supplies the rest, and any field still missing comes from\r\n * DEFAULT_APP_CONFIG. An `undefined` update field changes nothing. Pure.\r\n *\r\n * Throws when the result would not parse — the update itself, or a stored field the update does not\r\n * replace — so an invalid document is never written.\r\n */\r\nexport function mergeAppConfigUpdate(\r\n existing: Readonly<Record<string, unknown>> | null | undefined,\r\n update: Readonly<Partial<AppConfig>>,\r\n): AppConfig {\r\n const parsed = AppConfigSchema.safeParse({\r\n ...DEFAULT_APP_CONFIG,\r\n ...definedFields(existing ?? {}),\r\n ...definedFields(update),\r\n });\r\n if (!parsed.success) {\r\n const issue = parsed.error.issues[0];\r\n const field = issue?.path.join('.') || 'document';\r\n throw new Error(\r\n `_appConfig/app would not match its schema after this update (${field}: ${issue?.message ?? 'invalid'}). Nothing should be written.`,\r\n );\r\n }\r\n return parsed.data;\r\n}\r\n\r\n/**\r\n * One operator lever off an unvalidated `_appConfig/app` snapshot (`null` or `undefined` when the\r\n * document is missing or not yet read): the stored value when its schema field accepts it, otherwise\r\n * the lever's DEFAULT_APP_CONFIG value — for an absent field and a refused one alike. Never throws.\r\n */\r\nexport function readAppConfigLever<L extends AppConfigLeverKey>(\r\n snapshot: Readonly<Record<string, unknown>> | null | undefined,\r\n lever: L,\r\n): AppConfigLevers[L] {\r\n const parsed = AppConfigSchema.shape[lever].safeParse(snapshot?.[lever]);\r\n return parsed.success && parsed.data !== undefined\r\n ? (parsed.data as AppConfigLevers[L])\r\n : DEFAULT_APP_CONFIG[lever];\r\n}\r\n\r\n/**\r\n * True when an unvalidated `_appConfig/app` snapshot holds `lever` with a value its schema field\r\n * refuses — the value every reader passes over for the default. An absent field is not refused.\r\n * Never throws.\r\n */\r\nexport function isAppConfigLeverRefused(\r\n snapshot: Readonly<Record<string, unknown>> | null | undefined,\r\n lever: AppConfigLeverKey,\r\n): boolean {\r\n const stored = snapshot?.[lever];\r\n return stored !== undefined && !AppConfigSchema.shape[lever].safeParse(stored).success;\r\n}\r\n\r\n/** Every lever off an unvalidated snapshot, each through `readAppConfigLever`. Never throws. */\r\nexport function readAppConfigLevers(\r\n snapshot: Readonly<Record<string, unknown>> | null | undefined,\r\n): AppConfigLevers {\r\n const levers: Partial<Record<AppConfigLeverKey, unknown>> = {};\r\n for (const lever of APP_CONFIG_LEVER_KEYS) {\r\n levers[lever] = readAppConfigLever(snapshot, lever);\r\n }\r\n return levers as AppConfigLevers;\r\n}\r\n"]}
1
+ {"version":3,"file":"app-config.js","sourceRoot":"","sources":["../../src/utils/app-config.ts"],"names":[],"mappings":"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","sourcesContent":["// The `_appConfig/app` singleton at rest, and the one rule each for writing it and reading it.\n//\n// WRITE. `mergeAppConfigUpdate(existing, update)`: the writer's update on top of the document it\n// read, any field that document lacks — and any lever it holds at a value its schema refuses —\n// taken from DEFAULT_APP_CONFIG, the whole result validated. A writer that runs first on a fresh\n// environment therefore still writes a complete, valid document rather than a partial one, and a\n// hand edit in the console that broke one lever never blocks a save of another: the merge resets it\n// and reports the repair with the value it replaced, which the writer records.\n//\n// READ. The stored document is untrusted — an operator can edit any field in the console into any\n// shape — and a config read must never take the product down by itself. `readAppConfigLever`\n// answers one lever at a time: the stored value when its schema field accepts it, otherwise the\n// lever's DEFAULT_APP_CONFIG value, so one bad field never costs the good ones. The server's cached\n// snapshot reader and the client's readers both read through it.\n\nimport { AppConfigSchema } from '../doc-schemas/system.js';\nimport type { AppConfig } from '../doc-schemas/system.js';\n\n/** Every operator lever of `_appConfig/app`, each at a value its schema field accepts. */\nexport type AppConfigLevers = Readonly<Required<AppConfig>>;\n\n/** The name of one operator lever. */\nexport type AppConfigLeverKey = keyof AppConfigLevers;\n\n/** Every lever name, derived from the document schema. */\nexport const APP_CONFIG_LEVER_KEYS = AppConfigSchema.keyof().options;\n\n/**\n * The document a fresh environment behaves as if it held, and the value each reader falls back to\n * for a missing or unreadable field: maintenance off, registration open, no banner.\n *\n * `appVersion` is `''` — no version has been published — so the version gate stays dormant until an\n * operator sets a real one through `updateAppConfig`, whose input refuses `''`.\n */\nexport const DEFAULT_APP_CONFIG: AppConfigLevers = Object.freeze({\n appVersion: '',\n maintenanceMode: false,\n maintenanceMessage: '',\n registrationEnabled: true,\n announcementMessage: '',\n});\n\n/**\n * The line shown while maintenance is on and the operator left `maintenanceMessage` blank — on the\n * maintenance screen and in the callable refusal alike.\n */\nexport const DEFAULT_MAINTENANCE_MESSAGE =\n 'Q-Sports is down for maintenance right now. We will be back shortly — thank you for your patience.';\n\n/**\n * The code the callable maintenance refusal is thrown with. `unavailable` is also what a real outage\n * produces, so the code alone never identifies the refusal — its details do.\n */\nexport const MAINTENANCE_REFUSAL_CODE = 'unavailable' as const;\n\n/** The `details.reason` the callable maintenance refusal carries. */\nexport const MAINTENANCE_REFUSAL_REASON = 'maintenance' as const;\n\n/** Structured details carried by the callable maintenance refusal. */\nexport interface MaintenanceRefusalDetails {\n readonly reason: typeof MAINTENANCE_REFUSAL_REASON;\n}\n\n// The refusal code as the server (`unavailable`) and the Firebase client SDK\n// (`functions/unavailable`) spell it.\nconst MAINTENANCE_REFUSAL_CODES: ReadonlySet<string> = new Set([\n MAINTENANCE_REFUSAL_CODE,\n `functions/${MAINTENANCE_REFUSAL_CODE}`,\n]);\n\n/** True when `details` is the maintenance refusal's structured details. */\nexport function isMaintenanceRefusalDetails(details: unknown): details is MaintenanceRefusalDetails {\n if (!details || typeof details !== 'object') return false;\n return (details as Record<string, unknown>).reason === MAINTENANCE_REFUSAL_REASON;\n}\n\n/**\n * True when `err` is the callable maintenance refusal — an `unavailable` error in either spelling\n * whose details carry `reason: 'maintenance'`. Works on the server-side error and on the client-side\n * callable error alike, and is decided by the code and the details, never the message, so a genuine\n * outage is never mistaken for the designed refusal.\n */\nexport function isMaintenanceRefusalError(err: unknown): boolean {\n if (!err || typeof err !== 'object') return false;\n const candidate = err as { code?: unknown; details?: unknown };\n return (\n typeof candidate.code === 'string' &&\n MAINTENANCE_REFUSAL_CODES.has(candidate.code) &&\n isMaintenanceRefusalDetails(candidate.details)\n );\n}\n\nfunction definedFields(fields: Readonly<Record<string, unknown>>): Record<string, unknown> {\n return Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined));\n}\n\n/** A stored lever its schema field refused, with the raw value the document held. */\nexport interface AppConfigLeverRepair {\n readonly lever: AppConfigLeverKey;\n readonly storedValue: unknown;\n}\n\n/** The document a writer writes, and every stored lever the merge had to repair to write it. */\nexport interface AppConfigMerge {\n readonly next: AppConfig;\n readonly repairs: readonly AppConfigLeverRepair[];\n}\n\n/**\n * The complete `_appConfig/app` document to write after applying `update` to `existing` (the\n * document as read in the writer's transaction; `null` or `undefined` when it does not exist). The\n * update's fields win, `existing` supplies the rest, and any field still missing comes from\n * DEFAULT_APP_CONFIG. An `undefined` update field changes nothing. Pure.\n *\n * A stored lever its schema refuses never blocks a save — a console typo must not disable the\n * maintenance switch. It is dropped, so the update's value replaces it when the update sends that\n * lever and its DEFAULT_APP_CONFIG value (what every reader already falls back to) otherwise, and it\n * is reported in `repairs`. A valid stored lever is carried as it is.\n *\n * Throws when the update itself would not parse, so an invalid document is never written.\n */\nexport function mergeAppConfigUpdate(\n existing: Readonly<Record<string, unknown>> | null | undefined,\n update: Readonly<Partial<AppConfig>>,\n): AppConfigMerge {\n const stored = definedFields(existing ?? {});\n const repairs: AppConfigLeverRepair[] = [];\n for (const lever of APP_CONFIG_LEVER_KEYS) {\n if (!(lever in stored) || AppConfigSchema.shape[lever].safeParse(stored[lever]).success) continue;\n repairs.push({ lever, storedValue: stored[lever] });\n delete stored[lever];\n }\n const parsed = AppConfigSchema.safeParse({ ...DEFAULT_APP_CONFIG, ...stored, ...definedFields(update) });\n if (!parsed.success) {\n const issue = parsed.error.issues[0];\n const field = issue?.path.join('.') || 'document';\n throw new Error(\n `_appConfig/app would not match its schema after this update (${field}: ${issue?.message ?? 'invalid'}). Nothing should be written.`,\n );\n }\n return { next: parsed.data, repairs };\n}\n\n/**\n * One operator lever off an unvalidated `_appConfig/app` snapshot (`null` or `undefined` when the\n * document is missing or not yet read): the stored value when its schema field accepts it, otherwise\n * the lever's DEFAULT_APP_CONFIG value — for an absent field and a refused one alike. Never throws.\n */\nexport function readAppConfigLever<L extends AppConfigLeverKey>(\n snapshot: Readonly<Record<string, unknown>> | null | undefined,\n lever: L,\n): AppConfigLevers[L] {\n const parsed = AppConfigSchema.shape[lever].safeParse(snapshot?.[lever]);\n return parsed.success && parsed.data !== undefined\n ? (parsed.data as AppConfigLevers[L])\n : DEFAULT_APP_CONFIG[lever];\n}\n\n/**\n * True when an unvalidated `_appConfig/app` snapshot holds `lever` with a value its schema field\n * refuses — the value every reader passes over for the default. An absent field is not refused.\n * Never throws.\n */\nexport function isAppConfigLeverRefused(\n snapshot: Readonly<Record<string, unknown>> | null | undefined,\n lever: AppConfigLeverKey,\n): boolean {\n const stored = snapshot?.[lever];\n return stored !== undefined && !AppConfigSchema.shape[lever].safeParse(stored).success;\n}\n\n/** Every lever off an unvalidated snapshot, each through `readAppConfigLever`. Never throws. */\nexport function readAppConfigLevers(\n snapshot: Readonly<Record<string, unknown>> | null | undefined,\n): AppConfigLevers {\n const levers: Partial<Record<AppConfigLeverKey, unknown>> = {};\n for (const lever of APP_CONFIG_LEVER_KEYS) {\n levers[lever] = readAppConfigLever(snapshot, lever);\n }\n return levers as AppConfigLevers;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"app-routes.js","sourceRoot":"","sources":["../../src/utils/app-routes.ts"],"names":[],"mappings":"AAAA,oGAAoG;AACpG,sGAAsG;AACtG,oGAAoG;AACpG,sDAAsD;AAEtD,MAAM,WAAW,GAAG,SAAS,CAAC;AAE9B,iEAAiE;AACjE,MAAM,CAAC,MAAM,UAAU,GAAG,QAAQ,CAAC;AAEnC,+DAA+D;AAC/D,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAEvC,0DAA0D;AAC1D,MAAM,CAAC,MAAM,mBAAmB,GAAG,SAAS,CAAC;AAE7C,6FAA6F;AAC7F,MAAM,CAAC,MAAM,kBAAkB,GAAG,QAAQ,CAAC;AAE3C,2FAA2F;AAC3F,MAAM,CAAC,MAAM,kBAAkB,GAAG,qBAAqB,CAAC;AAExD,mGAAmG;AACnG,kDAAkD;AAClD,MAAM,CAAC,MAAM,mCAAmC,GAAG,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,CAAU,CAAC;AAG7G,2EAA2E;AAC3E,MAAM,CAAC,MAAM,uBAAuB,GAAG,UAAgD,CAAC;AAExF,8FAA8F;AAC9F,MAAM,CAAC,MAAM,oBAAoB,GAAG,OAA6C,CAAC;AAElF,4FAA4F;AAC5F,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAEvC,8EAA8E;AAC9E,MAAM,CAAC,MAAM,0BAA0B,GAAG,aAAmD,CAAC;AAE9F,sFAAsF;AACtF,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,OAAe;IAC5D,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IAC9C,OAAO,GAAG,IAAI,GAAG,MAAM,GAAG,mBAAmB,IAAI,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC;AACjF,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,QAAgB;IACzC,OAAO,GAAG,WAAW,IAAI,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,MAAc;IAC7D,OAAO,GAAG,UAAU,CAAC,QAAQ,CAAC,SAAS,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC;AACtE,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,QAAgB,EAAE,MAAc;IAC5D,OAAO,GAAG,UAAU,CAAC,QAAQ,CAAC,SAAS,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC;AACtE,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,oBAAoB,CAAC,QAAgB,EAAE,MAAc;IACnE,OAAO,GAAG,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;AACpD,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,eAAe,CAAC,QAAgB,EAAE,OAAqC;IACrF,MAAM,IAAI,GAAG,GAAG,kBAAkB,IAAI,kBAAkB,IAAI,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC;IAC3F,OAAO,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACxE,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,mBAAmB,CAAC,QAAgB,EAAE,MAAc;IAClE,OAAO,GAAG,eAAe,CAAC,QAAQ,EAAE,oBAAoB,CAAC,IAAI,gBAAgB,IAAI,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC;AAChH,CAAC","sourcesContent":["// The app addresses a link is built to: a league's pages — the league, one of its teams, one of its\r\n// games and that game's scoring page — and the admin Leagues workspace. Both roots build these links:\r\n// the server writes one onto an admin notification card, and the app links to every one of them, so\r\n// each address shape has one change point (ARCH-101).\r\n\r\nconst LEAGUE_PATH = '/league';\r\n\r\n/** The one admin page; every admin group is a `?view=` of it. */\r\nexport const ADMIN_PATH = '/admin';\r\n\r\n/** The query-string key the admin page's groups live under. */\r\nexport const ADMIN_VIEW_PARAM = 'view';\r\n\r\n/** The query-string key a group's sections live under. */\r\nexport const ADMIN_SECTION_PARAM = 'section';\r\n\r\n/** The query-string key the Leagues group names the league whose workspace is open under. */\r\nexport const ADMIN_LEAGUE_PARAM = 'league';\r\n\r\n/** The admin Leagues group: the league list, each league's workspace nested beneath it. */\r\nexport const ADMIN_LEAGUES_HREF = '/admin?view=leagues';\r\n\r\n// The sections a league's admin workspace is addressed by. The workspace declares which of them it\r\n// has built as tabs; a link may name any of them.\r\nexport const ADMIN_LEAGUE_WORKSPACE_SECTION_KEYS = ['settings', 'schedule', 'teams', 'free-agents'] as const;\r\nexport type AdminLeagueWorkspaceSection = (typeof ADMIN_LEAGUE_WORKSPACE_SECTION_KEYS)[number];\r\n\r\n/** The league workspace's section holding the league's weeks and games. */\r\nexport const LEAGUE_SCHEDULE_SECTION = 'schedule' satisfies AdminLeagueWorkspaceSection;\r\n\r\n/** The league workspace's section listing the league's teams, each opening its admin view. */\r\nexport const LEAGUE_TEAMS_SECTION = 'teams' satisfies AdminLeagueWorkspaceSection;\r\n\r\n/** The query-string key the Teams section names the team whose admin view is open under. */\r\nexport const ADMIN_TEAM_PARAM = 'team';\r\n\r\n/** The league workspace's section listing the league's free-agent signups. */\r\nexport const LEAGUE_FREE_AGENTS_SECTION = 'free-agents' satisfies AdminLeagueWorkspaceSection;\r\n\r\n/** An admin address on one of its sections — the one place a section joins a link. */\r\nexport function withAdminSection(href: string, section: string): string {\r\n const joiner = href.includes('?') ? '&' : '?';\r\n return `${href}${joiner}${ADMIN_SECTION_PARAM}=${encodeURIComponent(section)}`;\r\n}\r\n\r\nexport function leagueHref(leagueId: string): string {\r\n return `${LEAGUE_PATH}/${encodeURIComponent(leagueId)}`;\r\n}\r\n\r\nexport function leagueTeamHref(leagueId: string, teamId: string): string {\r\n return `${leagueHref(leagueId)}/team/${encodeURIComponent(teamId)}`;\r\n}\r\n\r\nexport function sportGameHref(leagueId: string, gameId: string): string {\r\n return `${leagueHref(leagueId)}/game/${encodeURIComponent(gameId)}`;\r\n}\r\n\r\n/** A game's scoring page, where a scorer records its stats. */\r\nexport function sportGameScoringHref(leagueId: string, gameId: string): string {\r\n return `${sportGameHref(leagueId, gameId)}/stats`;\r\n}\r\n\r\n/** A league's admin workspace, on one of its sections when one is named. */\r\nexport function adminLeagueHref(leagueId: string, section?: AdminLeagueWorkspaceSection): string {\r\n const href = `${ADMIN_LEAGUES_HREF}&${ADMIN_LEAGUE_PARAM}=${encodeURIComponent(leagueId)}`;\r\n return section === undefined ? href : withAdminSection(href, section);\r\n}\r\n\r\n/** One team's admin view, inside its league's workspace on the Teams section. */\r\nexport function adminLeagueTeamHref(leagueId: string, teamId: string): string {\r\n return `${adminLeagueHref(leagueId, LEAGUE_TEAMS_SECTION)}&${ADMIN_TEAM_PARAM}=${encodeURIComponent(teamId)}`;\r\n}\r\n"]}
1
+ {"version":3,"file":"app-routes.js","sourceRoot":"","sources":["../../src/utils/app-routes.ts"],"names":[],"mappings":"AAAA,oGAAoG;AACpG,sGAAsG;AACtG,oGAAoG;AACpG,sDAAsD;AAEtD,MAAM,WAAW,GAAG,SAAS,CAAC;AAE9B,iEAAiE;AACjE,MAAM,CAAC,MAAM,UAAU,GAAG,QAAQ,CAAC;AAEnC,+DAA+D;AAC/D,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAEvC,0DAA0D;AAC1D,MAAM,CAAC,MAAM,mBAAmB,GAAG,SAAS,CAAC;AAE7C,6FAA6F;AAC7F,MAAM,CAAC,MAAM,kBAAkB,GAAG,QAAQ,CAAC;AAE3C,2FAA2F;AAC3F,MAAM,CAAC,MAAM,kBAAkB,GAAG,qBAAqB,CAAC;AAExD,mGAAmG;AACnG,kDAAkD;AAClD,MAAM,CAAC,MAAM,mCAAmC,GAAG,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,CAAU,CAAC;AAG7G,2EAA2E;AAC3E,MAAM,CAAC,MAAM,uBAAuB,GAAG,UAAgD,CAAC;AAExF,8FAA8F;AAC9F,MAAM,CAAC,MAAM,oBAAoB,GAAG,OAA6C,CAAC;AAElF,4FAA4F;AAC5F,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAEvC,8EAA8E;AAC9E,MAAM,CAAC,MAAM,0BAA0B,GAAG,aAAmD,CAAC;AAE9F,sFAAsF;AACtF,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,OAAe;IAC5D,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IAC9C,OAAO,GAAG,IAAI,GAAG,MAAM,GAAG,mBAAmB,IAAI,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC;AACjF,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,QAAgB;IACzC,OAAO,GAAG,WAAW,IAAI,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,MAAc;IAC7D,OAAO,GAAG,UAAU,CAAC,QAAQ,CAAC,SAAS,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC;AACtE,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,QAAgB,EAAE,MAAc;IAC5D,OAAO,GAAG,UAAU,CAAC,QAAQ,CAAC,SAAS,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC;AACtE,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,oBAAoB,CAAC,QAAgB,EAAE,MAAc;IACnE,OAAO,GAAG,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;AACpD,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,eAAe,CAAC,QAAgB,EAAE,OAAqC;IACrF,MAAM,IAAI,GAAG,GAAG,kBAAkB,IAAI,kBAAkB,IAAI,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC;IAC3F,OAAO,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACxE,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,mBAAmB,CAAC,QAAgB,EAAE,MAAc;IAClE,OAAO,GAAG,eAAe,CAAC,QAAQ,EAAE,oBAAoB,CAAC,IAAI,gBAAgB,IAAI,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC;AAChH,CAAC","sourcesContent":["// The app addresses a link is built to: a league's pages — the league, one of its teams, one of its\n// games and that game's scoring page — and the admin Leagues workspace. Both roots build these links:\n// the server writes one onto an admin notification card, and the app links to every one of them, so\n// each address shape has one change point (ARCH-101).\n\nconst LEAGUE_PATH = '/league';\n\n/** The one admin page; every admin group is a `?view=` of it. */\nexport const ADMIN_PATH = '/admin';\n\n/** The query-string key the admin page's groups live under. */\nexport const ADMIN_VIEW_PARAM = 'view';\n\n/** The query-string key a group's sections live under. */\nexport const ADMIN_SECTION_PARAM = 'section';\n\n/** The query-string key the Leagues group names the league whose workspace is open under. */\nexport const ADMIN_LEAGUE_PARAM = 'league';\n\n/** The admin Leagues group: the league list, each league's workspace nested beneath it. */\nexport const ADMIN_LEAGUES_HREF = '/admin?view=leagues';\n\n// The sections a league's admin workspace is addressed by. The workspace declares which of them it\n// has built as tabs; a link may name any of them.\nexport const ADMIN_LEAGUE_WORKSPACE_SECTION_KEYS = ['settings', 'schedule', 'teams', 'free-agents'] as const;\nexport type AdminLeagueWorkspaceSection = (typeof ADMIN_LEAGUE_WORKSPACE_SECTION_KEYS)[number];\n\n/** The league workspace's section holding the league's weeks and games. */\nexport const LEAGUE_SCHEDULE_SECTION = 'schedule' satisfies AdminLeagueWorkspaceSection;\n\n/** The league workspace's section listing the league's teams, each opening its admin view. */\nexport const LEAGUE_TEAMS_SECTION = 'teams' satisfies AdminLeagueWorkspaceSection;\n\n/** The query-string key the Teams section names the team whose admin view is open under. */\nexport const ADMIN_TEAM_PARAM = 'team';\n\n/** The league workspace's section listing the league's free-agent signups. */\nexport const LEAGUE_FREE_AGENTS_SECTION = 'free-agents' satisfies AdminLeagueWorkspaceSection;\n\n/** An admin address on one of its sections — the one place a section joins a link. */\nexport function withAdminSection(href: string, section: string): string {\n const joiner = href.includes('?') ? '&' : '?';\n return `${href}${joiner}${ADMIN_SECTION_PARAM}=${encodeURIComponent(section)}`;\n}\n\nexport function leagueHref(leagueId: string): string {\n return `${LEAGUE_PATH}/${encodeURIComponent(leagueId)}`;\n}\n\nexport function leagueTeamHref(leagueId: string, teamId: string): string {\n return `${leagueHref(leagueId)}/team/${encodeURIComponent(teamId)}`;\n}\n\nexport function sportGameHref(leagueId: string, gameId: string): string {\n return `${leagueHref(leagueId)}/game/${encodeURIComponent(gameId)}`;\n}\n\n/** A game's scoring page, where a scorer records its stats. */\nexport function sportGameScoringHref(leagueId: string, gameId: string): string {\n return `${sportGameHref(leagueId, gameId)}/stats`;\n}\n\n/** A league's admin workspace, on one of its sections when one is named. */\nexport function adminLeagueHref(leagueId: string, section?: AdminLeagueWorkspaceSection): string {\n const href = `${ADMIN_LEAGUES_HREF}&${ADMIN_LEAGUE_PARAM}=${encodeURIComponent(leagueId)}`;\n return section === undefined ? href : withAdminSection(href, section);\n}\n\n/** One team's admin view, inside its league's workspace on the Teams section. */\nexport function adminLeagueTeamHref(leagueId: string, teamId: string): string {\n return `${adminLeagueHref(leagueId, LEAGUE_TEAMS_SECTION)}&${ADMIN_TEAM_PARAM}=${encodeURIComponent(teamId)}`;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,0BAA0B,CAAC;AACzC,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC","sourcesContent":["export * from './display-name.js';\r\nexport * from './app-config.js';\r\nexport * from './league-calendar.js';\r\nexport * from './team-creation.js';\r\nexport * from './account-status.js';\r\nexport * from './league-join-warning.js';\r\nexport * from './schedules.js';\r\nexport * from './app-routes.js';\r\nexport * from './team-member-actions.js';\r\nexport * from './league-mode.js';\r\nexport * from './league-joining.js';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,0BAA0B,CAAC;AACzC,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC","sourcesContent":["export * from './display-name.js';\nexport * from './app-config.js';\nexport * from './league-calendar.js';\nexport * from './team-creation.js';\nexport * from './account-status.js';\nexport * from './league-join-warning.js';\nexport * from './schedules.js';\nexport * from './app-routes.js';\nexport * from './team-member-actions.js';\nexport * from './league-mode.js';\nexport * from './league-joining.js';\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"league-calendar.js","sourceRoot":"","sources":["../../src/utils/league-calendar.ts"],"names":[],"mappings":"AAAA,uGAAuG;AACvG,qGAAqG;AACrG,+FAA+F;AAC/F,oGAAoG;AACpG,wCAAwC;AAExC,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAE5D,0EAA0E;AAC1E,MAAM,iBAAiB,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE;IACzD,QAAQ,EAAE,gBAAgB;IAC1B,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,SAAS;IAChB,GAAG,EAAE,SAAS;CACf,CAAC,CAAC;AAEH,MAAM,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEnC,iEAAiE;AACjE,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,OAAO,iBAAiB,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;AACrD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,IAAY;IAC1D,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,GAAG,MAAM,gBAAgB,CAAC,CAAC;IAChD,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,UAAU,CAAC,yBAAyB,MAAM,IAAI,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,GAAG,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC5E,CAAC;AAED,qGAAqG;AACrG,MAAM,wBAAwB,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE;IAChE,QAAQ,EAAE,gBAAgB;IAC1B,SAAS,EAAE,KAAK;IAChB,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,SAAS;IAChB,GAAG,EAAE,SAAS;IACd,IAAI,EAAE,SAAS;IACf,MAAM,EAAE,SAAS;IACjB,MAAM,EAAE,SAAS;CAClB,CAAC,CAAC;AAEH,MAAM,kBAAkB,GAAG,2BAA2B,CAAC;AACvD,MAAM,iBAAiB,GAAG,6BAA6B,CAAC;AAExD,iGAAiG;AACjG,SAAS,oBAAoB,CAAC,OAAe;IAC3C,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAC9B,wBAAwB,CAAC,aAAa,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CACjG,CAAC;IACF,OAAO,IAAI,CAAC,GAAG,CACb,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAClB,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EACvB,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EACjB,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAClB,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EACpB,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CACrB,CAAC;AACJ,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,iBAAiB,CAAC,OAAe;IAC/C,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC1D,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC/D,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,aAAa,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACnE,OAAO,GAAG,KAAK,IAAI,OAAO,EAAE,CAAC;AAC/B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,SAAiB;IAC/D,MAAM,GAAG,GAAG,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC/C,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,UAAU,CAAC,wCAAwC,MAAM,MAAM,SAAS,IAAI,CAAC,CAAC;IAC3G,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACjH,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,MAAM,EAAE,CAAC;QAC9D,MAAM,IAAI,UAAU,CAAC,wBAAwB,MAAM,IAAI,CAAC,CAAC;IAC3D,CAAC;IACD,oGAAoG;IACpG,gGAAgG;IAChG,MAAM,UAAU,GAAG,SAAS,GAAG,CAAC,oBAAoB,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC,CAAC;IAC7E,OAAO,SAAS,GAAG,CAAC,oBAAoB,CAAC,UAAU,CAAC,GAAG,UAAU,CAAC,CAAC;AACrE,CAAC","sourcesContent":["// Calendar days and times of day in the leagues' own time zone. A league's dates and a game's `dayKey`\r\n// are calendar dates with no time or zone (`YYYY-MM-DD`), so \"today\" is read in LEAGUE_TIME_ZONE and\r\n// every comparison is between two calendar dates, never between instants. A game's start is an\r\n// instant, entered and shown as a day and a time of day (`HH:MM`) in LEAGUE_TIME_ZONE, whatever the\r\n// zone of the browser it is entered in.\r\n\r\nimport { LEAGUE_TIME_ZONE } from '../constants/business.js';\r\n\r\n// `en-CA` formats a date as `YYYY-MM-DD`, the stored calendar-date shape.\r\nconst LEAGUE_DAY_FORMAT = new Intl.DateTimeFormat('en-CA', {\r\n timeZone: LEAGUE_TIME_ZONE,\r\n year: 'numeric',\r\n month: '2-digit',\r\n day: '2-digit',\r\n});\r\n\r\nconst DAY_MS = 24 * 60 * 60 * 1000;\r\n\r\n/** The calendar date an instant falls on in LEAGUE_TIME_ZONE. */\r\nexport function toLeagueDayKey(epochMs: number): string {\r\n return LEAGUE_DAY_FORMAT.format(new Date(epochMs));\r\n}\r\n\r\n/**\r\n * The calendar date `days` days after `dayKey` (before it, when negative). Pure calendar arithmetic\r\n * on the date itself, so no time zone or daylight-saving change moves it.\r\n */\r\nexport function addCalendarDays(dayKey: string, days: number): string {\r\n const day = new Date(`${dayKey}T00:00:00.000Z`);\r\n if (Number.isNaN(day.getTime())) {\r\n throw new RangeError(`Not a calendar date: '${dayKey}'.`);\r\n }\r\n return new Date(day.getTime() + days * DAY_MS).toISOString().slice(0, 10);\r\n}\r\n\r\n// Every part of an instant's wall-clock reading in LEAGUE_TIME_ZONE. `h23` keeps midnight at hour 0.\r\nconst LEAGUE_WALL_CLOCK_FORMAT = new Intl.DateTimeFormat('en-US', {\r\n timeZone: LEAGUE_TIME_ZONE,\r\n hourCycle: 'h23',\r\n year: 'numeric',\r\n month: '2-digit',\r\n day: '2-digit',\r\n hour: '2-digit',\r\n minute: '2-digit',\r\n second: '2-digit',\r\n});\r\n\r\nconst CALENDAR_DAY_SHAPE = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\r\nconst TIME_OF_DAY_SHAPE = /^([01]\\d|2[0-3]):([0-5]\\d)$/;\r\n\r\n/** The instant's wall-clock reading in LEAGUE_TIME_ZONE, written as if that reading were UTC. */\r\nfunction leagueWallClockAsUtc(epochMs: number): number {\r\n const parts = Object.fromEntries(\r\n LEAGUE_WALL_CLOCK_FORMAT.formatToParts(new Date(epochMs)).map((part) => [part.type, part.value]),\r\n );\r\n return Date.UTC(\r\n Number(parts.year),\r\n Number(parts.month) - 1,\r\n Number(parts.day),\r\n Number(parts.hour),\r\n Number(parts.minute),\r\n Number(parts.second),\r\n );\r\n}\r\n\r\n/** The time of day an instant falls at in LEAGUE_TIME_ZONE, as `HH:MM` on a 24-hour clock. */\r\nexport function toLeagueTimeOfDay(epochMs: number): string {\r\n const wallClock = new Date(leagueWallClockAsUtc(epochMs));\r\n const hours = String(wallClock.getUTCHours()).padStart(2, '0');\r\n const minutes = String(wallClock.getUTCMinutes()).padStart(2, '0');\r\n return `${hours}:${minutes}`;\r\n}\r\n\r\n/**\r\n * The instant a calendar day and a time of day (`HH:MM`, 24-hour) name in LEAGUE_TIME_ZONE, in epoch\r\n * milliseconds. The zone's offset is read at the instant itself, so a day on either side of a\r\n * daylight-saving change reads its own offset. Throws on a day or a time that is not one.\r\n */\r\nexport function leagueInstantOf(dayKey: string, timeOfDay: string): number {\r\n const day = CALENDAR_DAY_SHAPE.exec(dayKey);\r\n const time = TIME_OF_DAY_SHAPE.exec(timeOfDay);\r\n if (!day || !time) throw new RangeError(`Not a calendar day and time of day: '${dayKey}' '${timeOfDay}'.`);\r\n const wallClock = Date.UTC(Number(day[1]), Number(day[2]) - 1, Number(day[3]), Number(time[1]), Number(time[2]));\r\n if (new Date(wallClock).toISOString().slice(0, 10) !== dayKey) {\r\n throw new RangeError(`Not a calendar day: '${dayKey}'.`);\r\n }\r\n // The offset at the wall-clock reading taken as UTC is within a day of the right one; read again at\r\n // the instant that offset gives, so a reading on the other side of a change settles on its own.\r\n const firstGuess = wallClock - (leagueWallClockAsUtc(wallClock) - wallClock);\r\n return wallClock - (leagueWallClockAsUtc(firstGuess) - firstGuess);\r\n}\r\n"]}
1
+ {"version":3,"file":"league-calendar.js","sourceRoot":"","sources":["../../src/utils/league-calendar.ts"],"names":[],"mappings":"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","sourcesContent":["// Calendar days and times of day in the leagues' own time zone. A league's dates and a game's `dayKey`\n// are calendar dates with no time or zone (`YYYY-MM-DD`), so \"today\" is read in LEAGUE_TIME_ZONE and\n// every comparison is between two calendar dates, never between instants. A game's start is an\n// instant, entered and shown as a day and a time of day (`HH:MM`) in LEAGUE_TIME_ZONE, whatever the\n// zone of the browser it is entered in.\n\nimport { LEAGUE_TIME_ZONE } from '../constants/business.js';\n\n// `en-CA` formats a date as `YYYY-MM-DD`, the stored calendar-date shape.\nconst LEAGUE_DAY_FORMAT = new Intl.DateTimeFormat('en-CA', {\n timeZone: LEAGUE_TIME_ZONE,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n});\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\n\n/** The calendar date an instant falls on in LEAGUE_TIME_ZONE. */\nexport function toLeagueDayKey(epochMs: number): string {\n return LEAGUE_DAY_FORMAT.format(new Date(epochMs));\n}\n\n/**\n * The calendar date `days` days after `dayKey` (before it, when negative). Pure calendar arithmetic\n * on the date itself, so no time zone or daylight-saving change moves it.\n */\nexport function addCalendarDays(dayKey: string, days: number): string {\n const day = new Date(`${dayKey}T00:00:00.000Z`);\n if (Number.isNaN(day.getTime())) {\n throw new RangeError(`Not a calendar date: '${dayKey}'.`);\n }\n return new Date(day.getTime() + days * DAY_MS).toISOString().slice(0, 10);\n}\n\n// Every part of an instant's wall-clock reading in LEAGUE_TIME_ZONE. `h23` keeps midnight at hour 0.\nconst LEAGUE_WALL_CLOCK_FORMAT = new Intl.DateTimeFormat('en-US', {\n timeZone: LEAGUE_TIME_ZONE,\n hourCycle: 'h23',\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n});\n\nconst CALENDAR_DAY_SHAPE = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\nconst TIME_OF_DAY_SHAPE = /^([01]\\d|2[0-3]):([0-5]\\d)$/;\n\n/** The instant's wall-clock reading in LEAGUE_TIME_ZONE, written as if that reading were UTC. */\nfunction leagueWallClockAsUtc(epochMs: number): number {\n const parts = Object.fromEntries(\n LEAGUE_WALL_CLOCK_FORMAT.formatToParts(new Date(epochMs)).map((part) => [part.type, part.value]),\n );\n return Date.UTC(\n Number(parts.year),\n Number(parts.month) - 1,\n Number(parts.day),\n Number(parts.hour),\n Number(parts.minute),\n Number(parts.second),\n );\n}\n\n/** The time of day an instant falls at in LEAGUE_TIME_ZONE, as `HH:MM` on a 24-hour clock. */\nexport function toLeagueTimeOfDay(epochMs: number): string {\n const wallClock = new Date(leagueWallClockAsUtc(epochMs));\n const hours = String(wallClock.getUTCHours()).padStart(2, '0');\n const minutes = String(wallClock.getUTCMinutes()).padStart(2, '0');\n return `${hours}:${minutes}`;\n}\n\n/**\n * The instant a calendar day and a time of day (`HH:MM`, 24-hour) name in LEAGUE_TIME_ZONE, in epoch\n * milliseconds. The zone's offset is read at the instant itself, so a day on either side of a\n * daylight-saving change reads its own offset. Throws on a day or a time that is not one.\n */\nexport function leagueInstantOf(dayKey: string, timeOfDay: string): number {\n const day = CALENDAR_DAY_SHAPE.exec(dayKey);\n const time = TIME_OF_DAY_SHAPE.exec(timeOfDay);\n if (!day || !time) throw new RangeError(`Not a calendar day and time of day: '${dayKey}' '${timeOfDay}'.`);\n const wallClock = Date.UTC(Number(day[1]), Number(day[2]) - 1, Number(day[3]), Number(time[1]), Number(time[2]));\n if (new Date(wallClock).toISOString().slice(0, 10) !== dayKey) {\n throw new RangeError(`Not a calendar day: '${dayKey}'.`);\n }\n // The offset at the wall-clock reading taken as UTC is within a day of the right one; read again at\n // the instant that offset gives, so a reading on the other side of a change settles on its own.\n const firstGuess = wallClock - (leagueWallClockAsUtc(wallClock) - wallClock);\n return wallClock - (leagueWallClockAsUtc(firstGuess) - firstGuess);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"league-mode.js","sourceRoot":"","sources":["../../src/utils/league-mode.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,qGAAqG;AACrG,oGAAoG;AAEpG,OAAO,EAAE,sBAAsB,EAAmB,MAAM,oBAAoB,CAAC;AAE7E,6FAA6F;AAC7F,MAAM,UAAU,0BAA0B,CAAC,IAAgB;IACzD,OAAO,IAAI,KAAK,sBAAsB,CAAC;AACzC,CAAC","sourcesContent":["// Whether a league sorts its teams by type. It is the one reading every surface makes — the team\r\n// form's required type, the type control, the standings and rankings split into pools, the team list\r\n// grouped by type, and the admin's mode copy — so no reader compares the mode on its own (ENG-005).\r\n\r\nimport { TEAM_TYPED_LEAGUE_MODE, type LeagueMode } from '../types/unions.js';\r\n\r\n/** Whether a league in `mode` sorts its teams by type, so each team carries a `teamType`. */\r\nexport function leagueModeSortsTeamsByType(mode: LeagueMode): boolean {\r\n return mode === TEAM_TYPED_LEAGUE_MODE;\r\n}\r\n"]}
1
+ {"version":3,"file":"league-mode.js","sourceRoot":"","sources":["../../src/utils/league-mode.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,qGAAqG;AACrG,oGAAoG;AAEpG,OAAO,EAAE,sBAAsB,EAAmB,MAAM,oBAAoB,CAAC;AAE7E,6FAA6F;AAC7F,MAAM,UAAU,0BAA0B,CAAC,IAAgB;IACzD,OAAO,IAAI,KAAK,sBAAsB,CAAC;AACzC,CAAC","sourcesContent":["// Whether a league sorts its teams by type. It is the one reading every surface makes — the team\n// form's required type, the type control, the standings and rankings split into pools, the team list\n// grouped by type, and the admin's mode copy — so no reader compares the mode on its own (ENG-005).\n\nimport { TEAM_TYPED_LEAGUE_MODE, type LeagueMode } from '../types/unions.js';\n\n/** Whether a league in `mode` sorts its teams by type, so each team carries a `teamType`. */\nexport function leagueModeSortsTeamsByType(mode: LeagueMode): boolean {\n return mode === TEAM_TYPED_LEAGUE_MODE;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"team-member-actions.js","sourceRoot":"","sources":["../../src/utils/team-member-actions.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,kGAAkG;AAClG,qGAAqG;AACrG,EAAE;AACF,4FAA4F;AAC5F,sGAAsG;AACtG,uCAAuC;AACvC,2FAA2F;AAC3F,EAAE;AACF,+FAA+F;AAC/F,gBAAgB;AAGhB,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,GAGjB,MAAM,oBAAoB,CAAC;AAE5B,iFAAiF;AACjF,MAAM,CAAC,MAAM,uBAAuB,GAAG,OAAqC,CAAC;AAoB7E,MAAM,IAAI,GAAsB,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AAEjF,MAAM,UAAU,iBAAiB,CAAC,KAAsB,EAAE,MAAwB;IAChF,IAAI,MAAM,CAAC,IAAI,KAAK,iBAAiB;QAAE,OAAO,IAAI,CAAC;IACnD,MAAM,iBAAiB,GAAG,KAAK,KAAK,uBAAuB,IAAI,KAAK,KAAK,iBAAiB,CAAC;IAC3F,OAAO;QACL,OAAO,EAAE,iBAAiB,IAAI,MAAM,CAAC,IAAI,KAAK,gBAAgB,IAAI,CAAC,MAAM,CAAC,OAAO;QACjF,MAAM,EAAE,iBAAiB,IAAI,MAAM,CAAC,IAAI,KAAK,oBAAoB;QACjE,MAAM,EAAE,iBAAiB,IAAI,MAAM,CAAC,IAAI,KAAK,gBAAgB;KAC9D,CAAC;AACJ,CAAC","sourcesContent":["// What a team leader or an admin may do to one member of a team: promote a player to co-captain,\r\n// demote a co-captain to player, or remove them. The team's management callables refuse what this\r\n// leaves out, and the team page and the admin Teams tab offer exactly what it allows (FRONTEND-110):\r\n//\r\n// - the captain is never moved or removed here — an admin changes the team's captain first;\r\n// - only the captain and an admin promote or demote, and a ghost is never made a co-captain, since no\r\n// one could act with that authority;\r\n// - a co-captain removes plain players only — never another co-captain, never the captain.\r\n//\r\n// A ghost leaves through its own removal, which decides whether it has played; that is still a\r\n// removal here.\r\n\r\nimport type { CaptainTeamRole } from '../types/audit.js';\r\nimport {\r\n TEAM_CAPTAIN_ROLE,\r\n TEAM_CO_CAPTAIN_ROLE,\r\n TEAM_PLAYER_ROLE,\r\n type SystemRole,\r\n type TeamRole,\r\n} from '../types/unions.js';\r\n\r\n/** An action on a member taken through its admin entry, under the admin role. */\r\nexport const TEAM_MEMBER_ADMIN_ACTOR = 'admin' as const satisfies SystemRole;\r\n\r\n/** The authority an action on a member is taken under: an admin, or the caller's role on the team. */\r\nexport type TeamMemberActor = typeof TEAM_MEMBER_ADMIN_ACTOR | CaptainTeamRole;\r\n\r\n/** The member acted on: their role on the team, and whether they are a ghost. */\r\nexport interface TeamMemberTarget {\r\n role: TeamRole;\r\n isGhost: boolean;\r\n}\r\n\r\nexport interface TeamMemberActions {\r\n /** Make the player a co-captain. */\r\n promote: boolean;\r\n /** Make the co-captain a player. */\r\n demote: boolean;\r\n /** Take them off the team — a ghost through the ghost removal. */\r\n remove: boolean;\r\n}\r\n\r\nconst NONE: TeamMemberActions = { promote: false, demote: false, remove: false };\r\n\r\nexport function teamMemberActions(actor: TeamMemberActor, target: TeamMemberTarget): TeamMemberActions {\r\n if (target.role === TEAM_CAPTAIN_ROLE) return NONE;\r\n const managesCoCaptains = actor === TEAM_MEMBER_ADMIN_ACTOR || actor === TEAM_CAPTAIN_ROLE;\r\n return {\r\n promote: managesCoCaptains && target.role === TEAM_PLAYER_ROLE && !target.isGhost,\r\n demote: managesCoCaptains && target.role === TEAM_CO_CAPTAIN_ROLE,\r\n remove: managesCoCaptains || target.role === TEAM_PLAYER_ROLE,\r\n };\r\n}\r\n"]}
1
+ {"version":3,"file":"team-member-actions.js","sourceRoot":"","sources":["../../src/utils/team-member-actions.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,kGAAkG;AAClG,qGAAqG;AACrG,EAAE;AACF,4FAA4F;AAC5F,sGAAsG;AACtG,uCAAuC;AACvC,2FAA2F;AAC3F,EAAE;AACF,+FAA+F;AAC/F,gBAAgB;AAGhB,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,GAGjB,MAAM,oBAAoB,CAAC;AAE5B,iFAAiF;AACjF,MAAM,CAAC,MAAM,uBAAuB,GAAG,OAAqC,CAAC;AAoB7E,MAAM,IAAI,GAAsB,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AAEjF,MAAM,UAAU,iBAAiB,CAAC,KAAsB,EAAE,MAAwB;IAChF,IAAI,MAAM,CAAC,IAAI,KAAK,iBAAiB;QAAE,OAAO,IAAI,CAAC;IACnD,MAAM,iBAAiB,GAAG,KAAK,KAAK,uBAAuB,IAAI,KAAK,KAAK,iBAAiB,CAAC;IAC3F,OAAO;QACL,OAAO,EAAE,iBAAiB,IAAI,MAAM,CAAC,IAAI,KAAK,gBAAgB,IAAI,CAAC,MAAM,CAAC,OAAO;QACjF,MAAM,EAAE,iBAAiB,IAAI,MAAM,CAAC,IAAI,KAAK,oBAAoB;QACjE,MAAM,EAAE,iBAAiB,IAAI,MAAM,CAAC,IAAI,KAAK,gBAAgB;KAC9D,CAAC;AACJ,CAAC","sourcesContent":["// What a team leader or an admin may do to one member of a team: promote a player to co-captain,\n// demote a co-captain to player, or remove them. The team's management callables refuse what this\n// leaves out, and the team page and the admin Teams tab offer exactly what it allows (FRONTEND-110):\n//\n// - the captain is never moved or removed here — an admin changes the team's captain first;\n// - only the captain and an admin promote or demote, and a ghost is never made a co-captain, since no\n// one could act with that authority;\n// - a co-captain removes plain players only — never another co-captain, never the captain.\n//\n// A ghost leaves through its own removal, which decides whether it has played; that is still a\n// removal here.\n\nimport type { CaptainTeamRole } from '../types/audit.js';\nimport {\n TEAM_CAPTAIN_ROLE,\n TEAM_CO_CAPTAIN_ROLE,\n TEAM_PLAYER_ROLE,\n type SystemRole,\n type TeamRole,\n} from '../types/unions.js';\n\n/** An action on a member taken through its admin entry, under the admin role. */\nexport const TEAM_MEMBER_ADMIN_ACTOR = 'admin' as const satisfies SystemRole;\n\n/** The authority an action on a member is taken under: an admin, or the caller's role on the team. */\nexport type TeamMemberActor = typeof TEAM_MEMBER_ADMIN_ACTOR | CaptainTeamRole;\n\n/** The member acted on: their role on the team, and whether they are a ghost. */\nexport interface TeamMemberTarget {\n role: TeamRole;\n isGhost: boolean;\n}\n\nexport interface TeamMemberActions {\n /** Make the player a co-captain. */\n promote: boolean;\n /** Make the co-captain a player. */\n demote: boolean;\n /** Take them off the team — a ghost through the ghost removal. */\n remove: boolean;\n}\n\nconst NONE: TeamMemberActions = { promote: false, demote: false, remove: false };\n\nexport function teamMemberActions(actor: TeamMemberActor, target: TeamMemberTarget): TeamMemberActions {\n if (target.role === TEAM_CAPTAIN_ROLE) return NONE;\n const managesCoCaptains = actor === TEAM_MEMBER_ADMIN_ACTOR || actor === TEAM_CAPTAIN_ROLE;\n return {\n promote: managesCoCaptains && target.role === TEAM_PLAYER_ROLE && !target.isGhost,\n demote: managesCoCaptains && target.role === TEAM_CO_CAPTAIN_ROLE,\n remove: managesCoCaptains || target.role === TEAM_PLAYER_ROLE,\n };\n}\n"]}