@ttt-productions/q-core 0.1.28 → 0.1.30

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 (496) hide show
  1. package/dist/constants/admin-views.d.ts.map +1 -1
  2. package/dist/constants/admin-views.js +0 -3
  3. package/dist/constants/admin-views.js.map +1 -1
  4. package/dist/constants/business.d.ts.map +1 -1
  5. package/dist/constants/business.js +0 -52
  6. package/dist/constants/business.js.map +1 -1
  7. package/dist/constants/deployment.d.ts +0 -2
  8. package/dist/constants/deployment.d.ts.map +1 -1
  9. package/dist/constants/deployment.js +0 -2
  10. package/dist/constants/deployment.js.map +1 -1
  11. package/dist/constants/media.d.ts.map +1 -1
  12. package/dist/constants/media.js +4 -30
  13. package/dist/constants/media.js.map +1 -1
  14. package/dist/constants/scheduled-jobs.d.ts +0 -25
  15. package/dist/constants/scheduled-jobs.d.ts.map +1 -1
  16. package/dist/constants/scheduled-jobs.js +0 -26
  17. package/dist/constants/scheduled-jobs.js.map +1 -1
  18. package/dist/constants/sports.d.ts +1 -0
  19. package/dist/constants/sports.d.ts.map +1 -1
  20. package/dist/constants/sports.js +4 -25
  21. package/dist/constants/sports.js.map +1 -1
  22. package/dist/constants/storage-keys.d.ts +0 -38
  23. package/dist/constants/storage-keys.d.ts.map +1 -1
  24. package/dist/constants/storage-keys.js +0 -41
  25. package/dist/constants/storage-keys.js.map +1 -1
  26. package/dist/constants/telemetry.d.ts +0 -3
  27. package/dist/constants/telemetry.d.ts.map +1 -1
  28. package/dist/constants/telemetry.js +2 -11
  29. package/dist/constants/telemetry.js.map +1 -1
  30. package/dist/doc-schemas/admin-notifications.d.ts +1 -2
  31. package/dist/doc-schemas/admin-notifications.d.ts.map +1 -1
  32. package/dist/doc-schemas/admin-notifications.js +0 -41
  33. package/dist/doc-schemas/admin-notifications.js.map +1 -1
  34. package/dist/doc-schemas/aggregate-stats.d.ts.map +1 -1
  35. package/dist/doc-schemas/aggregate-stats.js +0 -35
  36. package/dist/doc-schemas/aggregate-stats.js.map +1 -1
  37. package/dist/doc-schemas/atoms.d.ts +0 -17
  38. package/dist/doc-schemas/atoms.d.ts.map +1 -1
  39. package/dist/doc-schemas/atoms.js +0 -36
  40. package/dist/doc-schemas/atoms.js.map +1 -1
  41. package/dist/doc-schemas/audit-event.d.ts +2 -0
  42. package/dist/doc-schemas/audit-event.d.ts.map +1 -1
  43. package/dist/doc-schemas/audit-event.js +0 -8
  44. package/dist/doc-schemas/audit-event.js.map +1 -1
  45. package/dist/doc-schemas/award-group-grant.d.ts +2 -8
  46. package/dist/doc-schemas/award-group-grant.d.ts.map +1 -1
  47. package/dist/doc-schemas/award-group-grant.js +0 -25
  48. package/dist/doc-schemas/award-group-grant.js.map +1 -1
  49. package/dist/doc-schemas/catalogs.d.ts.map +1 -1
  50. package/dist/doc-schemas/catalogs.js +0 -12
  51. package/dist/doc-schemas/catalogs.js.map +1 -1
  52. package/dist/doc-schemas/firestore-primitives.d.ts.map +1 -1
  53. package/dist/doc-schemas/firestore-primitives.js +0 -5
  54. package/dist/doc-schemas/firestore-primitives.js.map +1 -1
  55. package/dist/doc-schemas/gallery.d.ts.map +1 -1
  56. package/dist/doc-schemas/gallery.js +9 -18
  57. package/dist/doc-schemas/gallery.js.map +1 -1
  58. package/dist/doc-schemas/game-location.d.ts.map +1 -1
  59. package/dist/doc-schemas/game-location.js +2 -11
  60. package/dist/doc-schemas/game-location.js.map +1 -1
  61. package/dist/doc-schemas/game-stat-history.d.ts.map +1 -1
  62. package/dist/doc-schemas/game-stat-history.js +2 -11
  63. package/dist/doc-schemas/game-stat-history.js.map +1 -1
  64. package/dist/doc-schemas/game-stats.d.ts.map +1 -1
  65. package/dist/doc-schemas/game-stats.js +0 -56
  66. package/dist/doc-schemas/game-stats.js.map +1 -1
  67. package/dist/doc-schemas/league-team.d.ts.map +1 -1
  68. package/dist/doc-schemas/league-team.js +6 -29
  69. package/dist/doc-schemas/league-team.js.map +1 -1
  70. package/dist/doc-schemas/membership-roster-sync.d.ts.map +1 -1
  71. package/dist/doc-schemas/membership-roster-sync.js +0 -17
  72. package/dist/doc-schemas/membership-roster-sync.js.map +1 -1
  73. package/dist/doc-schemas/migration-console.d.ts +21 -15
  74. package/dist/doc-schemas/migration-console.d.ts.map +1 -1
  75. package/dist/doc-schemas/migration-console.js +0 -32
  76. package/dist/doc-schemas/migration-console.js.map +1 -1
  77. package/dist/doc-schemas/pending-media.d.ts.map +1 -1
  78. package/dist/doc-schemas/pending-media.js +8 -20
  79. package/dist/doc-schemas/pending-media.js.map +1 -1
  80. package/dist/doc-schemas/picture-retirement.d.ts.map +1 -1
  81. package/dist/doc-schemas/picture-retirement.js +8 -13
  82. package/dist/doc-schemas/picture-retirement.js.map +1 -1
  83. package/dist/doc-schemas/publicUser.d.ts.map +1 -1
  84. package/dist/doc-schemas/publicUser.js +0 -5
  85. package/dist/doc-schemas/publicUser.js.map +1 -1
  86. package/dist/doc-schemas/registration-start.d.ts.map +1 -1
  87. package/dist/doc-schemas/registration-start.js +2 -5
  88. package/dist/doc-schemas/registration-start.js.map +1 -1
  89. package/dist/doc-schemas/registry.d.ts +25 -14
  90. package/dist/doc-schemas/registry.d.ts.map +1 -1
  91. package/dist/doc-schemas/registry.js +0 -10
  92. package/dist/doc-schemas/registry.js.map +1 -1
  93. package/dist/doc-schemas/reserved-display-name.d.ts.map +1 -1
  94. package/dist/doc-schemas/reserved-display-name.js +0 -5
  95. package/dist/doc-schemas/reserved-display-name.js.map +1 -1
  96. package/dist/doc-schemas/sport-game.d.ts.map +1 -1
  97. package/dist/doc-schemas/sport-game.js +1 -29
  98. package/dist/doc-schemas/sport-game.js.map +1 -1
  99. package/dist/doc-schemas/sport-league.d.ts +0 -9
  100. package/dist/doc-schemas/sport-league.d.ts.map +1 -1
  101. package/dist/doc-schemas/sport-league.js +4 -26
  102. package/dist/doc-schemas/sport-league.js.map +1 -1
  103. package/dist/doc-schemas/stats-operation.d.ts +22 -18
  104. package/dist/doc-schemas/stats-operation.d.ts.map +1 -1
  105. package/dist/doc-schemas/stats-operation.js +7 -44
  106. package/dist/doc-schemas/stats-operation.js.map +1 -1
  107. package/dist/doc-schemas/status-reconcile-queue.d.ts +0 -6
  108. package/dist/doc-schemas/status-reconcile-queue.d.ts.map +1 -1
  109. package/dist/doc-schemas/status-reconcile-queue.js +0 -13
  110. package/dist/doc-schemas/status-reconcile-queue.js.map +1 -1
  111. package/dist/doc-schemas/sweep-state.d.ts.map +1 -1
  112. package/dist/doc-schemas/sweep-state.js +4 -20
  113. package/dist/doc-schemas/sweep-state.js.map +1 -1
  114. package/dist/doc-schemas/system.d.ts.map +1 -1
  115. package/dist/doc-schemas/system.js +1 -27
  116. package/dist/doc-schemas/system.js.map +1 -1
  117. package/dist/doc-schemas/team-membership.d.ts.map +1 -1
  118. package/dist/doc-schemas/team-membership.js +0 -10
  119. package/dist/doc-schemas/team-membership.js.map +1 -1
  120. package/dist/doc-schemas/user-profile.d.ts +0 -1
  121. package/dist/doc-schemas/user-profile.d.ts.map +1 -1
  122. package/dist/doc-schemas/user-profile.js +0 -28
  123. package/dist/doc-schemas/user-profile.js.map +1 -1
  124. package/dist/doc-schemas/waiver-template.d.ts.map +1 -1
  125. package/dist/doc-schemas/waiver-template.js +4 -25
  126. package/dist/doc-schemas/waiver-template.js.map +1 -1
  127. package/dist/doc-schemas/waivers-and-hardware.d.ts.map +1 -1
  128. package/dist/doc-schemas/waivers-and-hardware.js +0 -20
  129. package/dist/doc-schemas/waivers-and-hardware.js.map +1 -1
  130. package/dist/domain-events/domain-events.d.ts.map +1 -1
  131. package/dist/domain-events/domain-events.js +0 -67
  132. package/dist/domain-events/domain-events.js.map +1 -1
  133. package/dist/ids/award-grant-ids.d.ts.map +1 -1
  134. package/dist/ids/award-grant-ids.js +0 -7
  135. package/dist/ids/award-grant-ids.js.map +1 -1
  136. package/dist/ids/compound-ids.d.ts.map +1 -1
  137. package/dist/ids/compound-ids.js +0 -7
  138. package/dist/ids/compound-ids.js.map +1 -1
  139. package/dist/ids/minted-ids.d.ts.map +1 -1
  140. package/dist/ids/minted-ids.js +4 -14
  141. package/dist/ids/minted-ids.js.map +1 -1
  142. package/dist/ids/notification-ids.d.ts.map +1 -1
  143. package/dist/ids/notification-ids.js +0 -15
  144. package/dist/ids/notification-ids.js.map +1 -1
  145. package/dist/ids/picture-ids.d.ts +0 -5
  146. package/dist/ids/picture-ids.d.ts.map +1 -1
  147. package/dist/ids/picture-ids.js +0 -11
  148. package/dist/ids/picture-ids.js.map +1 -1
  149. package/dist/ids/stat-event-ids.d.ts +0 -6
  150. package/dist/ids/stat-event-ids.d.ts.map +1 -1
  151. package/dist/ids/stat-event-ids.js +0 -15
  152. package/dist/ids/stat-event-ids.js.map +1 -1
  153. package/dist/ids/stats-operation-ids.d.ts.map +1 -1
  154. package/dist/ids/stats-operation-ids.js +0 -7
  155. package/dist/ids/stats-operation-ids.js.map +1 -1
  156. package/dist/ids/waiver-template-ids.d.ts +0 -1
  157. package/dist/ids/waiver-template-ids.d.ts.map +1 -1
  158. package/dist/ids/waiver-template-ids.js +0 -2
  159. package/dist/ids/waiver-template-ids.js.map +1 -1
  160. package/dist/index.d.ts +2 -0
  161. package/dist/index.d.ts.map +1 -1
  162. package/dist/index.js +2 -2
  163. package/dist/index.js.map +1 -1
  164. package/dist/input-formats/input-formats.d.ts +0 -80
  165. package/dist/input-formats/input-formats.d.ts.map +1 -1
  166. package/dist/input-formats/input-formats.js +0 -86
  167. package/dist/input-formats/input-formats.js.map +1 -1
  168. package/dist/media/file-origins.d.ts +0 -3
  169. package/dist/media/file-origins.d.ts.map +1 -1
  170. package/dist/media/file-origins.js +0 -26
  171. package/dist/media/file-origins.js.map +1 -1
  172. package/dist/media/picture-aspects.d.ts +0 -2
  173. package/dist/media/picture-aspects.d.ts.map +1 -1
  174. package/dist/media/picture-aspects.js +0 -5
  175. package/dist/media/picture-aspects.js.map +1 -1
  176. package/dist/media/picture-url.d.ts +0 -6
  177. package/dist/media/picture-url.d.ts.map +1 -1
  178. package/dist/media/picture-url.js +0 -9
  179. package/dist/media/picture-url.js.map +1 -1
  180. package/dist/media/upload-tray-display.d.ts +0 -3
  181. package/dist/media/upload-tray-display.d.ts.map +1 -1
  182. package/dist/media/upload-tray-display.js +0 -6
  183. package/dist/media/upload-tray-display.js.map +1 -1
  184. package/dist/migration-console/action-ids.d.ts +4 -1
  185. package/dist/migration-console/action-ids.d.ts.map +1 -1
  186. package/dist/migration-console/action-ids.js +8 -45
  187. package/dist/migration-console/action-ids.js.map +1 -1
  188. package/dist/migration-console/console-lock.d.ts +10 -0
  189. package/dist/migration-console/console-lock.d.ts.map +1 -0
  190. package/dist/migration-console/console-lock.js +14 -0
  191. package/dist/migration-console/console-lock.js.map +1 -0
  192. package/dist/migration-console/consumed-source.d.ts +4 -0
  193. package/dist/migration-console/consumed-source.d.ts.map +1 -0
  194. package/dist/migration-console/consumed-source.js +10 -0
  195. package/dist/migration-console/consumed-source.js.map +1 -0
  196. package/dist/migration-console/manifest.d.ts +2 -24
  197. package/dist/migration-console/manifest.d.ts.map +1 -1
  198. package/dist/migration-console/manifest.js +174 -61
  199. package/dist/migration-console/manifest.js.map +1 -1
  200. package/dist/migration-console/run-state.d.ts +2 -14
  201. package/dist/migration-console/run-state.d.ts.map +1 -1
  202. package/dist/migration-console/run-state.js +3 -16
  203. package/dist/migration-console/run-state.js.map +1 -1
  204. package/dist/notifications/admin-notification-config.d.ts +0 -11
  205. package/dist/notifications/admin-notification-config.d.ts.map +1 -1
  206. package/dist/notifications/admin-notification-config.js +2 -13
  207. package/dist/notifications/admin-notification-config.js.map +1 -1
  208. package/dist/notifications/archive-all.d.ts.map +1 -1
  209. package/dist/notifications/archive-all.js +0 -2
  210. package/dist/notifications/archive-all.js.map +1 -1
  211. package/dist/notifications/occurrences.d.ts +0 -3
  212. package/dist/notifications/occurrences.d.ts.map +1 -1
  213. package/dist/notifications/occurrences.js +0 -7
  214. package/dist/notifications/occurrences.js.map +1 -1
  215. package/dist/paths/app-routes.d.ts +0 -12
  216. package/dist/paths/app-routes.d.ts.map +1 -1
  217. package/dist/paths/app-routes.js +0 -18
  218. package/dist/paths/app-routes.js.map +1 -1
  219. package/dist/paths/collection-groups.d.ts.map +1 -1
  220. package/dist/paths/collection-groups.js +0 -5
  221. package/dist/paths/collection-groups.js.map +1 -1
  222. package/dist/paths/collection-refs.d.ts.map +1 -1
  223. package/dist/paths/collection-refs.js +0 -4
  224. package/dist/paths/collection-refs.js.map +1 -1
  225. package/dist/paths/collections.d.ts +0 -5
  226. package/dist/paths/collections.d.ts.map +1 -1
  227. package/dist/paths/collections.js +0 -68
  228. package/dist/paths/collections.js.map +1 -1
  229. package/dist/paths/path-builders.d.ts.map +1 -1
  230. package/dist/paths/path-builders.js +0 -16
  231. package/dist/paths/path-builders.js.map +1 -1
  232. package/dist/paths/picture-owner-paths.d.ts +0 -4
  233. package/dist/paths/picture-owner-paths.d.ts.map +1 -1
  234. package/dist/paths/picture-owner-paths.js +2 -11
  235. package/dist/paths/picture-owner-paths.js.map +1 -1
  236. package/dist/paths/storage-paths.d.ts +0 -18
  237. package/dist/paths/storage-paths.d.ts.map +1 -1
  238. package/dist/paths/storage-paths.js +0 -23
  239. package/dist/paths/storage-paths.js.map +1 -1
  240. package/dist/paths/utils.d.ts +0 -6
  241. package/dist/paths/utils.d.ts.map +1 -1
  242. package/dist/paths/utils.js +0 -9
  243. package/dist/paths/utils.js.map +1 -1
  244. package/dist/schemas/account-settings.d.ts.map +1 -1
  245. package/dist/schemas/account-settings.js +0 -7
  246. package/dist/schemas/account-settings.js.map +1 -1
  247. package/dist/schemas/admin-notifications.d.ts.map +1 -1
  248. package/dist/schemas/admin-notifications.js +0 -17
  249. package/dist/schemas/admin-notifications.js.map +1 -1
  250. package/dist/schemas/admin-stats.d.ts.map +1 -1
  251. package/dist/schemas/admin-stats.js +0 -9
  252. package/dist/schemas/admin-stats.js.map +1 -1
  253. package/dist/schemas/app-config.d.ts.map +1 -1
  254. package/dist/schemas/app-config.js +0 -10
  255. package/dist/schemas/app-config.js.map +1 -1
  256. package/dist/schemas/atoms.d.ts.map +1 -1
  257. package/dist/schemas/atoms.js +0 -10
  258. package/dist/schemas/atoms.js.map +1 -1
  259. package/dist/schemas/awards.d.ts +3 -3
  260. package/dist/schemas/awards.d.ts.map +1 -1
  261. package/dist/schemas/awards.js +0 -29
  262. package/dist/schemas/awards.js.map +1 -1
  263. package/dist/schemas/dead-letters.d.ts +0 -3
  264. package/dist/schemas/dead-letters.d.ts.map +1 -1
  265. package/dist/schemas/dead-letters.js +0 -15
  266. package/dist/schemas/dead-letters.js.map +1 -1
  267. package/dist/schemas/game-locations.d.ts.map +1 -1
  268. package/dist/schemas/game-locations.js +0 -6
  269. package/dist/schemas/game-locations.js.map +1 -1
  270. package/dist/schemas/hardware-loan-contracts.d.ts.map +1 -1
  271. package/dist/schemas/hardware-loan-contracts.js +0 -13
  272. package/dist/schemas/hardware-loan-contracts.js.map +1 -1
  273. package/dist/schemas/index.d.ts.map +1 -1
  274. package/dist/schemas/index.js +0 -2
  275. package/dist/schemas/index.js.map +1 -1
  276. package/dist/schemas/leagues.d.ts.map +1 -1
  277. package/dist/schemas/leagues.js +0 -24
  278. package/dist/schemas/leagues.js.map +1 -1
  279. package/dist/schemas/migration-console.d.ts +58 -15
  280. package/dist/schemas/migration-console.d.ts.map +1 -1
  281. package/dist/schemas/migration-console.js +4 -34
  282. package/dist/schemas/migration-console.js.map +1 -1
  283. package/dist/schemas/pictures.d.ts.map +1 -1
  284. package/dist/schemas/pictures.js +0 -10
  285. package/dist/schemas/pictures.js.map +1 -1
  286. package/dist/schemas/schedules.d.ts.map +1 -1
  287. package/dist/schemas/schedules.js +0 -26
  288. package/dist/schemas/schedules.js.map +1 -1
  289. package/dist/schemas/scoring.d.ts +20 -26
  290. package/dist/schemas/scoring.d.ts.map +1 -1
  291. package/dist/schemas/scoring.js +0 -33
  292. package/dist/schemas/scoring.js.map +1 -1
  293. package/dist/schemas/signed-waivers.d.ts.map +1 -1
  294. package/dist/schemas/signed-waivers.js +0 -22
  295. package/dist/schemas/signed-waivers.js.map +1 -1
  296. package/dist/schemas/stats.d.ts +12 -0
  297. package/dist/schemas/stats.d.ts.map +1 -1
  298. package/dist/schemas/stats.js +21 -61
  299. package/dist/schemas/stats.js.map +1 -1
  300. package/dist/schemas/team-joining.d.ts.map +1 -1
  301. package/dist/schemas/team-joining.js +0 -30
  302. package/dist/schemas/team-joining.js.map +1 -1
  303. package/dist/schemas/teams.d.ts +0 -1
  304. package/dist/schemas/teams.d.ts.map +1 -1
  305. package/dist/schemas/teams.js +0 -83
  306. package/dist/schemas/teams.js.map +1 -1
  307. package/dist/schemas/uploads.d.ts +0 -8
  308. package/dist/schemas/uploads.d.ts.map +1 -1
  309. package/dist/schemas/uploads.js +0 -29
  310. package/dist/schemas/uploads.js.map +1 -1
  311. package/dist/schemas/users.d.ts +0 -1
  312. package/dist/schemas/users.d.ts.map +1 -1
  313. package/dist/schemas/users.js +0 -41
  314. package/dist/schemas/users.js.map +1 -1
  315. package/dist/schemas/waiver-templates.d.ts.map +1 -1
  316. package/dist/schemas/waiver-templates.js +0 -7
  317. package/dist/schemas/waiver-templates.js.map +1 -1
  318. package/dist/sports/basketball-scoring.d.ts +0 -4
  319. package/dist/sports/basketball-scoring.d.ts.map +1 -1
  320. package/dist/sports/basketball-scoring.js +0 -34
  321. package/dist/sports/basketball-scoring.js.map +1 -1
  322. package/dist/sports/basketball.d.ts.map +1 -1
  323. package/dist/sports/basketball.js +0 -5
  324. package/dist/sports/basketball.js.map +1 -1
  325. package/dist/sports/bulk-entry.d.ts +0 -22
  326. package/dist/sports/bulk-entry.d.ts.map +1 -1
  327. package/dist/sports/bulk-entry.js +0 -10
  328. package/dist/sports/bulk-entry.js.map +1 -1
  329. package/dist/sports/column-catalog.d.ts +0 -17
  330. package/dist/sports/column-catalog.d.ts.map +1 -1
  331. package/dist/sports/column-catalog.js +0 -10
  332. package/dist/sports/column-catalog.js.map +1 -1
  333. package/dist/sports/contribution.d.ts +2 -59
  334. package/dist/sports/contribution.d.ts.map +1 -1
  335. package/dist/sports/contribution.js +2 -45
  336. package/dist/sports/contribution.js.map +1 -1
  337. package/dist/sports/curling-scoring.d.ts +0 -1
  338. package/dist/sports/curling-scoring.d.ts.map +1 -1
  339. package/dist/sports/curling-scoring.js +0 -27
  340. package/dist/sports/curling-scoring.js.map +1 -1
  341. package/dist/sports/curling.d.ts.map +1 -1
  342. package/dist/sports/curling.js +0 -4
  343. package/dist/sports/curling.js.map +1 -1
  344. package/dist/sports/finalized-record.d.ts +2 -19
  345. package/dist/sports/finalized-record.d.ts.map +1 -1
  346. package/dist/sports/finalized-record.js +21 -22
  347. package/dist/sports/finalized-record.js.map +1 -1
  348. package/dist/sports/football-scoring.d.ts.map +1 -1
  349. package/dist/sports/football-scoring.js +0 -21
  350. package/dist/sports/football-scoring.js.map +1 -1
  351. package/dist/sports/football.d.ts.map +1 -1
  352. package/dist/sports/football.js +0 -4
  353. package/dist/sports/football.js.map +1 -1
  354. package/dist/sports/index.d.ts.map +1 -1
  355. package/dist/sports/index.js +0 -2
  356. package/dist/sports/index.js.map +1 -1
  357. package/dist/sports/ledger-continuity.d.ts +0 -2
  358. package/dist/sports/ledger-continuity.d.ts.map +1 -1
  359. package/dist/sports/ledger-continuity.js +0 -5
  360. package/dist/sports/ledger-continuity.js.map +1 -1
  361. package/dist/sports/lifetime-summary.d.ts +0 -6
  362. package/dist/sports/lifetime-summary.d.ts.map +1 -1
  363. package/dist/sports/lifetime-summary.js +0 -7
  364. package/dist/sports/lifetime-summary.js.map +1 -1
  365. package/dist/sports/rankings.d.ts +0 -12
  366. package/dist/sports/rankings.d.ts.map +1 -1
  367. package/dist/sports/rankings.js +0 -15
  368. package/dist/sports/rankings.js.map +1 -1
  369. package/dist/sports/registry.d.ts +0 -10
  370. package/dist/sports/registry.d.ts.map +1 -1
  371. package/dist/sports/registry.js +0 -9
  372. package/dist/sports/registry.js.map +1 -1
  373. package/dist/sports/replay.d.ts +0 -11
  374. package/dist/sports/replay.d.ts.map +1 -1
  375. package/dist/sports/replay.js +0 -16
  376. package/dist/sports/replay.js.map +1 -1
  377. package/dist/sports/rollup-check.d.ts +0 -7
  378. package/dist/sports/rollup-check.d.ts.map +1 -1
  379. package/dist/sports/rollup-check.js +0 -14
  380. package/dist/sports/rollup-check.js.map +1 -1
  381. package/dist/sports/score-mirror.d.ts +0 -1
  382. package/dist/sports/score-mirror.d.ts.map +1 -1
  383. package/dist/sports/score-mirror.js +0 -4
  384. package/dist/sports/score-mirror.js.map +1 -1
  385. package/dist/sports/scoring-display.d.ts +0 -30
  386. package/dist/sports/scoring-display.d.ts.map +1 -1
  387. package/dist/sports/scoring-display.js +0 -9
  388. package/dist/sports/scoring-display.js.map +1 -1
  389. package/dist/sports/scoring.d.ts +0 -36
  390. package/dist/sports/scoring.d.ts.map +1 -1
  391. package/dist/sports/scoring.js +0 -10
  392. package/dist/sports/scoring.js.map +1 -1
  393. package/dist/sports/shared-columns.d.ts +0 -1
  394. package/dist/sports/shared-columns.d.ts.map +1 -1
  395. package/dist/sports/shared-columns.js +0 -7
  396. package/dist/sports/shared-columns.js.map +1 -1
  397. package/dist/sports/sport-adapter.d.ts +0 -32
  398. package/dist/sports/sport-adapter.d.ts.map +1 -1
  399. package/dist/sports/sport-adapter.js +0 -7
  400. package/dist/sports/sport-adapter.js.map +1 -1
  401. package/dist/sports/store-driver.d.ts +0 -28
  402. package/dist/sports/store-driver.d.ts.map +1 -1
  403. package/dist/sports/store-driver.js +0 -38
  404. package/dist/sports/store-driver.js.map +1 -1
  405. package/dist/types/audit.d.ts +1 -1
  406. package/dist/types/audit.d.ts.map +1 -1
  407. package/dist/types/audit.js +1 -67
  408. package/dist/types/audit.js.map +1 -1
  409. package/dist/types/claims.d.ts +0 -10
  410. package/dist/types/claims.d.ts.map +1 -1
  411. package/dist/types/claims.js +0 -16
  412. package/dist/types/claims.js.map +1 -1
  413. package/dist/types/notifications.d.ts.map +1 -1
  414. package/dist/types/notifications.js +7 -25
  415. package/dist/types/notifications.js.map +1 -1
  416. package/dist/types/picture-owners.d.ts +0 -1
  417. package/dist/types/picture-owners.d.ts.map +1 -1
  418. package/dist/types/picture-owners.js +0 -6
  419. package/dist/types/picture-owners.js.map +1 -1
  420. package/dist/types/schedules.d.ts.map +1 -1
  421. package/dist/types/schedules.js +0 -9
  422. package/dist/types/schedules.js.map +1 -1
  423. package/dist/types/sports.d.ts.map +1 -1
  424. package/dist/types/sports.js +0 -24
  425. package/dist/types/sports.js.map +1 -1
  426. package/dist/types/stats.d.ts.map +1 -1
  427. package/dist/types/stats.js +0 -65
  428. package/dist/types/stats.js.map +1 -1
  429. package/dist/types/unions.d.ts +0 -2
  430. package/dist/types/unions.d.ts.map +1 -1
  431. package/dist/types/unions.js +3 -91
  432. package/dist/types/unions.js.map +1 -1
  433. package/dist/types/uploads.d.ts.map +1 -1
  434. package/dist/types/uploads.js +0 -7
  435. package/dist/types/uploads.js.map +1 -1
  436. package/dist/upload-variables/on-progress.d.ts.map +1 -1
  437. package/dist/upload-variables/on-progress.js +2 -5
  438. package/dist/upload-variables/on-progress.js.map +1 -1
  439. package/dist/upload-variables/picture-upload-variables.d.ts +0 -1
  440. package/dist/upload-variables/picture-upload-variables.d.ts.map +1 -1
  441. package/dist/upload-variables/picture-upload-variables.js +0 -4
  442. package/dist/upload-variables/picture-upload-variables.js.map +1 -1
  443. package/dist/upload-variables/sign-hardware-contract-variables.d.ts.map +1 -1
  444. package/dist/upload-variables/sign-hardware-contract-variables.js +0 -2
  445. package/dist/upload-variables/sign-hardware-contract-variables.js.map +1 -1
  446. package/dist/upload-variables/sign-waiver-variables.d.ts.map +1 -1
  447. package/dist/upload-variables/sign-waiver-variables.js +0 -4
  448. package/dist/upload-variables/sign-waiver-variables.js.map +1 -1
  449. package/dist/utils/account-status.d.ts.map +1 -1
  450. package/dist/utils/account-status.js +0 -4
  451. package/dist/utils/account-status.js.map +1 -1
  452. package/dist/utils/app-config.d.ts +0 -53
  453. package/dist/utils/app-config.d.ts.map +1 -1
  454. package/dist/utils/app-config.js +0 -62
  455. package/dist/utils/app-config.js.map +1 -1
  456. package/dist/utils/display-name.d.ts +0 -7
  457. package/dist/utils/display-name.d.ts.map +1 -1
  458. package/dist/utils/display-name.js +0 -8
  459. package/dist/utils/display-name.js.map +1 -1
  460. package/dist/utils/index.d.ts +1 -0
  461. package/dist/utils/index.d.ts.map +1 -1
  462. package/dist/utils/index.js +1 -0
  463. package/dist/utils/index.js.map +1 -1
  464. package/dist/utils/league-calendar.d.ts +0 -11
  465. package/dist/utils/league-calendar.d.ts.map +1 -1
  466. package/dist/utils/league-calendar.js +7 -19
  467. package/dist/utils/league-calendar.js.map +1 -1
  468. package/dist/utils/league-join-warning.d.ts +0 -11
  469. package/dist/utils/league-join-warning.d.ts.map +1 -1
  470. package/dist/utils/league-join-warning.js +0 -28
  471. package/dist/utils/league-join-warning.js.map +1 -1
  472. package/dist/utils/league-joining.d.ts +0 -1
  473. package/dist/utils/league-joining.d.ts.map +1 -1
  474. package/dist/utils/league-joining.js +0 -7
  475. package/dist/utils/league-joining.js.map +1 -1
  476. package/dist/utils/league-mode.d.ts +0 -1
  477. package/dist/utils/league-mode.d.ts.map +1 -1
  478. package/dist/utils/league-mode.js +0 -4
  479. package/dist/utils/league-mode.js.map +1 -1
  480. package/dist/utils/schedules.d.ts +0 -15
  481. package/dist/utils/schedules.d.ts.map +1 -1
  482. package/dist/utils/schedules.js +0 -19
  483. package/dist/utils/schedules.js.map +1 -1
  484. package/dist/utils/stats-operation-request.d.ts +20 -0
  485. package/dist/utils/stats-operation-request.d.ts.map +1 -0
  486. package/dist/utils/stats-operation-request.js +10 -0
  487. package/dist/utils/stats-operation-request.js.map +1 -0
  488. package/dist/utils/team-creation.d.ts +0 -2
  489. package/dist/utils/team-creation.d.ts.map +1 -1
  490. package/dist/utils/team-creation.js +0 -6
  491. package/dist/utils/team-creation.js.map +1 -1
  492. package/dist/utils/team-member-actions.d.ts +0 -6
  493. package/dist/utils/team-member-actions.d.ts.map +1 -1
  494. package/dist/utils/team-member-actions.js +0 -12
  495. package/dist/utils/team-member-actions.js.map +1 -1
  496. package/package.json +2 -1
