@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":"rollup-check.js","sourceRoot":"","sources":["../../src/sports/rollup-check.ts"],"names":[],"mappings":"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","sourcesContent":["// Whether a game's stored numbers are what produced them: the rollup's counters and the score mirror\r\n// against a replay — of the event documents for a live game, of the finalized record for a completed\r\n// one. Finalize, reopen, a ghost link, and validation ask this one comparison, so a hand edit to the\r\n// game document is found the same way everywhere and never adopted or discarded silently (ENG-009).\r\n//\r\n// Counters are compared, never a derived field: a stored FP a weight change left stale is not a\r\n// difference in what was recorded, and the writer re-derives it.\r\n\r\nimport type { GameStats } from '../doc-schemas/sport-game.js';\r\nimport { gameScoreMirror } from './score-mirror.js';\r\nimport { isAllZeroLine } from './scoring.js';\r\nimport type { StatLineValues } from './scoring.js';\r\nimport type { AnySportAdapter } from './sport-adapter.js';\r\n\r\n/** One place a stored rollup or score mirror differs from the replay. */\r\nexport interface RollupDifference {\r\n readonly kind: 'rollupMismatch' | 'scoreMirrorMismatch';\r\n /** The team id of a team line or score, or the roster-entry id of a player line. */\r\n readonly key: string;\r\n readonly field: string;\r\n readonly stored: unknown;\r\n readonly replayed: unknown;\r\n}\r\n\r\n/** Two stored line values alike: a number, or a list of round slots, slot by slot. */\r\nfunction sameValue(left: unknown, right: unknown): boolean {\r\n if (Array.isArray(left) && Array.isArray(right)) {\r\n return left.length === right.length && left.every((slot, index) => Object.is(slot, right[index]));\r\n }\r\n return Object.is(left, right);\r\n}\r\n\r\n/** The fields a line is compared on: every field of a team line, the counters of a player line. */\r\nfunction compared(line: StatLineValues | undefined, fields: readonly string[] | null): Record<string, unknown> {\r\n if (line === undefined) return {};\r\n const keys = fields ?? Object.keys(line);\r\n return Object.fromEntries(keys.filter((field) => line[field] !== undefined).map((field) => [field, line[field]]));\r\n}\r\n\r\n/**\r\n * Every difference between the stored rollup and score mirror and the replay. A player line that\r\n * holds nothing and one that is absent are alike; a team line is compared whole, its round slots\r\n * included, for each of the game's teams.\r\n */\r\nexport function rollupDifferences(\r\n adapter: AnySportAdapter,\r\n game: { readonly teamIds: readonly string[]; readonly stats: GameStats; readonly score: Readonly<Record<string, number>> },\r\n replayed: GameStats,\r\n): RollupDifference[] {\r\n const differences: RollupDifference[] = [];\r\n const storedTeams = game.stats.teamStats as Readonly<Record<string, StatLineValues>>;\r\n const replayedTeams = replayed.teamStats as Readonly<Record<string, StatLineValues>>;\r\n const teamKeys = new Set([...game.teamIds, ...Object.keys(storedTeams)]);\r\n for (const teamId of teamKeys) {\r\n const stored = compared(storedTeams[teamId], null);\r\n const replay = compared(replayedTeams[teamId], null);\r\n for (const field of new Set([...Object.keys(stored), ...Object.keys(replay)])) {\r\n if (!sameValue(stored[field], replay[field])) {\r\n differences.push({ kind: 'rollupMismatch', key: teamId, field, stored: stored[field], replayed: replay[field] });\r\n }\r\n }\r\n }\r\n\r\n const counters = adapter.playerStatKeys as readonly string[];\r\n const storedPlayers = game.stats.playerStats as Readonly<Record<string, StatLineValues>>;\r\n const replayedPlayers = replayed.playerStats as Readonly<Record<string, StatLineValues>>;\r\n const holding = (lines: Readonly<Record<string, StatLineValues>>) =>\r\n Object.keys(lines).filter((entryId) => !isAllZeroLine(lines[entryId] as StatLineValues, counters));\r\n for (const entryId of new Set([...holding(storedPlayers), ...holding(replayedPlayers)])) {\r\n const stored = compared(storedPlayers[entryId], counters);\r\n const replay = compared(replayedPlayers[entryId], counters);\r\n for (const field of counters) {\r\n const [left, right] = [stored[field] ?? 0, replay[field] ?? 0];\r\n if (left !== right) differences.push({ kind: 'rollupMismatch', key: entryId, field, stored: left, replayed: right });\r\n }\r\n }\r\n\r\n const mirror = gameScoreMirror(replayed as { teamStats: Record<string, { score: number }> });\r\n for (const teamId of new Set([...Object.keys(game.score), ...Object.keys(mirror)])) {\r\n if (game.score[teamId] !== mirror[teamId]) {\r\n differences.push({\r\n kind: 'scoreMirrorMismatch',\r\n key: teamId,\r\n field: 'score',\r\n stored: game.score[teamId],\r\n replayed: mirror[teamId],\r\n });\r\n }\r\n }\r\n return differences;\r\n}\r\n"]}
1
+ {"version":3,"file":"rollup-check.js","sourceRoot":"","sources":["../../src/sports/rollup-check.ts"],"names":[],"mappings":"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","sourcesContent":["// Whether a game's stored numbers are what produced them: the rollup's counters and the score mirror\n// against a replay — of the event documents for a live game, of the finalized record for a completed\n// one. Finalize, reopen, a ghost link, and validation ask this one comparison, so a hand edit to the\n// game document is found the same way everywhere and never adopted or discarded silently (ENG-009).\n//\n// Counters are compared, never a derived field: a stored FP a weight change left stale is not a\n// difference in what was recorded, and the writer re-derives it.\n\nimport type { GameStats } from '../doc-schemas/sport-game.js';\nimport { gameScoreMirror } from './score-mirror.js';\nimport { isAllZeroLine } from './scoring.js';\nimport type { StatLineValues } from './scoring.js';\nimport type { AnySportAdapter } from './sport-adapter.js';\n\n/** One place a stored rollup or score mirror differs from the replay. */\nexport interface RollupDifference {\n readonly kind: 'rollupMismatch' | 'scoreMirrorMismatch';\n /** The team id of a team line or score, or the roster-entry id of a player line. */\n readonly key: string;\n readonly field: string;\n readonly stored: unknown;\n readonly replayed: unknown;\n}\n\n/** Two stored line values alike: a number, or a list of round slots, slot by slot. */\nfunction sameValue(left: unknown, right: unknown): boolean {\n if (Array.isArray(left) && Array.isArray(right)) {\n return left.length === right.length && left.every((slot, index) => Object.is(slot, right[index]));\n }\n return Object.is(left, right);\n}\n\n/** The fields a line is compared on: every field of a team line, the counters of a player line. */\nfunction compared(line: StatLineValues | undefined, fields: readonly string[] | null): Record<string, unknown> {\n if (line === undefined) return {};\n const keys = fields ?? Object.keys(line);\n return Object.fromEntries(keys.filter((field) => line[field] !== undefined).map((field) => [field, line[field]]));\n}\n\n/**\n * Every difference between the stored rollup and score mirror and the replay. A player line that\n * holds nothing and one that is absent are alike; a team line is compared whole, its round slots\n * included, for each of the game's teams.\n */\nexport function rollupDifferences(\n adapter: AnySportAdapter,\n game: { readonly teamIds: readonly string[]; readonly stats: GameStats; readonly score: Readonly<Record<string, number>> },\n replayed: GameStats,\n): RollupDifference[] {\n const differences: RollupDifference[] = [];\n const storedTeams = game.stats.teamStats as Readonly<Record<string, StatLineValues>>;\n const replayedTeams = replayed.teamStats as Readonly<Record<string, StatLineValues>>;\n const teamKeys = new Set([...game.teamIds, ...Object.keys(storedTeams)]);\n for (const teamId of teamKeys) {\n const stored = compared(storedTeams[teamId], null);\n const replay = compared(replayedTeams[teamId], null);\n for (const field of new Set([...Object.keys(stored), ...Object.keys(replay)])) {\n if (!sameValue(stored[field], replay[field])) {\n differences.push({ kind: 'rollupMismatch', key: teamId, field, stored: stored[field], replayed: replay[field] });\n }\n }\n }\n\n const counters = adapter.playerStatKeys as readonly string[];\n const storedPlayers = game.stats.playerStats as Readonly<Record<string, StatLineValues>>;\n const replayedPlayers = replayed.playerStats as Readonly<Record<string, StatLineValues>>;\n const holding = (lines: Readonly<Record<string, StatLineValues>>) =>\n Object.keys(lines).filter((entryId) => !isAllZeroLine(lines[entryId] as StatLineValues, counters));\n for (const entryId of new Set([...holding(storedPlayers), ...holding(replayedPlayers)])) {\n const stored = compared(storedPlayers[entryId], counters);\n const replay = compared(replayedPlayers[entryId], counters);\n for (const field of counters) {\n const [left, right] = [stored[field] ?? 0, replay[field] ?? 0];\n if (left !== right) differences.push({ kind: 'rollupMismatch', key: entryId, field, stored: left, replayed: right });\n }\n }\n\n const mirror = gameScoreMirror(replayed as { teamStats: Record<string, { score: number }> });\n for (const teamId of new Set([...Object.keys(game.score), ...Object.keys(mirror)])) {\n if (game.score[teamId] !== mirror[teamId]) {\n differences.push({\n kind: 'scoreMirrorMismatch',\n key: teamId,\n field: 'score',\n stored: game.score[teamId],\n replayed: mirror[teamId],\n });\n }\n }\n return differences;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"scoring.js","sourceRoot":"","sources":["../../src/sports/scoring.ts"],"names":[],"mappings":"AAAA,qGAAqG;AACrG,+FAA+F;AAC/F,4DAA4D;AAC5D,EAAE;AACF,qGAAqG;AACrG,kGAAkG;AAClG,oGAAoG;AACpG,2CAA2C;AAK3C,OAAO,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AA2BrF,MAAM,UAAU,UAAU,CAAC,KAAoB;IAC7C,OAAO,KAAK,CAAC,IAAI,KAAK,oBAAoB,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAoB;IACnD,OAAO,KAAK,CAAC,IAAI,KAAK,0BAA0B,CAAC;AACnD,CAAC;AAED,uGAAuG;AACvG,MAAM,UAAU,kBAAkB,CAChC,UAA6B,EAC7B,KAAgC;IAEhC,OAAO,KAAK,KAAK,SAAS,IAAI,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAChE,CAAC;AAoBD,MAAM,CAAC,MAAM,mBAAmB,GAAqB,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AAElE,MAAM,UAAU,eAAe,CAAC,IAA0B;IACxD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AAC7B,CAAC;AAMD,MAAM,CAAC,MAAM,kBAAkB,GAA0B,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AA6CzE,0FAA0F;AAC1F,MAAM,UAAU,aAAa,CAAC,IAAoB,EAAE,WAA8B;IAChF,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC,CAAC;AAChF,CAAC","sourcesContent":["// The scoring half of the sport adapter contract: the pure members the store driver, the replay, the\r\n// client's optimistic queue, and the aggregate cores call (SPORT-003). None issues a Firestore\r\n// operation or an increment, reads a clock, or mints an id.\r\n//\r\n// Every member is typed over every sport's shapes, so any registered adapter is called the same way.\r\n// The registry chose the adapter by the game's `leagueType`, which is what makes the rollup it is\r\n// handed that sport's (SPORT-002); an event of another sport is refused by `validate` and thrown on\r\n// by `apply` and `undo`, never guessed at.\r\n\r\nimport type { GameStatEvent } from '../doc-schemas/game-stats.js';\r\nimport type { GameStats } from '../doc-schemas/sport-game.js';\r\nimport type { StatCounterKey } from '../types/sports.js';\r\nimport { STAT_CORRECTION_EVENT_TYPE, STAT_VOID_EVENT_TYPE } from '../types/stats.js';\r\nimport type {\r\n FinalizationNotReadyReason,\r\n StatEventRecorderField,\r\n StatEventRefusalCode,\r\n StatEventSnapshotField,\r\n StatLineKind,\r\n} from '../types/stats.js';\r\nimport type { FinalizedGame, GameContribution } from './contribution.js';\r\nimport type { GameTeamIds } from './sport-adapter.js';\r\n\r\ntype DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;\r\n\r\n/**\r\n * What an event says happened — its type and its payload. Its id, its `recordedAt`, and what the\r\n * writing core stamps on it are not part of it, so a stored event, a queued one, and a drafted one\r\n * are all bodies.\r\n */\r\nexport type StatEventBody = DistributiveOmit<\r\n GameStatEvent,\r\n 'eventId' | 'recordedAt' | StatEventRecorderField | StatEventSnapshotField\r\n>;\r\nexport type StatVoidBody = Extract<StatEventBody, { type: typeof STAT_VOID_EVENT_TYPE }>;\r\nexport type StatCorrectionBody = Extract<StatEventBody, { type: typeof STAT_CORRECTION_EVENT_TYPE }>;\r\n/** One of a sport's own events, as opposed to the store-level void and correction. */\r\nexport type SportStatEventBody = Exclude<StatEventBody, StatVoidBody | StatCorrectionBody>;\r\n\r\nexport function isStatVoid(event: StatEventBody): event is StatVoidBody {\r\n return event.type === STAT_VOID_EVENT_TYPE;\r\n}\r\n\r\nexport function isStatCorrection(event: StatEventBody): event is StatCorrectionBody {\r\n return event.type === STAT_CORRECTION_EVENT_TYPE;\r\n}\r\n\r\n/** Whether `event` is one of the sport events `eventTypes` declares — never a void or a correction. */\r\nexport function isSportStatEventIn(\r\n eventTypes: readonly string[],\r\n event: StatEventBody | undefined,\r\n): event is SportStatEventBody {\r\n return event !== undefined && eventTypes.includes(event.type);\r\n}\r\n\r\n/** One team's line in a game's rollup, whichever sport it counts. */\r\nexport type GameTeamLine = GameStats['teamStats'][string];\r\n/** One roster entry's line in a game's rollup, whichever sport it counts. */\r\nexport type GamePlayerLine = GameStats['playerStats'][string];\r\n\r\n/** A stat line of any kind as `deriveLine` reads and returns it: its counters, derived fields, and ends. */\r\nexport type StatLineValues = { readonly [field: string]: number | readonly (number | null)[] };\r\n\r\n/** The game an event is validated against: its teams, and the roster entries the batch names. */\r\nexport interface StatEventRoster {\r\n /** A team of the game is one of these — never inferred from which team lines a rollup holds. */\r\n readonly teamIds: readonly string[];\r\n /** The roster entries the driver read for the batch — never the whole roster. */\r\n readonly rosterPlayerIds: ReadonlySet<string>;\r\n}\r\n\r\nexport type StatEventVerdict = { readonly ok: true } | { readonly ok: false; readonly code: StatEventRefusalCode };\r\n\r\nexport const STAT_EVENT_ACCEPTED: StatEventVerdict = { ok: true };\r\n\r\nexport function refuseStatEvent(code: StatEventRefusalCode): StatEventVerdict {\r\n return { ok: false, code };\r\n}\r\n\r\nexport type FinalizationReadiness =\r\n | { readonly ready: true }\r\n | { readonly ready: false; readonly reason: FinalizationNotReadyReason };\r\n\r\nexport const FINALIZATION_READY: FinalizationReadiness = { ready: true };\r\n\r\n/** The fields a `STAT_CORRECTION` may change, on a team line and on a player line. */\r\nexport interface CorrectableFields {\r\n readonly team: readonly StatCounterKey[];\r\n readonly player: readonly StatCounterKey[];\r\n}\r\n\r\nexport interface SportScoring {\r\n /**\r\n * Whether a new event may be recorded against `state`: its type is the sport's, its team is one\r\n * of the game's, every person it names is a roster entry of that team, and its values are a real\r\n * result. Only the store driver calls it, and only for a new event — a replay never re-validates\r\n * what was already accepted.\r\n */\r\n readonly validate: (state: GameStats, event: StatEventBody, roster: StatEventRoster) => StatEventVerdict;\r\n /**\r\n * `state` with one sport event or one `STAT_CORRECTION` applied. A missing line is created from\r\n * the sport's empty line (SPORT-105); a player line whose counters all return to zero is dropped;\r\n * a team line never is. Throws on a void, another sport's event, or a correction to a field not in\r\n * `correctableFields`.\r\n */\r\n readonly apply: (state: GameStats, event: StatEventBody) => GameStats;\r\n /** The exact inverse of `apply(target)` for one of the sport's own events (SPORT-103). */\r\n readonly undo: (state: GameStats, target: StatEventBody) => GameStats;\r\n /** `line` with every derived field of its kind; the only caller of any derivation formula. */\r\n readonly deriveLine: (kind: StatLineKind, line: StatLineValues) => StatLineValues;\r\n readonly correctableFields: CorrectableFields;\r\n /** The next round to record for a sport played in rounds, `null` once all are or for any other sport. */\r\n readonly nextRound: (state: GameStats) => number | null;\r\n /** Whether the game may be finalized (SPORT-202). */\r\n readonly finalizationReadiness: (state: GameStats, game: GameTeamIds) => FinalizationReadiness;\r\n /**\r\n * The games-played decision finalize stores: the roster entries credited no game played, from\r\n * each entry's own line and absence, in roster order (SPORT-204).\r\n */\r\n readonly participation: (\r\n state: GameStats,\r\n rosterPlayerIds: readonly string[],\r\n absentRosterPlayerIds: readonly string[],\r\n ) => string[];\r\n /** What a completed game adds to every derived destination (SPORT-207). */\r\n readonly contribution: (game: FinalizedGame) => GameContribution;\r\n}\r\n\r\n/** Whether every counter of a line is zero — a player line the rollup then leaves out. */\r\nexport function isAllZeroLine(line: StatLineValues, counterKeys: readonly string[]): boolean {\r\n return counterKeys.every((key) => line[key] === 0 || line[key] === undefined);\r\n}\r\n"]}
1
+ {"version":3,"file":"scoring.js","sourceRoot":"","sources":["../../src/sports/scoring.ts"],"names":[],"mappings":"AAAA,qGAAqG;AACrG,+FAA+F;AAC/F,4DAA4D;AAC5D,EAAE;AACF,qGAAqG;AACrG,kGAAkG;AAClG,oGAAoG;AACpG,2CAA2C;AAK3C,OAAO,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AA2BrF,MAAM,UAAU,UAAU,CAAC,KAAoB;IAC7C,OAAO,KAAK,CAAC,IAAI,KAAK,oBAAoB,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAoB;IACnD,OAAO,KAAK,CAAC,IAAI,KAAK,0BAA0B,CAAC;AACnD,CAAC;AAED,uGAAuG;AACvG,MAAM,UAAU,kBAAkB,CAChC,UAA6B,EAC7B,KAAgC;IAEhC,OAAO,KAAK,KAAK,SAAS,IAAI,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAChE,CAAC;AAoBD,MAAM,CAAC,MAAM,mBAAmB,GAAqB,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AAElE,MAAM,UAAU,eAAe,CAAC,IAA0B;IACxD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AAC7B,CAAC;AAMD,MAAM,CAAC,MAAM,kBAAkB,GAA0B,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AA6CzE,0FAA0F;AAC1F,MAAM,UAAU,aAAa,CAAC,IAAoB,EAAE,WAA8B;IAChF,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC,CAAC;AAChF,CAAC","sourcesContent":["// The scoring half of the sport adapter contract: the pure members the store driver, the replay, the\n// client's optimistic queue, and the aggregate cores call (SPORT-003). None issues a Firestore\n// operation or an increment, reads a clock, or mints an id.\n//\n// Every member is typed over every sport's shapes, so any registered adapter is called the same way.\n// The registry chose the adapter by the game's `leagueType`, which is what makes the rollup it is\n// handed that sport's (SPORT-002); an event of another sport is refused by `validate` and thrown on\n// by `apply` and `undo`, never guessed at.\n\nimport type { GameStatEvent } from '../doc-schemas/game-stats.js';\nimport type { GameStats } from '../doc-schemas/sport-game.js';\nimport type { StatCounterKey } from '../types/sports.js';\nimport { STAT_CORRECTION_EVENT_TYPE, STAT_VOID_EVENT_TYPE } from '../types/stats.js';\nimport type {\n FinalizationNotReadyReason,\n StatEventRecorderField,\n StatEventRefusalCode,\n StatEventSnapshotField,\n StatLineKind,\n} from '../types/stats.js';\nimport type { FinalizedGame, GameContribution } from './contribution.js';\nimport type { GameTeamIds } from './sport-adapter.js';\n\ntype DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;\n\n/**\n * What an event says happened — its type and its payload. Its id, its `recordedAt`, and what the\n * writing core stamps on it are not part of it, so a stored event, a queued one, and a drafted one\n * are all bodies.\n */\nexport type StatEventBody = DistributiveOmit<\n GameStatEvent,\n 'eventId' | 'recordedAt' | StatEventRecorderField | StatEventSnapshotField\n>;\nexport type StatVoidBody = Extract<StatEventBody, { type: typeof STAT_VOID_EVENT_TYPE }>;\nexport type StatCorrectionBody = Extract<StatEventBody, { type: typeof STAT_CORRECTION_EVENT_TYPE }>;\n/** One of a sport's own events, as opposed to the store-level void and correction. */\nexport type SportStatEventBody = Exclude<StatEventBody, StatVoidBody | StatCorrectionBody>;\n\nexport function isStatVoid(event: StatEventBody): event is StatVoidBody {\n return event.type === STAT_VOID_EVENT_TYPE;\n}\n\nexport function isStatCorrection(event: StatEventBody): event is StatCorrectionBody {\n return event.type === STAT_CORRECTION_EVENT_TYPE;\n}\n\n/** Whether `event` is one of the sport events `eventTypes` declares — never a void or a correction. */\nexport function isSportStatEventIn(\n eventTypes: readonly string[],\n event: StatEventBody | undefined,\n): event is SportStatEventBody {\n return event !== undefined && eventTypes.includes(event.type);\n}\n\n/** One team's line in a game's rollup, whichever sport it counts. */\nexport type GameTeamLine = GameStats['teamStats'][string];\n/** One roster entry's line in a game's rollup, whichever sport it counts. */\nexport type GamePlayerLine = GameStats['playerStats'][string];\n\n/** A stat line of any kind as `deriveLine` reads and returns it: its counters, derived fields, and ends. */\nexport type StatLineValues = { readonly [field: string]: number | readonly (number | null)[] };\n\n/** The game an event is validated against: its teams, and the roster entries the batch names. */\nexport interface StatEventRoster {\n /** A team of the game is one of these — never inferred from which team lines a rollup holds. */\n readonly teamIds: readonly string[];\n /** The roster entries the driver read for the batch — never the whole roster. */\n readonly rosterPlayerIds: ReadonlySet<string>;\n}\n\nexport type StatEventVerdict = { readonly ok: true } | { readonly ok: false; readonly code: StatEventRefusalCode };\n\nexport const STAT_EVENT_ACCEPTED: StatEventVerdict = { ok: true };\n\nexport function refuseStatEvent(code: StatEventRefusalCode): StatEventVerdict {\n return { ok: false, code };\n}\n\nexport type FinalizationReadiness =\n | { readonly ready: true }\n | { readonly ready: false; readonly reason: FinalizationNotReadyReason };\n\nexport const FINALIZATION_READY: FinalizationReadiness = { ready: true };\n\n/** The fields a `STAT_CORRECTION` may change, on a team line and on a player line. */\nexport interface CorrectableFields {\n readonly team: readonly StatCounterKey[];\n readonly player: readonly StatCounterKey[];\n}\n\nexport interface SportScoring {\n /**\n * Whether a new event may be recorded against `state`: its type is the sport's, its team is one\n * of the game's, every person it names is a roster entry of that team, and its values are a real\n * result. Only the store driver calls it, and only for a new event — a replay never re-validates\n * what was already accepted.\n */\n readonly validate: (state: GameStats, event: StatEventBody, roster: StatEventRoster) => StatEventVerdict;\n /**\n * `state` with one sport event or one `STAT_CORRECTION` applied. A missing line is created from\n * the sport's empty line (SPORT-105); a player line whose counters all return to zero is dropped;\n * a team line never is. Throws on a void, another sport's event, or a correction to a field not in\n * `correctableFields`.\n */\n readonly apply: (state: GameStats, event: StatEventBody) => GameStats;\n /** The exact inverse of `apply(target)` for one of the sport's own events (SPORT-103). */\n readonly undo: (state: GameStats, target: StatEventBody) => GameStats;\n /** `line` with every derived field of its kind; the only caller of any derivation formula. */\n readonly deriveLine: (kind: StatLineKind, line: StatLineValues) => StatLineValues;\n readonly correctableFields: CorrectableFields;\n /** The next round to record for a sport played in rounds, `null` once all are or for any other sport. */\n readonly nextRound: (state: GameStats) => number | null;\n /** Whether the game may be finalized (SPORT-202). */\n readonly finalizationReadiness: (state: GameStats, game: GameTeamIds) => FinalizationReadiness;\n /**\n * The games-played decision finalize stores: the roster entries credited no game played, from\n * each entry's own line and absence, in roster order (SPORT-204).\n */\n readonly participation: (\n state: GameStats,\n rosterPlayerIds: readonly string[],\n absentRosterPlayerIds: readonly string[],\n ) => string[];\n /** What a completed game adds to every derived destination (SPORT-207). */\n readonly contribution: (game: FinalizedGame) => GameContribution;\n}\n\n/** Whether every counter of a line is zero — a player line the rollup then leaves out. */\nexport function isAllZeroLine(line: StatLineValues, counterKeys: readonly string[]): boolean {\n return counterKeys.every((key) => line[key] === 0 || line[key] === undefined);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"store-driver.js","sourceRoot":"","sources":["../../src/sports/store-driver.ts"],"names":[],"mappings":"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","sourcesContent":["// The store driver's decision half: what one scoring write does with the batch of events it was\r\n// sent, given what the game already holds. It owns ids, the sequence, void resolution, and the fold;\r\n// everything sport-shaped is the adapter's (SPORT-003). The writing core reads the game, the stored\r\n// events the batch names, and the roster entries it names, hands them here, and writes what comes\r\n// back — one transaction, all or nothing.\r\n//\r\n// An event id is the idempotency key (SPORT-102): an id already stored, or earlier in the batch, is\r\n// acknowledged when its content matches and never applied again, so re-sending a batch whose first\r\n// send timed out after committing changes nothing. A void's id is derived from its target, so a\r\n// second undo of one event is the same document and is acknowledged too.\r\n\r\nimport type { GameStatEvent } from '../doc-schemas/game-stats.js';\r\nimport type { GameStats } from '../doc-schemas/sport-game.js';\r\nimport { buildStatVoidId } from '../ids/stat-event-ids.js';\r\nimport type { StatEventSubmission } from '../schemas/scoring.js';\r\nimport { STAT_EVENT_RECORDER_FIELD_KEYS, STAT_EVENT_SNAPSHOT_FIELD_KEYS } from '../types/stats.js';\r\nimport type { RecordedStatEventOutcome, StatEventRefusalCode } from '../types/stats.js';\r\nimport { foldStatEvent, withGameTeamLines } from './replay.js';\r\nimport {\r\n isSportStatEventIn,\r\n isStatCorrection,\r\n isStatVoid,\r\n type StatEventBody,\r\n type StatEventRoster,\r\n} from './scoring.js';\r\nimport type { AnySportAdapter, GameTeamIds } from './sport-adapter.js';\r\n\r\n/** The document id a submission is stored at. */\r\nexport function statEventSubmissionId(submission: StatEventSubmission): string {\r\n return isStatVoid(submission) ? buildStatVoidId(submission.targetEventId) : submission.eventId;\r\n}\r\n\r\n/**\r\n * The ids a batch reads before it decides anything: every submission's own id, and every void's\r\n * target — the driver needs the stored document at each, or knows none is stored. A correction's is\r\n * never read: the driver refuses one before it looks at the store.\r\n */\r\nexport function statEventIdsToRead(submissions: readonly StatEventSubmission[]): string[] {\r\n const ids = new Set<string>();\r\n for (const submission of submissions) {\r\n if (isStatCorrection(submission)) continue;\r\n ids.add(statEventSubmissionId(submission));\r\n if (isStatVoid(submission)) ids.add(submission.targetEventId);\r\n }\r\n return [...ids];\r\n}\r\n\r\n/** An event the batch stores now: its id, its place in the replay, and every field its caller sent. */\r\nexport interface AcceptedStatEvent {\r\n readonly eventId: string;\r\n readonly sequence: number;\r\n /** The document's fields but the recorder facts, which the writing core stamps (ARCH-106). */\r\n readonly fields: Readonly<Record<string, unknown>>;\r\n}\r\n\r\nexport interface RecordedStatEventResult {\r\n readonly eventId: string;\r\n readonly outcome: RecordedStatEventOutcome;\r\n readonly sequence: number;\r\n}\r\n\r\nexport type StatEventBatchDecision =\r\n | {\r\n readonly ok: true;\r\n /** The rollup after the batch — unchanged when every event was already recorded. */\r\n readonly stats: GameStats;\r\n readonly statEventSequence: number;\r\n /** Each submission's result, in the order sent. */\r\n readonly results: readonly RecordedStatEventResult[];\r\n /** The events to create, in sequence order; empty when every event was already recorded. */\r\n readonly accepted: readonly AcceptedStatEvent[];\r\n }\r\n | {\r\n readonly ok: false;\r\n readonly code: StatEventRefusalCode;\r\n /** The first submission refused; nothing of the batch is written. */\r\n readonly eventId: string;\r\n };\r\n\r\nexport interface StatEventBatch {\r\n readonly adapter: AnySportAdapter;\r\n readonly game: GameTeamIds & { readonly stats: GameStats; readonly statEventSequence: number };\r\n /** The game's teams and the roster entries the batch names that exist. */\r\n readonly roster: StatEventRoster;\r\n readonly submissions: readonly StatEventSubmission[];\r\n /** The stored event at an id `statEventIdsToRead` named, or `undefined` where none is stored. */\r\n readonly storedEvent: (eventId: string) => GameStatEvent | undefined;\r\n}\r\n\r\n// The fields of a stored event the writing core derives, which a caller's re-send never carries.\r\nconst DERIVED_FIELDS: ReadonlySet<string> = new Set([...STAT_EVENT_RECORDER_FIELD_KEYS, ...STAT_EVENT_SNAPSHOT_FIELD_KEYS]);\r\n\r\nfunction callerFields(event: Readonly<Record<string, unknown>>): Record<string, unknown> {\r\n return Object.fromEntries(Object.entries(event).filter(([field, value]) => !DERIVED_FIELDS.has(field) && value !== undefined));\r\n}\r\n\r\nfunction sameFields(left: Readonly<Record<string, unknown>>, right: Readonly<Record<string, unknown>>): boolean {\r\n const leftFields = callerFields(left);\r\n const rightFields = callerFields(right);\r\n const keys = Object.keys(leftFields);\r\n return keys.length === Object.keys(rightFields).length && keys.every((key) => Object.is(leftFields[key], rightFields[key]));\r\n}\r\n\r\n/**\r\n * Whether a submission is the event already held at its id. A void is the same void when it names\r\n * the same target; a sport event only when every field its caller supplies is the same.\r\n */\r\nfunction isSameEvent(submission: StatEventSubmission, held: Readonly<Record<string, unknown>>): boolean {\r\n if (isStatVoid(submission)) return isStatVoid(held as StatEventBody) && held.targetEventId === submission.targetEventId;\r\n return sameFields(submission as Readonly<Record<string, unknown>>, held);\r\n}\r\n\r\n/** Whether any counter, or any recorded round, of any line is below zero. */\r\nfunction holdsNegativeValue(stats: GameStats): boolean {\r\n const lines = [...Object.values(stats.teamStats), ...Object.values(stats.playerStats)] as Record<string, unknown>[];\r\n return lines.some((line) =>\r\n Object.values(line).some((value) =>\r\n Array.isArray(value)\r\n ? value.some((slot) => typeof slot === 'number' && slot < 0)\r\n : typeof value === 'number' && value < 0,\r\n ),\r\n );\r\n}\r\n\r\n/** The stored fields of a new event: the void's derived id, or the caller's own id and fields. */\r\nfunction acceptedFields(submission: StatEventSubmission, eventId: string, sequence: number): Record<string, unknown> {\r\n return { ...callerFields(submission as Readonly<Record<string, unknown>>), eventId, sequence };\r\n}\r\n\r\n/**\r\n * Decide one scoring write. Every submission in order, against a running rollup that starts from\r\n * the stored one with a team line for each of the game's teams:\r\n *\r\n * - a correction → refused; only the game-reconcile core appends one;\r\n * - an id already stored, or earlier in the batch → acknowledged when it is the same event, and\r\n * refused (`eventIdConflict`) when it is not;\r\n * - a new sport event → the adapter validates it and applies it, and it takes the next sequence;\r\n * - a new void → its target, stored or earlier in the batch, must be one of the sport's events; the\r\n * adapter undoes it, and it takes the next sequence. A void that would leave a stat below zero —\r\n * the target's field moved down by a later edit — is refused (`undoBelowZero`).\r\n *\r\n * The first refusal refuses the whole batch.\r\n */\r\nexport function decideStatEventBatch(batch: StatEventBatch): StatEventBatchDecision {\r\n const { adapter, game, roster, submissions, storedEvent } = batch;\r\n let stats = withGameTeamLines(adapter, game.stats, game);\r\n let sequence = game.statEventSequence;\r\n const results: RecordedStatEventResult[] = [];\r\n const accepted: AcceptedStatEvent[] = [];\r\n // Every event the batch has placed so far — stored or new — by id, with the sequence it holds.\r\n const placed = new Map<string, { readonly event: Readonly<Record<string, unknown>>; readonly sequence: number }>();\r\n\r\n for (const submission of submissions) {\r\n const eventId = statEventSubmissionId(submission);\r\n const refuse = (code: StatEventRefusalCode): StatEventBatchDecision => ({ ok: false, code, eventId });\r\n\r\n if (isStatCorrection(submission)) return refuse('correctionNotAccepted');\r\n\r\n const held = placed.get(eventId) ?? (() => {\r\n const stored = storedEvent(eventId);\r\n return stored === undefined ? undefined : { event: stored as Readonly<Record<string, unknown>>, sequence: stored.sequence };\r\n })();\r\n if (held !== undefined) {\r\n if (!isSameEvent(submission, held.event)) return refuse('eventIdConflict');\r\n placed.set(eventId, held);\r\n results.push({ eventId, outcome: 'alreadyRecorded', sequence: held.sequence });\r\n continue;\r\n }\r\n\r\n if (isStatVoid(submission)) {\r\n const target = placed.get(submission.targetEventId)?.event ?? storedEvent(submission.targetEventId);\r\n if (!isSportStatEventIn(adapter.eventTypes, target as StatEventBody | undefined)) {\r\n return refuse('voidTargetNotSportEvent');\r\n }\r\n const next = foldStatEvent(adapter, stats, submission, () => target as StatEventBody);\r\n if (holdsNegativeValue(next)) return refuse('undoBelowZero');\r\n stats = next;\r\n } else {\r\n const verdict = adapter.validate(stats, submission, roster);\r\n if (!verdict.ok) return refuse(verdict.code);\r\n stats = foldStatEvent(adapter, stats, submission, () => undefined);\r\n }\r\n\r\n sequence += 1;\r\n const fields = acceptedFields(submission, eventId, sequence);\r\n accepted.push({ eventId, sequence, fields });\r\n placed.set(eventId, { event: fields, sequence });\r\n results.push({ eventId, outcome: 'recorded', sequence });\r\n }\r\n\r\n return { ok: true, stats, statEventSequence: sequence, results, accepted };\r\n}\r\n"]}
1
+ {"version":3,"file":"store-driver.js","sourceRoot":"","sources":["../../src/sports/store-driver.ts"],"names":[],"mappings":"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","sourcesContent":["// The store driver's decision half: what one scoring write does with the batch of events it was\n// sent, given what the game already holds. It owns ids, the sequence, void resolution, and the fold;\n// everything sport-shaped is the adapter's (SPORT-003). The writing core reads the game, the stored\n// events the batch names, and the roster entries it names, hands them here, and writes what comes\n// back — one transaction, all or nothing.\n//\n// An event id is the idempotency key (SPORT-102): an id already stored, or earlier in the batch, is\n// acknowledged when its content matches and never applied again, so re-sending a batch whose first\n// send timed out after committing changes nothing. A void's id is derived from its target, so a\n// second undo of one event is the same document and is acknowledged too.\n\nimport type { GameStatEvent } from '../doc-schemas/game-stats.js';\nimport type { GameStats } from '../doc-schemas/sport-game.js';\nimport { buildStatVoidId } from '../ids/stat-event-ids.js';\nimport type { StatEventSubmission } from '../schemas/scoring.js';\nimport { STAT_EVENT_RECORDER_FIELD_KEYS, STAT_EVENT_SNAPSHOT_FIELD_KEYS } from '../types/stats.js';\nimport type { RecordedStatEventOutcome, StatEventRefusalCode } from '../types/stats.js';\nimport { foldStatEvent, withGameTeamLines } from './replay.js';\nimport {\n isSportStatEventIn,\n isStatCorrection,\n isStatVoid,\n type StatEventBody,\n type StatEventRoster,\n} from './scoring.js';\nimport type { AnySportAdapter, GameTeamIds } from './sport-adapter.js';\n\n/** The document id a submission is stored at. */\nexport function statEventSubmissionId(submission: StatEventSubmission): string {\n return isStatVoid(submission) ? buildStatVoidId(submission.targetEventId) : submission.eventId;\n}\n\n/**\n * The ids a batch reads before it decides anything: every submission's own id, and every void's\n * target — the driver needs the stored document at each, or knows none is stored. A correction's is\n * never read: the driver refuses one before it looks at the store.\n */\nexport function statEventIdsToRead(submissions: readonly StatEventSubmission[]): string[] {\n const ids = new Set<string>();\n for (const submission of submissions) {\n if (isStatCorrection(submission)) continue;\n ids.add(statEventSubmissionId(submission));\n if (isStatVoid(submission)) ids.add(submission.targetEventId);\n }\n return [...ids];\n}\n\n/** An event the batch stores now: its id, its place in the replay, and every field its caller sent. */\nexport interface AcceptedStatEvent {\n readonly eventId: string;\n readonly sequence: number;\n /** The document's fields but the recorder facts, which the writing core stamps (ARCH-106). */\n readonly fields: Readonly<Record<string, unknown>>;\n}\n\nexport interface RecordedStatEventResult {\n readonly eventId: string;\n readonly outcome: RecordedStatEventOutcome;\n readonly sequence: number;\n}\n\nexport type StatEventBatchDecision =\n | {\n readonly ok: true;\n /** The rollup after the batch — unchanged when every event was already recorded. */\n readonly stats: GameStats;\n readonly statEventSequence: number;\n /** Each submission's result, in the order sent. */\n readonly results: readonly RecordedStatEventResult[];\n /** The events to create, in sequence order; empty when every event was already recorded. */\n readonly accepted: readonly AcceptedStatEvent[];\n }\n | {\n readonly ok: false;\n readonly code: StatEventRefusalCode;\n /** The first submission refused; nothing of the batch is written. */\n readonly eventId: string;\n };\n\nexport interface StatEventBatch {\n readonly adapter: AnySportAdapter;\n readonly game: GameTeamIds & { readonly stats: GameStats; readonly statEventSequence: number };\n /** The game's teams and the roster entries the batch names that exist. */\n readonly roster: StatEventRoster;\n readonly submissions: readonly StatEventSubmission[];\n /** The stored event at an id `statEventIdsToRead` named, or `undefined` where none is stored. */\n readonly storedEvent: (eventId: string) => GameStatEvent | undefined;\n}\n\n// The fields of a stored event the writing core derives, which a caller's re-send never carries.\nconst DERIVED_FIELDS: ReadonlySet<string> = new Set([...STAT_EVENT_RECORDER_FIELD_KEYS, ...STAT_EVENT_SNAPSHOT_FIELD_KEYS]);\n\nfunction callerFields(event: Readonly<Record<string, unknown>>): Record<string, unknown> {\n return Object.fromEntries(Object.entries(event).filter(([field, value]) => !DERIVED_FIELDS.has(field) && value !== undefined));\n}\n\nfunction sameFields(left: Readonly<Record<string, unknown>>, right: Readonly<Record<string, unknown>>): boolean {\n const leftFields = callerFields(left);\n const rightFields = callerFields(right);\n const keys = Object.keys(leftFields);\n return keys.length === Object.keys(rightFields).length && keys.every((key) => Object.is(leftFields[key], rightFields[key]));\n}\n\n/**\n * Whether a submission is the event already held at its id. A void is the same void when it names\n * the same target; a sport event only when every field its caller supplies is the same.\n */\nfunction isSameEvent(submission: StatEventSubmission, held: Readonly<Record<string, unknown>>): boolean {\n if (isStatVoid(submission)) return isStatVoid(held as StatEventBody) && held.targetEventId === submission.targetEventId;\n return sameFields(submission as Readonly<Record<string, unknown>>, held);\n}\n\n/** Whether any counter, or any recorded round, of any line is below zero. */\nfunction holdsNegativeValue(stats: GameStats): boolean {\n const lines = [...Object.values(stats.teamStats), ...Object.values(stats.playerStats)] as Record<string, unknown>[];\n return lines.some((line) =>\n Object.values(line).some((value) =>\n Array.isArray(value)\n ? value.some((slot) => typeof slot === 'number' && slot < 0)\n : typeof value === 'number' && value < 0,\n ),\n );\n}\n\n/** The stored fields of a new event: the void's derived id, or the caller's own id and fields. */\nfunction acceptedFields(submission: StatEventSubmission, eventId: string, sequence: number): Record<string, unknown> {\n return { ...callerFields(submission as Readonly<Record<string, unknown>>), eventId, sequence };\n}\n\n/**\n * Decide one scoring write. Every submission in order, against a running rollup that starts from\n * the stored one with a team line for each of the game's teams:\n *\n * - a correction → refused; only the game-reconcile core appends one;\n * - an id already stored, or earlier in the batch → acknowledged when it is the same event, and\n * refused (`eventIdConflict`) when it is not;\n * - a new sport event → the adapter validates it and applies it, and it takes the next sequence;\n * - a new void → its target, stored or earlier in the batch, must be one of the sport's events; the\n * adapter undoes it, and it takes the next sequence. A void that would leave a stat below zero —\n * the target's field moved down by a later edit — is refused (`undoBelowZero`).\n *\n * The first refusal refuses the whole batch.\n */\nexport function decideStatEventBatch(batch: StatEventBatch): StatEventBatchDecision {\n const { adapter, game, roster, submissions, storedEvent } = batch;\n let stats = withGameTeamLines(adapter, game.stats, game);\n let sequence = game.statEventSequence;\n const results: RecordedStatEventResult[] = [];\n const accepted: AcceptedStatEvent[] = [];\n // Every event the batch has placed so far — stored or new — by id, with the sequence it holds.\n const placed = new Map<string, { readonly event: Readonly<Record<string, unknown>>; readonly sequence: number }>();\n\n for (const submission of submissions) {\n const eventId = statEventSubmissionId(submission);\n const refuse = (code: StatEventRefusalCode): StatEventBatchDecision => ({ ok: false, code, eventId });\n\n if (isStatCorrection(submission)) return refuse('correctionNotAccepted');\n\n const held = placed.get(eventId) ?? (() => {\n const stored = storedEvent(eventId);\n return stored === undefined ? undefined : { event: stored as Readonly<Record<string, unknown>>, sequence: stored.sequence };\n })();\n if (held !== undefined) {\n if (!isSameEvent(submission, held.event)) return refuse('eventIdConflict');\n placed.set(eventId, held);\n results.push({ eventId, outcome: 'alreadyRecorded', sequence: held.sequence });\n continue;\n }\n\n if (isStatVoid(submission)) {\n const target = placed.get(submission.targetEventId)?.event ?? storedEvent(submission.targetEventId);\n if (!isSportStatEventIn(adapter.eventTypes, target as StatEventBody | undefined)) {\n return refuse('voidTargetNotSportEvent');\n }\n const next = foldStatEvent(adapter, stats, submission, () => target as StatEventBody);\n if (holdsNegativeValue(next)) return refuse('undoBelowZero');\n stats = next;\n } else {\n const verdict = adapter.validate(stats, submission, roster);\n if (!verdict.ok) return refuse(verdict.code);\n stats = foldStatEvent(adapter, stats, submission, () => undefined);\n }\n\n sequence += 1;\n const fields = acceptedFields(submission, eventId, sequence);\n accepted.push({ eventId, sequence, fields });\n placed.set(eventId, { event: fields, sequence });\n results.push({ eventId, outcome: 'recorded', sequence });\n }\n\n return { ok: true, stats, statEventSequence: sequence, results, accepted };\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import type { SystemRole } from './unions.js';
2
2
  import { CAPTAIN_TEAM_ROLES } from './unions.js';
3
- export declare const AUDIT_EVENT_TYPE_KEYS: readonly ["user.accountRegistered", "user.displayNameChanged", "user.abandonedRegistrationDeleted", "user.accountBanned", "user.accountUnbanned", "admin.systemRoleGranted", "admin.systemRoleRevoked", "admin.systemRoleClaimSynced", "appConfig.updated", "landingPage.updated", "league.created", "league.updated", "league.statusChanged", "league.modeSwitched", "waiverTemplate.created", "waiverTemplate.versionPublished", "waiverTemplate.retired", "waiverTemplate.deleted", "gameLocation.created", "gameLocation.updated", "gameLocation.deleted", "picture.added", "picture.replaced", "picture.moved", "picture.featuredChanged", "picture.removed", "award.created", "award.badgeReplaced", "award.deleted", "award.granted", "award.revoked", "hardwareLoanContract.created", "hardwareLoanContract.signed", "hardwareLoanContract.returned", "hardwareLoanContract.deleted", "leagueTeam.created", "ghostPlayer.created", "teamRoster.syncRetried", "teamMembership.roleChanged", "leagueTeam.captainChanged", "leagueTeam.updated", "leagueTeamContact.updated", "teamMembership.removed", "ghostPlayer.removed", "ghostPlayer.linked", "ghostPlayer.closedLeagueLinked", "leagueTeam.deleted", "ghostPlayer.removalStopped", "teamInvitation.created", "teamInvitation.cancelled", "teamInvitation.accepted", "teamInvitation.rejected", "teamJoinRequest.created", "teamJoinRequest.cancelled", "teamJoinRequest.accepted", "teamJoinRequest.rejected", "leagueFreeAgent.created", "leagueFreeAgent.deleted", "leagueFreeAgent.assigned", "signedWaiver.created", "scheduleWeek.created", "scheduleWeek.updated", "sportGame.created", "sportGame.updated", "sportGame.deleted", "sportGame.statEventsRejected", "sportGame.absenceChanged", "sportGame.memoUpdated", "sportGame.jumpBallChanged", "sportGame.finalized", "sportGame.reopened", "sportGame.statsCorrected", "sportGame.statsRecalculated", "stats.aggregateRecalculated", "stats.historicalStatsUpdated", "stats.validationRun", "stats.validationReportDeleted", "stats.unrosteredStatLineResolved", "adminNotification.archived", "adminNotification.archiveAllRequested", "system.manualIntervention", "migrationAction.pageRun", "migrationAction.controlRecorded", "migrationAction.checkpointRecorded", "migrationConsole.cutoverRecorded"];
3
+ export declare const AUDIT_EVENT_TYPE_KEYS: readonly ["user.accountRegistered", "user.displayNameChanged", "user.abandonedRegistrationDeleted", "user.accountBanned", "user.accountUnbanned", "admin.systemRoleGranted", "admin.systemRoleRevoked", "admin.systemRoleClaimSynced", "appConfig.updated", "landingPage.updated", "league.created", "league.updated", "league.statusChanged", "league.modeSwitched", "waiverTemplate.created", "waiverTemplate.versionPublished", "waiverTemplate.retired", "waiverTemplate.deleted", "gameLocation.created", "gameLocation.updated", "gameLocation.deleted", "picture.added", "picture.replaced", "picture.moved", "picture.featuredChanged", "picture.removed", "award.created", "award.badgeReplaced", "award.deleted", "award.granted", "award.revoked", "hardwareLoanContract.created", "hardwareLoanContract.signed", "hardwareLoanContract.returned", "hardwareLoanContract.deleted", "leagueTeam.created", "ghostPlayer.created", "teamRoster.syncRetried", "teamMembership.roleChanged", "leagueTeam.captainChanged", "leagueTeam.updated", "leagueTeamContact.updated", "teamMembership.removed", "ghostPlayer.removed", "ghostPlayer.linked", "ghostPlayer.closedLeagueLinked", "leagueTeam.deleted", "ghostPlayer.removalStopped", "teamInvitation.created", "teamInvitation.cancelled", "teamInvitation.accepted", "teamInvitation.rejected", "teamJoinRequest.created", "teamJoinRequest.cancelled", "teamJoinRequest.accepted", "teamJoinRequest.rejected", "leagueFreeAgent.created", "leagueFreeAgent.deleted", "leagueFreeAgent.assigned", "signedWaiver.created", "scheduleWeek.created", "scheduleWeek.updated", "sportGame.created", "sportGame.updated", "sportGame.deleted", "sportGame.statEventsRejected", "sportGame.absenceChanged", "sportGame.memoUpdated", "sportGame.jumpBallChanged", "sportGame.finalized", "sportGame.reopened", "sportGame.statsCorrected", "sportGame.statsRecalculated", "stats.aggregateRecalculated", "stats.historicalStatsUpdated", "stats.validationRun", "stats.validationReportDeleted", "stats.unrosteredStatLineResolved", "adminNotification.archived", "adminNotification.archiveAllRequested", "system.manualIntervention", "migrationAction.pageRun", "migrationAction.writesCommitted", "migrationAction.controlRecorded", "migrationAction.checkpointRecorded", "migrationConsole.cutoverRecorded"];
4
4
  export type AuditEventType = (typeof AUDIT_EVENT_TYPE_KEYS)[number];
5
5
  export declare const GHOST_LINK_AUDIT_EVENT_TYPE_KEYS: readonly ["ghostPlayer.linked", "ghostPlayer.closedLeagueLinked"];
6
6
  export type GhostLinkAuditEventType = (typeof GHOST_LINK_AUDIT_EVENT_TYPE_KEYS)[number];
@@ -1 +1 @@
1
- {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/types/audit.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,eAAO,MAAM,qBAAqB,usEAoIxB,CAAC;AACX,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAIpE,eAAO,MAAM,gCAAgC,mEAGC,CAAC;AAC/C,MAAM,MAAM,uBAAuB,GAAG,CAAC,OAAO,gCAAgC,CAAC,CAAC,MAAM,CAAC,CAAC;AAUxF,eAAO,MAAM,qBAAqB,4EAA6E,CAAC;AAChH,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpE,eAAO,MAAM,2BAA2B,2CAAgF,CAAC;AACzH,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,2BAA2B,CAAC,CAAC,MAAM,CAAC,CAAC;AAE/E,eAAO,MAAM,+BAA+B,4CAAiF,CAAC;AAC9H,MAAM,MAAM,sBAAsB,GAAG,CAAC,OAAO,+BAA+B,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtF,eAAO,MAAM,wBAAwB,mDAAoD,CAAC;AAC1F,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1E,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAMlE,MAAM,MAAM,UAAU,GAClB;IAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,sBAAsB,CAAC;IAAC,UAAU,CAAC,EAAE,KAAK,CAAA;CAAE,GAC/F;IAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,mBAAmB,CAAC;IAAC,UAAU,EAAE,UAAU,CAAA;CAAE,CAAC;AAErG,MAAM,WAAW,WAAW;IAE1B,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAEnB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAED,eAAO,MAAM,iBAAiB,iCAAkC,CAAC;AACjE,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC"}
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/types/audit.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,eAAO,MAAM,qBAAqB,0uEAuIxB,CAAC;AACX,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAIpE,eAAO,MAAM,gCAAgC,mEAGC,CAAC;AAC/C,MAAM,MAAM,uBAAuB,GAAG,CAAC,OAAO,gCAAgC,CAAC,CAAC,MAAM,CAAC,CAAC;AAUxF,eAAO,MAAM,qBAAqB,4EAA6E,CAAC;AAChH,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpE,eAAO,MAAM,2BAA2B,2CAAgF,CAAC;AACzH,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,2BAA2B,CAAC,CAAC,MAAM,CAAC,CAAC;AAE/E,eAAO,MAAM,+BAA+B,4CAAiF,CAAC;AAC9H,MAAM,MAAM,sBAAsB,GAAG,CAAC,OAAO,+BAA+B,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtF,eAAO,MAAM,wBAAwB,mDAAoD,CAAC;AAC1F,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1E,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAMlE,MAAM,MAAM,UAAU,GAClB;IAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,sBAAsB,CAAC;IAAC,UAAU,CAAC,EAAE,KAAK,CAAA;CAAE,GAC/F;IAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,mBAAmB,CAAC;IAAC,UAAU,EAAE,UAAU,CAAA;CAAE,CAAC;AAErG,MAAM,WAAW,WAAW;IAE1B,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAEnB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAED,eAAO,MAAM,iBAAiB,iCAAkC,CAAC;AACjE,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC"}
@@ -129,9 +129,12 @@ export const AUDIT_EVENT_TYPE_KEYS = [
129
129
  // or a refused attempt at one — with the reason the operator gave
130
130
  'system.manualIntervention',
131
131
  // The migration console — one page of a cutover action run, with what it read, wrote, and found;
132
- // a product control's run recorded once its evidence checked; an operator checkpoint recorded with
133
- // its evidence; and the cutover, after which the product owns every destination
132
+ // the documents an action core wrote and removed, committed in the same batch or transaction as
133
+ // those writes (its action id, operation id, and document ids only — BACKEND-203); a product
134
+ // control's run recorded once its evidence checked; an operator checkpoint recorded with its
135
+ // evidence; and the cutover, after which the product owns every destination
134
136
  'migrationAction.pageRun',
137
+ 'migrationAction.writesCommitted',
135
138
  'migrationAction.controlRecorded',
136
139
  'migrationAction.checkpointRecorded',
137
140
  'migrationConsole.cutoverRecorded',
@@ -1 +1 @@
1
- {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/types/audit.ts"],"names":[],"mappings":"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","sourcesContent":["// The audit vocabulary: the closed event-type union (BACKEND-202) and how an actor was acting\n// (BACKEND-205). The document schema is in ../doc-schemas/audit-event.ts.\n//\n// The union is closed: a new sensitive action adds its event types here, in the same change\n// (BACKEND-202).\n\nimport type { SystemRole } from './unions.js';\nimport { CAPTAIN_TEAM_ROLES } from './unions.js';\n\nexport const AUDIT_EVENT_TYPE_KEYS = [\n // Accounts\n 'user.accountRegistered',\n 'user.displayNameChanged',\n // The scheduled sweep deleted a sign-in account that never finished registering (no profile)\n 'user.abandonedRegistrationDeleted',\n // Account status — an admin banned an account or lifted its ban\n 'user.accountBanned',\n 'user.accountUnbanned',\n // Global roles — the policy decision (callable) and the claim propagation (trigger)\n 'admin.systemRoleGranted',\n 'admin.systemRoleRevoked',\n 'admin.systemRoleClaimSynced',\n // App configuration — the operational levers on `_appConfig/app`, and the landing page copy\n 'appConfig.updated',\n 'landingPage.updated',\n // Leagues — created, settings edited, status moved, mode switched\n 'league.created',\n 'league.updated',\n 'league.statusChanged',\n 'league.modeSwitched',\n // Waiver templates — created, a new wording published, retired or deleted\n 'waiverTemplate.created',\n 'waiverTemplate.versionPublished',\n 'waiverTemplate.retired',\n 'waiverTemplate.deleted',\n // Game locations\n 'gameLocation.created',\n 'gameLocation.updated',\n 'gameLocation.deleted',\n // Pictures — published to a gallery or a slot by the picture processor (attributed to the admin\n // who uploaded it), and ordered, featured, and removed by the admin picture callables\n 'picture.added',\n 'picture.replaced',\n 'picture.moved',\n 'picture.featuredChanged',\n 'picture.removed',\n // Awards — an award created by its first badge's publication, its badge replaced, the award\n // deleted once nobody holds it, and an award granted to or revoked from one account\n 'award.created',\n 'award.badgeReplaced',\n 'award.deleted',\n 'award.granted',\n 'award.revoked',\n // Hardware contracts — a loan created for a referee, signed by them, its equipment returned, and a\n // contract never signed deleted\n 'hardwareLoanContract.created',\n 'hardwareLoanContract.signed',\n 'hardwareLoanContract.returned',\n 'hardwareLoanContract.deleted',\n // Teams — created with its captain, a ghost player added, a team's failed roster work retried\n 'leagueTeam.created',\n 'ghostPlayer.created',\n 'teamRoster.syncRetried',\n // Team management — a member promoted to co-captain or demoted to player, the captain changed by\n // an admin, the team's name or type edited by an admin, and its contact phone set by an admin\n 'teamMembership.roleChanged',\n 'leagueTeam.captainChanged',\n 'leagueTeam.updated',\n 'leagueTeamContact.updated',\n // Exits — a membership removed, a never-played ghost removed, a ghost linked by either link, and a\n // never-played team deleted; each targets what it deleted, which the migration's live\n // verifications read as proof the product removed it\n 'teamMembership.removed',\n 'ghostPlayer.removed',\n 'ghostPlayer.linked',\n 'ghostPlayer.closedLeagueLinked',\n 'leagueTeam.deleted',\n // A ghost's removal stopped part-way because the ghost came to have played: the games it held are\n // released, and the finished-game entries it had already taken off are named\n 'ghostPlayer.removalStopped',\n // Joining a team — an invitation sent, withdrawn, accepted, or declined; a join request sent,\n // withdrawn, accepted, or rejected; a free-agent signup made, withdrawn, or answered by an admin\n // placing the player on a team\n 'teamInvitation.created',\n 'teamInvitation.cancelled',\n 'teamInvitation.accepted',\n 'teamInvitation.rejected',\n 'teamJoinRequest.created',\n 'teamJoinRequest.cancelled',\n 'teamJoinRequest.accepted',\n 'teamJoinRequest.rejected',\n 'leagueFreeAgent.created',\n 'leagueFreeAgent.deleted',\n 'leagueFreeAgent.assigned',\n // Signed waivers — a legal record, signed by its subject or witnessed for a ghost\n 'signedWaiver.created',\n // Schedules — a week created with its games or its shared fields edited; one game added, edited\n // (moved, rescheduled, or its teams replaced), or deleted with its events and roster\n 'scheduleWeek.created',\n 'scheduleWeek.updated',\n 'sportGame.created',\n 'sportGame.updated',\n 'sportGame.deleted',\n // Live scoring — a scoring write refused its caller's authority (a click writes no success event:\n // the stat event document is its own record), a roster entry marked absent or present, a\n // scorer's note on one person changed, and the jump-ball possession arrow moved\n 'sportGame.statEventsRejected',\n 'sportGame.absenceChanged',\n 'sportGame.memoUpdated',\n 'sportGame.jumpBallChanged',\n // Finalizing — a game finalized, its numbers made permanent in every derived destination, and an\n // admin's reopening of a finalized game, which takes them back out\n 'sportGame.finalized',\n 'sportGame.reopened',\n // Fix stats — a game's numbers corrected (targets, or the stored numbers kept) or restored from\n // its events; a derived aggregate rebuilt; and a person's pre-Q-Sports baseline written\n 'sportGame.statsCorrected',\n 'sportGame.statsRecalculated',\n 'stats.aggregateRecalculated',\n 'stats.historicalStatsUpdated',\n // A stats check ran to its end: its scope, its summary, and its counts by kind.\n 'stats.validationRun',\n // An admin deleted a stats check's report: its id, scope, summary, and counts by kind.\n 'stats.validationReportDeleted',\n // The migration's placement of a recorded stat line no roster entry or play could place: the\n // admin names the team, and the game's copy places the line there\n 'stats.unrosteredStatLineResolved',\n // Admin notifications — a shared card archived, which takes it off every admin's list; and an\n // admin's archive-all decision, recorded once when the job is written, with the request's origin\n 'adminNotification.archived',\n 'adminNotification.archiveAllRequested',\n // An operator's manual intervention in a durable pipeline — a dead-lettered row put back to work,\n // or a refused attempt at one — with the reason the operator gave\n 'system.manualIntervention',\n // The migration console — one page of a cutover action run, with what it read, wrote, and found;\n // a product control's run recorded once its evidence checked; an operator checkpoint recorded with\n // its evidence; and the cutover, after which the product owns every destination\n 'migrationAction.pageRun',\n 'migrationAction.controlRecorded',\n 'migrationAction.checkpointRecorded',\n 'migrationConsole.cutoverRecorded',\n] as const;\nexport type AuditEventType = (typeof AUDIT_EVENT_TYPE_KEYS)[number];\n\n// The two ways a ghost is linked to an account — the normal link and the closed-league link — each\n// committing its own event over the one artifact-moving core.\nexport const GHOST_LINK_AUDIT_EVENT_TYPE_KEYS = [\n 'ghostPlayer.linked',\n 'ghostPlayer.closedLeagueLinked',\n] as const satisfies readonly AuditEventType[];\nexport type GhostLinkAuditEventType = (typeof GHOST_LINK_AUDIT_EVENT_TYPE_KEYS)[number];\n\n// How the actor was acting for the write — never merely which authorities they happen to hold. It is\n// orthogonal to the event type: one event type is produced under several modes, and the mode is what\n// tells an ordinary action from an admin override.\n// user a signed-in account acting on its own behalf\n// teamManager a captain or co-captain acting through their team's membership permissions\n// adminReview an admin or referee doing admin work that no team permission covers\n// adminOverride an admin or referee superseding the team permission a normal entry checks\n// system backend automation with no human caller\nexport const AUDIT_ACTOR_MODE_KEYS = ['user', 'teamManager', 'adminReview', 'adminOverride', 'system'] as const;\nexport type AuditActorMode = (typeof AUDIT_ACTOR_MODE_KEYS)[number];\n\nexport const AUDIT_ADMIN_ACTOR_MODE_KEYS = ['adminReview', 'adminOverride'] as const satisfies readonly AuditActorMode[];\nexport type AuditAdminActorMode = (typeof AUDIT_ADMIN_ACTOR_MODE_KEYS)[number];\n\nexport const AUDIT_NON_ADMIN_ACTOR_MODE_KEYS = ['user', 'teamManager', 'system'] as const satisfies readonly AuditActorMode[];\nexport type AuditNonAdminActorMode = (typeof AUDIT_NON_ADMIN_ACTOR_MODE_KEYS)[number];\n\nexport const AUDIT_SYSTEM_REASON_KEYS = ['trigger', 'scheduledJob', 'migration'] as const;\nexport type AuditSystemReason = (typeof AUDIT_SYSTEM_REASON_KEYS)[number];\n\nexport type CaptainTeamRole = (typeof CAPTAIN_TEAM_ROLES)[number];\n\n// `isAdmin` is metadata only: it records that the caller's token carried a global-role claim (admin\n// or referee) when they acted, and grants nothing. `systemRole` is required on the admin modes and\n// forbidden on the others, so an admin action is never recorded without the authority it used and a\n// non-admin action never carries one.\nexport type AuditActor =\n | { uid: string | null; isAdmin: boolean; actorMode: AuditNonAdminActorMode; systemRole?: never }\n | { uid: string | null; isAdmin: boolean; actorMode: AuditAdminActorMode; systemRole: SystemRole };\n\nexport interface AuditTarget {\n // The user the action was performed on, when there is one.\n uid: string | null;\n // The slash path of the document the action changed, when there is one.\n ref: string | null;\n}\n\nexport const AUDIT_RESULT_KEYS = ['success', 'failure'] as const;\nexport type AuditResult = (typeof AUDIT_RESULT_KEYS)[number];\n"]}
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/types/audit.ts"],"names":[],"mappings":"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","sourcesContent":["// The audit vocabulary: the closed event-type union (BACKEND-202) and how an actor was acting\n// (BACKEND-205). The document schema is in ../doc-schemas/audit-event.ts.\n//\n// The union is closed: a new sensitive action adds its event types here, in the same change\n// (BACKEND-202).\n\nimport type { SystemRole } from './unions.js';\nimport { CAPTAIN_TEAM_ROLES } from './unions.js';\n\nexport const AUDIT_EVENT_TYPE_KEYS = [\n // Accounts\n 'user.accountRegistered',\n 'user.displayNameChanged',\n // The scheduled sweep deleted a sign-in account that never finished registering (no profile)\n 'user.abandonedRegistrationDeleted',\n // Account status — an admin banned an account or lifted its ban\n 'user.accountBanned',\n 'user.accountUnbanned',\n // Global roles — the policy decision (callable) and the claim propagation (trigger)\n 'admin.systemRoleGranted',\n 'admin.systemRoleRevoked',\n 'admin.systemRoleClaimSynced',\n // App configuration — the operational levers on `_appConfig/app`, and the landing page copy\n 'appConfig.updated',\n 'landingPage.updated',\n // Leagues — created, settings edited, status moved, mode switched\n 'league.created',\n 'league.updated',\n 'league.statusChanged',\n 'league.modeSwitched',\n // Waiver templates — created, a new wording published, retired or deleted\n 'waiverTemplate.created',\n 'waiverTemplate.versionPublished',\n 'waiverTemplate.retired',\n 'waiverTemplate.deleted',\n // Game locations\n 'gameLocation.created',\n 'gameLocation.updated',\n 'gameLocation.deleted',\n // Pictures — published to a gallery or a slot by the picture processor (attributed to the admin\n // who uploaded it), and ordered, featured, and removed by the admin picture callables\n 'picture.added',\n 'picture.replaced',\n 'picture.moved',\n 'picture.featuredChanged',\n 'picture.removed',\n // Awards — an award created by its first badge's publication, its badge replaced, the award\n // deleted once nobody holds it, and an award granted to or revoked from one account\n 'award.created',\n 'award.badgeReplaced',\n 'award.deleted',\n 'award.granted',\n 'award.revoked',\n // Hardware contracts — a loan created for a referee, signed by them, its equipment returned, and a\n // contract never signed deleted\n 'hardwareLoanContract.created',\n 'hardwareLoanContract.signed',\n 'hardwareLoanContract.returned',\n 'hardwareLoanContract.deleted',\n // Teams — created with its captain, a ghost player added, a team's failed roster work retried\n 'leagueTeam.created',\n 'ghostPlayer.created',\n 'teamRoster.syncRetried',\n // Team management — a member promoted to co-captain or demoted to player, the captain changed by\n // an admin, the team's name or type edited by an admin, and its contact phone set by an admin\n 'teamMembership.roleChanged',\n 'leagueTeam.captainChanged',\n 'leagueTeam.updated',\n 'leagueTeamContact.updated',\n // Exits — a membership removed, a never-played ghost removed, a ghost linked by either link, and a\n // never-played team deleted; each targets what it deleted, which the migration's live\n // verifications read as proof the product removed it\n 'teamMembership.removed',\n 'ghostPlayer.removed',\n 'ghostPlayer.linked',\n 'ghostPlayer.closedLeagueLinked',\n 'leagueTeam.deleted',\n // A ghost's removal stopped part-way because the ghost came to have played: the games it held are\n // released, and the finished-game entries it had already taken off are named\n 'ghostPlayer.removalStopped',\n // Joining a team — an invitation sent, withdrawn, accepted, or declined; a join request sent,\n // withdrawn, accepted, or rejected; a free-agent signup made, withdrawn, or answered by an admin\n // placing the player on a team\n 'teamInvitation.created',\n 'teamInvitation.cancelled',\n 'teamInvitation.accepted',\n 'teamInvitation.rejected',\n 'teamJoinRequest.created',\n 'teamJoinRequest.cancelled',\n 'teamJoinRequest.accepted',\n 'teamJoinRequest.rejected',\n 'leagueFreeAgent.created',\n 'leagueFreeAgent.deleted',\n 'leagueFreeAgent.assigned',\n // Signed waivers — a legal record, signed by its subject or witnessed for a ghost\n 'signedWaiver.created',\n // Schedules — a week created with its games or its shared fields edited; one game added, edited\n // (moved, rescheduled, or its teams replaced), or deleted with its events and roster\n 'scheduleWeek.created',\n 'scheduleWeek.updated',\n 'sportGame.created',\n 'sportGame.updated',\n 'sportGame.deleted',\n // Live scoring — a scoring write refused its caller's authority (a click writes no success event:\n // the stat event document is its own record), a roster entry marked absent or present, a\n // scorer's note on one person changed, and the jump-ball possession arrow moved\n 'sportGame.statEventsRejected',\n 'sportGame.absenceChanged',\n 'sportGame.memoUpdated',\n 'sportGame.jumpBallChanged',\n // Finalizing — a game finalized, its numbers made permanent in every derived destination, and an\n // admin's reopening of a finalized game, which takes them back out\n 'sportGame.finalized',\n 'sportGame.reopened',\n // Fix stats — a game's numbers corrected (targets, or the stored numbers kept) or restored from\n // its events; a derived aggregate rebuilt; and a person's pre-Q-Sports baseline written\n 'sportGame.statsCorrected',\n 'sportGame.statsRecalculated',\n 'stats.aggregateRecalculated',\n 'stats.historicalStatsUpdated',\n // A stats check ran to its end: its scope, its summary, and its counts by kind.\n 'stats.validationRun',\n // An admin deleted a stats check's report: its id, scope, summary, and counts by kind.\n 'stats.validationReportDeleted',\n // The migration's placement of a recorded stat line no roster entry or play could place: the\n // admin names the team, and the game's copy places the line there\n 'stats.unrosteredStatLineResolved',\n // Admin notifications — a shared card archived, which takes it off every admin's list; and an\n // admin's archive-all decision, recorded once when the job is written, with the request's origin\n 'adminNotification.archived',\n 'adminNotification.archiveAllRequested',\n // An operator's manual intervention in a durable pipeline — a dead-lettered row put back to work,\n // or a refused attempt at one — with the reason the operator gave\n 'system.manualIntervention',\n // The migration console — one page of a cutover action run, with what it read, wrote, and found;\n // the documents an action core wrote and removed, committed in the same batch or transaction as\n // those writes (its action id, operation id, and document ids only — BACKEND-203); a product\n // control's run recorded once its evidence checked; an operator checkpoint recorded with its\n // evidence; and the cutover, after which the product owns every destination\n 'migrationAction.pageRun',\n 'migrationAction.writesCommitted',\n 'migrationAction.controlRecorded',\n 'migrationAction.checkpointRecorded',\n 'migrationConsole.cutoverRecorded',\n] as const;\nexport type AuditEventType = (typeof AUDIT_EVENT_TYPE_KEYS)[number];\n\n// The two ways a ghost is linked to an account — the normal link and the closed-league link — each\n// committing its own event over the one artifact-moving core.\nexport const GHOST_LINK_AUDIT_EVENT_TYPE_KEYS = [\n 'ghostPlayer.linked',\n 'ghostPlayer.closedLeagueLinked',\n] as const satisfies readonly AuditEventType[];\nexport type GhostLinkAuditEventType = (typeof GHOST_LINK_AUDIT_EVENT_TYPE_KEYS)[number];\n\n// How the actor was acting for the write — never merely which authorities they happen to hold. It is\n// orthogonal to the event type: one event type is produced under several modes, and the mode is what\n// tells an ordinary action from an admin override.\n// user a signed-in account acting on its own behalf\n// teamManager a captain or co-captain acting through their team's membership permissions\n// adminReview an admin or referee doing admin work that no team permission covers\n// adminOverride an admin or referee superseding the team permission a normal entry checks\n// system backend automation with no human caller\nexport const AUDIT_ACTOR_MODE_KEYS = ['user', 'teamManager', 'adminReview', 'adminOverride', 'system'] as const;\nexport type AuditActorMode = (typeof AUDIT_ACTOR_MODE_KEYS)[number];\n\nexport const AUDIT_ADMIN_ACTOR_MODE_KEYS = ['adminReview', 'adminOverride'] as const satisfies readonly AuditActorMode[];\nexport type AuditAdminActorMode = (typeof AUDIT_ADMIN_ACTOR_MODE_KEYS)[number];\n\nexport const AUDIT_NON_ADMIN_ACTOR_MODE_KEYS = ['user', 'teamManager', 'system'] as const satisfies readonly AuditActorMode[];\nexport type AuditNonAdminActorMode = (typeof AUDIT_NON_ADMIN_ACTOR_MODE_KEYS)[number];\n\nexport const AUDIT_SYSTEM_REASON_KEYS = ['trigger', 'scheduledJob', 'migration'] as const;\nexport type AuditSystemReason = (typeof AUDIT_SYSTEM_REASON_KEYS)[number];\n\nexport type CaptainTeamRole = (typeof CAPTAIN_TEAM_ROLES)[number];\n\n// `isAdmin` is metadata only: it records that the caller's token carried a global-role claim (admin\n// or referee) when they acted, and grants nothing. `systemRole` is required on the admin modes and\n// forbidden on the others, so an admin action is never recorded without the authority it used and a\n// non-admin action never carries one.\nexport type AuditActor =\n | { uid: string | null; isAdmin: boolean; actorMode: AuditNonAdminActorMode; systemRole?: never }\n | { uid: string | null; isAdmin: boolean; actorMode: AuditAdminActorMode; systemRole: SystemRole };\n\nexport interface AuditTarget {\n // The user the action was performed on, when there is one.\n uid: string | null;\n // The slash path of the document the action changed, when there is one.\n ref: string | null;\n}\n\nexport const AUDIT_RESULT_KEYS = ['success', 'failure'] as const;\nexport type AuditResult = (typeof AUDIT_RESULT_KEYS)[number];\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"documents.js","sourceRoot":"","sources":["../../src/types/documents.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,qGAAqG;AACrG,sCAAsC","sourcesContent":["// Document types. Every shape is defined as a zod schema in ../doc-schemas and its type inferred\r\n// there; this file re-exports the TYPES so they reach the root barrel while the runtime schemas stay\r\n// behind the `./doc-schemas` subpath.\r\n\r\nexport type { FirestoreTimestampLike } from '../doc-schemas/firestore-primitives.js';\r\nexport type { GrantedAward, HistoricalStats, UserProfile } from '../doc-schemas/user-profile.js';\r\nexport type { PublicUser } from '../doc-schemas/publicUser.js';\r\nexport type { SportLeague } from '../doc-schemas/sport-league.js';\r\nexport type { LeagueTeam, LeagueTeamContact } from '../doc-schemas/league-team.js';\r\nexport type {\r\n MembershipRosterSync,\r\n MembershipRosterSyncCursor,\r\n} from '../doc-schemas/membership-roster-sync.js';\r\nexport type {\r\n LeagueFreeAgent,\r\n TeamJoinRequest,\r\n TeamInvitation,\r\n TeamMembership,\r\n} from '../doc-schemas/team-membership.js';\r\nexport type {\r\n BasketballPlayerStats,\r\n BasketballStatEvent,\r\n BasketballTeamStats,\r\n CurlingBlankEndEvent,\r\n CurlingRoundScoreEvent,\r\n CurlingStatEvent,\r\n CurlingTeamStats,\r\n FootballStatEvent,\r\n FootballTeamStats,\r\n GameStatEvent,\r\n GameStatEventType,\r\n StatCorrectionEvent,\r\n StatVoidEvent,\r\n} from '../doc-schemas/game-stats.js';\r\nexport type {\r\n BasketballGame,\r\n BasketballGameStats,\r\n CurlingGame,\r\n CurlingGameStats,\r\n FootballGame,\r\n FootballGameStats,\r\n GameRosterPlayer,\r\n GameStats,\r\n GameTeam,\r\n SportGame,\r\n} from '../doc-schemas/sport-game.js';\r\nexport type {\r\n GameStatHistoryContinuationPage,\r\n GameStatHistoryFirstPage,\r\n GameStatHistoryPage,\r\n} from '../doc-schemas/game-stat-history.js';\r\nexport type {\r\n BasketballPlayerTotals,\r\n BasketballTeamSeasonStats,\r\n GamesPlayedStats,\r\n PlayerSeasonStats,\r\n SportHallOfFame,\r\n TeamSeasonBaseStats,\r\n TeamSeasonStats,\r\n UserLifetimeStats,\r\n} from '../doc-schemas/aggregate-stats.js';\r\nexport type {\r\n StatsDestinationChange,\r\n StatsOperation,\r\n StatsOperationCounts,\r\n} from '../doc-schemas/stats-operation.js';\r\nexport type {\r\n HardwareLoanContract,\r\n HardwareItem,\r\n SignedWaiver,\r\n} from '../doc-schemas/waivers-and-hardware.js';\r\nexport type { WaiverTemplate, WaiverTemplateVersion } from '../doc-schemas/waiver-template.js';\r\nexport type { GameLocation } from '../doc-schemas/game-location.js';\r\nexport type { AwardDefinition } from '../doc-schemas/catalogs.js';\r\nexport type {\r\n StatusReconcileQueueAccountStatusEntry,\r\n StatusReconcileQueueEntry,\r\n StatusReconcileQueueRoleClaimsEntry,\r\n} from '../doc-schemas/status-reconcile-queue.js';\r\nexport type { AdminStats } from '../schemas/admin-stats.js';\r\nexport type { GalleryPicture, GallerySummary } from '../doc-schemas/gallery.js';\r\nexport type {\r\n PendingMedia,\r\n PendingMediaCompleted,\r\n PendingMediaErrorCategory,\r\n PendingMediaFailed,\r\n PendingMediaPending,\r\n PendingMediaProcessing,\r\n PendingMediaResult,\r\n PendingMediaTarget,\r\n PictureOwnerRef,\r\n StorageObjectRef,\r\n} from '../doc-schemas/pending-media.js';\r\nexport type { PictureRetirement, PictureRetirementTarget } from '../doc-schemas/picture-retirement.js';\r\nexport type {\r\n AppConfig,\r\n LandingPageConfig,\r\n AdminList,\r\n StatsBeacon,\r\n StatsDiscrepancy,\r\n StatsValidationReport,\r\n StatsValidationScope,\r\n StatsValidationSummary,\r\n} from '../doc-schemas/system.js';\r\nexport type {\r\n ActiveAdminNotification,\r\n AdminNotificationHistory,\r\n AdminNotificationMetadataByType,\r\n LeagueFreeAgentSignedUpNotificationMetadata,\r\n LeagueTeamCreatedNotificationMetadata,\r\n NotificationArchiveAllJob,\r\n NotificationDelivery,\r\n NotificationDeliveryPayload,\r\n} from '../doc-schemas/admin-notifications.js';\r\n"]}
1
+ {"version":3,"file":"documents.js","sourceRoot":"","sources":["../../src/types/documents.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,qGAAqG;AACrG,sCAAsC","sourcesContent":["// Document types. Every shape is defined as a zod schema in ../doc-schemas and its type inferred\n// there; this file re-exports the TYPES so they reach the root barrel while the runtime schemas stay\n// behind the `./doc-schemas` subpath.\n\nexport type { FirestoreTimestampLike } from '../doc-schemas/firestore-primitives.js';\nexport type { GrantedAward, HistoricalStats, UserProfile } from '../doc-schemas/user-profile.js';\nexport type { PublicUser } from '../doc-schemas/publicUser.js';\nexport type { SportLeague } from '../doc-schemas/sport-league.js';\nexport type { LeagueTeam, LeagueTeamContact } from '../doc-schemas/league-team.js';\nexport type {\n MembershipRosterSync,\n MembershipRosterSyncCursor,\n} from '../doc-schemas/membership-roster-sync.js';\nexport type {\n LeagueFreeAgent,\n TeamJoinRequest,\n TeamInvitation,\n TeamMembership,\n} from '../doc-schemas/team-membership.js';\nexport type {\n BasketballPlayerStats,\n BasketballStatEvent,\n BasketballTeamStats,\n CurlingBlankEndEvent,\n CurlingRoundScoreEvent,\n CurlingStatEvent,\n CurlingTeamStats,\n FootballStatEvent,\n FootballTeamStats,\n GameStatEvent,\n GameStatEventType,\n StatCorrectionEvent,\n StatVoidEvent,\n} from '../doc-schemas/game-stats.js';\nexport type {\n BasketballGame,\n BasketballGameStats,\n CurlingGame,\n CurlingGameStats,\n FootballGame,\n FootballGameStats,\n GameRosterPlayer,\n GameStats,\n GameTeam,\n SportGame,\n} from '../doc-schemas/sport-game.js';\nexport type {\n GameStatHistoryContinuationPage,\n GameStatHistoryFirstPage,\n GameStatHistoryPage,\n} from '../doc-schemas/game-stat-history.js';\nexport type {\n BasketballPlayerTotals,\n BasketballTeamSeasonStats,\n GamesPlayedStats,\n PlayerSeasonStats,\n SportHallOfFame,\n TeamSeasonBaseStats,\n TeamSeasonStats,\n UserLifetimeStats,\n} from '../doc-schemas/aggregate-stats.js';\nexport type {\n StatsDestinationChange,\n StatsOperation,\n StatsOperationCounts,\n} from '../doc-schemas/stats-operation.js';\nexport type {\n HardwareLoanContract,\n HardwareItem,\n SignedWaiver,\n} from '../doc-schemas/waivers-and-hardware.js';\nexport type { WaiverTemplate, WaiverTemplateVersion } from '../doc-schemas/waiver-template.js';\nexport type { GameLocation } from '../doc-schemas/game-location.js';\nexport type { AwardDefinition } from '../doc-schemas/catalogs.js';\nexport type {\n StatusReconcileQueueAccountStatusEntry,\n StatusReconcileQueueEntry,\n StatusReconcileQueueRoleClaimsEntry,\n} from '../doc-schemas/status-reconcile-queue.js';\nexport type { AdminStats } from '../schemas/admin-stats.js';\nexport type { GalleryPicture, GallerySummary } from '../doc-schemas/gallery.js';\nexport type {\n PendingMedia,\n PendingMediaCompleted,\n PendingMediaErrorCategory,\n PendingMediaFailed,\n PendingMediaPending,\n PendingMediaProcessing,\n PendingMediaResult,\n PendingMediaTarget,\n PictureOwnerRef,\n StorageObjectRef,\n} from '../doc-schemas/pending-media.js';\nexport type { PictureRetirement, PictureRetirementTarget } from '../doc-schemas/picture-retirement.js';\nexport type {\n AppConfig,\n LandingPageConfig,\n AdminList,\n StatsBeacon,\n StatsDiscrepancy,\n StatsValidationReport,\n StatsValidationScope,\n StatsValidationSummary,\n} from '../doc-schemas/system.js';\nexport type {\n ActiveAdminNotification,\n AdminNotificationHistory,\n AdminNotificationMetadataByType,\n LeagueFreeAgentSignedUpNotificationMetadata,\n LeagueTeamCreatedNotificationMetadata,\n NotificationArchiveAllJob,\n NotificationDelivery,\n NotificationDeliveryPayload,\n} from '../doc-schemas/admin-notifications.js';\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"notifications.js","sourceRoot":"","sources":["../../src/types/notifications.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,4FAA4F;AAC5F,+FAA+F;AAC/F,mGAAmG;AACnG,gDAAgD;AAShD,oGAAoG;AACpG,kGAAkG;AAClG,oGAAoG;AACpG,uDAAuD;AACvD,SAAS,WAAW;IAClB,OAAO,CACL,IAAmH,EAC7G,EAAE,CAAC,IAAI,CAAC;AAClB,CAAC;AAED,2EAA2E;AAC3E,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,OAAO,CAAU,CAAC;AAE7D,MAAM,CAAC,MAAM,2BAA2B,GAAG,OAA+C,CAAC;AAE3F,oGAAoG;AACpG,qCAAqC;AACrC,MAAM,CAAC,MAAM,kCAAkC,GAAG,QAAQ,CAAC;AAE3D,kGAAkG;AAClG,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,mBAAmB,EAAE,yBAAyB,CAAU,CAAC;AAGtG,uGAAuG;AACvG,iGAAiG;AACjG,oFAAoF;AACpF,MAAM,CAAC,MAAM,gCAAgC,GAAG,WAAW,EAAiB,CAAC;IAC3E,QAAQ;IACR,cAAc;IACd,SAAS;IACT,YAAY;CACb,CAAC,CAAC;AAGH,gDAAgD;AAChD,MAAM,CAAC,MAAM,sCAAsC,GAAG,WAAW,EAAsB,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC;AAEjH,mGAAmG;AACnG,kGAAkG;AAClG,gGAAgG;AAChG,uGAAuG;AACvG,yBAAyB;AACzB,MAAM,CAAC,MAAM,uCAAuC,GAAG,WAAW,EAAiD,CAAC;IAClH,cAAc;IACd,kBAAkB;IAClB,OAAO;CACR,CAAC,CAAC;AAGH,mGAAmG;AACnG,kEAAkE;AAClE,MAAM,CAAC,MAAM,qCAAqC,GAAG,CAAC,CAAC;AAEvD,yFAAyF;AACzF,MAAM,CAAC,MAAM,uCAAuC,GAAG,WAAW,EAAkC,CAAC;IACnG,aAAa;IACb,UAAU;IACV,YAAY;IACZ,QAAQ;CACT,CAAC,CAAC","sourcesContent":["// The admin notification vocabulary. Q-Sports notifies admins only, through one shared audience:\r\n// every admin reads the same cards, and seeing or archiving a card does so for all of them.\r\n// `@ttt-productions/notification-core` owns the delivery, seen, and archive mechanism; what is\r\n// declared here is Q-Sports' side of it — the category, the notification types, and the states the\r\n// ledger and the archive-all jobs move through.\r\n\r\nimport type { ArchiveAllJobSnapshot } from '@ttt-productions/notification-core';\r\nimport type {\r\n DeliverySkipReason,\r\n DeliveryState,\r\n MaterializationClass,\r\n} from '@ttt-productions/notification-core/server';\r\n\r\n// The package exports these vocabularies as types only, so each is restated here as a tuple the doc\r\n// schemas build their enums from. `everyMember<Union>()` accepts a tuple only when it names every\r\n// member of `Union` and nothing else, so a vocabulary the package grows or renames fails to compile\r\n// here instead of failing a row's parse in production.\r\nfunction everyMember<Union extends string>() {\r\n return <const Keys extends readonly Union[]>(\r\n keys: Keys & ([Exclude<Union, Keys[number]>] extends [never] ? unknown : { missing: Exclude<Union, Keys[number]> }),\r\n ): Keys => keys;\r\n}\r\n\r\n// The one notification category. It is shared, so a card has no recipient.\r\nexport const NOTIFICATION_CATEGORY_KEYS = ['admin'] as const;\r\nexport type NotificationCategory = (typeof NOTIFICATION_CATEGORY_KEYS)[number];\r\nexport const ADMIN_NOTIFICATION_CATEGORY = 'admin' as const satisfies NotificationCategory;\r\n\r\n// The audience scope notification-core records on an archived shared card, and hashes into a shared\r\n// card's id in place of a recipient.\r\nexport const SHARED_NOTIFICATION_AUDIENCE_SCOPE = 'shared';\r\n\r\n// What an admin is notified of: a team joining a league, and a player signing up as a free agent.\r\nexport const ADMIN_NOTIFICATION_TYPE_KEYS = ['leagueTeamCreated', 'leagueFreeAgentSignedUp'] as const;\r\nexport type AdminNotificationType = (typeof ADMIN_NOTIFICATION_TYPE_KEYS)[number];\r\n\r\n// A delivery row's life on the ledger, as notification-core writes it. `skipped` is the terminal state\r\n// of a row whose recipient may no longer receive; a Q-Sports row names no recipient, so it never\r\n// reaches it, but the state is the package's and a row is read against all of them.\r\nexport const NOTIFICATION_DELIVERY_STATE_KEYS = everyMember<DeliveryState>()([\r\n 'queued',\r\n 'materialized',\r\n 'skipped',\r\n 'deadLetter',\r\n]);\r\nexport type NotificationDeliveryState = (typeof NOTIFICATION_DELIVERY_STATE_KEYS)[number];\r\n\r\n// Why notification-core turned a row `skipped`.\r\nexport const NOTIFICATION_DELIVERY_SKIP_REASON_KEYS = everyMember<DeliverySkipReason>()(['recipientIneligible']);\r\n\r\n// The worker lane a delivery row waits in: `directQueued` for a type only the worker materializes,\r\n// `realtimeFallback` for a type its owning callable also materializes right after its commit, and\r\n// `retry`, which notification-core moves a row into after a failed attempt. notification-core's\r\n// fourth lane, `fanoutOrphan`, holds rows of an audience fanout, which Q-Sports does not run, so it is\r\n// the one lane left out.\r\nexport const NOTIFICATION_MATERIALIZATION_CLASS_KEYS = everyMember<Exclude<MaterializationClass, 'fanoutOrphan'>>()([\r\n 'directQueued',\r\n 'realtimeFallback',\r\n 'retry',\r\n]);\r\nexport type NotificationMaterializationClass = (typeof NOTIFICATION_MATERIALIZATION_CLASS_KEYS)[number];\r\n\r\n// The shape of the occurrence payload every delivery row carries. A change to it is a new version,\r\n// so a row written under the old one is still read as what it is.\r\nexport const NOTIFICATION_DELIVERY_PAYLOAD_VERSION = 1;\r\n\r\n// An archive-all job's state, which the status read hands the client's poller unchanged.\r\nexport const NOTIFICATION_ARCHIVE_ALL_JOB_STATE_KEYS = everyMember<ArchiveAllJobSnapshot['state']>()([\r\n 'in-progress',\r\n 'complete',\r\n 'incomplete',\r\n 'failed',\r\n]);\r\nexport type NotificationArchiveAllJobState = (typeof NOTIFICATION_ARCHIVE_ALL_JOB_STATE_KEYS)[number];\r\n"]}
1
+ {"version":3,"file":"notifications.js","sourceRoot":"","sources":["../../src/types/notifications.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,4FAA4F;AAC5F,+FAA+F;AAC/F,mGAAmG;AACnG,gDAAgD;AAShD,oGAAoG;AACpG,kGAAkG;AAClG,oGAAoG;AACpG,uDAAuD;AACvD,SAAS,WAAW;IAClB,OAAO,CACL,IAAmH,EAC7G,EAAE,CAAC,IAAI,CAAC;AAClB,CAAC;AAED,2EAA2E;AAC3E,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,OAAO,CAAU,CAAC;AAE7D,MAAM,CAAC,MAAM,2BAA2B,GAAG,OAA+C,CAAC;AAE3F,oGAAoG;AACpG,qCAAqC;AACrC,MAAM,CAAC,MAAM,kCAAkC,GAAG,QAAQ,CAAC;AAE3D,kGAAkG;AAClG,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,mBAAmB,EAAE,yBAAyB,CAAU,CAAC;AAGtG,uGAAuG;AACvG,iGAAiG;AACjG,oFAAoF;AACpF,MAAM,CAAC,MAAM,gCAAgC,GAAG,WAAW,EAAiB,CAAC;IAC3E,QAAQ;IACR,cAAc;IACd,SAAS;IACT,YAAY;CACb,CAAC,CAAC;AAGH,gDAAgD;AAChD,MAAM,CAAC,MAAM,sCAAsC,GAAG,WAAW,EAAsB,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC;AAEjH,mGAAmG;AACnG,kGAAkG;AAClG,gGAAgG;AAChG,uGAAuG;AACvG,yBAAyB;AACzB,MAAM,CAAC,MAAM,uCAAuC,GAAG,WAAW,EAAiD,CAAC;IAClH,cAAc;IACd,kBAAkB;IAClB,OAAO;CACR,CAAC,CAAC;AAGH,mGAAmG;AACnG,kEAAkE;AAClE,MAAM,CAAC,MAAM,qCAAqC,GAAG,CAAC,CAAC;AAEvD,yFAAyF;AACzF,MAAM,CAAC,MAAM,uCAAuC,GAAG,WAAW,EAAkC,CAAC;IACnG,aAAa;IACb,UAAU;IACV,YAAY;IACZ,QAAQ;CACT,CAAC,CAAC","sourcesContent":["// The admin notification vocabulary. Q-Sports notifies admins only, through one shared audience:\n// every admin reads the same cards, and seeing or archiving a card does so for all of them.\n// `@ttt-productions/notification-core` owns the delivery, seen, and archive mechanism; what is\n// declared here is Q-Sports' side of it — the category, the notification types, and the states the\n// ledger and the archive-all jobs move through.\n\nimport type { ArchiveAllJobSnapshot } from '@ttt-productions/notification-core';\nimport type {\n DeliverySkipReason,\n DeliveryState,\n MaterializationClass,\n} from '@ttt-productions/notification-core/server';\n\n// The package exports these vocabularies as types only, so each is restated here as a tuple the doc\n// schemas build their enums from. `everyMember<Union>()` accepts a tuple only when it names every\n// member of `Union` and nothing else, so a vocabulary the package grows or renames fails to compile\n// here instead of failing a row's parse in production.\nfunction everyMember<Union extends string>() {\n return <const Keys extends readonly Union[]>(\n keys: Keys & ([Exclude<Union, Keys[number]>] extends [never] ? unknown : { missing: Exclude<Union, Keys[number]> }),\n ): Keys => keys;\n}\n\n// The one notification category. It is shared, so a card has no recipient.\nexport const NOTIFICATION_CATEGORY_KEYS = ['admin'] as const;\nexport type NotificationCategory = (typeof NOTIFICATION_CATEGORY_KEYS)[number];\nexport const ADMIN_NOTIFICATION_CATEGORY = 'admin' as const satisfies NotificationCategory;\n\n// The audience scope notification-core records on an archived shared card, and hashes into a shared\n// card's id in place of a recipient.\nexport const SHARED_NOTIFICATION_AUDIENCE_SCOPE = 'shared';\n\n// What an admin is notified of: a team joining a league, and a player signing up as a free agent.\nexport const ADMIN_NOTIFICATION_TYPE_KEYS = ['leagueTeamCreated', 'leagueFreeAgentSignedUp'] as const;\nexport type AdminNotificationType = (typeof ADMIN_NOTIFICATION_TYPE_KEYS)[number];\n\n// A delivery row's life on the ledger, as notification-core writes it. `skipped` is the terminal state\n// of a row whose recipient may no longer receive; a Q-Sports row names no recipient, so it never\n// reaches it, but the state is the package's and a row is read against all of them.\nexport const NOTIFICATION_DELIVERY_STATE_KEYS = everyMember<DeliveryState>()([\n 'queued',\n 'materialized',\n 'skipped',\n 'deadLetter',\n]);\nexport type NotificationDeliveryState = (typeof NOTIFICATION_DELIVERY_STATE_KEYS)[number];\n\n// Why notification-core turned a row `skipped`.\nexport const NOTIFICATION_DELIVERY_SKIP_REASON_KEYS = everyMember<DeliverySkipReason>()(['recipientIneligible']);\n\n// The worker lane a delivery row waits in: `directQueued` for a type only the worker materializes,\n// `realtimeFallback` for a type its owning callable also materializes right after its commit, and\n// `retry`, which notification-core moves a row into after a failed attempt. notification-core's\n// fourth lane, `fanoutOrphan`, holds rows of an audience fanout, which Q-Sports does not run, so it is\n// the one lane left out.\nexport const NOTIFICATION_MATERIALIZATION_CLASS_KEYS = everyMember<Exclude<MaterializationClass, 'fanoutOrphan'>>()([\n 'directQueued',\n 'realtimeFallback',\n 'retry',\n]);\nexport type NotificationMaterializationClass = (typeof NOTIFICATION_MATERIALIZATION_CLASS_KEYS)[number];\n\n// The shape of the occurrence payload every delivery row carries. A change to it is a new version,\n// so a row written under the old one is still read as what it is.\nexport const NOTIFICATION_DELIVERY_PAYLOAD_VERSION = 1;\n\n// An archive-all job's state, which the status read hands the client's poller unchanged.\nexport const NOTIFICATION_ARCHIVE_ALL_JOB_STATE_KEYS = everyMember<ArchiveAllJobSnapshot['state']>()([\n 'in-progress',\n 'complete',\n 'incomplete',\n 'failed',\n]);\nexport type NotificationArchiveAllJobState = (typeof NOTIFICATION_ARCHIVE_ALL_JOB_STATE_KEYS)[number];\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"picture-owners.js","sourceRoot":"","sources":["../../src/types/picture-owners.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,iGAAiG;AACjG,iGAAiG;AAEjG,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,aAAa;IACb,YAAY;IACZ,aAAa;IACb,cAAc;IACd,iBAAiB;CACT,CAAC;AAGX,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,aAAa;IACb,YAAY;IACZ,aAAa;CACiC,CAAC;AAIjD,mEAAmE;AACnE,MAAM,CAAC,MAAM,2BAA2B,GAAG,iBAAqD,CAAC;AAEjG,6DAA6D;AAC7D,MAAM,CAAC,MAAM,8BAA8B,GAAG,YAAgD,CAAC;AAE/F,6EAA6E;AAC7E,MAAM,UAAU,kBAAkB,CAAC,IAAsB;IACvD,OAAQ,uBAAuD,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AACjF,CAAC","sourcesContent":["// Which document owns a picture (ARCH-102). An upload's target, its indexes, and a picture's\r\n// removal take the full union; a gallery picture document takes the gallery subset, because only\r\n// those three owners hold a gallery — a location and an award hold one picture in a single slot.\r\n\r\nexport const PICTURE_OWNER_KIND_KEYS = [\r\n 'sportLeague',\r\n 'leagueTeam',\r\n 'landingPage',\r\n 'gameLocation',\r\n 'awardDefinition',\r\n] as const;\r\nexport type PictureOwnerKind = (typeof PICTURE_OWNER_KIND_KEYS)[number];\r\n\r\nexport const GALLERY_OWNER_KIND_KEYS = [\r\n 'sportLeague',\r\n 'leagueTeam',\r\n 'landingPage',\r\n] as const satisfies readonly PictureOwnerKind[];\r\nexport type GalleryOwnerKind = (typeof GALLERY_OWNER_KIND_KEYS)[number];\r\nexport type PictureSlotOwnerKind = Exclude<PictureOwnerKind, GalleryOwnerKind>;\r\n\r\n// An award's badge slot, which the award catalog's own core fills.\r\nexport const AWARD_DEFINITION_OWNER_KIND = 'awardDefinition' as const satisfies PictureOwnerKind;\r\n\r\n// A team's gallery, which the team's deletion retires whole.\r\nexport const LEAGUE_TEAM_GALLERY_OWNER_KIND = 'leagueTeam' as const satisfies GalleryOwnerKind;\r\n\r\n/** Whether an owner holds a gallery, as opposed to a single picture slot. */\r\nexport function isGalleryOwnerKind(kind: PictureOwnerKind): kind is GalleryOwnerKind {\r\n return (GALLERY_OWNER_KIND_KEYS as readonly PictureOwnerKind[]).includes(kind);\r\n}\r\n"]}
1
+ {"version":3,"file":"picture-owners.js","sourceRoot":"","sources":["../../src/types/picture-owners.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,iGAAiG;AACjG,iGAAiG;AAEjG,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,aAAa;IACb,YAAY;IACZ,aAAa;IACb,cAAc;IACd,iBAAiB;CACT,CAAC;AAGX,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,aAAa;IACb,YAAY;IACZ,aAAa;CACiC,CAAC;AAIjD,mEAAmE;AACnE,MAAM,CAAC,MAAM,2BAA2B,GAAG,iBAAqD,CAAC;AAEjG,6DAA6D;AAC7D,MAAM,CAAC,MAAM,8BAA8B,GAAG,YAAgD,CAAC;AAE/F,6EAA6E;AAC7E,MAAM,UAAU,kBAAkB,CAAC,IAAsB;IACvD,OAAQ,uBAAuD,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AACjF,CAAC","sourcesContent":["// Which document owns a picture (ARCH-102). An upload's target, its indexes, and a picture's\n// removal take the full union; a gallery picture document takes the gallery subset, because only\n// those three owners hold a gallery — a location and an award hold one picture in a single slot.\n\nexport const PICTURE_OWNER_KIND_KEYS = [\n 'sportLeague',\n 'leagueTeam',\n 'landingPage',\n 'gameLocation',\n 'awardDefinition',\n] as const;\nexport type PictureOwnerKind = (typeof PICTURE_OWNER_KIND_KEYS)[number];\n\nexport const GALLERY_OWNER_KIND_KEYS = [\n 'sportLeague',\n 'leagueTeam',\n 'landingPage',\n] as const satisfies readonly PictureOwnerKind[];\nexport type GalleryOwnerKind = (typeof GALLERY_OWNER_KIND_KEYS)[number];\nexport type PictureSlotOwnerKind = Exclude<PictureOwnerKind, GalleryOwnerKind>;\n\n// An award's badge slot, which the award catalog's own core fills.\nexport const AWARD_DEFINITION_OWNER_KIND = 'awardDefinition' as const satisfies PictureOwnerKind;\n\n// A team's gallery, which the team's deletion retires whole.\nexport const LEAGUE_TEAM_GALLERY_OWNER_KIND = 'leagueTeam' as const satisfies GalleryOwnerKind;\n\n/** Whether an owner holds a gallery, as opposed to a single picture slot. */\nexport function isGalleryOwnerKind(kind: PictureOwnerKind): kind is GalleryOwnerKind {\n return (GALLERY_OWNER_KIND_KEYS as readonly PictureOwnerKind[]).includes(kind);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"sports.js","sourceRoot":"","sources":["../../src/types/sports.ts"],"names":[],"mappings":"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","sourcesContent":["// The canonical sport discriminator (ARCH-108), and each sport's declared vocabulary (SPORT-007).\r\n//\r\n// `leagueType` identifies a sport everywhere. The lowercase sport key is the storage key derived\r\n// from it — the `sportHallOfFame/{sportKey}` and `historicalStats/{sportKey}` document ids and the\r\n// `lifetimeStats.{sportKey}` field. Both directions of the mapping live here, never in an inline\r\n// conditional. What each sport DOES belongs to its sport adapter (SPORT-003), not to this file.\r\n\r\nexport const LEAGUE_TYPE_KEYS = ['Basketball', 'Curling', 'Football'] as const;\r\nexport type LeagueType = (typeof LEAGUE_TYPE_KEYS)[number];\r\n\r\nexport const SPORT_KEYS = ['basketball', 'curling', 'football'] as const;\r\nexport type SportKey = (typeof SPORT_KEYS)[number];\r\n\r\nexport const SPORT_KEY_BY_LEAGUE_TYPE = {\r\n Basketball: 'basketball',\r\n Curling: 'curling',\r\n Football: 'football',\r\n} as const satisfies Record<LeagueType, SportKey>;\r\n\r\nexport const LEAGUE_TYPE_BY_SPORT_KEY = {\r\n basketball: 'Basketball',\r\n curling: 'Curling',\r\n football: 'Football',\r\n} as const satisfies Record<SportKey, LeagueType>;\r\n\r\n// The glyph a sport renders, named by its adapter's `display.iconKey`. The app's one sport-icon\r\n// component maps each key to a glyph, so no surface picks an icon by sport.\r\nexport const SPORT_ICON_KEYS = ['basketballBall', 'curlingStone', 'footballBall'] as const;\r\nexport type SportIconKey = (typeof SPORT_ICON_KEYS)[number];\r\n\r\n// Every counter a stat line holds, across every sport. Each sport's team and player counters are a\r\n// subset; a derived field (basketball's `fantasyPoints`) is not a counter.\r\nexport const STAT_COUNTER_KEYS = ['score', 'timeouts', 'points', 'rebounds', 'assists'] as const;\r\nexport type StatCounterKey = (typeof STAT_COUNTER_KEYS)[number];\r\n\r\n// ---------------------------------------------------------------------------------------------\r\n// Basketball — player-scored: a team's score is the sum of its players' points.\r\n// ---------------------------------------------------------------------------------------------\r\n\r\nexport const BASKETBALL_TEAM_STAT_KEYS = ['score', 'timeouts'] as const satisfies readonly StatCounterKey[];\r\nexport type BasketballTeamStatKey = (typeof BASKETBALL_TEAM_STAT_KEYS)[number];\r\n\r\nexport const BASKETBALL_PLAYER_STAT_KEYS = ['points', 'rebounds', 'assists'] as const satisfies readonly StatCounterKey[];\r\nexport type BasketballPlayerStatKey = (typeof BASKETBALL_PLAYER_STAT_KEYS)[number];\r\n\r\n// A bulk edit names the player counter it moves.\r\nexport const BASKETBALL_BULK_STAT_KEYS = BASKETBALL_PLAYER_STAT_KEYS;\r\nexport type BasketballBulkStatKey = BasketballPlayerStatKey;\r\n\r\nexport const BASKETBALL_ONE_POINT_EVENT_TYPE = '1PT';\r\nexport const BASKETBALL_TWO_POINT_EVENT_TYPE = '2PT';\r\nexport const BASKETBALL_THREE_POINT_EVENT_TYPE = '3PT';\r\n// Only a two- or three-point shot may name the teammate who assisted it.\r\nexport const BASKETBALL_ASSISTABLE_SHOT_EVENT_TYPE_KEYS = [\r\n BASKETBALL_TWO_POINT_EVENT_TYPE,\r\n BASKETBALL_THREE_POINT_EVENT_TYPE,\r\n] as const;\r\nexport const BASKETBALL_SCORE_EVENT_TYPE_KEYS = [\r\n BASKETBALL_ONE_POINT_EVENT_TYPE,\r\n ...BASKETBALL_ASSISTABLE_SHOT_EVENT_TYPE_KEYS,\r\n] as const;\r\nexport const BASKETBALL_REBOUND_EVENT_TYPE = 'REBOUND';\r\nexport const BASKETBALL_ASSIST_EVENT_TYPE = 'ASSIST';\r\nexport const BASKETBALL_PLAYER_TALLY_EVENT_TYPE_KEYS = [\r\n BASKETBALL_REBOUND_EVENT_TYPE,\r\n BASKETBALL_ASSIST_EVENT_TYPE,\r\n] as const;\r\n// A timeout is the team's; it names no player.\r\nexport const BASKETBALL_TIMEOUT_EVENT_TYPE = 'TIMEOUT';\r\nexport const BASKETBALL_BULK_EDIT_EVENT_TYPE = 'BULK_EDIT';\r\n\r\nexport const BASKETBALL_STAT_EVENT_TYPE_KEYS = [\r\n ...BASKETBALL_SCORE_EVENT_TYPE_KEYS,\r\n ...BASKETBALL_PLAYER_TALLY_EVENT_TYPE_KEYS,\r\n BASKETBALL_TIMEOUT_EVENT_TYPE,\r\n BASKETBALL_BULK_EDIT_EVENT_TYPE,\r\n] as const;\r\nexport type BasketballStatEventType = (typeof BASKETBALL_STAT_EVENT_TYPE_KEYS)[number];\r\n\r\n// ---------------------------------------------------------------------------------------------\r\n// Curling — team-scored by end; no player lines.\r\n// ---------------------------------------------------------------------------------------------\r\n\r\nexport const CURLING_TEAM_STAT_KEYS = ['score'] as const satisfies readonly StatCounterKey[];\r\nexport type CurlingTeamStatKey = (typeof CURLING_TEAM_STAT_KEYS)[number];\r\nexport const CURLING_PLAYER_STAT_KEYS = [] as const satisfies readonly StatCounterKey[];\r\n\r\nexport const CURLING_ROUND_SCORE_EVENT_TYPE = 'CURLING_ROUND_SCORE';\r\n// An end nobody scored; it names no team.\r\nexport const CURLING_ROUND_BLANK_EVENT_TYPE = 'CURLING_ROUND_BLANK';\r\nexport const CURLING_STAT_EVENT_TYPE_KEYS = [\r\n CURLING_ROUND_SCORE_EVENT_TYPE,\r\n CURLING_ROUND_BLANK_EVENT_TYPE,\r\n] as const;\r\nexport type CurlingStatEventType = (typeof CURLING_STAT_EVENT_TYPE_KEYS)[number];\r\n\r\n// ---------------------------------------------------------------------------------------------\r\n// Football — team-scored; no player lines.\r\n// ---------------------------------------------------------------------------------------------\r\n\r\nexport const FOOTBALL_TEAM_STAT_KEYS = ['score'] as const satisfies readonly StatCounterKey[];\r\nexport type FootballTeamStatKey = (typeof FOOTBALL_TEAM_STAT_KEYS)[number];\r\nexport const FOOTBALL_PLAYER_STAT_KEYS = [] as const satisfies readonly StatCounterKey[];\r\n\r\n// Named by the points each adds: a +2 is a safety or a conversion, and the game records only points.\r\nexport const FOOTBALL_SIX_POINT_EVENT_TYPE = 'FOOTBALL_6PT';\r\nexport const FOOTBALL_TWO_POINT_EVENT_TYPE = 'FOOTBALL_2PT';\r\nexport const FOOTBALL_ONE_POINT_EVENT_TYPE = 'FOOTBALL_1PT';\r\nexport const FOOTBALL_SCORE_EVENT_TYPE_KEYS = [\r\n FOOTBALL_SIX_POINT_EVENT_TYPE,\r\n FOOTBALL_TWO_POINT_EVENT_TYPE,\r\n FOOTBALL_ONE_POINT_EVENT_TYPE,\r\n] as const;\r\nexport const FOOTBALL_BULK_EDIT_EVENT_TYPE = 'FOOTBALL_BULK_EDIT';\r\nexport const FOOTBALL_STAT_EVENT_TYPE_KEYS = [\r\n ...FOOTBALL_SCORE_EVENT_TYPE_KEYS,\r\n FOOTBALL_BULK_EDIT_EVENT_TYPE,\r\n] as const;\r\nexport type FootballStatEventType = (typeof FOOTBALL_STAT_EVENT_TYPE_KEYS)[number];\r\n"]}
1
+ {"version":3,"file":"sports.js","sourceRoot":"","sources":["../../src/types/sports.ts"],"names":[],"mappings":"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","sourcesContent":["// The canonical sport discriminator (ARCH-108), and each sport's declared vocabulary (SPORT-007).\n//\n// `leagueType` identifies a sport everywhere. The lowercase sport key is the storage key derived\n// from it — the `sportHallOfFame/{sportKey}` and `historicalStats/{sportKey}` document ids and the\n// `lifetimeStats.{sportKey}` field. Both directions of the mapping live here, never in an inline\n// conditional. What each sport DOES belongs to its sport adapter (SPORT-003), not to this file.\n\nexport const LEAGUE_TYPE_KEYS = ['Basketball', 'Curling', 'Football'] as const;\nexport type LeagueType = (typeof LEAGUE_TYPE_KEYS)[number];\n\nexport const SPORT_KEYS = ['basketball', 'curling', 'football'] as const;\nexport type SportKey = (typeof SPORT_KEYS)[number];\n\nexport const SPORT_KEY_BY_LEAGUE_TYPE = {\n Basketball: 'basketball',\n Curling: 'curling',\n Football: 'football',\n} as const satisfies Record<LeagueType, SportKey>;\n\nexport const LEAGUE_TYPE_BY_SPORT_KEY = {\n basketball: 'Basketball',\n curling: 'Curling',\n football: 'Football',\n} as const satisfies Record<SportKey, LeagueType>;\n\n// The glyph a sport renders, named by its adapter's `display.iconKey`. The app's one sport-icon\n// component maps each key to a glyph, so no surface picks an icon by sport.\nexport const SPORT_ICON_KEYS = ['basketballBall', 'curlingStone', 'footballBall'] as const;\nexport type SportIconKey = (typeof SPORT_ICON_KEYS)[number];\n\n// Every counter a stat line holds, across every sport. Each sport's team and player counters are a\n// subset; a derived field (basketball's `fantasyPoints`) is not a counter.\nexport const STAT_COUNTER_KEYS = ['score', 'timeouts', 'points', 'rebounds', 'assists'] as const;\nexport type StatCounterKey = (typeof STAT_COUNTER_KEYS)[number];\n\n// ---------------------------------------------------------------------------------------------\n// Basketball — player-scored: a team's score is the sum of its players' points.\n// ---------------------------------------------------------------------------------------------\n\nexport const BASKETBALL_TEAM_STAT_KEYS = ['score', 'timeouts'] as const satisfies readonly StatCounterKey[];\nexport type BasketballTeamStatKey = (typeof BASKETBALL_TEAM_STAT_KEYS)[number];\n\nexport const BASKETBALL_PLAYER_STAT_KEYS = ['points', 'rebounds', 'assists'] as const satisfies readonly StatCounterKey[];\nexport type BasketballPlayerStatKey = (typeof BASKETBALL_PLAYER_STAT_KEYS)[number];\n\n// A bulk edit names the player counter it moves.\nexport const BASKETBALL_BULK_STAT_KEYS = BASKETBALL_PLAYER_STAT_KEYS;\nexport type BasketballBulkStatKey = BasketballPlayerStatKey;\n\nexport const BASKETBALL_ONE_POINT_EVENT_TYPE = '1PT';\nexport const BASKETBALL_TWO_POINT_EVENT_TYPE = '2PT';\nexport const BASKETBALL_THREE_POINT_EVENT_TYPE = '3PT';\n// Only a two- or three-point shot may name the teammate who assisted it.\nexport const BASKETBALL_ASSISTABLE_SHOT_EVENT_TYPE_KEYS = [\n BASKETBALL_TWO_POINT_EVENT_TYPE,\n BASKETBALL_THREE_POINT_EVENT_TYPE,\n] as const;\nexport const BASKETBALL_SCORE_EVENT_TYPE_KEYS = [\n BASKETBALL_ONE_POINT_EVENT_TYPE,\n ...BASKETBALL_ASSISTABLE_SHOT_EVENT_TYPE_KEYS,\n] as const;\nexport const BASKETBALL_REBOUND_EVENT_TYPE = 'REBOUND';\nexport const BASKETBALL_ASSIST_EVENT_TYPE = 'ASSIST';\nexport const BASKETBALL_PLAYER_TALLY_EVENT_TYPE_KEYS = [\n BASKETBALL_REBOUND_EVENT_TYPE,\n BASKETBALL_ASSIST_EVENT_TYPE,\n] as const;\n// A timeout is the team's; it names no player.\nexport const BASKETBALL_TIMEOUT_EVENT_TYPE = 'TIMEOUT';\nexport const BASKETBALL_BULK_EDIT_EVENT_TYPE = 'BULK_EDIT';\n\nexport const BASKETBALL_STAT_EVENT_TYPE_KEYS = [\n ...BASKETBALL_SCORE_EVENT_TYPE_KEYS,\n ...BASKETBALL_PLAYER_TALLY_EVENT_TYPE_KEYS,\n BASKETBALL_TIMEOUT_EVENT_TYPE,\n BASKETBALL_BULK_EDIT_EVENT_TYPE,\n] as const;\nexport type BasketballStatEventType = (typeof BASKETBALL_STAT_EVENT_TYPE_KEYS)[number];\n\n// ---------------------------------------------------------------------------------------------\n// Curling — team-scored by end; no player lines.\n// ---------------------------------------------------------------------------------------------\n\nexport const CURLING_TEAM_STAT_KEYS = ['score'] as const satisfies readonly StatCounterKey[];\nexport type CurlingTeamStatKey = (typeof CURLING_TEAM_STAT_KEYS)[number];\nexport const CURLING_PLAYER_STAT_KEYS = [] as const satisfies readonly StatCounterKey[];\n\nexport const CURLING_ROUND_SCORE_EVENT_TYPE = 'CURLING_ROUND_SCORE';\n// An end nobody scored; it names no team.\nexport const CURLING_ROUND_BLANK_EVENT_TYPE = 'CURLING_ROUND_BLANK';\nexport const CURLING_STAT_EVENT_TYPE_KEYS = [\n CURLING_ROUND_SCORE_EVENT_TYPE,\n CURLING_ROUND_BLANK_EVENT_TYPE,\n] as const;\nexport type CurlingStatEventType = (typeof CURLING_STAT_EVENT_TYPE_KEYS)[number];\n\n// ---------------------------------------------------------------------------------------------\n// Football — team-scored; no player lines.\n// ---------------------------------------------------------------------------------------------\n\nexport const FOOTBALL_TEAM_STAT_KEYS = ['score'] as const satisfies readonly StatCounterKey[];\nexport type FootballTeamStatKey = (typeof FOOTBALL_TEAM_STAT_KEYS)[number];\nexport const FOOTBALL_PLAYER_STAT_KEYS = [] as const satisfies readonly StatCounterKey[];\n\n// Named by the points each adds: a +2 is a safety or a conversion, and the game records only points.\nexport const FOOTBALL_SIX_POINT_EVENT_TYPE = 'FOOTBALL_6PT';\nexport const FOOTBALL_TWO_POINT_EVENT_TYPE = 'FOOTBALL_2PT';\nexport const FOOTBALL_ONE_POINT_EVENT_TYPE = 'FOOTBALL_1PT';\nexport const FOOTBALL_SCORE_EVENT_TYPE_KEYS = [\n FOOTBALL_SIX_POINT_EVENT_TYPE,\n FOOTBALL_TWO_POINT_EVENT_TYPE,\n FOOTBALL_ONE_POINT_EVENT_TYPE,\n] as const;\nexport const FOOTBALL_BULK_EDIT_EVENT_TYPE = 'FOOTBALL_BULK_EDIT';\nexport const FOOTBALL_STAT_EVENT_TYPE_KEYS = [\n ...FOOTBALL_SCORE_EVENT_TYPE_KEYS,\n FOOTBALL_BULK_EDIT_EVENT_TYPE,\n] as const;\nexport type FootballStatEventType = (typeof FOOTBALL_STAT_EVENT_TYPE_KEYS)[number];\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"stats.js","sourceRoot":"","sources":["../../src/types/stats.ts"],"names":[],"mappings":"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","sourcesContent":["// Canonical unions of the stat event store and the stats operations (ARCH-102). What each sport's\r\n// own events and lines are is ./sports.ts; these are the sport-agnostic halves every sport shares.\r\n\r\nimport { CAPTAIN_TEAM_ROLES, SYSTEM_ROLE_KEYS } from './unions.js';\r\n\r\n// The two store-level events, members of every sport's event union. A void undoes the one event it\r\n// targets; a correction adds a signed value to one correctable field of one line.\r\nexport const STAT_VOID_EVENT_TYPE = 'STAT_VOID';\r\nexport const STAT_CORRECTION_EVENT_TYPE = 'STAT_CORRECTION';\r\nexport const STAT_STORE_EVENT_TYPE_KEYS = [STAT_VOID_EVENT_TYPE, STAT_CORRECTION_EVENT_TYPE] as const;\r\nexport type StatStoreEventType = (typeof STAT_STORE_EVENT_TYPE_KEYS)[number];\r\n\r\n// The fields of a stored stat event the writing core derives — its place in the replay, and who\r\n// recorded it under which authority — so a caller never sends them (ARCH-106).\r\nexport const STAT_EVENT_RECORDER_FIELD_KEYS = [\r\n 'sequence',\r\n 'acceptedAt',\r\n 'recordedByUserId',\r\n 'recordedByAuthority',\r\n 'recordedByTeamId',\r\n] as const;\r\nexport type StatEventRecorderField = (typeof STAT_EVENT_RECORDER_FIELD_KEYS)[number];\r\n\r\n// The name snapshots only a copied event carries; nothing writes one on a new event (SPORT-104).\r\nexport const STAT_EVENT_SNAPSHOT_FIELD_KEYS = ['displayNameSnapshot', 'assistedByDisplayNameSnapshot'] as const;\r\nexport type StatEventSnapshotField = (typeof STAT_EVENT_SNAPSHOT_FIELD_KEYS)[number];\r\n\r\n// Who appended a correction: an admin reconciling a game, or the copy that carries a recorded\r\n// result its events do not explain.\r\nexport const STAT_CORRECTION_SOURCE_KEYS = ['admin', 'migration'] as const;\r\nexport type StatCorrectionSource = (typeof STAT_CORRECTION_SOURCE_KEYS)[number];\r\n\r\n// The authority the writing core confirmed for an event: a global role, or a team role on one of\r\n// the game's teams.\r\nexport const STAT_EVENT_AUTHORITY_KEYS = [...SYSTEM_ROLE_KEYS, ...CAPTAIN_TEAM_ROLES] as const;\r\nexport type StatEventAuthority = (typeof STAT_EVENT_AUTHORITY_KEYS)[number];\r\n\r\n// Every kind of stat line: the four player kinds, then the two team kinds. A sport's line of each\r\n// kind is declared once, in `SPORT_LINE_SCHEMAS`; a kind a sport has no line for is `never` there.\r\nexport const PLAYER_STAT_LINE_KIND_KEYS = ['gamePlayer', 'membershipRow', 'lifetime', 'hallOfFame'] as const;\r\nexport const TEAM_STAT_LINE_KIND_KEYS = ['gameTeam', 'teamSeason'] as const;\r\nexport const STAT_LINE_KIND_KEYS = [...PLAYER_STAT_LINE_KIND_KEYS, ...TEAM_STAT_LINE_KIND_KEYS] as const;\r\nexport type StatLineKind = (typeof STAT_LINE_KIND_KEYS)[number];\r\nexport type PlayerStatLineKind = (typeof PLAYER_STAT_LINE_KIND_KEYS)[number];\r\n\r\n// One logical run of a stats action, recorded on its `statsOperations` document.\r\nexport const STATS_OPERATION_KIND_KEYS = [\r\n 'leagueStatsRebuild',\r\n 'lifetimeStatsRebuild',\r\n 'hallOfFameRebuild',\r\n 'statsValidation',\r\n 'gameStatsCorrection',\r\n 'gameStatsRecalculation',\r\n 'historicalStatsUpdate',\r\n 'ghostPlayerStats',\r\n 'gameDeletion',\r\n] as const;\r\nexport type StatsOperationKind = (typeof STATS_OPERATION_KIND_KEYS)[number];\r\n\r\n// The rebuilds: each replaces one destination from the finalized games, a unit at a time, and shares\r\n// one paged run.\r\nexport const STATS_REBUILD_OPERATION_KIND_KEYS = [\r\n 'leagueStatsRebuild',\r\n 'lifetimeStatsRebuild',\r\n 'hallOfFameRebuild',\r\n] as const satisfies readonly StatsOperationKind[];\r\nexport type StatsRebuildOperationKind = (typeof STATS_REBUILD_OPERATION_KIND_KEYS)[number];\r\n\r\nexport const STATS_OPERATION_STATUS_KEYS = ['running', 'complete', 'stopped'] as const;\r\nexport type StatsOperationStatus = (typeof STATS_OPERATION_STATUS_KEYS)[number];\r\n\r\n// A validation run checks every game first, then the destinations those games feed.\r\nexport const STATS_VALIDATION_PHASE_KEYS = ['games', 'destinations'] as const;\r\nexport type StatsValidationPhase = (typeof STATS_VALIDATION_PHASE_KEYS)[number];\r\n\r\n// A ghost has one operation for both of its ways out, so a link and a removal refuse each other.\r\nexport const GHOST_STATS_OPERATION_ACTION_KEYS = ['link', 'removal'] as const;\r\nexport type GhostStatsOperationAction = (typeof GHOST_STATS_OPERATION_ACTION_KEYS)[number];\r\n\r\n// Why a scoring write refused an event. The first ten are a sport adapter's `validate`; the last\r\n// four are the store driver's own: an id already stored, or earlier in the batch, with different\r\n// content; a void whose target is missing, a void, or a correction; a correction sent to a scoring\r\n// entry, which only the game-reconcile core appends; and a void that would leave a stat below zero,\r\n// because a later edit already moved its target's field down.\r\nexport const STAT_EVENT_REFUSAL_KEYS = [\r\n 'eventTypeNotInSport',\r\n 'teamNotInGame',\r\n 'playerNotRostered',\r\n 'assisterNotRostered',\r\n 'assistNotAllowed',\r\n 'playerNotAllowed',\r\n 'roundNotNext',\r\n 'roundsComplete',\r\n 'bulkValueInvalid',\r\n 'bulkStale',\r\n 'eventIdConflict',\r\n 'voidTargetNotSportEvent',\r\n 'correctionNotAccepted',\r\n 'undoBelowZero',\r\n] as const;\r\nexport type StatEventRefusalCode = (typeof STAT_EVENT_REFUSAL_KEYS)[number];\r\n\r\n// Why a game may not be finalized yet — a sport played in rounds with an end still unrecorded.\r\nexport const FINALIZATION_NOT_READY_KEYS = ['roundsIncomplete'] as const;\r\nexport type FinalizationNotReadyReason = (typeof FINALIZATION_NOT_READY_KEYS)[number];\r\n\r\n// The scoring panel a sport's adapter names; the scoring screen renders one component per kind.\r\nexport const SCORING_PANEL_KIND_KEYS = ['playerEvents', 'teamPoints', 'rounds'] as const;\r\nexport type ScoringPanelKind = (typeof SCORING_PANEL_KIND_KEYS)[number];\r\n\r\n// The bulk-entry form body a sport's adapter names; the bulk-entry dialog renders one per kind.\r\nexport const BULK_ENTRY_KIND_KEYS = ['playerLines', 'teamScores', 'rounds'] as const;\r\nexport type BulkEntryKind = (typeof BULK_ENTRY_KIND_KEYS)[number];\r\n\r\n// What a scoring write did with each event it was sent: stored it now, or found it already stored\r\n// by an earlier send and acknowledged it without applying it again.\r\nexport const RECORDED_STAT_EVENT_OUTCOME_KEYS = ['recorded', 'alreadyRecorded'] as const;\r\nexport type RecordedStatEventOutcome = (typeof RECORDED_STAT_EVENT_OUTCOME_KEYS)[number];\r\n\r\n// What finalize did: made the game's numbers permanent, or found it already finalized and changed\r\n// nothing (SPORT-203).\r\nexport const GAME_FINALIZE_OUTCOME_KEYS = ['finalized', 'alreadyFinalized'] as const;\r\nexport type GameFinalizeOutcome = (typeof GAME_FINALIZE_OUTCOME_KEYS)[number];\r\n\r\n// What reopen did: took a finalized game's numbers back out, or found the game already open and\r\n// changed nothing.\r\nexport const GAME_REOPEN_OUTCOME_KEYS = ['reopened', 'alreadyOpen'] as const;\r\nexport type GameReopenOutcome = (typeof GAME_REOPEN_OUTCOME_KEYS)[number];\r\n\r\n// Why a path that makes numbers permanent, or takes them back, refused a game whose stored numbers\r\n// disagree with what produced them — something changed them outside the app, and an admin\r\n// reconciles the game in Fix stats first. Finalize, reopen, and a ghost link never adopt or discard\r\n// an unexplained difference (ENG-009):\r\n// statsOutOfBand the rollup or score mirror is not the replay of the game's events or record\r\n// finalRecordMissing a completed game has no finalized record\r\n// aggregateBelowZero a stored derived line holds less than the game put into it\r\nexport const STATS_INTEGRITY_REFUSAL_KEYS = ['statsOutOfBand', 'finalRecordMissing', 'aggregateBelowZero'] as const;\r\nexport type StatsIntegrityRefusal = (typeof STATS_INTEGRITY_REFUSAL_KEYS)[number];\r\n\r\n// What a game repair in Fix stats did: changed the game (`reconciled`), found nothing to change\r\n// (`unchanged`), or found its operation already committed and acknowledged the retry\r\n// (`alreadyApplied`).\r\nexport const GAME_STATS_REPAIR_OUTCOME_KEYS = ['reconciled', 'unchanged', 'alreadyApplied'] as const;\r\nexport type GameStatsRepairOutcome = (typeof GAME_STATS_REPAIR_OUTCOME_KEYS)[number];\r\n\r\n// Why a game repair in Fix stats was refused, in the refusal's details, so the form says what to do:\r\n// eventUnreadable an event document no longer parses or replays — fix it in the console first\r\n// sourcesDisagree the record, the stored rollup, and the events all disagree: no one of them\r\n// says what finalize applied\r\n// scoreAmbiguous keep-stored: the team line's score and the score mirror were both edited, to\r\n// different values — give the score as a target instead\r\n// fieldNotCorrectable a field the sport's adapter does not let a correction change — restore from\r\n// the events instead, or reopen and re-score\r\n// lineNotRostered a line for someone not on the game's roster\r\n// teamNotInGame a target names a team the game does not hold\r\n// gameTooLarge the repair would not fit in one commit\r\nexport const GAME_REPAIR_REFUSAL_KEYS = [\r\n 'eventUnreadable',\r\n 'sourcesDisagree',\r\n 'scoreAmbiguous',\r\n 'fieldNotCorrectable',\r\n 'lineNotRostered',\r\n 'teamNotInGame',\r\n 'gameTooLarge',\r\n] as const;\r\nexport type GameRepairRefusal = (typeof GAME_REPAIR_REFUSAL_KEYS)[number];\r\n\r\n// What a ghost link did with the team's memberships: the ghost's membership became the account's\r\n// (`handedOver`); the account had none and the ghost none either, so the closed-league link gave the\r\n// account one (`created`); both held one and one survives with the higher role (`merged`); or only\r\n// the account held one, and it stands (`kept`).\r\nexport const GHOST_LINK_MEMBERSHIP_OUTCOME_KEYS = ['handedOver', 'created', 'merged', 'kept'] as const;\r\nexport type GhostLinkMembershipOutcome = (typeof GHOST_LINK_MEMBERSHIP_OUTCOME_KEYS)[number];\r\n\r\n// What the migration's placement of an unplaceable V1 stat line did: `alreadyResolved` when the game\r\n// already held the entry it would write — nothing was written.\r\nexport const UNROSTERED_STAT_LINE_RESOLUTION_OUTCOME_KEYS = ['resolved', 'alreadyResolved'] as const;\r\nexport type UnrosteredStatLineResolutionOutcome = (typeof UNROSTERED_STAT_LINE_RESOLUTION_OUTCOME_KEYS)[number];\r\n\r\n// Why a ghost's removal was refused, in the refusal's details, so the client answers each one: the\r\n// ghost has played (link it instead) or its removal stopped on finding it had; a game the removal\r\n// needs is held by another operation (try again shortly); the ghost is being, or was, linked; the\r\n// ghost is the team's captain (change the captain first).\r\nexport const GHOST_REMOVAL_REFUSAL_KEYS = ['ghostHasPlayed', 'removalStopped', 'gameHeld', 'ghostBeingLinked', 'captainNotRemovable'] as const;\r\nexport type GhostRemovalRefusal = (typeof GHOST_REMOVAL_REFUSAL_KEYS)[number];\r\n\r\n"]}
1
+ {"version":3,"file":"stats.js","sourceRoot":"","sources":["../../src/types/stats.ts"],"names":[],"mappings":"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","sourcesContent":["// Canonical unions of the stat event store and the stats operations (ARCH-102). What each sport's\n// own events and lines are is ./sports.ts; these are the sport-agnostic halves every sport shares.\n\nimport { CAPTAIN_TEAM_ROLES, SYSTEM_ROLE_KEYS } from './unions.js';\n\n// The two store-level events, members of every sport's event union. A void undoes the one event it\n// targets; a correction adds a signed value to one correctable field of one line.\nexport const STAT_VOID_EVENT_TYPE = 'STAT_VOID';\nexport const STAT_CORRECTION_EVENT_TYPE = 'STAT_CORRECTION';\nexport const STAT_STORE_EVENT_TYPE_KEYS = [STAT_VOID_EVENT_TYPE, STAT_CORRECTION_EVENT_TYPE] as const;\nexport type StatStoreEventType = (typeof STAT_STORE_EVENT_TYPE_KEYS)[number];\n\n// The fields of a stored stat event the writing core derives — its place in the replay, and who\n// recorded it under which authority — so a caller never sends them (ARCH-106).\nexport const STAT_EVENT_RECORDER_FIELD_KEYS = [\n 'sequence',\n 'acceptedAt',\n 'recordedByUserId',\n 'recordedByAuthority',\n 'recordedByTeamId',\n] as const;\nexport type StatEventRecorderField = (typeof STAT_EVENT_RECORDER_FIELD_KEYS)[number];\n\n// The name snapshots only a copied event carries; nothing writes one on a new event (SPORT-104).\nexport const STAT_EVENT_SNAPSHOT_FIELD_KEYS = ['displayNameSnapshot', 'assistedByDisplayNameSnapshot'] as const;\nexport type StatEventSnapshotField = (typeof STAT_EVENT_SNAPSHOT_FIELD_KEYS)[number];\n\n// Who appended a correction: an admin reconciling a game, or the copy that carries a recorded\n// result its events do not explain.\nexport const STAT_CORRECTION_SOURCE_KEYS = ['admin', 'migration'] as const;\nexport type StatCorrectionSource = (typeof STAT_CORRECTION_SOURCE_KEYS)[number];\n\n// The authority the writing core confirmed for an event: a global role, or a team role on one of\n// the game's teams.\nexport const STAT_EVENT_AUTHORITY_KEYS = [...SYSTEM_ROLE_KEYS, ...CAPTAIN_TEAM_ROLES] as const;\nexport type StatEventAuthority = (typeof STAT_EVENT_AUTHORITY_KEYS)[number];\n\n// Every kind of stat line: the four player kinds, then the two team kinds. A sport's line of each\n// kind is declared once, in `SPORT_LINE_SCHEMAS`; a kind a sport has no line for is `never` there.\nexport const PLAYER_STAT_LINE_KIND_KEYS = ['gamePlayer', 'membershipRow', 'lifetime', 'hallOfFame'] as const;\nexport const TEAM_STAT_LINE_KIND_KEYS = ['gameTeam', 'teamSeason'] as const;\nexport const STAT_LINE_KIND_KEYS = [...PLAYER_STAT_LINE_KIND_KEYS, ...TEAM_STAT_LINE_KIND_KEYS] as const;\nexport type StatLineKind = (typeof STAT_LINE_KIND_KEYS)[number];\nexport type PlayerStatLineKind = (typeof PLAYER_STAT_LINE_KIND_KEYS)[number];\n\n// One logical run of a stats action, recorded on its `statsOperations` document.\nexport const STATS_OPERATION_KIND_KEYS = [\n 'leagueStatsRebuild',\n 'lifetimeStatsRebuild',\n 'hallOfFameRebuild',\n 'statsValidation',\n 'gameStatsCorrection',\n 'gameStatsRecalculation',\n 'historicalStatsUpdate',\n 'ghostPlayerStats',\n 'gameDeletion',\n] as const;\nexport type StatsOperationKind = (typeof STATS_OPERATION_KIND_KEYS)[number];\n\n// The rebuilds: each replaces one destination from the finalized games, a unit at a time, and shares\n// one paged run.\nexport const STATS_REBUILD_OPERATION_KIND_KEYS = [\n 'leagueStatsRebuild',\n 'lifetimeStatsRebuild',\n 'hallOfFameRebuild',\n] as const satisfies readonly StatsOperationKind[];\nexport type StatsRebuildOperationKind = (typeof STATS_REBUILD_OPERATION_KIND_KEYS)[number];\n\nexport const STATS_OPERATION_STATUS_KEYS = ['running', 'complete', 'stopped'] as const;\nexport type StatsOperationStatus = (typeof STATS_OPERATION_STATUS_KEYS)[number];\n\n// A validation run checks every game first, then the destinations those games feed.\nexport const STATS_VALIDATION_PHASE_KEYS = ['games', 'destinations'] as const;\nexport type StatsValidationPhase = (typeof STATS_VALIDATION_PHASE_KEYS)[number];\n\n// A ghost has one operation for both of its ways out, so a link and a removal refuse each other.\nexport const GHOST_STATS_OPERATION_ACTION_KEYS = ['link', 'removal'] as const;\nexport type GhostStatsOperationAction = (typeof GHOST_STATS_OPERATION_ACTION_KEYS)[number];\n\n// Why a scoring write refused an event. The first ten are a sport adapter's `validate`; the last\n// four are the store driver's own: an id already stored, or earlier in the batch, with different\n// content; a void whose target is missing, a void, or a correction; a correction sent to a scoring\n// entry, which only the game-reconcile core appends; and a void that would leave a stat below zero,\n// because a later edit already moved its target's field down.\nexport const STAT_EVENT_REFUSAL_KEYS = [\n 'eventTypeNotInSport',\n 'teamNotInGame',\n 'playerNotRostered',\n 'assisterNotRostered',\n 'assistNotAllowed',\n 'playerNotAllowed',\n 'roundNotNext',\n 'roundsComplete',\n 'bulkValueInvalid',\n 'bulkStale',\n 'eventIdConflict',\n 'voidTargetNotSportEvent',\n 'correctionNotAccepted',\n 'undoBelowZero',\n] as const;\nexport type StatEventRefusalCode = (typeof STAT_EVENT_REFUSAL_KEYS)[number];\n\n// Why a game may not be finalized yet — a sport played in rounds with an end still unrecorded.\nexport const FINALIZATION_NOT_READY_KEYS = ['roundsIncomplete'] as const;\nexport type FinalizationNotReadyReason = (typeof FINALIZATION_NOT_READY_KEYS)[number];\n\n// The scoring panel a sport's adapter names; the scoring screen renders one component per kind.\nexport const SCORING_PANEL_KIND_KEYS = ['playerEvents', 'teamPoints', 'rounds'] as const;\nexport type ScoringPanelKind = (typeof SCORING_PANEL_KIND_KEYS)[number];\n\n// The bulk-entry form body a sport's adapter names; the bulk-entry dialog renders one per kind.\nexport const BULK_ENTRY_KIND_KEYS = ['playerLines', 'teamScores', 'rounds'] as const;\nexport type BulkEntryKind = (typeof BULK_ENTRY_KIND_KEYS)[number];\n\n// What a scoring write did with each event it was sent: stored it now, or found it already stored\n// by an earlier send and acknowledged it without applying it again.\nexport const RECORDED_STAT_EVENT_OUTCOME_KEYS = ['recorded', 'alreadyRecorded'] as const;\nexport type RecordedStatEventOutcome = (typeof RECORDED_STAT_EVENT_OUTCOME_KEYS)[number];\n\n// What finalize did: made the game's numbers permanent, or found it already finalized and changed\n// nothing (SPORT-203).\nexport const GAME_FINALIZE_OUTCOME_KEYS = ['finalized', 'alreadyFinalized'] as const;\nexport type GameFinalizeOutcome = (typeof GAME_FINALIZE_OUTCOME_KEYS)[number];\n\n// What reopen did: took a finalized game's numbers back out, or found the game already open and\n// changed nothing.\nexport const GAME_REOPEN_OUTCOME_KEYS = ['reopened', 'alreadyOpen'] as const;\nexport type GameReopenOutcome = (typeof GAME_REOPEN_OUTCOME_KEYS)[number];\n\n// Why a path that makes numbers permanent, or takes them back, refused a game whose stored numbers\n// disagree with what produced them — something changed them outside the app, and an admin\n// reconciles the game in Fix stats first. Finalize, reopen, and a ghost link never adopt or discard\n// an unexplained difference (ENG-009):\n// statsOutOfBand the rollup or score mirror is not the replay of the game's events or record\n// finalRecordMissing a completed game has no finalized record\n// aggregateBelowZero a stored derived line holds less than the game put into it\nexport const STATS_INTEGRITY_REFUSAL_KEYS = ['statsOutOfBand', 'finalRecordMissing', 'aggregateBelowZero'] as const;\nexport type StatsIntegrityRefusal = (typeof STATS_INTEGRITY_REFUSAL_KEYS)[number];\n\n// What a game repair in Fix stats did: changed the game (`reconciled`), found nothing to change\n// (`unchanged`), or found its operation already committed and acknowledged the retry\n// (`alreadyApplied`).\nexport const GAME_STATS_REPAIR_OUTCOME_KEYS = ['reconciled', 'unchanged', 'alreadyApplied'] as const;\nexport type GameStatsRepairOutcome = (typeof GAME_STATS_REPAIR_OUTCOME_KEYS)[number];\n\n// Why a game repair in Fix stats was refused, in the refusal's details, so the form says what to do:\n// eventUnreadable an event document no longer parses or replays — fix it in the console first\n// sourcesDisagree the record, the stored rollup, and the events all disagree: no one of them\n// says what finalize applied\n// scoreAmbiguous keep-stored: the team line's score and the score mirror were both edited, to\n// different values — give the score as a target instead\n// fieldNotCorrectable a field the sport's adapter does not let a correction change — restore from\n// the events instead, or reopen and re-score\n// lineNotRostered a line for someone not on the game's roster\n// teamNotInGame a target names a team the game does not hold\n// gameTooLarge the repair would not fit in one commit\nexport const GAME_REPAIR_REFUSAL_KEYS = [\n 'eventUnreadable',\n 'sourcesDisagree',\n 'scoreAmbiguous',\n 'fieldNotCorrectable',\n 'lineNotRostered',\n 'teamNotInGame',\n 'gameTooLarge',\n] as const;\nexport type GameRepairRefusal = (typeof GAME_REPAIR_REFUSAL_KEYS)[number];\n\n// What a ghost link did with the team's memberships: the ghost's membership became the account's\n// (`handedOver`); the account had none and the ghost none either, so the closed-league link gave the\n// account one (`created`); both held one and one survives with the higher role (`merged`); or only\n// the account held one, and it stands (`kept`).\nexport const GHOST_LINK_MEMBERSHIP_OUTCOME_KEYS = ['handedOver', 'created', 'merged', 'kept'] as const;\nexport type GhostLinkMembershipOutcome = (typeof GHOST_LINK_MEMBERSHIP_OUTCOME_KEYS)[number];\n\n// What the migration's placement of an unplaceable V1 stat line did: `alreadyResolved` when the game\n// already held the entry it would write — nothing was written.\nexport const UNROSTERED_STAT_LINE_RESOLUTION_OUTCOME_KEYS = ['resolved', 'alreadyResolved'] as const;\nexport type UnrosteredStatLineResolutionOutcome = (typeof UNROSTERED_STAT_LINE_RESOLUTION_OUTCOME_KEYS)[number];\n\n// Why a ghost's removal was refused, in the refusal's details, so the client answers each one: the\n// ghost has played (link it instead) or its removal stopped on finding it had; a game the removal\n// needs is held by another operation (try again shortly); the ghost is being, or was, linked; the\n// ghost is the team's captain (change the captain first).\nexport const GHOST_REMOVAL_REFUSAL_KEYS = ['ghostHasPlayed', 'removalStopped', 'gameHeld', 'ghostBeingLinked', 'captainNotRemovable'] as const;\nexport type GhostRemovalRefusal = (typeof GHOST_REMOVAL_REFUSAL_KEYS)[number];\n\n"]}
@@ -60,6 +60,8 @@ export declare const HARDWARE_LOAN_CONTRACT_STATUS_KEYS: readonly ["pending", "l
60
60
  export type HardwareLoanContractStatus = (typeof HARDWARE_LOAN_CONTRACT_STATUS_KEYS)[number];
61
61
  export declare const AWARD_GRANT_SKIP_REASON_KEYS: readonly ["noProfile", "ghost"];
62
62
  export type AwardGrantSkipReason = (typeof AWARD_GRANT_SKIP_REASON_KEYS)[number];
63
+ export declare const FEATURED_AWARD_SLOT_KEYS: readonly ["primary", "secondary"];
64
+ export type FeaturedAwardSlot = (typeof FEATURED_AWARD_SLOT_KEYS)[number];
63
65
  export declare const PENDING_HARDWARE_LOAN_CONTRACT_STATUS: "pending";
64
66
  export declare const LOANED_HARDWARE_LOAN_CONTRACT_STATUS: "loaned";
65
67
  export declare const RETURNED_HARDWARE_LOAN_CONTRACT_STATUS: "returned";
@@ -85,8 +87,8 @@ export declare const TEAM_ICON_KEYS: readonly ["Shield", "Swords", "Skull", "Gho
85
87
  export type TeamIcon = (typeof TEAM_ICON_KEYS)[number];
86
88
  export declare const PICTURE_VARIANT_KEYS: readonly ["sm", "md", "lg"];
87
89
  export type PictureVariantKey = (typeof PICTURE_VARIANT_KEYS)[number];
88
- export declare const GAME_TIER_DISCREPANCY_KIND_KEYS: readonly ["invalidEvent", "eventSequenceMismatch", "rollupMismatch", "scoreMirrorMismatch", "unrosteredStatLine", "fantasyPointsStale", "finalRecordMissing", "finalRecordMismatch", "participationSnapshotMismatch", "gameHeld"];
90
+ export declare const GAME_TIER_DISCREPANCY_KIND_KEYS: readonly ["gameUnreadable", "invalidEvent", "eventSequenceMismatch", "eventSequenceGap", "rollupMismatch", "scoreMirrorMismatch", "unrosteredStatLine", "fantasyPointsStale", "finalRecordMissing", "finalRecordMismatch", "participationSnapshotMismatch", "gameHeld"];
89
91
  export declare const AGGREGATE_TIER_DISCREPANCY_KIND_KEYS: readonly ["aggregateMismatch", "aggregateEntryStale", "aggregateEntryMissing", "hallOfFameLifetimeMismatch"];
90
- export declare const STATS_DISCREPANCY_KIND_KEYS: readonly ["invalidEvent", "eventSequenceMismatch", "rollupMismatch", "scoreMirrorMismatch", "unrosteredStatLine", "fantasyPointsStale", "finalRecordMissing", "finalRecordMismatch", "participationSnapshotMismatch", "gameHeld", "aggregateMismatch", "aggregateEntryStale", "aggregateEntryMissing", "hallOfFameLifetimeMismatch"];
92
+ export declare const STATS_DISCREPANCY_KIND_KEYS: readonly ["gameUnreadable", "invalidEvent", "eventSequenceMismatch", "eventSequenceGap", "rollupMismatch", "scoreMirrorMismatch", "unrosteredStatLine", "fantasyPointsStale", "finalRecordMissing", "finalRecordMismatch", "participationSnapshotMismatch", "gameHeld", "aggregateMismatch", "aggregateEntryStale", "aggregateEntryMissing", "hallOfFameLifetimeMismatch"];
91
93
  export type StatsDiscrepancyKind = (typeof STATS_DISCREPANCY_KIND_KEYS)[number];
92
94
  //# sourceMappingURL=unions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"unions.d.ts","sourceRoot":"","sources":["../../src/types/unions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"unions.d.ts","sourceRoot":"","sources":["../../src/types/unions.ts"],"names":[],"mappings":"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"}
@@ -110,6 +110,8 @@ export const HARDWARE_LOAN_CONTRACT_STATUS_KEYS = ['pending', 'loaned', 'returne
110
110
  // Why an award grant passed over an account its teams or leagues named: it has no profile, or it is
111
111
  // a ghost, which holds no account.
112
112
  export const AWARD_GRANT_SKIP_REASON_KEYS = ['noProfile', 'ghost'];
113
+ // The two badges an account shows beside its name.
114
+ export const FEATURED_AWARD_SLOT_KEYS = ['primary', 'secondary'];
113
115
  export const PENDING_HARDWARE_LOAN_CONTRACT_STATUS = 'pending';
114
116
  export const LOANED_HARDWARE_LOAN_CONTRACT_STATUS = 'loaned';
115
117
  export const RETURNED_HARDWARE_LOAN_CONTRACT_STATUS = 'returned';
@@ -273,8 +275,12 @@ export const PICTURE_VARIANT_KEYS = ['sm', 'md', 'lg'];
273
275
  // What a stats check found, game tier first — a game's own numbers are the cause of the aggregate
274
276
  // findings, so Fix stats repairs games before it rebuilds what they feed.
275
277
  export const GAME_TIER_DISCREPANCY_KIND_KEYS = [
278
+ // A game whose document the check could not read: nothing else about it could be checked.
279
+ 'gameUnreadable',
276
280
  'invalidEvent',
277
281
  'eventSequenceMismatch',
282
+ // A game whose events do not hold exactly the sequences 1..n: a gap or a duplicate.
283
+ 'eventSequenceGap',
278
284
  'rollupMismatch',
279
285
  'scoreMirrorMismatch',
280
286
  'unrosteredStatLine',