@@ -1,8 +1,3 @@
1
- // The curling adapter's scoring, display, and bulk-entry members. Curling is team-scored by round: a
2
- // round event records one round — one team's points, or a blank round for every team — and nothing
3
- // names a player. A round is recorded once, in order: `validate` accepts a round event only for the
4
- // first unrecorded round, and re-scoring a round is a void of its event plus a new one, so no curling
5
- // event is ever superseded and undoing one always returns its round to unrecorded (RULE-QS-SPORT-913).
6
1
  import { z } from 'zod';
7
2
  import { CURLING_ROUND_COUNT, MAX_CURLING_ROUND_POINTS, MAX_STAT_EVENTS_PER_WRITE, } from '../constants/sports.js';
8
3
  import { CURLING_ROUND_BLANK_EVENT_TYPE, CURLING_ROUND_SCORE_EVENT_TYPE, CURLING_STAT_EVENT_TYPE_KEYS, } from '../types/sports.js';
@@ -14,8 +9,6 @@ import { describeStoreEvent, } from './scoring-display.js';
14
9
  function isCurlingEvent(event) {
15
10
  return isSportStatEventIn(CURLING_STAT_EVENT_TYPE_KEYS, event);
16
11
  }
17
- // The registry chose this adapter by the game's `leagueType`, so the rollup it is handed is a
18
- // curling game's (RULE-QS-SPORT-902).
19
12
  function ownStats(state) {
20
13
  return state;
21
14
  }
@@ -25,7 +18,6 @@ function ownTeamLine(line) {
25
18
  function sumOfRounds(roundScores) {
26
19
  return roundScores.reduce((total, slot) => total + (slot ?? 0), 0);
27
20
  }
28
- /** Every round unrecorded — `null`, where a blank round is `0`. */
29
21
  export function emptyCurlingTeamLine() {
30
22
  const roundScores = Array.from({ length: CURLING_ROUND_COUNT }, () => null);
31
23
  return { score: sumOfRounds(roundScores), roundScores };
@@ -35,10 +27,6 @@ function withRound(line, roundNumber, slot) {
35
27
  roundScores[roundNumber - 1] = slot;
36
28
  return { score: sumOfRounds(roundScores), roundScores };
37
29
  }
38
- /**
39
- * Record or clear one round on every team line: a scored round is the scoring team's points and 0
40
- * for the others, a blank round 0 for all, and a cleared round `null` for all.
41
- */
42
30
  function setRound(stateIn, event, recorded) {
43
31
  const state = ownStats(stateIn);
44
32
  const teamStats = { ...state.teamStats };
@@ -60,11 +48,6 @@ function nextCurlingRound(stateIn) {
60
48
  }
61
49
  return null;
62
50
  }
63
- // ---------------------------------------------------------------------------------------------
64
- // Scoring
65
- // ---------------------------------------------------------------------------------------------
66
- // No curling field is correctable: a curling result changes only by reopening the game and
67
- // re-scoring the round.
68
51
  const CORRECTABLE_FIELDS = { team: [], player: [] };
69
52
  function namesPlayer(event) {
70
53
  return 'playerUserId' in event && event.playerUserId !== undefined;
@@ -87,9 +70,6 @@ function finalizationReadiness(state) {
87
70
  ? FINALIZATION_READY
88
71
  : { ready: false, reason: 'roundsIncomplete' };
89
72
  }
90
- // What a completed curling game adds: the standings line from the two scores, and a game played
91
- // to every present roster entry, whatever its team scored. There are no individual stats, so only
92
- // absence withholds a game played and no entry is ever non-scoring.
93
73
  const CURLING_CONTRIBUTION = {
94
74
  playerStatKeys: [],
95
75
  teamSeasonPlayerSums: [],
@@ -110,7 +90,6 @@ export const CURLING_SCORING = {
110
90
  throw new Error(`The curling adapter does not undo ${target.type}.`);
111
91
  return setRound(state, target, false);
112
92
  },
113
- // A curling line has no derived field.
114
93
  deriveLine: CURLING_CONTRIBUTION.deriveLine,
115
94
  correctableFields: CORRECTABLE_FIELDS,
116
95
  nextRound: nextCurlingRound,
@@ -118,9 +97,6 @@ export const CURLING_SCORING = {
118
97
  participation: (state, rosterPlayerIds, absentRosterPlayerIds) => decideParticipation(CURLING_CONTRIBUTION, state, rosterPlayerIds, absentRosterPlayerIds),
119
98
  contribution: (game) => buildGameContribution(CURLING_CONTRIBUTION, game),
120
99
  };
121
- // ---------------------------------------------------------------------------------------------
122
- // Display
123
- // ---------------------------------------------------------------------------------------------
124
100
  export const CURLING_ROUNDS = { count: CURLING_ROUND_COUNT, label: 'Round' };
125
101
  export const CURLING_EVENT_LABELS = {
126
102
  [CURLING_ROUND_SCORE_EVENT_TYPE]: { button: 'Score', play: 'Round score' },
@@ -152,9 +128,6 @@ export const CURLING_SCORING_PANEL = {
152
128
  blankEventType: CURLING_ROUND_BLANK_EVENT_TYPE,
153
129
  maxPoints: MAX_CURLING_ROUND_POINTS,
154
130
  };
155
- // ---------------------------------------------------------------------------------------------
156
- // Bulk entry — one row per round, a cell per team
157
- // ---------------------------------------------------------------------------------------------
158
131
  export const CURLING_ROUND_POINTS_MESSAGE = `A round is worth 0 to ${MAX_CURLING_ROUND_POINTS} points.`;
159
132
  export const CURLING_ONE_SCORING_TEAM_MESSAGE = 'A round is scored by one team (the other 0 or empty), blank (0 for every team), or left empty.';
160
133
  export const CURLING_NO_SKIPPED_ROUND_MESSAGE = 'Rounds are recorded in order: fill in every earlier round first.';
@@ -1 +1 @@
1
- {"version":3,"file":"curling-scoring.js","sourceRoot":"","sources":["../../src/sports/curling-scoring.ts"],"names":[],"mappings":"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","sourcesContent":["// The curling adapter's scoring, display, and bulk-entry members. Curling is team-scored by round: a\n// round event records one round — one team's points, or a blank round for every team — and nothing\n// names a player. A round is recorded once, in order: `validate` accepts a round event only for the\n// first unrecorded round, and re-scoring a round is a void of its event plus a new one, so no curling\n// event is ever superseded and undoing one always returns its round to unrecorded (RULE-QS-SPORT-913).\n\nimport { z } from 'zod';\nimport {\n CURLING_ROUND_COUNT,\n MAX_CURLING_ROUND_POINTS,\n MAX_STAT_EVENTS_PER_WRITE,\n} from '../constants/sports.js';\nimport type { CurlingTeamStats, GameStatEvent } from '../doc-schemas/game-stats.js';\nimport type { CurlingGameStats, GameStats } from '../doc-schemas/sport-game.js';\nimport {\n CURLING_ROUND_BLANK_EVENT_TYPE,\n CURLING_ROUND_SCORE_EVENT_TYPE,\n CURLING_STAT_EVENT_TYPE_KEYS,\n} from '../types/sports.js';\nimport type { CurlingStatEventType, CurlingTeamStatKey } from '../types/sports.js';\nimport { STAT_VOID_EVENT_TYPE } from '../types/stats.js';\nimport {\n BULK_ENTRY_TOO_LARGE_MESSAGE,\n bulkEntryCountField,\n type BulkEntryContext,\n type RoundsBulkEntry,\n type RoundsValues,\n type StatEventDraft,\n} from './bulk-entry.js';\nimport {\n buildGameContribution,\n decideParticipation,\n type ContributionSpec,\n} from './contribution.js';\nimport {\n FINALIZATION_READY,\n STAT_EVENT_ACCEPTED,\n isSportStatEventIn,\n isStatCorrection,\n refuseStatEvent,\n type CorrectableFields,\n type FinalizationReadiness,\n type GameTeamLine,\n type SportScoring,\n type SportStatEventBody,\n type StatEventBody,\n} from './scoring.js';\nimport {\n describeStoreEvent,\n type RoundsDisplay,\n type RoundsScoringPanel,\n type Scoreboard,\n type StatEventLabel,\n type StatEventNameResolver,\n type StatFieldLabels,\n} from './scoring-display.js';\n\ntype CurlingEvent = Extract<SportStatEventBody, { type: CurlingStatEventType }>;\n\nfunction isCurlingEvent(event: StatEventBody | undefined): event is CurlingEvent {\n return isSportStatEventIn(CURLING_STAT_EVENT_TYPE_KEYS, event);\n}\n\n// The registry chose this adapter by the game's `leagueType`, so the rollup it is handed is a\n// curling game's (RULE-QS-SPORT-902).\nfunction ownStats(state: GameStats): CurlingGameStats {\n return state as CurlingGameStats;\n}\n\nfunction ownTeamLine(line: GameTeamLine): CurlingTeamStats {\n return line as CurlingTeamStats;\n}\n\n// ---------------------------------------------------------------------------------------------\n// Lines — a team's `score` is always the sum of its recorded rounds\n// ---------------------------------------------------------------------------------------------\n\ntype RoundSlots = CurlingTeamStats['roundScores'];\n\nfunction sumOfRounds(roundScores: RoundSlots): number {\n return roundScores.reduce<number>((total, slot) => total + (slot ?? 0), 0);\n}\n\n/** Every round unrecorded — `null`, where a blank round is `0`. */\nexport function emptyCurlingTeamLine(): CurlingTeamStats {\n const roundScores: RoundSlots = Array.from({ length: CURLING_ROUND_COUNT }, () => null);\n return { score: sumOfRounds(roundScores), roundScores };\n}\n\nfunction withRound(\n line: CurlingTeamStats,\n roundNumber: number,\n slot: number | null,\n): CurlingTeamStats {\n const roundScores = [...line.roundScores];\n roundScores[roundNumber - 1] = slot;\n return { score: sumOfRounds(roundScores), roundScores };\n}\n\n/**\n * Record or clear one round on every team line: a scored round is the scoring team's points and 0\n * for the others, a blank round 0 for all, and a cleared round `null` for all.\n */\nfunction setRound(stateIn: GameStats, event: CurlingEvent, recorded: boolean): CurlingGameStats {\n const state = ownStats(stateIn);\n const teamStats = { ...state.teamStats };\n const scoringTeamId = event.type === CURLING_ROUND_SCORE_EVENT_TYPE ? event.teamId : undefined;\n if (scoringTeamId !== undefined && teamStats[scoringTeamId] === undefined) {\n teamStats[scoringTeamId] = emptyCurlingTeamLine();\n }\n for (const [teamId, line] of Object.entries(teamStats)) {\n const points =\n event.type === CURLING_ROUND_SCORE_EVENT_TYPE && teamId === scoringTeamId ? event.points : 0;\n teamStats[teamId] = withRound(line, event.roundNumber, recorded ? points : null);\n }\n return { teamStats, playerStats: state.playerStats };\n}\n\nfunction nextCurlingRound(stateIn: GameStats): number | null {\n const lines = Object.values(ownStats(stateIn).teamStats);\n for (let roundNumber = 1; roundNumber <= CURLING_ROUND_COUNT; roundNumber += 1) {\n if (lines.length === 0 || lines.some((line) => line.roundScores[roundNumber - 1] === null))\n return roundNumber;\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------------------------\n// Scoring\n// ---------------------------------------------------------------------------------------------\n\n// No curling field is correctable: a curling result changes only by reopening the game and\n// re-scoring the round.\nconst CORRECTABLE_FIELDS: CorrectableFields = { team: [], player: [] };\n\nfunction namesPlayer(event: StatEventBody): boolean {\n return 'playerUserId' in event && event.playerUserId !== undefined;\n}\n\nconst validate: SportScoring['validate'] = (state, event, roster) => {\n if (!isCurlingEvent(event)) return refuseStatEvent('eventTypeNotInSport');\n if (namesPlayer(event)) return refuseStatEvent('playerNotAllowed');\n if (event.type === CURLING_ROUND_SCORE_EVENT_TYPE && !roster.teamIds.includes(event.teamId)) {\n return refuseStatEvent('teamNotInGame');\n }\n const next = nextCurlingRound(state);\n if (next === null) return refuseStatEvent('roundsComplete');\n return event.roundNumber === next ? STAT_EVENT_ACCEPTED : refuseStatEvent('roundNotNext');\n};\n\nfunction finalizationReadiness(state: GameStats): FinalizationReadiness {\n return nextCurlingRound(state) === null\n ? FINALIZATION_READY\n : { ready: false, reason: 'roundsIncomplete' };\n}\n\n// What a completed curling game adds: the standings line from the two scores, and a game played\n// to every present roster entry, whatever its team scored. There are no individual stats, so only\n// absence withholds a game played and no entry is ever non-scoring.\nconst CURLING_CONTRIBUTION: ContributionSpec = {\n playerStatKeys: [],\n teamSeasonPlayerSums: [],\n zeroLineIsNonScoring: false,\n deriveLine: (_kind, line) => line,\n};\n\nexport const CURLING_SCORING: SportScoring = {\n validate,\n apply: (state, event) => {\n if (isStatCorrection(event)) throw new Error('No curling field is correctable.');\n if (!isCurlingEvent(event))\n throw new Error(`The curling adapter does not apply ${event.type}.`);\n return setRound(state, event, true);\n },\n undo: (state, target) => {\n if (!isCurlingEvent(target))\n throw new Error(`The curling adapter does not undo ${target.type}.`);\n return setRound(state, target, false);\n },\n // A curling line has no derived field.\n deriveLine: CURLING_CONTRIBUTION.deriveLine,\n correctableFields: CORRECTABLE_FIELDS,\n nextRound: nextCurlingRound,\n finalizationReadiness,\n participation: (state, rosterPlayerIds, absentRosterPlayerIds) =>\n decideParticipation(CURLING_CONTRIBUTION, state, rosterPlayerIds, absentRosterPlayerIds),\n contribution: (game) => buildGameContribution(CURLING_CONTRIBUTION, game),\n};\n\n// ---------------------------------------------------------------------------------------------\n// Display\n// ---------------------------------------------------------------------------------------------\n\nexport const CURLING_ROUNDS: RoundsDisplay = { count: CURLING_ROUND_COUNT, label: 'Round' };\n\nexport const CURLING_EVENT_LABELS = {\n [CURLING_ROUND_SCORE_EVENT_TYPE]: { button: 'Score', play: 'Round score' },\n [CURLING_ROUND_BLANK_EVENT_TYPE]: { button: '+0 (Blank)', play: 'Blank end' },\n} as const satisfies Record<CurlingStatEventType, StatEventLabel>;\n\nexport const CURLING_FIELD_LABELS = {\n team: { score: 'Score' },\n player: {},\n} as const satisfies StatFieldLabels<CurlingTeamStatKey, never>;\n\nexport function describeCurlingEvent(event: GameStatEvent, resolve: StatEventNameResolver): string {\n const storeEvent = describeStoreEvent(event, resolve, CURLING_FIELD_LABELS);\n if (storeEvent !== null) return storeEvent;\n if (!isCurlingEvent(event)) return event.type;\n const round = `${CURLING_ROUNDS.label} ${event.roundNumber}`;\n if (event.type === CURLING_ROUND_BLANK_EVENT_TYPE)\n return `${round}: ${CURLING_EVENT_LABELS[event.type].play.toLowerCase()}`;\n return `${round}: ${resolve.team(event.teamId)} scored ${event.points}`;\n}\n\nexport const CURLING_SCOREBOARD: Scoreboard = {\n figures: [{ id: 'points', label: 'Points', value: (teamLine) => teamLine.score }],\n jumpBall: false,\n roundSlots: (teamLine) => ownTeamLine(teamLine).roundScores,\n};\n\nexport const CURLING_SCORING_PANEL: RoundsScoringPanel = {\n kind: 'rounds',\n scoreEventType: CURLING_ROUND_SCORE_EVENT_TYPE,\n blankEventType: CURLING_ROUND_BLANK_EVENT_TYPE,\n maxPoints: MAX_CURLING_ROUND_POINTS,\n};\n\n// ---------------------------------------------------------------------------------------------\n// Bulk entry — one row per round, a cell per team\n// ---------------------------------------------------------------------------------------------\n\nexport const CURLING_ROUND_POINTS_MESSAGE = `A round is worth 0 to ${MAX_CURLING_ROUND_POINTS} points.`;\nexport const CURLING_ONE_SCORING_TEAM_MESSAGE =\n 'A round is scored by one team (the other 0 or empty), blank (0 for every team), or left empty.';\nexport const CURLING_NO_SKIPPED_ROUND_MESSAGE =\n 'Rounds are recorded in order: fill in every earlier round first.';\n\ntype RoundOutcome =\n | { readonly kind: 'unrecorded' }\n | { readonly kind: 'blank' }\n | { readonly kind: 'scored'; readonly teamId: string; readonly points: number }\n | { readonly kind: 'invalid' };\n\nfunction roundOutcome(\n row: Readonly<Record<string, number | null | undefined>>,\n teamIds: readonly string[],\n): RoundOutcome {\n const cells = teamIds.map((teamId) => ({ teamId, points: row[teamId] ?? null }));\n if (cells.every((cell) => cell.points === null)) return { kind: 'unrecorded' };\n if (cells.every((cell) => cell.points === 0)) return { kind: 'blank' };\n const scorers = cells.filter((cell) => cell.points !== null && cell.points > 0);\n const [scorer] = scorers;\n if (scorers.length === 1 && scorer !== undefined && scorer.points !== null) {\n return { kind: 'scored', teamId: scorer.teamId, points: scorer.points };\n }\n return { kind: 'invalid' };\n}\n\nfunction sameOutcome(left: RoundOutcome, right: RoundOutcome): boolean {\n if (left.kind === 'scored' && right.kind === 'scored')\n return left.teamId === right.teamId && left.points === right.points;\n return left.kind === right.kind;\n}\n\nfunction storedRound(\n context: BulkEntryContext,\n roundNumber: number,\n): Record<string, number | null> {\n const state = ownStats(context.state);\n return Object.fromEntries(\n context.game.teamIds.map((teamId) => [\n teamId,\n state.teamStats[teamId]?.roundScores[roundNumber - 1] ?? null,\n ]),\n );\n}\n\nfunction roundDrafts(values: RoundsValues, context: BulkEntryContext): StatEventDraft[] {\n const { teamIds } = context.game;\n const liveEventByRound = new Map(\n context.liveRoundEvents.map((event) => [event.roundNumber, event.eventId]),\n );\n const voids: StatEventDraft[] = [];\n const recorded: StatEventDraft[] = [];\n values.rounds.forEach((row, index) => {\n const roundNumber = index + 1;\n const entered = roundOutcome(row, teamIds);\n if (sameOutcome(entered, roundOutcome(storedRound(context, roundNumber), teamIds))) return;\n const liveEventId = liveEventByRound.get(roundNumber);\n if (liveEventId !== undefined)\n voids.push({ type: STAT_VOID_EVENT_TYPE, targetEventId: liveEventId });\n if (entered.kind === 'scored') {\n recorded.push({\n type: CURLING_ROUND_SCORE_EVENT_TYPE,\n teamId: entered.teamId,\n roundNumber,\n points: entered.points,\n });\n } else if (entered.kind === 'blank') {\n recorded.push({ type: CURLING_ROUND_BLANK_EVENT_TYPE, roundNumber });\n }\n });\n // Every void first, then the new rounds in order: each changed round is unrecorded again before\n // any new event is validated, so each new event is the next unrecorded round when it arrives.\n return [...voids, ...recorded];\n}\n\nconst roundCell = bulkEntryCountField\n .pipe(z.number().max(MAX_CURLING_ROUND_POINTS, CURLING_ROUND_POINTS_MESSAGE))\n .nullable();\n\nexport const CURLING_BULK_ENTRY: RoundsBulkEntry = {\n kind: 'rounds',\n formDefaults: (context) => ({\n rounds: Array.from({ length: CURLING_ROUND_COUNT }, (_, index) =>\n storedRound(context, index + 1),\n ),\n }),\n formSchema: (context) => {\n const { teamIds } = context.game;\n const row = z.strictObject(Object.fromEntries(teamIds.map((teamId) => [teamId, roundCell])));\n return z\n .object({ rounds: z.array(row).length(CURLING_ROUND_COUNT) })\n .superRefine((values, issues) => {\n let unrecordedBefore = false;\n values.rounds.forEach((cells, index) => {\n const outcome = roundOutcome(cells, teamIds);\n if (outcome.kind === 'invalid') {\n issues.addIssue({\n code: 'custom',\n path: ['rounds', index],\n message: CURLING_ONE_SCORING_TEAM_MESSAGE,\n });\n } else if (outcome.kind === 'unrecorded') {\n unrecordedBefore = true;\n } else if (unrecordedBefore) {\n issues.addIssue({\n code: 'custom',\n path: ['rounds', index],\n message: CURLING_NO_SKIPPED_ROUND_MESSAGE,\n });\n }\n });\n if (roundDrafts(values, context).length > MAX_STAT_EVENTS_PER_WRITE) {\n issues.addIssue({\n code: 'custom',\n path: ['rounds'],\n message: BULK_ENTRY_TOO_LARGE_MESSAGE,\n });\n }\n });\n },\n toEvents: roundDrafts,\n};\n"]}
1
+ {"version":3,"file":"curling-scoring.js","sourceRoot":"","sources":["../../src/sports/curling-scoring.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\nimport {\n CURLING_ROUND_COUNT,\n MAX_CURLING_ROUND_POINTS,\n MAX_STAT_EVENTS_PER_WRITE,\n} from '../constants/sports.js';\nimport type { CurlingTeamStats, GameStatEvent } from '../doc-schemas/game-stats.js';\nimport type { CurlingGameStats, GameStats } from '../doc-schemas/sport-game.js';\nimport {\n CURLING_ROUND_BLANK_EVENT_TYPE,\n CURLING_ROUND_SCORE_EVENT_TYPE,\n CURLING_STAT_EVENT_TYPE_KEYS,\n} from '../types/sports.js';\nimport type { CurlingStatEventType, CurlingTeamStatKey } from '../types/sports.js';\nimport { STAT_VOID_EVENT_TYPE } from '../types/stats.js';\nimport {\n BULK_ENTRY_TOO_LARGE_MESSAGE,\n bulkEntryCountField,\n type BulkEntryContext,\n type RoundsBulkEntry,\n type RoundsValues,\n type StatEventDraft,\n} from './bulk-entry.js';\nimport {\n buildGameContribution,\n decideParticipation,\n type ContributionSpec,\n} from './contribution.js';\nimport {\n FINALIZATION_READY,\n STAT_EVENT_ACCEPTED,\n isSportStatEventIn,\n isStatCorrection,\n refuseStatEvent,\n type CorrectableFields,\n type FinalizationReadiness,\n type GameTeamLine,\n type SportScoring,\n type SportStatEventBody,\n type StatEventBody,\n} from './scoring.js';\nimport {\n describeStoreEvent,\n type RoundsDisplay,\n type RoundsScoringPanel,\n type Scoreboard,\n type StatEventLabel,\n type StatEventNameResolver,\n type StatFieldLabels,\n} from './scoring-display.js';\n\ntype CurlingEvent = Extract<SportStatEventBody, { type: CurlingStatEventType }>;\n\nfunction isCurlingEvent(event: StatEventBody | undefined): event is CurlingEvent {\n return isSportStatEventIn(CURLING_STAT_EVENT_TYPE_KEYS, event);\n}\n\nfunction ownStats(state: GameStats): CurlingGameStats {\n return state as CurlingGameStats;\n}\n\nfunction ownTeamLine(line: GameTeamLine): CurlingTeamStats {\n return line as CurlingTeamStats;\n}\n\ntype RoundSlots = CurlingTeamStats['roundScores'];\n\nfunction sumOfRounds(roundScores: RoundSlots): number {\n return roundScores.reduce<number>((total, slot) => total + (slot ?? 0), 0);\n}\n\nexport function emptyCurlingTeamLine(): CurlingTeamStats {\n const roundScores: RoundSlots = Array.from({ length: CURLING_ROUND_COUNT }, () => null);\n return { score: sumOfRounds(roundScores), roundScores };\n}\n\nfunction withRound(\n line: CurlingTeamStats,\n roundNumber: number,\n slot: number | null,\n): CurlingTeamStats {\n const roundScores = [...line.roundScores];\n roundScores[roundNumber - 1] = slot;\n return { score: sumOfRounds(roundScores), roundScores };\n}\n\nfunction setRound(stateIn: GameStats, event: CurlingEvent, recorded: boolean): CurlingGameStats {\n const state = ownStats(stateIn);\n const teamStats = { ...state.teamStats };\n const scoringTeamId = event.type === CURLING_ROUND_SCORE_EVENT_TYPE ? event.teamId : undefined;\n if (scoringTeamId !== undefined && teamStats[scoringTeamId] === undefined) {\n teamStats[scoringTeamId] = emptyCurlingTeamLine();\n }\n for (const [teamId, line] of Object.entries(teamStats)) {\n const points =\n event.type === CURLING_ROUND_SCORE_EVENT_TYPE && teamId === scoringTeamId ? event.points : 0;\n teamStats[teamId] = withRound(line, event.roundNumber, recorded ? points : null);\n }\n return { teamStats, playerStats: state.playerStats };\n}\n\nfunction nextCurlingRound(stateIn: GameStats): number | null {\n const lines = Object.values(ownStats(stateIn).teamStats);\n for (let roundNumber = 1; roundNumber <= CURLING_ROUND_COUNT; roundNumber += 1) {\n if (lines.length === 0 || lines.some((line) => line.roundScores[roundNumber - 1] === null))\n return roundNumber;\n }\n return null;\n}\n\nconst CORRECTABLE_FIELDS: CorrectableFields = { team: [], player: [] };\n\nfunction namesPlayer(event: StatEventBody): boolean {\n return 'playerUserId' in event && event.playerUserId !== undefined;\n}\n\nconst validate: SportScoring['validate'] = (state, event, roster) => {\n if (!isCurlingEvent(event)) return refuseStatEvent('eventTypeNotInSport');\n if (namesPlayer(event)) return refuseStatEvent('playerNotAllowed');\n if (event.type === CURLING_ROUND_SCORE_EVENT_TYPE && !roster.teamIds.includes(event.teamId)) {\n return refuseStatEvent('teamNotInGame');\n }\n const next = nextCurlingRound(state);\n if (next === null) return refuseStatEvent('roundsComplete');\n return event.roundNumber === next ? STAT_EVENT_ACCEPTED : refuseStatEvent('roundNotNext');\n};\n\nfunction finalizationReadiness(state: GameStats): FinalizationReadiness {\n return nextCurlingRound(state) === null\n ? FINALIZATION_READY\n : { ready: false, reason: 'roundsIncomplete' };\n}\n\nconst CURLING_CONTRIBUTION: ContributionSpec = {\n playerStatKeys: [],\n teamSeasonPlayerSums: [],\n zeroLineIsNonScoring: false,\n deriveLine: (_kind, line) => line,\n};\n\nexport const CURLING_SCORING: SportScoring = {\n validate,\n apply: (state, event) => {\n if (isStatCorrection(event)) throw new Error('No curling field is correctable.');\n if (!isCurlingEvent(event))\n throw new Error(`The curling adapter does not apply ${event.type}.`);\n return setRound(state, event, true);\n },\n undo: (state, target) => {\n if (!isCurlingEvent(target))\n throw new Error(`The curling adapter does not undo ${target.type}.`);\n return setRound(state, target, false);\n },\n deriveLine: CURLING_CONTRIBUTION.deriveLine,\n correctableFields: CORRECTABLE_FIELDS,\n nextRound: nextCurlingRound,\n finalizationReadiness,\n participation: (state, rosterPlayerIds, absentRosterPlayerIds) =>\n decideParticipation(CURLING_CONTRIBUTION, state, rosterPlayerIds, absentRosterPlayerIds),\n contribution: (game) => buildGameContribution(CURLING_CONTRIBUTION, game),\n};\n\nexport const CURLING_ROUNDS: RoundsDisplay = { count: CURLING_ROUND_COUNT, label: 'Round' };\n\nexport const CURLING_EVENT_LABELS = {\n [CURLING_ROUND_SCORE_EVENT_TYPE]: { button: 'Score', play: 'Round score' },\n [CURLING_ROUND_BLANK_EVENT_TYPE]: { button: '+0 (Blank)', play: 'Blank end' },\n} as const satisfies Record<CurlingStatEventType, StatEventLabel>;\n\nexport const CURLING_FIELD_LABELS = {\n team: { score: 'Score' },\n player: {},\n} as const satisfies StatFieldLabels<CurlingTeamStatKey, never>;\n\nexport function describeCurlingEvent(event: GameStatEvent, resolve: StatEventNameResolver): string {\n const storeEvent = describeStoreEvent(event, resolve, CURLING_FIELD_LABELS);\n if (storeEvent !== null) return storeEvent;\n if (!isCurlingEvent(event)) return event.type;\n const round = `${CURLING_ROUNDS.label} ${event.roundNumber}`;\n if (event.type === CURLING_ROUND_BLANK_EVENT_TYPE)\n return `${round}: ${CURLING_EVENT_LABELS[event.type].play.toLowerCase()}`;\n return `${round}: ${resolve.team(event.teamId)} scored ${event.points}`;\n}\n\nexport const CURLING_SCOREBOARD: Scoreboard = {\n figures: [{ id: 'points', label: 'Points', value: (teamLine) => teamLine.score }],\n jumpBall: false,\n roundSlots: (teamLine) => ownTeamLine(teamLine).roundScores,\n};\n\nexport const CURLING_SCORING_PANEL: RoundsScoringPanel = {\n kind: 'rounds',\n scoreEventType: CURLING_ROUND_SCORE_EVENT_TYPE,\n blankEventType: CURLING_ROUND_BLANK_EVENT_TYPE,\n maxPoints: MAX_CURLING_ROUND_POINTS,\n};\n\nexport const CURLING_ROUND_POINTS_MESSAGE = `A round is worth 0 to ${MAX_CURLING_ROUND_POINTS} points.`;\nexport const CURLING_ONE_SCORING_TEAM_MESSAGE =\n 'A round is scored by one team (the other 0 or empty), blank (0 for every team), or left empty.';\nexport const CURLING_NO_SKIPPED_ROUND_MESSAGE =\n 'Rounds are recorded in order: fill in every earlier round first.';\n\ntype RoundOutcome =\n | { readonly kind: 'unrecorded' }\n | { readonly kind: 'blank' }\n | { readonly kind: 'scored'; readonly teamId: string; readonly points: number }\n | { readonly kind: 'invalid' };\n\nfunction roundOutcome(\n row: Readonly<Record<string, number | null | undefined>>,\n teamIds: readonly string[],\n): RoundOutcome {\n const cells = teamIds.map((teamId) => ({ teamId, points: row[teamId] ?? null }));\n if (cells.every((cell) => cell.points === null)) return { kind: 'unrecorded' };\n if (cells.every((cell) => cell.points === 0)) return { kind: 'blank' };\n const scorers = cells.filter((cell) => cell.points !== null && cell.points > 0);\n const [scorer] = scorers;\n if (scorers.length === 1 && scorer !== undefined && scorer.points !== null) {\n return { kind: 'scored', teamId: scorer.teamId, points: scorer.points };\n }\n return { kind: 'invalid' };\n}\n\nfunction sameOutcome(left: RoundOutcome, right: RoundOutcome): boolean {\n if (left.kind === 'scored' && right.kind === 'scored')\n return left.teamId === right.teamId && left.points === right.points;\n return left.kind === right.kind;\n}\n\nfunction storedRound(\n context: BulkEntryContext,\n roundNumber: number,\n): Record<string, number | null> {\n const state = ownStats(context.state);\n return Object.fromEntries(\n context.game.teamIds.map((teamId) => [\n teamId,\n state.teamStats[teamId]?.roundScores[roundNumber - 1] ?? null,\n ]),\n );\n}\n\nfunction roundDrafts(values: RoundsValues, context: BulkEntryContext): StatEventDraft[] {\n const { teamIds } = context.game;\n const liveEventByRound = new Map(\n context.liveRoundEvents.map((event) => [event.roundNumber, event.eventId]),\n );\n const voids: StatEventDraft[] = [];\n const recorded: StatEventDraft[] = [];\n values.rounds.forEach((row, index) => {\n const roundNumber = index + 1;\n const entered = roundOutcome(row, teamIds);\n if (sameOutcome(entered, roundOutcome(storedRound(context, roundNumber), teamIds))) return;\n const liveEventId = liveEventByRound.get(roundNumber);\n if (liveEventId !== undefined)\n voids.push({ type: STAT_VOID_EVENT_TYPE, targetEventId: liveEventId });\n if (entered.kind === 'scored') {\n recorded.push({\n type: CURLING_ROUND_SCORE_EVENT_TYPE,\n teamId: entered.teamId,\n roundNumber,\n points: entered.points,\n });\n } else if (entered.kind === 'blank') {\n recorded.push({ type: CURLING_ROUND_BLANK_EVENT_TYPE, roundNumber });\n }\n });\n // Every void first, then the new rounds in order: each changed round is unrecorded again before\n // any new event is validated, so each new event is the next unrecorded round when it arrives.\n return [...voids, ...recorded];\n}\n\nconst roundCell = bulkEntryCountField\n .pipe(z.number().max(MAX_CURLING_ROUND_POINTS, CURLING_ROUND_POINTS_MESSAGE))\n .nullable();\n\nexport const CURLING_BULK_ENTRY: RoundsBulkEntry = {\n kind: 'rounds',\n formDefaults: (context) => ({\n rounds: Array.from({ length: CURLING_ROUND_COUNT }, (_, index) =>\n storedRound(context, index + 1),\n ),\n }),\n formSchema: (context) => {\n const { teamIds } = context.game;\n const row = z.strictObject(Object.fromEntries(teamIds.map((teamId) => [teamId, roundCell])));\n return z\n .object({ rounds: z.array(row).length(CURLING_ROUND_COUNT) })\n .superRefine((values, issues) => {\n let unrecordedBefore = false;\n values.rounds.forEach((cells, index) => {\n const outcome = roundOutcome(cells, teamIds);\n if (outcome.kind === 'invalid') {\n issues.addIssue({\n code: 'custom',\n path: ['rounds', index],\n message: CURLING_ONE_SCORING_TEAM_MESSAGE,\n });\n } else if (outcome.kind === 'unrecorded') {\n unrecordedBefore = true;\n } else if (unrecordedBefore) {\n issues.addIssue({\n code: 'custom',\n path: ['rounds', index],\n message: CURLING_NO_SKIPPED_ROUND_MESSAGE,\n });\n }\n });\n if (roundDrafts(values, context).length > MAX_STAT_EVENTS_PER_WRITE) {\n issues.addIssue({\n code: 'custom',\n path: ['rounds'],\n message: BULK_ENTRY_TOO_LARGE_MESSAGE,\n });\n }\n });\n },\n toEvents: roundDrafts,\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"curling.d.ts","sourceRoot":"","sources":["../../src/sports/curling.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIvD,eAAO,MAAM,eAAe,EAAE,YAAY,CAAC,SAAS,CAiDnD,CAAC"}
1
+ {"version":3,"file":"curling.d.ts","sourceRoot":"","sources":["../../src/sports/curling.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIvD,eAAO,MAAM,eAAe,EAAE,YAAY,CAAC,SAAS,CAgDnD,CAAC"}
@@ -1,6 +1,3 @@
1
- // The curling adapter: team-scored by end, with no individual stats — every present roster entry is
2
- // credited a game played — and no hall of fame. Its scoring, display, and bulk-entry members are in
3
- // ./curling-scoring.ts.
4
1
  import { SPORT_LINE_SCHEMAS } from '../doc-schemas/aggregate-stats.js';
5
2
  import { CURLING_PLAYER_STAT_KEYS, CURLING_STAT_EVENT_TYPE_KEYS, CURLING_TEAM_STAT_KEYS, SPORT_KEY_BY_LEAGUE_TYPE, } from '../types/sports.js';
6
3
  import { CURLING_BULK_ENTRY, CURLING_EVENT_LABELS, CURLING_FIELD_LABELS, CURLING_ROUNDS, CURLING_SCOREBOARD, CURLING_SCORING, CURLING_SCORING_PANEL, describeCurlingEvent, emptyCurlingTeamLine, } from './curling-scoring.js';
@@ -34,7 +31,6 @@ export const CURLING_ADAPTER = {
34
31
  defaultSort: [{ columnId: GAMES_PLAYED_COLUMN.id, direction: 'desc' }],
35
32
  defaultLayout: [GAMES_PLAYED_COLUMN.id],
36
33
  },
37
- // No player game line, so the game page renders no player table.
38
34
  gamePlayer: { columns: [], defaultSort: [], defaultLayout: [] },
39
35
  teamSeason: {
40
36
  columns: TEAM_SEASON_BASE_COLUMNS,
@@ -1 +1 @@
1
- {"version":3,"file":"curling.js","sourceRoot":"","sources":["../../src/sports/curling.ts"],"names":[],"mappings":"AAAA,oGAAoG;AACpG,oGAAoG;AACpG,wBAAwB;AAExB,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,EACL,wBAAwB,EACxB,4BAA4B,EAC5B,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,oBAAoB,EACpB,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,wBAAwB,GACzB,MAAM,qBAAqB,CAAC;AAG7B,MAAM,mBAAmB,GAAG,CAAC,mBAAmB,CAAC,CAAC;AAElD,MAAM,CAAC,MAAM,eAAe,GAA4B;IACtD,UAAU,EAAE,SAAS;IACrB,QAAQ,EAAE,wBAAwB,CAAC,OAAO;IAC1C,UAAU,EAAE,4BAA4B;IACxC,YAAY,EAAE,sBAAsB;IACpC,cAAc,EAAE,wBAAwB;IACxC,UAAU,EAAE,KAAK;IACjB,kBAAkB,EAAE,KAAK;IACzB,WAAW,EAAE,kBAAkB,CAAC,OAAO;IACvC,GAAG,eAAe;IAClB,OAAO,EAAE;QACP,KAAK,EAAE,SAAS;QAChB,OAAO,EAAE,cAAc;QACvB,eAAe,EAAE,0BAA0B,CAAC,mBAAmB,CAAC;QAChE,WAAW,EAAE,oBAAoB;QACjC,WAAW,EAAE,oBAAoB;QACjC,aAAa,EAAE,oBAAoB;QACnC,MAAM,EAAE,cAAc;QACtB,UAAU,EAAE,kBAAkB;QAC9B,YAAY,EAAE,qBAAqB;KACpC;IACD,OAAO,EAAE;QACP,YAAY,EAAE;YACZ,OAAO,EAAE,mBAAmB;YAC5B,WAAW,EAAE,CAAC,EAAE,QAAQ,EAAE,mBAAmB,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;YACtE,aAAa,EAAE,CAAC,mBAAmB,CAAC,EAAE,CAAC;SACxC;QACD,iEAAiE;QACjE,UAAU,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE;QAC/D,UAAU,EAAE;YACV,OAAO,EAAE,wBAAwB;YACjC,WAAW,EAAE;gBACX,EAAE,QAAQ,EAAE,uBAAuB,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;gBAChE,EAAE,QAAQ,EAAE,uBAAuB,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;gBACjE,EAAE,QAAQ,EAAE,uBAAuB,CAAC,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;aACtE;YACD,aAAa,EAAE;gBACb,uBAAuB,CAAC,IAAI,CAAC,EAAE;gBAC/B,uBAAuB,CAAC,MAAM,CAAC,EAAE;gBACjC,uBAAuB,CAAC,IAAI,CAAC,EAAE;gBAC/B,uBAAuB,CAAC,SAAS,CAAC,EAAE;aACrC;SACF;KACF;IACD,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,oBAAoB,EAAE,CAAC,CAAC,CAAC;QAC7F,WAAW,EAAE,EAAE;KAChB,CAAC;IACF,SAAS,EAAE,kBAAkB;CAC9B,CAAC","sourcesContent":["// The curling adapter: team-scored by end, with no individual stats — every present roster entry is\n// credited a game played — and no hall of fame. Its scoring, display, and bulk-entry members are in\n// ./curling-scoring.ts.\n\nimport { SPORT_LINE_SCHEMAS } from '../doc-schemas/aggregate-stats.js';\nimport {\n CURLING_PLAYER_STAT_KEYS,\n CURLING_STAT_EVENT_TYPE_KEYS,\n CURLING_TEAM_STAT_KEYS,\n SPORT_KEY_BY_LEAGUE_TYPE,\n} from '../types/sports.js';\nimport {\n CURLING_BULK_ENTRY,\n CURLING_EVENT_LABELS,\n CURLING_FIELD_LABELS,\n CURLING_ROUNDS,\n CURLING_SCOREBOARD,\n CURLING_SCORING,\n CURLING_SCORING_PANEL,\n describeCurlingEvent,\n emptyCurlingTeamLine,\n} from './curling-scoring.js';\nimport { lifetimeSummaryFromColumns } from './lifetime-summary.js';\nimport {\n GAMES_PLAYED_COLUMN,\n TEAM_SEASON_BASE_COLUMN,\n TEAM_SEASON_BASE_COLUMNS,\n} from './shared-columns.js';\nimport type { SportAdapter } from './sport-adapter.js';\n\nconst playerTotalsColumns = [GAMES_PLAYED_COLUMN];\n\nexport const CURLING_ADAPTER: SportAdapter<'Curling'> = {\n leagueType: 'Curling',\n sportKey: SPORT_KEY_BY_LEAGUE_TYPE.Curling,\n eventTypes: CURLING_STAT_EVENT_TYPE_KEYS,\n teamStatKeys: CURLING_TEAM_STAT_KEYS,\n playerStatKeys: CURLING_PLAYER_STAT_KEYS,\n hallOfFame: false,\n historicalBaseline: false,\n lineSchemas: SPORT_LINE_SCHEMAS.Curling,\n ...CURLING_SCORING,\n display: {\n label: 'Curling',\n iconKey: 'curlingStone',\n lifetimeSummary: lifetimeSummaryFromColumns(playerTotalsColumns),\n eventLabels: CURLING_EVENT_LABELS,\n fieldLabels: CURLING_FIELD_LABELS,\n describeEvent: describeCurlingEvent,\n rounds: CURLING_ROUNDS,\n scoreboard: CURLING_SCOREBOARD,\n scoringPanel: CURLING_SCORING_PANEL,\n },\n columns: {\n playerTotals: {\n columns: playerTotalsColumns,\n defaultSort: [{ columnId: GAMES_PLAYED_COLUMN.id, direction: 'desc' }],\n defaultLayout: [GAMES_PLAYED_COLUMN.id],\n },\n // No player game line, so the game page renders no player table.\n gamePlayer: { columns: [], defaultSort: [], defaultLayout: [] },\n teamSeason: {\n columns: TEAM_SEASON_BASE_COLUMNS,\n defaultSort: [\n { columnId: TEAM_SEASON_BASE_COLUMN.wins.id, direction: 'desc' },\n { columnId: TEAM_SEASON_BASE_COLUMN.losses.id, direction: 'asc' },\n { columnId: TEAM_SEASON_BASE_COLUMN.pointsFor.id, direction: 'desc' },\n ],\n defaultLayout: [\n TEAM_SEASON_BASE_COLUMN.wins.id,\n TEAM_SEASON_BASE_COLUMN.losses.id,\n TEAM_SEASON_BASE_COLUMN.ties.id,\n TEAM_SEASON_BASE_COLUMN.pointsFor.id,\n ],\n },\n },\n emptyStats: (game) => ({\n teamStats: Object.fromEntries(game.teamIds.map((teamId) => [teamId, emptyCurlingTeamLine()])),\n playerStats: {},\n }),\n bulkEntry: CURLING_BULK_ENTRY,\n};\n"]}
1
+ {"version":3,"file":"curling.js","sourceRoot":"","sources":["../../src/sports/curling.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,EACL,wBAAwB,EACxB,4BAA4B,EAC5B,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,oBAAoB,EACpB,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,wBAAwB,GACzB,MAAM,qBAAqB,CAAC;AAG7B,MAAM,mBAAmB,GAAG,CAAC,mBAAmB,CAAC,CAAC;AAElD,MAAM,CAAC,MAAM,eAAe,GAA4B;IACtD,UAAU,EAAE,SAAS;IACrB,QAAQ,EAAE,wBAAwB,CAAC,OAAO;IAC1C,UAAU,EAAE,4BAA4B;IACxC,YAAY,EAAE,sBAAsB;IACpC,cAAc,EAAE,wBAAwB;IACxC,UAAU,EAAE,KAAK;IACjB,kBAAkB,EAAE,KAAK;IACzB,WAAW,EAAE,kBAAkB,CAAC,OAAO;IACvC,GAAG,eAAe;IAClB,OAAO,EAAE;QACP,KAAK,EAAE,SAAS;QAChB,OAAO,EAAE,cAAc;QACvB,eAAe,EAAE,0BAA0B,CAAC,mBAAmB,CAAC;QAChE,WAAW,EAAE,oBAAoB;QACjC,WAAW,EAAE,oBAAoB;QACjC,aAAa,EAAE,oBAAoB;QACnC,MAAM,EAAE,cAAc;QACtB,UAAU,EAAE,kBAAkB;QAC9B,YAAY,EAAE,qBAAqB;KACpC;IACD,OAAO,EAAE;QACP,YAAY,EAAE;YACZ,OAAO,EAAE,mBAAmB;YAC5B,WAAW,EAAE,CAAC,EAAE,QAAQ,EAAE,mBAAmB,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;YACtE,aAAa,EAAE,CAAC,mBAAmB,CAAC,EAAE,CAAC;SACxC;QACD,UAAU,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE;QAC/D,UAAU,EAAE;YACV,OAAO,EAAE,wBAAwB;YACjC,WAAW,EAAE;gBACX,EAAE,QAAQ,EAAE,uBAAuB,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;gBAChE,EAAE,QAAQ,EAAE,uBAAuB,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;gBACjE,EAAE,QAAQ,EAAE,uBAAuB,CAAC,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;aACtE;YACD,aAAa,EAAE;gBACb,uBAAuB,CAAC,IAAI,CAAC,EAAE;gBAC/B,uBAAuB,CAAC,MAAM,CAAC,EAAE;gBACjC,uBAAuB,CAAC,IAAI,CAAC,EAAE;gBAC/B,uBAAuB,CAAC,SAAS,CAAC,EAAE;aACrC;SACF;KACF;IACD,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,oBAAoB,EAAE,CAAC,CAAC,CAAC;QAC7F,WAAW,EAAE,EAAE;KAChB,CAAC;IACF,SAAS,EAAE,kBAAkB;CAC9B,CAAC","sourcesContent":["import { SPORT_LINE_SCHEMAS } from '../doc-schemas/aggregate-stats.js';\nimport {\n CURLING_PLAYER_STAT_KEYS,\n CURLING_STAT_EVENT_TYPE_KEYS,\n CURLING_TEAM_STAT_KEYS,\n SPORT_KEY_BY_LEAGUE_TYPE,\n} from '../types/sports.js';\nimport {\n CURLING_BULK_ENTRY,\n CURLING_EVENT_LABELS,\n CURLING_FIELD_LABELS,\n CURLING_ROUNDS,\n CURLING_SCOREBOARD,\n CURLING_SCORING,\n CURLING_SCORING_PANEL,\n describeCurlingEvent,\n emptyCurlingTeamLine,\n} from './curling-scoring.js';\nimport { lifetimeSummaryFromColumns } from './lifetime-summary.js';\nimport {\n GAMES_PLAYED_COLUMN,\n TEAM_SEASON_BASE_COLUMN,\n TEAM_SEASON_BASE_COLUMNS,\n} from './shared-columns.js';\nimport type { SportAdapter } from './sport-adapter.js';\n\nconst playerTotalsColumns = [GAMES_PLAYED_COLUMN];\n\nexport const CURLING_ADAPTER: SportAdapter<'Curling'> = {\n leagueType: 'Curling',\n sportKey: SPORT_KEY_BY_LEAGUE_TYPE.Curling,\n eventTypes: CURLING_STAT_EVENT_TYPE_KEYS,\n teamStatKeys: CURLING_TEAM_STAT_KEYS,\n playerStatKeys: CURLING_PLAYER_STAT_KEYS,\n hallOfFame: false,\n historicalBaseline: false,\n lineSchemas: SPORT_LINE_SCHEMAS.Curling,\n ...CURLING_SCORING,\n display: {\n label: 'Curling',\n iconKey: 'curlingStone',\n lifetimeSummary: lifetimeSummaryFromColumns(playerTotalsColumns),\n eventLabels: CURLING_EVENT_LABELS,\n fieldLabels: CURLING_FIELD_LABELS,\n describeEvent: describeCurlingEvent,\n rounds: CURLING_ROUNDS,\n scoreboard: CURLING_SCOREBOARD,\n scoringPanel: CURLING_SCORING_PANEL,\n },\n columns: {\n playerTotals: {\n columns: playerTotalsColumns,\n defaultSort: [{ columnId: GAMES_PLAYED_COLUMN.id, direction: 'desc' }],\n defaultLayout: [GAMES_PLAYED_COLUMN.id],\n },\n gamePlayer: { columns: [], defaultSort: [], defaultLayout: [] },\n teamSeason: {\n columns: TEAM_SEASON_BASE_COLUMNS,\n defaultSort: [\n { columnId: TEAM_SEASON_BASE_COLUMN.wins.id, direction: 'desc' },\n { columnId: TEAM_SEASON_BASE_COLUMN.losses.id, direction: 'asc' },\n { columnId: TEAM_SEASON_BASE_COLUMN.pointsFor.id, direction: 'desc' },\n ],\n defaultLayout: [\n TEAM_SEASON_BASE_COLUMN.wins.id,\n TEAM_SEASON_BASE_COLUMN.losses.id,\n TEAM_SEASON_BASE_COLUMN.ties.id,\n TEAM_SEASON_BASE_COLUMN.pointsFor.id,\n ],\n },\n },\n emptyStats: (game) => ({\n teamStats: Object.fromEntries(game.teamIds.map((teamId) => [teamId, emptyCurlingTeamLine()])),\n playerStats: {},\n }),\n bulkEntry: CURLING_BULK_ENTRY,\n};\n"]}
@@ -2,33 +2,16 @@ import type { GameStatEvent } from '../doc-schemas/game-stats.js';
2
2
  import type { GameStatHistoryFirstPage, GameStatHistoryPage } from '../doc-schemas/game-stat-history.js';
3
3
  import type { FinalizedGame } from './contribution.js';
4
4
  import type { AnySportAdapter } from './sport-adapter.js';
5
- /** What a record holds beside its events: the sport, the teams, and the participation snapshot. */
6
5
  export type FinalizedRecordSnapshot = Omit<GameStatHistoryFirstPage, 'events' | 'pageCount'>;
7
- /** One page as written, at its id. */
8
6
  export interface FinalizedRecordPage {
9
7
  readonly pageId: string;
10
8
  readonly page: GameStatHistoryPage;
11
9
  }
12
- /**
13
- * The record's pages: the events in `sequence` order, cut into pages, the first carrying the page
14
- * count and the snapshot. A game with no events still has its first page.
15
- */
16
10
  export declare function buildFinalizedRecordPages(events: readonly GameStatEvent[], snapshot: Pick<FinalizedGame, 'leagueType' | 'teamIds' | 'rosterPlayerIds' | 'absentRosterPlayerIds' | 'nonScoringRosterPlayerIds'>): FinalizedRecordPage[];
17
- /** Whether a page is a record's first page, which carries the snapshot. */
18
11
  export declare function isFirstRecordPage(page: GameStatHistoryPage): page is GameStatHistoryFirstPage;
19
- /**
20
- * The record read back: the first page's snapshot and every event, in order. Throws when the pages
21
- * are not one whole record — no first page, a page count they do not match, or events out of order —
22
- * which is damage to a completed game, never a designed answer.
23
- */
24
- export declare function readFinalizedRecord(pages: readonly GameStatHistoryPage[]): {
12
+ export declare function readFinalizedRecord(pages: readonly FinalizedRecordPage[]): {
25
13
  readonly snapshot: FinalizedRecordSnapshot;
26
14
  readonly events: GameStatEvent[];
27
15
  };
28
- /**
29
- * The completed game a record describes: its events replayed through the sport's adapter, with the
30
- * snapshot finalize stored. Every path after finalize derives a game's contribution from this, never
31
- * from the game document.
32
- */
33
- export declare function finalizedGameFromRecord(adapter: AnySportAdapter, pages: readonly GameStatHistoryPage[]): FinalizedGame;
16
+ export declare function finalizedGameFromRecord(adapter: AnySportAdapter, pages: readonly FinalizedRecordPage[]): FinalizedGame;
34
17
  //# sourceMappingURL=finalized-record.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"finalized-record.d.ts","sourceRoot":"","sources":["../../src/sports/finalized-record.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EACV,wBAAwB,EACxB,mBAAmB,EACpB,MAAM,qCAAqC,CAAC;AAE7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAE1D,mGAAmG;AACnG,MAAM,MAAM,uBAAuB,GAAG,IAAI,CAAC,wBAAwB,EAAE,QAAQ,GAAG,WAAW,CAAC,CAAC;AAE7F,sCAAsC;AACtC,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;CACpC;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,SAAS,aAAa,EAAE,EAChC,QAAQ,EAAE,IAAI,CACZ,aAAa,EACX,YAAY,GACZ,SAAS,GACT,iBAAiB,GACjB,uBAAuB,GACvB,2BAA2B,CAC9B,GACA,mBAAmB,EAAE,CAwBvB;AAED,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,mBAAmB,GAAG,IAAI,IAAI,wBAAwB,CAE7F;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,SAAS,mBAAmB,EAAE,GAAG;IAC1E,QAAQ,CAAC,QAAQ,EAAE,uBAAuB,CAAC;IAC3C,QAAQ,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC;CAClC,CAqBA;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,SAAS,mBAAmB,EAAE,GACpC,aAAa,CAUf"}
1
+ {"version":3,"file":"finalized-record.d.ts","sourceRoot":"","sources":["../../src/sports/finalized-record.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EACV,wBAAwB,EACxB,mBAAmB,EACpB,MAAM,qCAAqC,CAAC;AAE7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAE1D,MAAM,MAAM,uBAAuB,GAAG,IAAI,CAAC,wBAAwB,EAAE,QAAQ,GAAG,WAAW,CAAC,CAAC;AAE7F,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;CACpC;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,SAAS,aAAa,EAAE,EAChC,QAAQ,EAAE,IAAI,CACZ,aAAa,EACX,YAAY,GACZ,SAAS,GACT,iBAAiB,GACjB,uBAAuB,GACvB,2BAA2B,CAC9B,GACA,mBAAmB,EAAE,CA6BvB;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,mBAAmB,GAAG,IAAI,IAAI,wBAAwB,CAE7F;AAKD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,SAAS,mBAAmB,EAAE,GAAG;IAC1E,QAAQ,CAAC,QAAQ,EAAE,uBAAuB,CAAC;IAC3C,QAAQ,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC;CAClC,CAkCA;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,SAAS,mBAAmB,EAAE,GACpC,aAAa,CAUf"}
@@ -1,18 +1,12 @@
1
- // A completed game's finalized record, `sportGames/{gameId}/gameStatHistory/{pageId}`: every event
2
- // document of the game in `sequence` order, in pages of up to STAT_HISTORY_PAGE_SIZE, the first page
3
- // carrying the sport, the teams, and the participation snapshot finalize applied. These are the one
4
- // builder and the one reader of its pages, so finalize, the game copy, a ghost link, and the week
5
- // view all write and read the same record.
6
- import { STAT_HISTORY_PAGE_SIZE } from '../constants/sports.js';
1
+ import { MAX_STAT_HISTORY_PAGES, STAT_HISTORY_PAGE_SIZE } from '../constants/sports.js';
7
2
  import { buildStatHistoryPageId } from '../ids/stat-event-ids.js';
8
3
  import { replayGameStats } from './replay.js';
9
- /**
10
- * The record's pages: the events in `sequence` order, cut into pages, the first carrying the page
11
- * count and the snapshot. A game with no events still has its first page.
12
- */
13
4
  export function buildFinalizedRecordPages(events, snapshot) {
14
5
  const ordered = [...events].sort((left, right) => left.sequence - right.sequence);
15
6
  const pageCount = Math.max(1, Math.ceil(ordered.length / STAT_HISTORY_PAGE_SIZE));
7
+ if (pageCount > MAX_STAT_HISTORY_PAGES) {
8
+ throw new RangeError(`A finalized record holds at most ${MAX_STAT_HISTORY_PAGES} pages; ${ordered.length} events need ${pageCount}.`);
9
+ }
16
10
  const pages = [];
17
11
  for (let index = 0; index < pageCount; index += 1) {
18
12
  const slice = ordered.slice(index * STAT_HISTORY_PAGE_SIZE, (index + 1) * STAT_HISTORY_PAGE_SIZE);
@@ -31,22 +25,32 @@ export function buildFinalizedRecordPages(events, snapshot) {
31
25
  }
32
26
  return pages;
33
27
  }
34
- /** Whether a page is a record's first page, which carries the snapshot. */
35
28
  export function isFirstRecordPage(page) {
36
29
  return 'pageCount' in page;
37
30
  }
38
- /**
39
- * The record read back: the first page's snapshot and every event, in order. Throws when the pages
40
- * are not one whole record — no first page, a page count they do not match, or events out of order —
41
- * which is damage to a completed game, never a designed answer.
42
- */
31
+ // The pages as the record's collection lists them, in id order. They are one whole record only as
32
+ // the exact set: the first page at `page_0000`, then a continuation page at each next id, as many in
33
+ // all as the first page counts.
43
34
  export function readFinalizedRecord(pages) {
44
- const [first, ...rest] = pages;
35
+ if (pages.length > MAX_STAT_HISTORY_PAGES) {
36
+ throw new Error(`A finalized record holds at most ${MAX_STAT_HISTORY_PAGES} pages.`);
37
+ }
38
+ const [firstEntry, ...restEntries] = pages;
39
+ const first = firstEntry?.page;
45
40
  if (first === undefined || !isFirstRecordPage(first))
46
41
  throw new Error('A finalized record starts with its first page.');
47
42
  if (first.pageCount !== pages.length) {
48
43
  throw new Error(`A finalized record of ${first.pageCount} pages was read as ${pages.length}.`);
49
44
  }
45
+ pages.forEach(({ pageId, page }, index) => {
46
+ if (pageId !== buildStatHistoryPageId(index)) {
47
+ throw new Error(`A finalized record holds page ${pageId} at position ${index}.`);
48
+ }
49
+ if (index > 0 && isFirstRecordPage(page)) {
50
+ throw new Error(`A finalized record holds a second first page at ${pageId}.`);
51
+ }
52
+ });
53
+ const rest = restEntries.map(({ page }) => page);
50
54
  const events = [...first.events, ...rest.flatMap((page) => page.events)];
51
55
  events.forEach((event, index) => {
52
56
  if (index > 0 && events[index - 1].sequence >= event.sequence) {
@@ -62,11 +66,6 @@ export function readFinalizedRecord(pages) {
62
66
  };
63
67
  return { snapshot, events };
64
68
  }
65
- /**
66
- * The completed game a record describes: its events replayed through the sport's adapter, with the
67
- * snapshot finalize stored. Every path after finalize derives a game's contribution from this, never
68
- * from the game document.
69
- */
70
69
  export function finalizedGameFromRecord(adapter, pages) {
71
70
  const { snapshot, events } = readFinalizedRecord(pages);
72
71
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"finalized-record.js","sourceRoot":"","sources":["../../src/sports/finalized-record.ts"],"names":[],"mappings":"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","sourcesContent":["// A completed game's finalized record, `sportGames/{gameId}/gameStatHistory/{pageId}`: every event\n// document of the game in `sequence` order, in pages of up to STAT_HISTORY_PAGE_SIZE, the first page\n// carrying the sport, the teams, and the participation snapshot finalize applied. These are the one\n// builder and the one reader of its pages, so finalize, the game copy, a ghost link, and the week\n// view all write and read the same record.\n\nimport { STAT_HISTORY_PAGE_SIZE } from '../constants/sports.js';\nimport type { GameStatEvent } from '../doc-schemas/game-stats.js';\nimport type {\n GameStatHistoryFirstPage,\n GameStatHistoryPage,\n} from '../doc-schemas/game-stat-history.js';\nimport { buildStatHistoryPageId } from '../ids/stat-event-ids.js';\nimport type { FinalizedGame } from './contribution.js';\nimport { replayGameStats } from './replay.js';\nimport type { AnySportAdapter } from './sport-adapter.js';\n\n/** What a record holds beside its events: the sport, the teams, and the participation snapshot. */\nexport type FinalizedRecordSnapshot = Omit<GameStatHistoryFirstPage, 'events' | 'pageCount'>;\n\n/** One page as written, at its id. */\nexport interface FinalizedRecordPage {\n readonly pageId: string;\n readonly page: GameStatHistoryPage;\n}\n\n/**\n * The record's pages: the events in `sequence` order, cut into pages, the first carrying the page\n * count and the snapshot. A game with no events still has its first page.\n */\nexport function buildFinalizedRecordPages(\n events: readonly GameStatEvent[],\n snapshot: Pick<\n FinalizedGame,\n | 'leagueType'\n | 'teamIds'\n | 'rosterPlayerIds'\n | 'absentRosterPlayerIds'\n | 'nonScoringRosterPlayerIds'\n >,\n): FinalizedRecordPage[] {\n const ordered = [...events].sort((left, right) => left.sequence - right.sequence);\n const pageCount = Math.max(1, Math.ceil(ordered.length / STAT_HISTORY_PAGE_SIZE));\n const pages: FinalizedRecordPage[] = [];\n for (let index = 0; index < pageCount; index += 1) {\n const slice = ordered.slice(\n index * STAT_HISTORY_PAGE_SIZE,\n (index + 1) * STAT_HISTORY_PAGE_SIZE,\n );\n const page: GameStatHistoryPage =\n index === 0\n ? {\n events: slice,\n pageCount,\n leagueType: snapshot.leagueType,\n teamIds: [...snapshot.teamIds],\n rosterPlayerIds: [...snapshot.rosterPlayerIds],\n absentRosterPlayerIds: [...snapshot.absentRosterPlayerIds],\n nonScoringRosterPlayerIds: [...snapshot.nonScoringRosterPlayerIds],\n }\n : { events: slice };\n pages.push({ pageId: buildStatHistoryPageId(index), page });\n }\n return pages;\n}\n\n/** Whether a page is a record's first page, which carries the snapshot. */\nexport function isFirstRecordPage(page: GameStatHistoryPage): page is GameStatHistoryFirstPage {\n return 'pageCount' in page;\n}\n\n/**\n * The record read back: the first page's snapshot and every event, in order. Throws when the pages\n * are not one whole record — no first page, a page count they do not match, or events out of order —\n * which is damage to a completed game, never a designed answer.\n */\nexport function readFinalizedRecord(pages: readonly GameStatHistoryPage[]): {\n readonly snapshot: FinalizedRecordSnapshot;\n readonly events: GameStatEvent[];\n} {\n const [first, ...rest] = pages;\n if (first === undefined || !isFirstRecordPage(first))\n throw new Error('A finalized record starts with its first page.');\n if (first.pageCount !== pages.length) {\n throw new Error(`A finalized record of ${first.pageCount} pages was read as ${pages.length}.`);\n }\n const events = [...first.events, ...rest.flatMap((page) => page.events)];\n events.forEach((event, index) => {\n if (index > 0 && (events[index - 1] as GameStatEvent).sequence >= event.sequence) {\n throw new Error(`A finalized record holds event ${event.eventId} out of sequence.`);\n }\n });\n const snapshot: FinalizedRecordSnapshot = {\n leagueType: first.leagueType,\n teamIds: first.teamIds,\n rosterPlayerIds: first.rosterPlayerIds,\n absentRosterPlayerIds: first.absentRosterPlayerIds,\n nonScoringRosterPlayerIds: first.nonScoringRosterPlayerIds,\n };\n return { snapshot, events };\n}\n\n/**\n * The completed game a record describes: its events replayed through the sport's adapter, with the\n * snapshot finalize stored. Every path after finalize derives a game's contribution from this, never\n * from the game document.\n */\nexport function finalizedGameFromRecord(\n adapter: AnySportAdapter,\n pages: readonly GameStatHistoryPage[],\n): FinalizedGame {\n const { snapshot, events } = readFinalizedRecord(pages);\n return {\n leagueType: snapshot.leagueType,\n teamIds: snapshot.teamIds,\n stats: replayGameStats(adapter, snapshot, events),\n rosterPlayerIds: snapshot.rosterPlayerIds,\n absentRosterPlayerIds: snapshot.absentRosterPlayerIds,\n nonScoringRosterPlayerIds: snapshot.nonScoringRosterPlayerIds,\n };\n}\n"]}
1
+ {"version":3,"file":"finalized-record.js","sourceRoot":"","sources":["../../src/sports/finalized-record.ts"],"names":[],"mappings":"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","sourcesContent":["import { MAX_STAT_HISTORY_PAGES, STAT_HISTORY_PAGE_SIZE } from '../constants/sports.js';\nimport type { GameStatEvent } from '../doc-schemas/game-stats.js';\nimport type {\n GameStatHistoryFirstPage,\n GameStatHistoryPage,\n} from '../doc-schemas/game-stat-history.js';\nimport { buildStatHistoryPageId } from '../ids/stat-event-ids.js';\nimport type { FinalizedGame } from './contribution.js';\nimport { replayGameStats } from './replay.js';\nimport type { AnySportAdapter } from './sport-adapter.js';\n\nexport type FinalizedRecordSnapshot = Omit<GameStatHistoryFirstPage, 'events' | 'pageCount'>;\n\nexport interface FinalizedRecordPage {\n readonly pageId: string;\n readonly page: GameStatHistoryPage;\n}\n\nexport function buildFinalizedRecordPages(\n events: readonly GameStatEvent[],\n snapshot: Pick<\n FinalizedGame,\n | 'leagueType'\n | 'teamIds'\n | 'rosterPlayerIds'\n | 'absentRosterPlayerIds'\n | 'nonScoringRosterPlayerIds'\n >,\n): FinalizedRecordPage[] {\n const ordered = [...events].sort((left, right) => left.sequence - right.sequence);\n const pageCount = Math.max(1, Math.ceil(ordered.length / STAT_HISTORY_PAGE_SIZE));\n if (pageCount > MAX_STAT_HISTORY_PAGES) {\n throw new RangeError(\n `A finalized record holds at most ${MAX_STAT_HISTORY_PAGES} pages; ${ordered.length} events need ${pageCount}.`,\n );\n }\n const pages: FinalizedRecordPage[] = [];\n for (let index = 0; index < pageCount; index += 1) {\n const slice = ordered.slice(\n index * STAT_HISTORY_PAGE_SIZE,\n (index + 1) * STAT_HISTORY_PAGE_SIZE,\n );\n const page: GameStatHistoryPage =\n index === 0\n ? {\n events: slice,\n pageCount,\n leagueType: snapshot.leagueType,\n teamIds: [...snapshot.teamIds],\n rosterPlayerIds: [...snapshot.rosterPlayerIds],\n absentRosterPlayerIds: [...snapshot.absentRosterPlayerIds],\n nonScoringRosterPlayerIds: [...snapshot.nonScoringRosterPlayerIds],\n }\n : { events: slice };\n pages.push({ pageId: buildStatHistoryPageId(index), page });\n }\n return pages;\n}\n\nexport function isFirstRecordPage(page: GameStatHistoryPage): page is GameStatHistoryFirstPage {\n return 'pageCount' in page;\n}\n\n// The pages as the record's collection lists them, in id order. They are one whole record only as\n// the exact set: the first page at `page_0000`, then a continuation page at each next id, as many in\n// all as the first page counts.\nexport function readFinalizedRecord(pages: readonly FinalizedRecordPage[]): {\n readonly snapshot: FinalizedRecordSnapshot;\n readonly events: GameStatEvent[];\n} {\n if (pages.length > MAX_STAT_HISTORY_PAGES) {\n throw new Error(`A finalized record holds at most ${MAX_STAT_HISTORY_PAGES} pages.`);\n }\n const [firstEntry, ...restEntries] = pages;\n const first = firstEntry?.page;\n if (first === undefined || !isFirstRecordPage(first))\n throw new Error('A finalized record starts with its first page.');\n if (first.pageCount !== pages.length) {\n throw new Error(`A finalized record of ${first.pageCount} pages was read as ${pages.length}.`);\n }\n pages.forEach(({ pageId, page }, index) => {\n if (pageId !== buildStatHistoryPageId(index)) {\n throw new Error(`A finalized record holds page ${pageId} at position ${index}.`);\n }\n if (index > 0 && isFirstRecordPage(page)) {\n throw new Error(`A finalized record holds a second first page at ${pageId}.`);\n }\n });\n const rest = restEntries.map(({ page }) => page);\n const events = [...first.events, ...rest.flatMap((page) => page.events)];\n events.forEach((event, index) => {\n if (index > 0 && (events[index - 1] as GameStatEvent).sequence >= event.sequence) {\n throw new Error(`A finalized record holds event ${event.eventId} out of sequence.`);\n }\n });\n const snapshot: FinalizedRecordSnapshot = {\n leagueType: first.leagueType,\n teamIds: first.teamIds,\n rosterPlayerIds: first.rosterPlayerIds,\n absentRosterPlayerIds: first.absentRosterPlayerIds,\n nonScoringRosterPlayerIds: first.nonScoringRosterPlayerIds,\n };\n return { snapshot, events };\n}\n\nexport function finalizedGameFromRecord(\n adapter: AnySportAdapter,\n pages: readonly FinalizedRecordPage[],\n): FinalizedGame {\n const { snapshot, events } = readFinalizedRecord(pages);\n return {\n leagueType: snapshot.leagueType,\n teamIds: snapshot.teamIds,\n stats: replayGameStats(adapter, snapshot, events),\n rosterPlayerIds: snapshot.rosterPlayerIds,\n absentRosterPlayerIds: snapshot.absentRosterPlayerIds,\n nonScoringRosterPlayerIds: snapshot.nonScoringRosterPlayerIds,\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"football-scoring.d.ts","sourceRoot":"","sources":["../../src/sports/football-scoring.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAYrF,OAAO,EAKL,KAAK,mBAAmB,EAEzB,MAAM,iBAAiB,CAAC;AAMzB,OAAO,EAOL,KAAK,YAAY,EAIlB,MAAM,cAAc,CAAC;AACtB,OAAO,EAGL,KAAK,UAAU,EAEf,KAAK,qBAAqB,EAE1B,KAAK,sBAAsB,EAC5B,MAAM,sBAAsB,CAAC;AAc9B,wBAAgB,qBAAqB,IAAI,iBAAiB,CAEzD;AAgED,eAAO,MAAM,gBAAgB,EAAE,YAsB9B,CAAC;AAMF,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;CAKgC,CAAC;AAEnE,eAAO,MAAM,qBAAqB;;;;;CAG8B,CAAC;AAEjE,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,aAAa,EACpB,OAAO,EAAE,qBAAqB,GAC7B,MAAM,CASR;AAED,eAAO,MAAM,mBAAmB,EAAE,UAGjC,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,sBAGpC,CAAC;AAuBF,eAAO,MAAM,mBAAmB,EAAE,mBA8BjC,CAAC"}
1
+ {"version":3,"file":"football-scoring.d.ts","sourceRoot":"","sources":["../../src/sports/football-scoring.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAYrF,OAAO,EAKL,KAAK,mBAAmB,EAEzB,MAAM,iBAAiB,CAAC;AAMzB,OAAO,EAOL,KAAK,YAAY,EAIlB,MAAM,cAAc,CAAC;AACtB,OAAO,EAGL,KAAK,UAAU,EAEf,KAAK,qBAAqB,EAE1B,KAAK,sBAAsB,EAC5B,MAAM,sBAAsB,CAAC;AAY9B,wBAAgB,qBAAqB,IAAI,iBAAiB,CAEzD;AA2DD,eAAO,MAAM,gBAAgB,EAAE,YAoB9B,CAAC;AAEF,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;CAKgC,CAAC;AAEnE,eAAO,MAAM,qBAAqB;;;;;CAG8B,CAAC;AAEjE,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,aAAa,EACpB,OAAO,EAAE,qBAAqB,GAC7B,MAAM,CASR;AAED,eAAO,MAAM,mBAAmB,EAAE,UAGjC,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,sBAGpC,CAAC;AAmBF,eAAO,MAAM,mBAAmB,EAAE,mBA8BjC,CAAC"}
@@ -1,9 +1,3 @@
1
- // The football adapter's scoring, display, and bulk-entry members. Football is team-scored: each
2
- // event adds its points to one team's score, no event names a player, and there is no finish rule —
3
- // the game is finalized by hand. Bulk entry takes each team's final score.
4
- //
5
- // `apply` and `undo` are one function over an event's points with opposite signs, so they are exact
6
- // inverses by construction (RULE-QS-SPORT-913).
7
1
  import { z } from 'zod';
8
2
  import { FOOTBALL_POINTS_BY_EVENT_TYPE, MAX_STAT_EVENTS_PER_WRITE, TEAM_NOT_IN_GAME_MESSAGE, } from '../constants/sports.js';
9
3
  import { FOOTBALL_BULK_EDIT_EVENT_TYPE, FOOTBALL_ONE_POINT_EVENT_TYPE, FOOTBALL_SCORE_EVENT_TYPE_KEYS, FOOTBALL_SIX_POINT_EVENT_TYPE, FOOTBALL_STAT_EVENT_TYPE_KEYS, FOOTBALL_TEAM_STAT_KEYS, FOOTBALL_TWO_POINT_EVENT_TYPE, } from '../types/sports.js';
@@ -14,8 +8,6 @@ import { describeStoreEvent, formatSignedValue, } from './scoring-display.js';
14
8
  function isFootballEvent(event) {
15
9
  return isSportStatEventIn(FOOTBALL_STAT_EVENT_TYPE_KEYS, event);
16
10
  }
17
- // The registry chose this adapter by the game's `leagueType`, so the rollup it is handed is a
18
- // football game's (RULE-QS-SPORT-902).
19
11
  function ownStats(state) {
20
12
  return state;
21
13
  }
@@ -25,7 +17,6 @@ export function emptyFootballTeamLine() {
25
17
  function teamScoreOf(state, teamId) {
26
18
  return (state.teamStats[teamId] ?? emptyFootballTeamLine()).score;
27
19
  }
28
- /** The points an event adds to its team's score: a scoring event's own, or a bulk edit's `value`. */
29
20
  function pointsOf(event) {
30
21
  return event.type === FOOTBALL_BULK_EDIT_EVENT_TYPE
31
22
  ? event.value
@@ -39,7 +30,6 @@ function addToScore(stateIn, teamId, amount) {
39
30
  playerStats: state.playerStats,
40
31
  };
41
32
  }
42
- // A correction to the team's score; football keeps no player line to correct.
43
33
  const CORRECTABLE_FIELDS = { team: FOOTBALL_TEAM_STAT_KEYS, player: [] };
44
34
  function correctionAmount(event) {
45
35
  const fields = event.playerUserId === undefined ? CORRECTABLE_FIELDS.team : CORRECTABLE_FIELDS.player;
@@ -69,9 +59,6 @@ const validate = (stateIn, event, roster) => {
69
59
  }
70
60
  return STAT_EVENT_ACCEPTED;
71
61
  };
72
- // What a completed football game adds: the standings line from the two scores, and a game played
73
- // to every present roster entry, whatever its team scored. There are no individual stats, so only
74
- // absence withholds a game played and no entry is ever non-scoring.
75
62
  const FOOTBALL_CONTRIBUTION = {
76
63
  playerStatKeys: [],
77
64
  teamSeasonPlayerSums: [],
@@ -92,18 +79,13 @@ export const FOOTBALL_SCORING = {
92
79
  throw new Error(`The football adapter does not undo ${target.type}.`);
93
80
  return addToScore(state, target.teamId, -pointsOf(target));
94
81
  },
95
- // A football line has no derived field.
96
82
  deriveLine: FOOTBALL_CONTRIBUTION.deriveLine,
97
83
  correctableFields: CORRECTABLE_FIELDS,
98
- // No rounds and no finish rule: the game is finalized by hand.
99
84
  nextRound: () => null,
100
85
  finalizationReadiness: () => FINALIZATION_READY,
101
86
  participation: (state, rosterPlayerIds, absentRosterPlayerIds) => decideParticipation(FOOTBALL_CONTRIBUTION, state, rosterPlayerIds, absentRosterPlayerIds),
102
87
  contribution: (game) => buildGameContribution(FOOTBALL_CONTRIBUTION, game),
103
88
  };
104
- // ---------------------------------------------------------------------------------------------
105
- // Display
106
- // ---------------------------------------------------------------------------------------------
107
89
  export const FOOTBALL_EVENT_LABELS = {
108
90
  [FOOTBALL_SIX_POINT_EVENT_TYPE]: { button: '+6', play: '6 points' },
109
91
  [FOOTBALL_TWO_POINT_EVENT_TYPE]: { button: '+2', play: '2 points' },
@@ -134,9 +116,6 @@ export const FOOTBALL_SCORING_PANEL = {
134
116
  kind: 'teamPoints',
135
117
  eventTypes: FOOTBALL_SCORE_EVENT_TYPE_KEYS,
136
118
  };
137
- // ---------------------------------------------------------------------------------------------
138
- // Bulk entry — each team's final score
139
- // ---------------------------------------------------------------------------------------------
140
119
  function scoreDrafts(values, context) {
141
120
  const state = ownStats(context.state);
142
121
  const drafts = [];
@@ -1 +1 @@
1
- {"version":3,"file":"football-scoring.js","sourceRoot":"","sources":["../../src/sports/football-scoring.ts"],"names":[],"mappings":"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","sourcesContent":["// The football adapter's scoring, display, and bulk-entry members. Football is team-scored: each\n// event adds its points to one team's score, no event names a player, and there is no finish rule —\n// the game is finalized by hand. Bulk entry takes each team's final score.\n//\n// `apply` and `undo` are one function over an event's points with opposite signs, so they are exact\n// inverses by construction (RULE-QS-SPORT-913).\n\nimport { z } from 'zod';\nimport {\n FOOTBALL_POINTS_BY_EVENT_TYPE,\n MAX_STAT_EVENTS_PER_WRITE,\n TEAM_NOT_IN_GAME_MESSAGE,\n} from '../constants/sports.js';\nimport type { FootballTeamStats, GameStatEvent } from '../doc-schemas/game-stats.js';\nimport type { FootballGameStats, GameStats } from '../doc-schemas/sport-game.js';\nimport {\n FOOTBALL_BULK_EDIT_EVENT_TYPE,\n FOOTBALL_ONE_POINT_EVENT_TYPE,\n FOOTBALL_SCORE_EVENT_TYPE_KEYS,\n FOOTBALL_SIX_POINT_EVENT_TYPE,\n FOOTBALL_STAT_EVENT_TYPE_KEYS,\n FOOTBALL_TEAM_STAT_KEYS,\n FOOTBALL_TWO_POINT_EVENT_TYPE,\n} from '../types/sports.js';\nimport type { FootballStatEventType, FootballTeamStatKey } from '../types/sports.js';\nimport {\n BULK_ENTRY_TOO_LARGE_MESSAGE,\n bulkEntryCountField,\n type BulkEntryContext,\n type StatEventDraft,\n type TeamScoresBulkEntry,\n type TeamScoresValues,\n} from './bulk-entry.js';\nimport {\n buildGameContribution,\n decideParticipation,\n type ContributionSpec,\n} from './contribution.js';\nimport {\n FINALIZATION_READY,\n STAT_EVENT_ACCEPTED,\n isSportStatEventIn,\n isStatCorrection,\n refuseStatEvent,\n type CorrectableFields,\n type SportScoring,\n type SportStatEventBody,\n type StatCorrectionBody,\n type StatEventBody,\n} from './scoring.js';\nimport {\n describeStoreEvent,\n formatSignedValue,\n type Scoreboard,\n type StatEventLabel,\n type StatEventNameResolver,\n type StatFieldLabels,\n type TeamPointsScoringPanel,\n} from './scoring-display.js';\n\ntype FootballEvent = Extract<SportStatEventBody, { type: FootballStatEventType }>;\n\nfunction isFootballEvent(event: StatEventBody | undefined): event is FootballEvent {\n return isSportStatEventIn(FOOTBALL_STAT_EVENT_TYPE_KEYS, event);\n}\n\n// The registry chose this adapter by the game's `leagueType`, so the rollup it is handed is a\n// football game's (RULE-QS-SPORT-902).\nfunction ownStats(state: GameStats): FootballGameStats {\n return state as FootballGameStats;\n}\n\nexport function emptyFootballTeamLine(): FootballTeamStats {\n return Object.fromEntries(FOOTBALL_TEAM_STAT_KEYS.map((key) => [key, 0])) as FootballTeamStats;\n}\n\nfunction teamScoreOf(state: FootballGameStats, teamId: string): number {\n return (state.teamStats[teamId] ?? emptyFootballTeamLine()).score;\n}\n\n/** The points an event adds to its team's score: a scoring event's own, or a bulk edit's `value`. */\nfunction pointsOf(event: FootballEvent): number {\n return event.type === FOOTBALL_BULK_EDIT_EVENT_TYPE\n ? event.value\n : FOOTBALL_POINTS_BY_EVENT_TYPE[event.type];\n}\n\nfunction addToScore(stateIn: GameStats, teamId: string, amount: number): FootballGameStats {\n const state = ownStats(stateIn);\n const line = state.teamStats[teamId] ?? emptyFootballTeamLine();\n return {\n teamStats: { ...state.teamStats, [teamId]: { ...line, score: line.score + amount } },\n playerStats: state.playerStats,\n };\n}\n\n// A correction to the team's score; football keeps no player line to correct.\nconst CORRECTABLE_FIELDS: CorrectableFields = { team: FOOTBALL_TEAM_STAT_KEYS, player: [] };\n\nfunction correctionAmount(event: StatCorrectionBody): number {\n const fields =\n event.playerUserId === undefined ? CORRECTABLE_FIELDS.team : CORRECTABLE_FIELDS.player;\n if (!fields.includes(event.stat)) {\n throw new Error(\n `A football correction cannot change ${event.stat} on a ${event.playerUserId === undefined ? 'team' : 'player'} line.`,\n );\n }\n return event.value;\n}\n\nfunction namesPlayer(event: StatEventBody): boolean {\n return 'playerUserId' in event && event.playerUserId !== undefined;\n}\n\nconst validate: SportScoring['validate'] = (stateIn, event, roster) => {\n if (!isFootballEvent(event)) return refuseStatEvent('eventTypeNotInSport');\n if (!roster.teamIds.includes(event.teamId)) return refuseStatEvent('teamNotInGame');\n if (namesPlayer(event)) return refuseStatEvent('playerNotAllowed');\n if (event.type === FOOTBALL_BULK_EDIT_EVENT_TYPE) {\n if (!Number.isInteger(event.value) || event.value === 0)\n return refuseStatEvent('bulkValueInvalid');\n const score = teamScoreOf(ownStats(stateIn), event.teamId);\n if (event.previousValue !== score) return refuseStatEvent('bulkStale');\n if (score + event.value < 0) return refuseStatEvent('bulkValueInvalid');\n }\n return STAT_EVENT_ACCEPTED;\n};\n\n// What a completed football game adds: the standings line from the two scores, and a game played\n// to every present roster entry, whatever its team scored. There are no individual stats, so only\n// absence withholds a game played and no entry is ever non-scoring.\nconst FOOTBALL_CONTRIBUTION: ContributionSpec = {\n playerStatKeys: [],\n teamSeasonPlayerSums: [],\n zeroLineIsNonScoring: false,\n deriveLine: (_kind, line) => line,\n};\n\nexport const FOOTBALL_SCORING: SportScoring = {\n validate,\n apply: (state, event) => {\n if (isStatCorrection(event)) return addToScore(state, event.teamId, correctionAmount(event));\n if (!isFootballEvent(event))\n throw new Error(`The football adapter does not apply ${event.type}.`);\n return addToScore(state, event.teamId, pointsOf(event));\n },\n undo: (state, target) => {\n if (!isFootballEvent(target))\n throw new Error(`The football adapter does not undo ${target.type}.`);\n return addToScore(state, target.teamId, -pointsOf(target));\n },\n // A football line has no derived field.\n deriveLine: FOOTBALL_CONTRIBUTION.deriveLine,\n correctableFields: CORRECTABLE_FIELDS,\n // No rounds and no finish rule: the game is finalized by hand.\n nextRound: () => null,\n finalizationReadiness: () => FINALIZATION_READY,\n participation: (state, rosterPlayerIds, absentRosterPlayerIds) =>\n decideParticipation(FOOTBALL_CONTRIBUTION, state, rosterPlayerIds, absentRosterPlayerIds),\n contribution: (game) => buildGameContribution(FOOTBALL_CONTRIBUTION, game),\n};\n\n// ---------------------------------------------------------------------------------------------\n// Display\n// ---------------------------------------------------------------------------------------------\n\nexport const FOOTBALL_EVENT_LABELS = {\n [FOOTBALL_SIX_POINT_EVENT_TYPE]: { button: '+6', play: '6 points' },\n [FOOTBALL_TWO_POINT_EVENT_TYPE]: { button: '+2', play: '2 points' },\n [FOOTBALL_ONE_POINT_EVENT_TYPE]: { button: '+1', play: '1 point' },\n [FOOTBALL_BULK_EDIT_EVENT_TYPE]: { button: 'Final scores', play: 'Final score entry' },\n} as const satisfies Record<FootballStatEventType, StatEventLabel>;\n\nexport const FOOTBALL_FIELD_LABELS = {\n team: { score: 'Score' },\n player: {},\n} as const satisfies StatFieldLabels<FootballTeamStatKey, never>;\n\nexport function describeFootballEvent(\n event: GameStatEvent,\n resolve: StatEventNameResolver,\n): string {\n const storeEvent = describeStoreEvent(event, resolve, FOOTBALL_FIELD_LABELS);\n if (storeEvent !== null) return storeEvent;\n if (!isFootballEvent(event)) return event.type;\n const team = resolve.team(event.teamId);\n if (event.type === FOOTBALL_BULK_EDIT_EVENT_TYPE) {\n return `${team}: ${formatSignedValue(event.value)} points (${FOOTBALL_EVENT_LABELS[event.type].play.toLowerCase()})`;\n }\n return `${team}: ${FOOTBALL_EVENT_LABELS[event.type].play}`;\n}\n\nexport const FOOTBALL_SCOREBOARD: Scoreboard = {\n figures: [{ id: 'points', label: 'Points', value: (teamLine) => teamLine.score }],\n jumpBall: false,\n};\n\nexport const FOOTBALL_SCORING_PANEL: TeamPointsScoringPanel = {\n kind: 'teamPoints',\n eventTypes: FOOTBALL_SCORE_EVENT_TYPE_KEYS,\n};\n\n// ---------------------------------------------------------------------------------------------\n// Bulk entry — each team's final score\n// ---------------------------------------------------------------------------------------------\n\nfunction scoreDrafts(values: TeamScoresValues, context: BulkEntryContext): StatEventDraft[] {\n const state = ownStats(context.state);\n const drafts: StatEventDraft[] = [];\n for (const teamId of context.game.teamIds) {\n const entered = values.scores[teamId];\n const previousValue = teamScoreOf(state, teamId);\n if (entered === undefined || entered === previousValue) continue;\n drafts.push({\n type: FOOTBALL_BULK_EDIT_EVENT_TYPE,\n teamId,\n value: entered - previousValue,\n previousValue,\n });\n }\n return drafts;\n}\n\nexport const FOOTBALL_BULK_ENTRY: TeamScoresBulkEntry = {\n kind: 'teamScores',\n formDefaults: (context) => ({\n scores: Object.fromEntries(\n context.game.teamIds.map((teamId) => [teamId, teamScoreOf(ownStats(context.state), teamId)]),\n ),\n }),\n formSchema: (context) => {\n const teams = new Set(context.game.teamIds);\n return z\n .object({ scores: z.record(z.string(), bulkEntryCountField) })\n .superRefine((values, issues) => {\n for (const teamId of Object.keys(values.scores)) {\n if (!teams.has(teamId))\n issues.addIssue({\n code: 'custom',\n path: ['scores', teamId],\n message: TEAM_NOT_IN_GAME_MESSAGE,\n });\n }\n if (scoreDrafts(values, context).length > MAX_STAT_EVENTS_PER_WRITE) {\n issues.addIssue({\n code: 'custom',\n path: ['scores'],\n message: BULK_ENTRY_TOO_LARGE_MESSAGE,\n });\n }\n });\n },\n toEvents: scoreDrafts,\n};\n"]}
1
+ {"version":3,"file":"football-scoring.js","sourceRoot":"","sources":["../../src/sports/football-scoring.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\nimport {\n FOOTBALL_POINTS_BY_EVENT_TYPE,\n MAX_STAT_EVENTS_PER_WRITE,\n TEAM_NOT_IN_GAME_MESSAGE,\n} from '../constants/sports.js';\nimport type { FootballTeamStats, GameStatEvent } from '../doc-schemas/game-stats.js';\nimport type { FootballGameStats, GameStats } from '../doc-schemas/sport-game.js';\nimport {\n FOOTBALL_BULK_EDIT_EVENT_TYPE,\n FOOTBALL_ONE_POINT_EVENT_TYPE,\n FOOTBALL_SCORE_EVENT_TYPE_KEYS,\n FOOTBALL_SIX_POINT_EVENT_TYPE,\n FOOTBALL_STAT_EVENT_TYPE_KEYS,\n FOOTBALL_TEAM_STAT_KEYS,\n FOOTBALL_TWO_POINT_EVENT_TYPE,\n} from '../types/sports.js';\nimport type { FootballStatEventType, FootballTeamStatKey } from '../types/sports.js';\nimport {\n BULK_ENTRY_TOO_LARGE_MESSAGE,\n bulkEntryCountField,\n type BulkEntryContext,\n type StatEventDraft,\n type TeamScoresBulkEntry,\n type TeamScoresValues,\n} from './bulk-entry.js';\nimport {\n buildGameContribution,\n decideParticipation,\n type ContributionSpec,\n} from './contribution.js';\nimport {\n FINALIZATION_READY,\n STAT_EVENT_ACCEPTED,\n isSportStatEventIn,\n isStatCorrection,\n refuseStatEvent,\n type CorrectableFields,\n type SportScoring,\n type SportStatEventBody,\n type StatCorrectionBody,\n type StatEventBody,\n} from './scoring.js';\nimport {\n describeStoreEvent,\n formatSignedValue,\n type Scoreboard,\n type StatEventLabel,\n type StatEventNameResolver,\n type StatFieldLabels,\n type TeamPointsScoringPanel,\n} from './scoring-display.js';\n\ntype FootballEvent = Extract<SportStatEventBody, { type: FootballStatEventType }>;\n\nfunction isFootballEvent(event: StatEventBody | undefined): event is FootballEvent {\n return isSportStatEventIn(FOOTBALL_STAT_EVENT_TYPE_KEYS, event);\n}\n\nfunction ownStats(state: GameStats): FootballGameStats {\n return state as FootballGameStats;\n}\n\nexport function emptyFootballTeamLine(): FootballTeamStats {\n return Object.fromEntries(FOOTBALL_TEAM_STAT_KEYS.map((key) => [key, 0])) as FootballTeamStats;\n}\n\nfunction teamScoreOf(state: FootballGameStats, teamId: string): number {\n return (state.teamStats[teamId] ?? emptyFootballTeamLine()).score;\n}\n\nfunction pointsOf(event: FootballEvent): number {\n return event.type === FOOTBALL_BULK_EDIT_EVENT_TYPE\n ? event.value\n : FOOTBALL_POINTS_BY_EVENT_TYPE[event.type];\n}\n\nfunction addToScore(stateIn: GameStats, teamId: string, amount: number): FootballGameStats {\n const state = ownStats(stateIn);\n const line = state.teamStats[teamId] ?? emptyFootballTeamLine();\n return {\n teamStats: { ...state.teamStats, [teamId]: { ...line, score: line.score + amount } },\n playerStats: state.playerStats,\n };\n}\n\nconst CORRECTABLE_FIELDS: CorrectableFields = { team: FOOTBALL_TEAM_STAT_KEYS, player: [] };\n\nfunction correctionAmount(event: StatCorrectionBody): number {\n const fields =\n event.playerUserId === undefined ? CORRECTABLE_FIELDS.team : CORRECTABLE_FIELDS.player;\n if (!fields.includes(event.stat)) {\n throw new Error(\n `A football correction cannot change ${event.stat} on a ${event.playerUserId === undefined ? 'team' : 'player'} line.`,\n );\n }\n return event.value;\n}\n\nfunction namesPlayer(event: StatEventBody): boolean {\n return 'playerUserId' in event && event.playerUserId !== undefined;\n}\n\nconst validate: SportScoring['validate'] = (stateIn, event, roster) => {\n if (!isFootballEvent(event)) return refuseStatEvent('eventTypeNotInSport');\n if (!roster.teamIds.includes(event.teamId)) return refuseStatEvent('teamNotInGame');\n if (namesPlayer(event)) return refuseStatEvent('playerNotAllowed');\n if (event.type === FOOTBALL_BULK_EDIT_EVENT_TYPE) {\n if (!Number.isInteger(event.value) || event.value === 0)\n return refuseStatEvent('bulkValueInvalid');\n const score = teamScoreOf(ownStats(stateIn), event.teamId);\n if (event.previousValue !== score) return refuseStatEvent('bulkStale');\n if (score + event.value < 0) return refuseStatEvent('bulkValueInvalid');\n }\n return STAT_EVENT_ACCEPTED;\n};\n\nconst FOOTBALL_CONTRIBUTION: ContributionSpec = {\n playerStatKeys: [],\n teamSeasonPlayerSums: [],\n zeroLineIsNonScoring: false,\n deriveLine: (_kind, line) => line,\n};\n\nexport const FOOTBALL_SCORING: SportScoring = {\n validate,\n apply: (state, event) => {\n if (isStatCorrection(event)) return addToScore(state, event.teamId, correctionAmount(event));\n if (!isFootballEvent(event))\n throw new Error(`The football adapter does not apply ${event.type}.`);\n return addToScore(state, event.teamId, pointsOf(event));\n },\n undo: (state, target) => {\n if (!isFootballEvent(target))\n throw new Error(`The football adapter does not undo ${target.type}.`);\n return addToScore(state, target.teamId, -pointsOf(target));\n },\n deriveLine: FOOTBALL_CONTRIBUTION.deriveLine,\n correctableFields: CORRECTABLE_FIELDS,\n nextRound: () => null,\n finalizationReadiness: () => FINALIZATION_READY,\n participation: (state, rosterPlayerIds, absentRosterPlayerIds) =>\n decideParticipation(FOOTBALL_CONTRIBUTION, state, rosterPlayerIds, absentRosterPlayerIds),\n contribution: (game) => buildGameContribution(FOOTBALL_CONTRIBUTION, game),\n};\n\nexport const FOOTBALL_EVENT_LABELS = {\n [FOOTBALL_SIX_POINT_EVENT_TYPE]: { button: '+6', play: '6 points' },\n [FOOTBALL_TWO_POINT_EVENT_TYPE]: { button: '+2', play: '2 points' },\n [FOOTBALL_ONE_POINT_EVENT_TYPE]: { button: '+1', play: '1 point' },\n [FOOTBALL_BULK_EDIT_EVENT_TYPE]: { button: 'Final scores', play: 'Final score entry' },\n} as const satisfies Record<FootballStatEventType, StatEventLabel>;\n\nexport const FOOTBALL_FIELD_LABELS = {\n team: { score: 'Score' },\n player: {},\n} as const satisfies StatFieldLabels<FootballTeamStatKey, never>;\n\nexport function describeFootballEvent(\n event: GameStatEvent,\n resolve: StatEventNameResolver,\n): string {\n const storeEvent = describeStoreEvent(event, resolve, FOOTBALL_FIELD_LABELS);\n if (storeEvent !== null) return storeEvent;\n if (!isFootballEvent(event)) return event.type;\n const team = resolve.team(event.teamId);\n if (event.type === FOOTBALL_BULK_EDIT_EVENT_TYPE) {\n return `${team}: ${formatSignedValue(event.value)} points (${FOOTBALL_EVENT_LABELS[event.type].play.toLowerCase()})`;\n }\n return `${team}: ${FOOTBALL_EVENT_LABELS[event.type].play}`;\n}\n\nexport const FOOTBALL_SCOREBOARD: Scoreboard = {\n figures: [{ id: 'points', label: 'Points', value: (teamLine) => teamLine.score }],\n jumpBall: false,\n};\n\nexport const FOOTBALL_SCORING_PANEL: TeamPointsScoringPanel = {\n kind: 'teamPoints',\n eventTypes: FOOTBALL_SCORE_EVENT_TYPE_KEYS,\n};\n\nfunction scoreDrafts(values: TeamScoresValues, context: BulkEntryContext): StatEventDraft[] {\n const state = ownStats(context.state);\n const drafts: StatEventDraft[] = [];\n for (const teamId of context.game.teamIds) {\n const entered = values.scores[teamId];\n const previousValue = teamScoreOf(state, teamId);\n if (entered === undefined || entered === previousValue) continue;\n drafts.push({\n type: FOOTBALL_BULK_EDIT_EVENT_TYPE,\n teamId,\n value: entered - previousValue,\n previousValue,\n });\n }\n return drafts;\n}\n\nexport const FOOTBALL_BULK_ENTRY: TeamScoresBulkEntry = {\n kind: 'teamScores',\n formDefaults: (context) => ({\n scores: Object.fromEntries(\n context.game.teamIds.map((teamId) => [teamId, teamScoreOf(ownStats(context.state), teamId)]),\n ),\n }),\n formSchema: (context) => {\n const teams = new Set(context.game.teamIds);\n return z\n .object({ scores: z.record(z.string(), bulkEntryCountField) })\n .superRefine((values, issues) => {\n for (const teamId of Object.keys(values.scores)) {\n if (!teams.has(teamId))\n issues.addIssue({\n code: 'custom',\n path: ['scores', teamId],\n message: TEAM_NOT_IN_GAME_MESSAGE,\n });\n }\n if (scoreDrafts(values, context).length > MAX_STAT_EVENTS_PER_WRITE) {\n issues.addIssue({\n code: 'custom',\n path: ['scores'],\n message: BULK_ENTRY_TOO_LARGE_MESSAGE,\n });\n }\n });\n },\n toEvents: scoreDrafts,\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"football.d.ts","sourceRoot":"","sources":["../../src/sports/football.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIvD,eAAO,MAAM,gBAAgB,EAAE,YAAY,CAAC,UAAU,CAiDrD,CAAC"}
1
+ {"version":3,"file":"football.d.ts","sourceRoot":"","sources":["../../src/sports/football.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIvD,eAAO,MAAM,gBAAgB,EAAE,YAAY,CAAC,UAAU,CAgDrD,CAAC"}
@@ -1,6 +1,3 @@
1
- // The football adapter: team-scored, with no individual stats — every present roster entry is
2
- // credited a game played — no finish rule, and no hall of fame. Its scoring, display, and
3
- // bulk-entry members are in ./football-scoring.ts.
4
1
  import { SPORT_LINE_SCHEMAS } from '../doc-schemas/aggregate-stats.js';
5
2
  import { FOOTBALL_PLAYER_STAT_KEYS, FOOTBALL_STAT_EVENT_TYPE_KEYS, FOOTBALL_TEAM_STAT_KEYS, SPORT_KEY_BY_LEAGUE_TYPE, } from '../types/sports.js';
6
3
  import { FOOTBALL_BULK_ENTRY, FOOTBALL_EVENT_LABELS, FOOTBALL_FIELD_LABELS, FOOTBALL_SCOREBOARD, FOOTBALL_SCORING, FOOTBALL_SCORING_PANEL, describeFootballEvent, emptyFootballTeamLine, } from './football-scoring.js';
@@ -33,7 +30,6 @@ export const FOOTBALL_ADAPTER = {
33
30
  defaultSort: [{ columnId: GAMES_PLAYED_COLUMN.id, direction: 'desc' }],
34
31
  defaultLayout: [GAMES_PLAYED_COLUMN.id],
35
32
  },
36
- // No player game line, so the game page renders no player table.
37
33
  gamePlayer: { columns: [], defaultSort: [], defaultLayout: [] },
38
34
  teamSeason: {
39
35
  columns: TEAM_SEASON_BASE_COLUMNS,