@ttt-productions/q-core 0.1.49 → 0.1.51

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 (173) hide show
  1. package/dist/constants/business.d.ts +3 -4
  2. package/dist/constants/business.d.ts.map +1 -1
  3. package/dist/constants/business.js +3 -4
  4. package/dist/constants/business.js.map +1 -1
  5. package/dist/constants/sports.d.ts +0 -13
  6. package/dist/constants/sports.d.ts.map +1 -1
  7. package/dist/constants/sports.js +0 -13
  8. package/dist/constants/sports.js.map +1 -1
  9. package/dist/constants/text-fields.d.ts +0 -6
  10. package/dist/constants/text-fields.d.ts.map +1 -1
  11. package/dist/constants/text-fields.js +0 -6
  12. package/dist/constants/text-fields.js.map +1 -1
  13. package/dist/doc-schemas/admin-notifications.d.ts +1 -1
  14. package/dist/doc-schemas/atoms.d.ts.map +1 -1
  15. package/dist/doc-schemas/atoms.js +1 -1
  16. package/dist/doc-schemas/atoms.js.map +1 -1
  17. package/dist/doc-schemas/audit-event.d.ts +4 -8
  18. package/dist/doc-schemas/audit-event.d.ts.map +1 -1
  19. package/dist/doc-schemas/game-stat-history.d.ts +0 -1
  20. package/dist/doc-schemas/game-stat-history.d.ts.map +1 -1
  21. package/dist/doc-schemas/game-stats.d.ts +0 -2
  22. package/dist/doc-schemas/game-stats.d.ts.map +1 -1
  23. package/dist/doc-schemas/game-stats.js +1 -2
  24. package/dist/doc-schemas/game-stats.js.map +1 -1
  25. package/dist/doc-schemas/index.d.ts +2 -2
  26. package/dist/doc-schemas/index.d.ts.map +1 -1
  27. package/dist/doc-schemas/index.js +2 -2
  28. package/dist/doc-schemas/index.js.map +1 -1
  29. package/dist/doc-schemas/migration-console.d.ts +6 -20
  30. package/dist/doc-schemas/migration-console.d.ts.map +1 -1
  31. package/dist/doc-schemas/migration-console.js +2 -7
  32. package/dist/doc-schemas/migration-console.js.map +1 -1
  33. package/dist/doc-schemas/pending-media.d.ts +35 -55
  34. package/dist/doc-schemas/pending-media.d.ts.map +1 -1
  35. package/dist/doc-schemas/registry.d.ts +49 -229
  36. package/dist/doc-schemas/registry.d.ts.map +1 -1
  37. package/dist/doc-schemas/registry.js +3 -7
  38. package/dist/doc-schemas/registry.js.map +1 -1
  39. package/dist/doc-schemas/stats-changes.d.ts +21 -0
  40. package/dist/doc-schemas/stats-changes.d.ts.map +1 -0
  41. package/dist/doc-schemas/stats-changes.js +19 -0
  42. package/dist/doc-schemas/stats-changes.js.map +1 -0
  43. package/dist/doc-schemas/stats-job.d.ts +36 -0
  44. package/dist/doc-schemas/stats-job.d.ts.map +1 -0
  45. package/dist/doc-schemas/stats-job.js +26 -0
  46. package/dist/doc-schemas/stats-job.js.map +1 -0
  47. package/dist/doc-schemas/system.d.ts +1 -1
  48. package/dist/doc-schemas/system.d.ts.map +1 -1
  49. package/dist/doc-schemas/system.js +2 -2
  50. package/dist/doc-schemas/system.js.map +1 -1
  51. package/dist/domain-events/domain-events.d.ts +16 -24
  52. package/dist/domain-events/domain-events.d.ts.map +1 -1
  53. package/dist/domain-events/domain-events.js +11 -11
  54. package/dist/domain-events/domain-events.js.map +1 -1
  55. package/dist/ids/index.d.ts +2 -1
  56. package/dist/ids/index.d.ts.map +1 -1
  57. package/dist/ids/index.js +2 -1
  58. package/dist/ids/index.js.map +1 -1
  59. package/dist/ids/notification-ids.d.ts +2 -4
  60. package/dist/ids/notification-ids.d.ts.map +1 -1
  61. package/dist/ids/notification-ids.js +3 -12
  62. package/dist/ids/notification-ids.js.map +1 -1
  63. package/dist/ids/request-job-ids.d.ts +7 -0
  64. package/dist/ids/request-job-ids.d.ts.map +1 -0
  65. package/dist/ids/request-job-ids.js +18 -0
  66. package/dist/ids/request-job-ids.js.map +1 -0
  67. package/dist/ids/stats-ids.d.ts +7 -0
  68. package/dist/ids/stats-ids.d.ts.map +1 -0
  69. package/dist/ids/stats-ids.js +17 -0
  70. package/dist/ids/stats-ids.js.map +1 -0
  71. package/dist/migration-console/action-ids.d.ts +1 -3
  72. package/dist/migration-console/action-ids.d.ts.map +1 -1
  73. package/dist/migration-console/action-ids.js +1 -3
  74. package/dist/migration-console/action-ids.js.map +1 -1
  75. package/dist/migration-console/manifest.d.ts +1 -2
  76. package/dist/migration-console/manifest.d.ts.map +1 -1
  77. package/dist/migration-console/manifest.js +12 -43
  78. package/dist/migration-console/manifest.js.map +1 -1
  79. package/dist/paths/collection-groups.d.ts +0 -1
  80. package/dist/paths/collection-groups.d.ts.map +1 -1
  81. package/dist/paths/collection-groups.js +1 -2
  82. package/dist/paths/collection-groups.js.map +1 -1
  83. package/dist/paths/collection-refs.d.ts +1 -2
  84. package/dist/paths/collection-refs.d.ts.map +1 -1
  85. package/dist/paths/collection-refs.js +1 -2
  86. package/dist/paths/collection-refs.js.map +1 -1
  87. package/dist/paths/collections.d.ts +1 -5
  88. package/dist/paths/collections.d.ts.map +1 -1
  89. package/dist/paths/collections.js +1 -5
  90. package/dist/paths/collections.js.map +1 -1
  91. package/dist/paths/path-builders.d.ts +1 -4
  92. package/dist/paths/path-builders.d.ts.map +1 -1
  93. package/dist/paths/path-builders.js +2 -17
  94. package/dist/paths/path-builders.js.map +1 -1
  95. package/dist/schemas/admin-notifications.d.ts +1 -1
  96. package/dist/schemas/awards.d.ts +5 -75
  97. package/dist/schemas/awards.d.ts.map +1 -1
  98. package/dist/schemas/awards.js +6 -30
  99. package/dist/schemas/awards.js.map +1 -1
  100. package/dist/schemas/dead-letters.d.ts +7 -1
  101. package/dist/schemas/dead-letters.d.ts.map +1 -1
  102. package/dist/schemas/dead-letters.js +2 -1
  103. package/dist/schemas/dead-letters.js.map +1 -1
  104. package/dist/schemas/migration-console.d.ts +16 -57
  105. package/dist/schemas/migration-console.d.ts.map +1 -1
  106. package/dist/schemas/migration-console.js +3 -10
  107. package/dist/schemas/migration-console.js.map +1 -1
  108. package/dist/schemas/pictures.d.ts +21 -33
  109. package/dist/schemas/pictures.d.ts.map +1 -1
  110. package/dist/schemas/scoring.d.ts +6 -12
  111. package/dist/schemas/scoring.d.ts.map +1 -1
  112. package/dist/schemas/scoring.js +9 -5
  113. package/dist/schemas/scoring.js.map +1 -1
  114. package/dist/schemas/stats.d.ts +66 -101
  115. package/dist/schemas/stats.d.ts.map +1 -1
  116. package/dist/schemas/stats.js +39 -72
  117. package/dist/schemas/stats.js.map +1 -1
  118. package/dist/sports/basketball-scoring.d.ts.map +1 -1
  119. package/dist/sports/basketball-scoring.js +16 -7
  120. package/dist/sports/basketball-scoring.js.map +1 -1
  121. package/dist/sports/bulk-entry.d.ts +7 -2
  122. package/dist/sports/bulk-entry.d.ts.map +1 -1
  123. package/dist/sports/bulk-entry.js.map +1 -1
  124. package/dist/sports/football-scoring.d.ts.map +1 -1
  125. package/dist/sports/football-scoring.js +13 -10
  126. package/dist/sports/football-scoring.js.map +1 -1
  127. package/dist/sports/replay.d.ts.map +1 -1
  128. package/dist/sports/replay.js +16 -1
  129. package/dist/sports/replay.js.map +1 -1
  130. package/dist/sports/scoring.d.ts +18 -2
  131. package/dist/sports/scoring.d.ts.map +1 -1
  132. package/dist/sports/scoring.js.map +1 -1
  133. package/dist/sports/store-driver.d.ts +2 -1
  134. package/dist/sports/store-driver.d.ts.map +1 -1
  135. package/dist/sports/store-driver.js +30 -15
  136. package/dist/sports/store-driver.js.map +1 -1
  137. package/dist/types/audit.d.ts +1 -1
  138. package/dist/types/audit.d.ts.map +1 -1
  139. package/dist/types/audit.js +1 -3
  140. package/dist/types/audit.js.map +1 -1
  141. package/dist/types/schedules.d.ts +1 -1
  142. package/dist/types/schedules.d.ts.map +1 -1
  143. package/dist/types/schedules.js +1 -2
  144. package/dist/types/schedules.js.map +1 -1
  145. package/dist/types/stats.d.ts +13 -13
  146. package/dist/types/stats.d.ts.map +1 -1
  147. package/dist/types/stats.js +13 -33
  148. package/dist/types/stats.js.map +1 -1
  149. package/dist/types/unions.d.ts +0 -6
  150. package/dist/types/unions.d.ts.map +1 -1
  151. package/dist/types/unions.js +0 -3
  152. package/dist/types/unions.js.map +1 -1
  153. package/dist/utils/index.d.ts +0 -1
  154. package/dist/utils/index.d.ts.map +1 -1
  155. package/dist/utils/index.js +0 -1
  156. package/dist/utils/index.js.map +1 -1
  157. package/package.json +1 -1
  158. package/dist/doc-schemas/award-group-grant.d.ts +0 -54
  159. package/dist/doc-schemas/award-group-grant.d.ts.map +0 -1
  160. package/dist/doc-schemas/award-group-grant.js +0 -55
  161. package/dist/doc-schemas/award-group-grant.js.map +0 -1
  162. package/dist/doc-schemas/stats-operation.d.ts +0 -348
  163. package/dist/doc-schemas/stats-operation.d.ts.map +0 -1
  164. package/dist/doc-schemas/stats-operation.js +0 -92
  165. package/dist/doc-schemas/stats-operation.js.map +0 -1
  166. package/dist/ids/award-grant-ids.d.ts +0 -9
  167. package/dist/ids/award-grant-ids.d.ts.map +0 -1
  168. package/dist/ids/award-grant-ids.js +0 -10
  169. package/dist/ids/award-grant-ids.js.map +0 -1
  170. package/dist/utils/stats-operation-request.d.ts +0 -20
  171. package/dist/utils/stats-operation-request.d.ts.map +0 -1
  172. package/dist/utils/stats-operation-request.js +0 -10
  173. package/dist/utils/stats-operation-request.js.map +0 -1
@@ -169,8 +169,6 @@ const validate = (stateIn, event, roster) => {
169
169
  return refuseStatEvent('bulkValueInvalid');
170
170
  const state = ownStats(stateIn);
171
171
  const current = playerCountersOf(state.playerStats[scorer])[event.stat];
172
- if (event.previousValue !== current)
173
- return refuseStatEvent('bulkStale');
174
172
  if (current + event.value < 0)
175
173
  return refuseStatEvent('bulkValueInvalid');
176
174
  const teamScore = (state.teamStats[event.teamId] ?? emptyBasketballTeamLine()).score;
@@ -185,8 +183,20 @@ const BASKETBALL_CONTRIBUTION = {
185
183
  zeroLineIsNonScoring: true,
186
184
  deriveLine: deriveBasketballLine,
187
185
  };
186
+ const resolveBulkEntry = (state, submission) => {
187
+ if (submission.type !== BASKETBALL_BULK_EDIT_EVENT_TYPE)
188
+ return null;
189
+ const { target, ...event } = submission;
190
+ const scorer = buildGameRosterPlayerId(event.teamId, event.playerUserId);
191
+ const previousValue = playerCountersOf(ownStats(state).playerStats[scorer])[event.stat];
192
+ const value = target - previousValue;
193
+ return value === 0
194
+ ? { kind: 'unchanged' }
195
+ : { kind: 'change', event: { ...event, value, previousValue } };
196
+ };
188
197
  export const BASKETBALL_SCORING = {
189
198
  validate,
199
+ resolveBulkEntry,
190
200
  apply: (state, event) => {
191
201
  if (isStatCorrection(event))
192
202
  return applyEffects(ownStats(state), correctionEffects(event), 1);
@@ -289,18 +299,17 @@ function bulkDrafts(values, context) {
289
299
  continue;
290
300
  const before = currentPlayerCounters(context, entryId);
291
301
  for (const stat of BASKETBALL_PLAYER_STAT_KEYS) {
292
- const value = (entered[stat] ?? before[stat]) - before[stat];
293
- if (value === 0)
302
+ const target = entered[stat] ?? before[stat];
303
+ if (target === before[stat])
294
304
  continue;
295
305
  const draft = {
296
306
  type: BASKETBALL_BULK_EDIT_EVENT_TYPE,
297
307
  teamId: entry.teamId,
298
308
  playerUserId: entry.userId,
299
309
  stat,
300
- value,
301
- previousValue: before[stat],
310
+ target,
302
311
  };
303
- (value > 0 ? increases : decreases).push(draft);
312
+ (target > before[stat] ? increases : decreases).push(draft);
304
313
  }
305
314
  }
306
315
  // Every increase before any decrease, so a team's running score never dips below the floor its
@@ -1 +1 @@
1
- {"version":3,"file":"basketball-scoring.js","sourceRoot":"","sources":["../../src/sports/basketball-scoring.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\nimport {\n BASKETBALL_FANTASY_POINT_WEIGHTS,\n BASKETBALL_POINTS_BY_EVENT_TYPE,\n MAX_STAT_EVENTS_PER_WRITE,\n NOT_ON_GAME_ROSTER_MESSAGE,\n} from '../constants/sports.js';\nimport type {\n BasketballPlayerStats,\n BasketballTeamStats,\n GameStatEvent,\n} from '../doc-schemas/game-stats.js';\nimport type { BasketballGameStats, GameStats } from '../doc-schemas/sport-game.js';\nimport { buildGameRosterPlayerId, parseGameRosterPlayerId } from '../ids/compound-ids.js';\nimport {\n BASKETBALL_ASSISTABLE_SHOT_EVENT_TYPE_KEYS,\n BASKETBALL_ASSIST_EVENT_TYPE,\n BASKETBALL_BULK_EDIT_EVENT_TYPE,\n BASKETBALL_ONE_POINT_EVENT_TYPE,\n BASKETBALL_PLAYER_STAT_KEYS,\n BASKETBALL_PLAYER_TALLY_EVENT_TYPE_KEYS,\n BASKETBALL_REBOUND_EVENT_TYPE,\n BASKETBALL_SCORE_EVENT_TYPE_KEYS,\n BASKETBALL_STAT_EVENT_TYPE_KEYS,\n BASKETBALL_TEAM_STAT_KEYS,\n BASKETBALL_THREE_POINT_EVENT_TYPE,\n BASKETBALL_TIMEOUT_EVENT_TYPE,\n BASKETBALL_TWO_POINT_EVENT_TYPE,\n} from '../types/sports.js';\nimport type {\n BasketballPlayerStatKey,\n BasketballStatEventType,\n BasketballTeamStatKey,\n} from '../types/sports.js';\nimport { PLAYER_STAT_LINE_KIND_KEYS } from '../types/stats.js';\nimport type { StatLineKind } from '../types/stats.js';\nimport {\n BULK_ENTRY_TOO_LARGE_MESSAGE,\n bulkEntryCountField,\n type BulkEntryContext,\n type PlayerLinesBulkEntry,\n type PlayerLinesValues,\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 isAllZeroLine,\n isSportStatEventIn,\n isStatCorrection,\n refuseStatEvent,\n type CorrectableFields,\n type GameTeamLine,\n type SportScoring,\n type SportStatEventBody,\n type StatCorrectionBody,\n type StatEventBody,\n type StatLineValues,\n} from './scoring.js';\nimport {\n describeStoreEvent,\n formatSignedValue,\n type PlayerEventsScoringPanel,\n type Scoreboard,\n type StatEventLabel,\n type StatEventNameResolver,\n type StatFieldLabels,\n} from './scoring-display.js';\n\ntype BasketballEvent = Extract<SportStatEventBody, { type: BasketballStatEventType }>;\ntype PlayerCounters = Record<BasketballPlayerStatKey, number>;\n\nfunction isBasketballEvent(event: StatEventBody | undefined): event is BasketballEvent {\n return isSportStatEventIn(BASKETBALL_STAT_EVENT_TYPE_KEYS, event);\n}\n\nfunction ownStats(state: GameStats): BasketballGameStats {\n return state as BasketballGameStats;\n}\n\nfunction ownTeamLine(line: GameTeamLine): BasketballTeamStats {\n return line as BasketballTeamStats;\n}\n\nexport function basketballFantasyPoints(\n line: Pick<BasketballPlayerStats, BasketballPlayerStatKey>,\n): number {\n return BASKETBALL_PLAYER_STAT_KEYS.reduce(\n (total, key) => total + line[key] * BASKETBALL_FANTASY_POINT_WEIGHTS[key],\n 0,\n );\n}\n\nconst FANTASY_POINTS_FIELD = 'fantasyPoints' satisfies keyof BasketballPlayerStats;\nconst PLAYER_LINE_KINDS: readonly StatLineKind[] = PLAYER_STAT_LINE_KIND_KEYS;\n\nexport function emptyBasketballTeamLine(): BasketballTeamStats {\n return Object.fromEntries(\n BASKETBALL_TEAM_STAT_KEYS.map((key) => [key, 0]),\n ) as BasketballTeamStats;\n}\n\nfunction emptyPlayerCounters(): PlayerCounters {\n return Object.fromEntries(BASKETBALL_PLAYER_STAT_KEYS.map((key) => [key, 0])) as PlayerCounters;\n}\n\nfunction counterOf(line: StatLineValues, field: string): number {\n const value = line[field] ?? 0;\n if (typeof value !== 'number')\n throw new TypeError(`A basketball line's ${field} is not a number.`);\n return value;\n}\n\nfunction playerCountersOf(line: StatLineValues | undefined): PlayerCounters {\n const counters = emptyPlayerCounters();\n if (line === undefined) return counters;\n for (const key of BASKETBALL_PLAYER_STAT_KEYS) counters[key] = counterOf(line, key);\n return counters;\n}\n\nfunction deriveBasketballLine(kind: StatLineKind, line: StatLineValues): StatLineValues {\n const withoutDerived = Object.fromEntries(\n Object.entries(line).filter(([field]) => field !== FANTASY_POINTS_FIELD),\n );\n if (!PLAYER_LINE_KINDS.includes(kind)) return withoutDerived;\n return {\n ...withoutDerived,\n [FANTASY_POINTS_FIELD]: basketballFantasyPoints(playerCountersOf(line)),\n };\n}\n\nconst CORRECTABLE_FIELDS: CorrectableFields = {\n team: BASKETBALL_TEAM_STAT_KEYS,\n player: BASKETBALL_PLAYER_STAT_KEYS,\n};\n\nconst TALLY_FIELD = {\n [BASKETBALL_REBOUND_EVENT_TYPE]: 'rebounds',\n [BASKETBALL_ASSIST_EVENT_TYPE]: 'assists',\n} as const satisfies Record<\n (typeof BASKETBALL_PLAYER_TALLY_EVENT_TYPE_KEYS)[number],\n BasketballPlayerStatKey\n>;\n\ninterface LineChange<Field extends string> {\n readonly line: string;\n readonly field: Field;\n readonly amount: number;\n}\n\ninterface Effects {\n readonly team: readonly LineChange<BasketballTeamStatKey>[];\n readonly player: readonly LineChange<BasketballPlayerStatKey>[];\n}\n\nfunction assisterOf(event: StatEventBody): string | undefined {\n return 'assistedByUserId' in event && typeof event.assistedByUserId === 'string'\n ? event.assistedByUserId\n : undefined;\n}\n\nfunction namesPlayer(event: StatEventBody): boolean {\n return 'playerUserId' in event && event.playerUserId !== undefined;\n}\n\nfunction effectsOf(event: BasketballEvent): Effects {\n if (event.type === BASKETBALL_TIMEOUT_EVENT_TYPE) {\n return { team: [{ line: event.teamId, field: 'timeouts', amount: 1 }], player: [] };\n }\n const scorer = buildGameRosterPlayerId(event.teamId, event.playerUserId);\n if (event.type === BASKETBALL_BULK_EDIT_EVENT_TYPE) {\n return {\n team:\n event.stat === 'points'\n ? [{ line: event.teamId, field: 'score', amount: event.value }]\n : [],\n player: [{ line: scorer, field: event.stat, amount: event.value }],\n };\n }\n\n const team: LineChange<BasketballTeamStatKey>[] = [];\n const player: LineChange<BasketballPlayerStatKey>[] = [];\n const points = BASKETBALL_POINTS_BY_EVENT_TYPE[event.type];\n if (points > 0) {\n team.push({ line: event.teamId, field: 'score', amount: points });\n player.push({ line: scorer, field: 'points', amount: points });\n }\n if (event.type === BASKETBALL_REBOUND_EVENT_TYPE || event.type === BASKETBALL_ASSIST_EVENT_TYPE) {\n player.push({ line: scorer, field: TALLY_FIELD[event.type], amount: 1 });\n }\n const assister = assisterOf(event);\n if (assister !== undefined) {\n player.push({\n line: buildGameRosterPlayerId(event.teamId, assister),\n field: 'assists',\n amount: 1,\n });\n }\n return { team, player };\n}\n\nfunction correctionEffects(event: StatCorrectionBody): Effects {\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 basketball correction cannot change ${event.stat} on a ${event.playerUserId === undefined ? 'team' : 'player'} line.`,\n );\n }\n return event.playerUserId === undefined\n ? {\n team: [\n { line: event.teamId, field: event.stat as BasketballTeamStatKey, amount: event.value },\n ],\n player: [],\n }\n : {\n team: [],\n player: [\n {\n line: buildGameRosterPlayerId(event.teamId, event.playerUserId),\n field: event.stat as BasketballPlayerStatKey,\n amount: event.value,\n },\n ],\n };\n}\n\nfunction applyEffects(\n state: BasketballGameStats,\n effects: Effects,\n sign: 1 | -1,\n): BasketballGameStats {\n const teamStats = { ...state.teamStats };\n for (const change of effects.team) {\n const line = { ...(teamStats[change.line] ?? emptyBasketballTeamLine()) };\n line[change.field] += sign * change.amount;\n teamStats[change.line] = line;\n }\n\n const playerStats = { ...state.playerStats };\n for (const change of effects.player) {\n const counters = playerCountersOf(playerStats[change.line]);\n counters[change.field] += sign * change.amount;\n if (isAllZeroLine(counters, BASKETBALL_PLAYER_STAT_KEYS)) delete playerStats[change.line];\n else\n playerStats[change.line] = deriveBasketballLine(\n 'gamePlayer',\n counters,\n ) as BasketballPlayerStats;\n }\n return { teamStats, playerStats };\n}\n\nfunction isAssistableShot(type: BasketballStatEventType): boolean {\n return (BASKETBALL_ASSISTABLE_SHOT_EVENT_TYPE_KEYS as readonly string[]).includes(type);\n}\n\nconst validate: SportScoring['validate'] = (stateIn, event, roster) => {\n if (!isBasketballEvent(event)) return refuseStatEvent('eventTypeNotInSport');\n if (!roster.teamIds.includes(event.teamId)) return refuseStatEvent('teamNotInGame');\n if (event.type === BASKETBALL_TIMEOUT_EVENT_TYPE) {\n return namesPlayer(event) || assisterOf(event) !== undefined\n ? refuseStatEvent('playerNotAllowed')\n : STAT_EVENT_ACCEPTED;\n }\n\n const scorer = buildGameRosterPlayerId(event.teamId, event.playerUserId);\n if (!roster.rosterPlayerIds.has(scorer)) return refuseStatEvent('playerNotRostered');\n\n const assister = assisterOf(event);\n if (assister !== undefined) {\n if (!isAssistableShot(event.type) || assister === event.playerUserId)\n return refuseStatEvent('assistNotAllowed');\n if (!roster.rosterPlayerIds.has(buildGameRosterPlayerId(event.teamId, assister))) {\n return refuseStatEvent('assisterNotRostered');\n }\n }\n\n if (event.type === BASKETBALL_BULK_EDIT_EVENT_TYPE) {\n if (!Number.isInteger(event.value) || event.value === 0)\n return refuseStatEvent('bulkValueInvalid');\n const state = ownStats(stateIn);\n const current = playerCountersOf(state.playerStats[scorer])[event.stat];\n if (event.previousValue !== current) return refuseStatEvent('bulkStale');\n if (current + event.value < 0) return refuseStatEvent('bulkValueInvalid');\n const teamScore = (state.teamStats[event.teamId] ?? emptyBasketballTeamLine()).score;\n if (event.stat === 'points' && teamScore + event.value < 0)\n return refuseStatEvent('bulkValueInvalid');\n }\n return STAT_EVENT_ACCEPTED;\n};\n\nconst BASKETBALL_CONTRIBUTION: ContributionSpec = {\n playerStatKeys: BASKETBALL_PLAYER_STAT_KEYS,\n teamSeasonPlayerSums: ['rebounds', 'assists'] satisfies BasketballPlayerStatKey[],\n zeroLineIsNonScoring: true,\n deriveLine: deriveBasketballLine,\n};\n\nexport const BASKETBALL_SCORING: SportScoring = {\n validate,\n apply: (state, event) => {\n if (isStatCorrection(event)) return applyEffects(ownStats(state), correctionEffects(event), 1);\n if (!isBasketballEvent(event))\n throw new Error(`The basketball adapter does not apply ${event.type}.`);\n return applyEffects(ownStats(state), effectsOf(event), 1);\n },\n undo: (state, target) => {\n if (!isBasketballEvent(target))\n throw new Error(`The basketball adapter does not undo ${target.type}.`);\n return applyEffects(ownStats(state), effectsOf(target), -1);\n },\n deriveLine: deriveBasketballLine,\n correctableFields: CORRECTABLE_FIELDS,\n nextRound: () => null,\n finalizationReadiness: () => FINALIZATION_READY,\n participation: (state, rosterPlayerIds, absentRosterPlayerIds) =>\n decideParticipation(BASKETBALL_CONTRIBUTION, state, rosterPlayerIds, absentRosterPlayerIds),\n contribution: (game) => buildGameContribution(BASKETBALL_CONTRIBUTION, game),\n};\n\nexport const BASKETBALL_EVENT_LABELS = {\n [BASKETBALL_ONE_POINT_EVENT_TYPE]: { button: '+1 PT', play: '1-point basket' },\n [BASKETBALL_TWO_POINT_EVENT_TYPE]: { button: '+2 PT', play: '2-point basket' },\n [BASKETBALL_THREE_POINT_EVENT_TYPE]: { button: '+3 PT', play: '3-point basket' },\n [BASKETBALL_REBOUND_EVENT_TYPE]: { button: 'Rebound', play: 'Rebound' },\n [BASKETBALL_ASSIST_EVENT_TYPE]: { button: 'Assist', play: 'Assist' },\n [BASKETBALL_TIMEOUT_EVENT_TYPE]: { button: 'Timeout', play: 'Timeout' },\n [BASKETBALL_BULK_EDIT_EVENT_TYPE]: { button: 'Bulk entry', play: 'Bulk entry' },\n} as const satisfies Record<BasketballStatEventType, StatEventLabel>;\n\nexport const BASKETBALL_FIELD_LABELS = {\n team: { score: 'Score', timeouts: 'Timeouts' },\n player: { points: 'Points', rebounds: 'Rebounds', assists: 'Assists' },\n} as const satisfies StatFieldLabels<BasketballTeamStatKey, BasketballPlayerStatKey>;\n\nexport function describeBasketballEvent(\n event: GameStatEvent,\n resolve: StatEventNameResolver,\n): string {\n const storeEvent = describeStoreEvent(event, resolve, BASKETBALL_FIELD_LABELS);\n if (storeEvent !== null) return storeEvent;\n if (!isBasketballEvent(event)) return event.type;\n\n const team = resolve.team(event.teamId);\n if (event.type === BASKETBALL_TIMEOUT_EVENT_TYPE)\n return `${team}: ${BASKETBALL_EVENT_LABELS[event.type].play}`;\n\n const player = `${resolve.player(event.playerUserId)} (${team})`;\n if (event.type === BASKETBALL_BULK_EDIT_EVENT_TYPE) {\n const field = BASKETBALL_FIELD_LABELS.player[event.stat].toLowerCase();\n return `${player}: ${formatSignedValue(event.value)} ${field} (${BASKETBALL_EVENT_LABELS[event.type].play.toLowerCase()})`;\n }\n const play = `${player}: ${BASKETBALL_EVENT_LABELS[event.type].play}`;\n const assister = assisterOf(event);\n return assister === undefined ? play : `${play}, assisted by ${resolve.player(assister)}`;\n}\n\nfunction sumOf(\n lines: readonly { readonly rebounds: number; readonly assists: number }[],\n field: 'rebounds' | 'assists',\n): number {\n return lines.reduce((total, line) => total + line[field], 0);\n}\n\nexport const BASKETBALL_SCOREBOARD: Scoreboard = {\n figures: [\n {\n id: 'points',\n label: BASKETBALL_FIELD_LABELS.player.points,\n value: (teamLine) => teamLine.score,\n },\n {\n id: 'rebounds',\n label: BASKETBALL_FIELD_LABELS.player.rebounds,\n value: (_teamLine, sideLines) => sumOf(sideLines, 'rebounds'),\n },\n {\n id: 'assists',\n label: BASKETBALL_FIELD_LABELS.player.assists,\n value: (_teamLine, sideLines) => sumOf(sideLines, 'assists'),\n },\n {\n id: 'timeouts',\n label: BASKETBALL_FIELD_LABELS.team.timeouts,\n value: (teamLine) => ownTeamLine(teamLine).timeouts,\n },\n ],\n jumpBall: true,\n};\n\nexport const BASKETBALL_SCORING_PANEL: PlayerEventsScoringPanel = {\n kind: 'playerEvents',\n playerEventTypes: [\n ...BASKETBALL_SCORE_EVENT_TYPE_KEYS,\n ...BASKETBALL_PLAYER_TALLY_EVENT_TYPE_KEYS,\n ],\n assistableEventTypes: BASKETBALL_ASSISTABLE_SHOT_EVENT_TYPE_KEYS,\n teamEventTypes: [BASKETBALL_TIMEOUT_EVENT_TYPE],\n};\n\nexport const BASKETBALL_TEAM_SCORE_FLOOR_MESSAGE =\n \"These changes would leave a team's score below zero.\";\n\nconst playerLineValuesSchema = z.strictObject(\n Object.fromEntries(\n BASKETBALL_PLAYER_STAT_KEYS.map((key) => [key, bulkEntryCountField]),\n ) as Record<BasketballPlayerStatKey, typeof bulkEntryCountField>,\n);\n\nfunction currentPlayerCounters(context: BulkEntryContext, entryId: string): PlayerCounters {\n return playerCountersOf(ownStats(context.state).playerStats[entryId]);\n}\n\nfunction bulkDrafts(values: PlayerLinesValues, context: BulkEntryContext): StatEventDraft[] {\n const increases: StatEventDraft[] = [];\n const decreases: StatEventDraft[] = [];\n for (const entryId of context.rosterPlayerIds) {\n const entered = values.lines[entryId];\n const entry = parseGameRosterPlayerId(entryId);\n if (entered === undefined || entry === null) continue;\n const before = currentPlayerCounters(context, entryId);\n for (const stat of BASKETBALL_PLAYER_STAT_KEYS) {\n const value = (entered[stat] ?? before[stat]) - before[stat];\n if (value === 0) continue;\n const draft: StatEventDraft = {\n type: BASKETBALL_BULK_EDIT_EVENT_TYPE,\n teamId: entry.teamId,\n playerUserId: entry.userId,\n stat,\n value,\n previousValue: before[stat],\n };\n (value > 0 ? increases : decreases).push(draft);\n }\n }\n // Every increase before any decrease, so a team's running score never dips below the floor its\n // final score clears — each draft passes `validate` against the state the ones before it leave.\n return [...increases, ...decreases];\n}\n\nexport const BASKETBALL_BULK_ENTRY: PlayerLinesBulkEntry = {\n kind: 'playerLines',\n formDefaults: (context) => ({\n lines: Object.fromEntries(\n context.rosterPlayerIds.map((entryId) => [entryId, currentPlayerCounters(context, entryId)]),\n ),\n }),\n formSchema: (context) => {\n const roster = new Set(context.rosterPlayerIds);\n return z\n .object({ lines: z.record(z.string(), playerLineValuesSchema) })\n .superRefine((values, issues) => {\n for (const entryId of Object.keys(values.lines)) {\n if (!roster.has(entryId)) {\n issues.addIssue({\n code: 'custom',\n path: ['lines', entryId],\n message: NOT_ON_GAME_ROSTER_MESSAGE,\n });\n }\n }\n const state = ownStats(context.state);\n for (const teamId of context.game.teamIds) {\n let score = (state.teamStats[teamId] ?? emptyBasketballTeamLine()).score;\n for (const [entryId, entered] of Object.entries(values.lines)) {\n if (parseGameRosterPlayerId(entryId)?.teamId !== teamId) continue;\n score += entered.points - currentPlayerCounters(context, entryId).points;\n }\n if (score < 0)\n issues.addIssue({\n code: 'custom',\n path: ['lines'],\n message: BASKETBALL_TEAM_SCORE_FLOOR_MESSAGE,\n });\n }\n if (bulkDrafts(values, context).length > MAX_STAT_EVENTS_PER_WRITE) {\n issues.addIssue({\n code: 'custom',\n path: ['lines'],\n message: BULK_ENTRY_TOO_LARGE_MESSAGE,\n });\n }\n });\n },\n toEvents: bulkDrafts,\n};\n"]}
1
+ {"version":3,"file":"basketball-scoring.js","sourceRoot":"","sources":["../../src/sports/basketball-scoring.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\nimport {\n BASKETBALL_FANTASY_POINT_WEIGHTS,\n BASKETBALL_POINTS_BY_EVENT_TYPE,\n MAX_STAT_EVENTS_PER_WRITE,\n NOT_ON_GAME_ROSTER_MESSAGE,\n} from '../constants/sports.js';\nimport type {\n BasketballPlayerStats,\n BasketballTeamStats,\n GameStatEvent,\n} from '../doc-schemas/game-stats.js';\nimport type { BasketballGameStats, GameStats } from '../doc-schemas/sport-game.js';\nimport { buildGameRosterPlayerId, parseGameRosterPlayerId } from '../ids/compound-ids.js';\nimport {\n BASKETBALL_ASSISTABLE_SHOT_EVENT_TYPE_KEYS,\n BASKETBALL_ASSIST_EVENT_TYPE,\n BASKETBALL_BULK_EDIT_EVENT_TYPE,\n BASKETBALL_ONE_POINT_EVENT_TYPE,\n BASKETBALL_PLAYER_STAT_KEYS,\n BASKETBALL_PLAYER_TALLY_EVENT_TYPE_KEYS,\n BASKETBALL_REBOUND_EVENT_TYPE,\n BASKETBALL_SCORE_EVENT_TYPE_KEYS,\n BASKETBALL_STAT_EVENT_TYPE_KEYS,\n BASKETBALL_TEAM_STAT_KEYS,\n BASKETBALL_THREE_POINT_EVENT_TYPE,\n BASKETBALL_TIMEOUT_EVENT_TYPE,\n BASKETBALL_TWO_POINT_EVENT_TYPE,\n} from '../types/sports.js';\nimport type {\n BasketballPlayerStatKey,\n BasketballStatEventType,\n BasketballTeamStatKey,\n} from '../types/sports.js';\nimport { PLAYER_STAT_LINE_KIND_KEYS } from '../types/stats.js';\nimport type { StatLineKind } from '../types/stats.js';\nimport {\n BULK_ENTRY_TOO_LARGE_MESSAGE,\n bulkEntryCountField,\n type BulkEntryContext,\n type PlayerLinesBulkEntry,\n type PlayerLinesValues,\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 isAllZeroLine,\n isSportStatEventIn,\n isStatCorrection,\n refuseStatEvent,\n type CorrectableFields,\n type GameTeamLine,\n type SportScoring,\n type SportStatEventBody,\n type StatCorrectionBody,\n type StatEventBody,\n type StatLineValues,\n} from './scoring.js';\nimport {\n describeStoreEvent,\n formatSignedValue,\n type PlayerEventsScoringPanel,\n type Scoreboard,\n type StatEventLabel,\n type StatEventNameResolver,\n type StatFieldLabels,\n} from './scoring-display.js';\n\ntype BasketballEvent = Extract<SportStatEventBody, { type: BasketballStatEventType }>;\ntype PlayerCounters = Record<BasketballPlayerStatKey, number>;\n\nfunction isBasketballEvent(event: StatEventBody | undefined): event is BasketballEvent {\n return isSportStatEventIn(BASKETBALL_STAT_EVENT_TYPE_KEYS, event);\n}\n\nfunction ownStats(state: GameStats): BasketballGameStats {\n return state as BasketballGameStats;\n}\n\nfunction ownTeamLine(line: GameTeamLine): BasketballTeamStats {\n return line as BasketballTeamStats;\n}\n\nexport function basketballFantasyPoints(\n line: Pick<BasketballPlayerStats, BasketballPlayerStatKey>,\n): number {\n return BASKETBALL_PLAYER_STAT_KEYS.reduce(\n (total, key) => total + line[key] * BASKETBALL_FANTASY_POINT_WEIGHTS[key],\n 0,\n );\n}\n\nconst FANTASY_POINTS_FIELD = 'fantasyPoints' satisfies keyof BasketballPlayerStats;\nconst PLAYER_LINE_KINDS: readonly StatLineKind[] = PLAYER_STAT_LINE_KIND_KEYS;\n\nexport function emptyBasketballTeamLine(): BasketballTeamStats {\n return Object.fromEntries(\n BASKETBALL_TEAM_STAT_KEYS.map((key) => [key, 0]),\n ) as BasketballTeamStats;\n}\n\nfunction emptyPlayerCounters(): PlayerCounters {\n return Object.fromEntries(BASKETBALL_PLAYER_STAT_KEYS.map((key) => [key, 0])) as PlayerCounters;\n}\n\nfunction counterOf(line: StatLineValues, field: string): number {\n const value = line[field] ?? 0;\n if (typeof value !== 'number')\n throw new TypeError(`A basketball line's ${field} is not a number.`);\n return value;\n}\n\nfunction playerCountersOf(line: StatLineValues | undefined): PlayerCounters {\n const counters = emptyPlayerCounters();\n if (line === undefined) return counters;\n for (const key of BASKETBALL_PLAYER_STAT_KEYS) counters[key] = counterOf(line, key);\n return counters;\n}\n\nfunction deriveBasketballLine(kind: StatLineKind, line: StatLineValues): StatLineValues {\n const withoutDerived = Object.fromEntries(\n Object.entries(line).filter(([field]) => field !== FANTASY_POINTS_FIELD),\n );\n if (!PLAYER_LINE_KINDS.includes(kind)) return withoutDerived;\n return {\n ...withoutDerived,\n [FANTASY_POINTS_FIELD]: basketballFantasyPoints(playerCountersOf(line)),\n };\n}\n\nconst CORRECTABLE_FIELDS: CorrectableFields = {\n team: BASKETBALL_TEAM_STAT_KEYS,\n player: BASKETBALL_PLAYER_STAT_KEYS,\n};\n\nconst TALLY_FIELD = {\n [BASKETBALL_REBOUND_EVENT_TYPE]: 'rebounds',\n [BASKETBALL_ASSIST_EVENT_TYPE]: 'assists',\n} as const satisfies Record<\n (typeof BASKETBALL_PLAYER_TALLY_EVENT_TYPE_KEYS)[number],\n BasketballPlayerStatKey\n>;\n\ninterface LineChange<Field extends string> {\n readonly line: string;\n readonly field: Field;\n readonly amount: number;\n}\n\ninterface Effects {\n readonly team: readonly LineChange<BasketballTeamStatKey>[];\n readonly player: readonly LineChange<BasketballPlayerStatKey>[];\n}\n\nfunction assisterOf(event: StatEventBody): string | undefined {\n return 'assistedByUserId' in event && typeof event.assistedByUserId === 'string'\n ? event.assistedByUserId\n : undefined;\n}\n\nfunction namesPlayer(event: StatEventBody): boolean {\n return 'playerUserId' in event && event.playerUserId !== undefined;\n}\n\nfunction effectsOf(event: BasketballEvent): Effects {\n if (event.type === BASKETBALL_TIMEOUT_EVENT_TYPE) {\n return { team: [{ line: event.teamId, field: 'timeouts', amount: 1 }], player: [] };\n }\n const scorer = buildGameRosterPlayerId(event.teamId, event.playerUserId);\n if (event.type === BASKETBALL_BULK_EDIT_EVENT_TYPE) {\n return {\n team:\n event.stat === 'points'\n ? [{ line: event.teamId, field: 'score', amount: event.value }]\n : [],\n player: [{ line: scorer, field: event.stat, amount: event.value }],\n };\n }\n\n const team: LineChange<BasketballTeamStatKey>[] = [];\n const player: LineChange<BasketballPlayerStatKey>[] = [];\n const points = BASKETBALL_POINTS_BY_EVENT_TYPE[event.type];\n if (points > 0) {\n team.push({ line: event.teamId, field: 'score', amount: points });\n player.push({ line: scorer, field: 'points', amount: points });\n }\n if (event.type === BASKETBALL_REBOUND_EVENT_TYPE || event.type === BASKETBALL_ASSIST_EVENT_TYPE) {\n player.push({ line: scorer, field: TALLY_FIELD[event.type], amount: 1 });\n }\n const assister = assisterOf(event);\n if (assister !== undefined) {\n player.push({\n line: buildGameRosterPlayerId(event.teamId, assister),\n field: 'assists',\n amount: 1,\n });\n }\n return { team, player };\n}\n\nfunction correctionEffects(event: StatCorrectionBody): Effects {\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 basketball correction cannot change ${event.stat} on a ${event.playerUserId === undefined ? 'team' : 'player'} line.`,\n );\n }\n return event.playerUserId === undefined\n ? {\n team: [\n { line: event.teamId, field: event.stat as BasketballTeamStatKey, amount: event.value },\n ],\n player: [],\n }\n : {\n team: [],\n player: [\n {\n line: buildGameRosterPlayerId(event.teamId, event.playerUserId),\n field: event.stat as BasketballPlayerStatKey,\n amount: event.value,\n },\n ],\n };\n}\n\nfunction applyEffects(\n state: BasketballGameStats,\n effects: Effects,\n sign: 1 | -1,\n): BasketballGameStats {\n const teamStats = { ...state.teamStats };\n for (const change of effects.team) {\n const line = { ...(teamStats[change.line] ?? emptyBasketballTeamLine()) };\n line[change.field] += sign * change.amount;\n teamStats[change.line] = line;\n }\n\n const playerStats = { ...state.playerStats };\n for (const change of effects.player) {\n const counters = playerCountersOf(playerStats[change.line]);\n counters[change.field] += sign * change.amount;\n if (isAllZeroLine(counters, BASKETBALL_PLAYER_STAT_KEYS)) delete playerStats[change.line];\n else\n playerStats[change.line] = deriveBasketballLine(\n 'gamePlayer',\n counters,\n ) as BasketballPlayerStats;\n }\n return { teamStats, playerStats };\n}\n\nfunction isAssistableShot(type: BasketballStatEventType): boolean {\n return (BASKETBALL_ASSISTABLE_SHOT_EVENT_TYPE_KEYS as readonly string[]).includes(type);\n}\n\nconst validate: SportScoring['validate'] = (stateIn, event, roster) => {\n if (!isBasketballEvent(event)) return refuseStatEvent('eventTypeNotInSport');\n if (!roster.teamIds.includes(event.teamId)) return refuseStatEvent('teamNotInGame');\n if (event.type === BASKETBALL_TIMEOUT_EVENT_TYPE) {\n return namesPlayer(event) || assisterOf(event) !== undefined\n ? refuseStatEvent('playerNotAllowed')\n : STAT_EVENT_ACCEPTED;\n }\n\n const scorer = buildGameRosterPlayerId(event.teamId, event.playerUserId);\n if (!roster.rosterPlayerIds.has(scorer)) return refuseStatEvent('playerNotRostered');\n\n const assister = assisterOf(event);\n if (assister !== undefined) {\n if (!isAssistableShot(event.type) || assister === event.playerUserId)\n return refuseStatEvent('assistNotAllowed');\n if (!roster.rosterPlayerIds.has(buildGameRosterPlayerId(event.teamId, assister))) {\n return refuseStatEvent('assisterNotRostered');\n }\n }\n\n if (event.type === BASKETBALL_BULK_EDIT_EVENT_TYPE) {\n if (!Number.isInteger(event.value) || event.value === 0)\n return refuseStatEvent('bulkValueInvalid');\n const state = ownStats(stateIn);\n const current = playerCountersOf(state.playerStats[scorer])[event.stat];\n if (current + event.value < 0) return refuseStatEvent('bulkValueInvalid');\n const teamScore = (state.teamStats[event.teamId] ?? emptyBasketballTeamLine()).score;\n if (event.stat === 'points' && teamScore + event.value < 0)\n return refuseStatEvent('bulkValueInvalid');\n }\n return STAT_EVENT_ACCEPTED;\n};\n\nconst BASKETBALL_CONTRIBUTION: ContributionSpec = {\n playerStatKeys: BASKETBALL_PLAYER_STAT_KEYS,\n teamSeasonPlayerSums: ['rebounds', 'assists'] satisfies BasketballPlayerStatKey[],\n zeroLineIsNonScoring: true,\n deriveLine: deriveBasketballLine,\n};\n\nconst resolveBulkEntry: NonNullable<SportScoring['resolveBulkEntry']> = (state, submission) => {\n if (submission.type !== BASKETBALL_BULK_EDIT_EVENT_TYPE) return null;\n const { target, ...event } = submission;\n const scorer = buildGameRosterPlayerId(event.teamId, event.playerUserId);\n const previousValue = playerCountersOf(ownStats(state).playerStats[scorer])[event.stat];\n const value = target - previousValue;\n return value === 0\n ? { kind: 'unchanged' }\n : { kind: 'change', event: { ...event, value, previousValue } };\n};\n\nexport const BASKETBALL_SCORING: SportScoring = {\n validate,\n resolveBulkEntry,\n apply: (state, event) => {\n if (isStatCorrection(event)) return applyEffects(ownStats(state), correctionEffects(event), 1);\n if (!isBasketballEvent(event))\n throw new Error(`The basketball adapter does not apply ${event.type}.`);\n return applyEffects(ownStats(state), effectsOf(event), 1);\n },\n undo: (state, target) => {\n if (!isBasketballEvent(target))\n throw new Error(`The basketball adapter does not undo ${target.type}.`);\n return applyEffects(ownStats(state), effectsOf(target), -1);\n },\n deriveLine: deriveBasketballLine,\n correctableFields: CORRECTABLE_FIELDS,\n nextRound: () => null,\n finalizationReadiness: () => FINALIZATION_READY,\n participation: (state, rosterPlayerIds, absentRosterPlayerIds) =>\n decideParticipation(BASKETBALL_CONTRIBUTION, state, rosterPlayerIds, absentRosterPlayerIds),\n contribution: (game) => buildGameContribution(BASKETBALL_CONTRIBUTION, game),\n};\n\nexport const BASKETBALL_EVENT_LABELS = {\n [BASKETBALL_ONE_POINT_EVENT_TYPE]: { button: '+1 PT', play: '1-point basket' },\n [BASKETBALL_TWO_POINT_EVENT_TYPE]: { button: '+2 PT', play: '2-point basket' },\n [BASKETBALL_THREE_POINT_EVENT_TYPE]: { button: '+3 PT', play: '3-point basket' },\n [BASKETBALL_REBOUND_EVENT_TYPE]: { button: 'Rebound', play: 'Rebound' },\n [BASKETBALL_ASSIST_EVENT_TYPE]: { button: 'Assist', play: 'Assist' },\n [BASKETBALL_TIMEOUT_EVENT_TYPE]: { button: 'Timeout', play: 'Timeout' },\n [BASKETBALL_BULK_EDIT_EVENT_TYPE]: { button: 'Bulk entry', play: 'Bulk entry' },\n} as const satisfies Record<BasketballStatEventType, StatEventLabel>;\n\nexport const BASKETBALL_FIELD_LABELS = {\n team: { score: 'Score', timeouts: 'Timeouts' },\n player: { points: 'Points', rebounds: 'Rebounds', assists: 'Assists' },\n} as const satisfies StatFieldLabels<BasketballTeamStatKey, BasketballPlayerStatKey>;\n\nexport function describeBasketballEvent(\n event: GameStatEvent,\n resolve: StatEventNameResolver,\n): string {\n const storeEvent = describeStoreEvent(event, resolve, BASKETBALL_FIELD_LABELS);\n if (storeEvent !== null) return storeEvent;\n if (!isBasketballEvent(event)) return event.type;\n\n const team = resolve.team(event.teamId);\n if (event.type === BASKETBALL_TIMEOUT_EVENT_TYPE)\n return `${team}: ${BASKETBALL_EVENT_LABELS[event.type].play}`;\n\n const player = `${resolve.player(event.playerUserId)} (${team})`;\n if (event.type === BASKETBALL_BULK_EDIT_EVENT_TYPE) {\n const field = BASKETBALL_FIELD_LABELS.player[event.stat].toLowerCase();\n return `${player}: ${formatSignedValue(event.value)} ${field} (${BASKETBALL_EVENT_LABELS[event.type].play.toLowerCase()})`;\n }\n const play = `${player}: ${BASKETBALL_EVENT_LABELS[event.type].play}`;\n const assister = assisterOf(event);\n return assister === undefined ? play : `${play}, assisted by ${resolve.player(assister)}`;\n}\n\nfunction sumOf(\n lines: readonly { readonly rebounds: number; readonly assists: number }[],\n field: 'rebounds' | 'assists',\n): number {\n return lines.reduce((total, line) => total + line[field], 0);\n}\n\nexport const BASKETBALL_SCOREBOARD: Scoreboard = {\n figures: [\n {\n id: 'points',\n label: BASKETBALL_FIELD_LABELS.player.points,\n value: (teamLine) => teamLine.score,\n },\n {\n id: 'rebounds',\n label: BASKETBALL_FIELD_LABELS.player.rebounds,\n value: (_teamLine, sideLines) => sumOf(sideLines, 'rebounds'),\n },\n {\n id: 'assists',\n label: BASKETBALL_FIELD_LABELS.player.assists,\n value: (_teamLine, sideLines) => sumOf(sideLines, 'assists'),\n },\n {\n id: 'timeouts',\n label: BASKETBALL_FIELD_LABELS.team.timeouts,\n value: (teamLine) => ownTeamLine(teamLine).timeouts,\n },\n ],\n jumpBall: true,\n};\n\nexport const BASKETBALL_SCORING_PANEL: PlayerEventsScoringPanel = {\n kind: 'playerEvents',\n playerEventTypes: [\n ...BASKETBALL_SCORE_EVENT_TYPE_KEYS,\n ...BASKETBALL_PLAYER_TALLY_EVENT_TYPE_KEYS,\n ],\n assistableEventTypes: BASKETBALL_ASSISTABLE_SHOT_EVENT_TYPE_KEYS,\n teamEventTypes: [BASKETBALL_TIMEOUT_EVENT_TYPE],\n};\n\nexport const BASKETBALL_TEAM_SCORE_FLOOR_MESSAGE =\n \"These changes would leave a team's score below zero.\";\n\nconst playerLineValuesSchema = z.strictObject(\n Object.fromEntries(\n BASKETBALL_PLAYER_STAT_KEYS.map((key) => [key, bulkEntryCountField]),\n ) as Record<BasketballPlayerStatKey, typeof bulkEntryCountField>,\n);\n\nfunction currentPlayerCounters(context: BulkEntryContext, entryId: string): PlayerCounters {\n return playerCountersOf(ownStats(context.state).playerStats[entryId]);\n}\n\nfunction bulkDrafts(values: PlayerLinesValues, context: BulkEntryContext): StatEventDraft[] {\n const increases: StatEventDraft[] = [];\n const decreases: StatEventDraft[] = [];\n for (const entryId of context.rosterPlayerIds) {\n const entered = values.lines[entryId];\n const entry = parseGameRosterPlayerId(entryId);\n if (entered === undefined || entry === null) continue;\n const before = currentPlayerCounters(context, entryId);\n for (const stat of BASKETBALL_PLAYER_STAT_KEYS) {\n const target = entered[stat] ?? before[stat];\n if (target === before[stat]) continue;\n const draft: StatEventDraft = {\n type: BASKETBALL_BULK_EDIT_EVENT_TYPE,\n teamId: entry.teamId,\n playerUserId: entry.userId,\n stat,\n target,\n };\n (target > before[stat] ? increases : decreases).push(draft);\n }\n }\n // Every increase before any decrease, so a team's running score never dips below the floor its\n // final score clears — each draft passes `validate` against the state the ones before it leave.\n return [...increases, ...decreases];\n}\n\nexport const BASKETBALL_BULK_ENTRY: PlayerLinesBulkEntry = {\n kind: 'playerLines',\n formDefaults: (context) => ({\n lines: Object.fromEntries(\n context.rosterPlayerIds.map((entryId) => [entryId, currentPlayerCounters(context, entryId)]),\n ),\n }),\n formSchema: (context) => {\n const roster = new Set(context.rosterPlayerIds);\n return z\n .object({ lines: z.record(z.string(), playerLineValuesSchema) })\n .superRefine((values, issues) => {\n for (const entryId of Object.keys(values.lines)) {\n if (!roster.has(entryId)) {\n issues.addIssue({\n code: 'custom',\n path: ['lines', entryId],\n message: NOT_ON_GAME_ROSTER_MESSAGE,\n });\n }\n }\n const state = ownStats(context.state);\n for (const teamId of context.game.teamIds) {\n let score = (state.teamStats[teamId] ?? emptyBasketballTeamLine()).score;\n for (const [entryId, entered] of Object.entries(values.lines)) {\n if (parseGameRosterPlayerId(entryId)?.teamId !== teamId) continue;\n score += entered.points - currentPlayerCounters(context, entryId).points;\n }\n if (score < 0)\n issues.addIssue({\n code: 'custom',\n path: ['lines'],\n message: BASKETBALL_TEAM_SCORE_FLOOR_MESSAGE,\n });\n }\n if (bulkDrafts(values, context).length > MAX_STAT_EVENTS_PER_WRITE) {\n issues.addIssue({\n code: 'custom',\n path: ['lines'],\n message: BULK_ENTRY_TOO_LARGE_MESSAGE,\n });\n }\n });\n },\n toEvents: bulkDrafts,\n};\n"]}
@@ -1,7 +1,8 @@
1
1
  import { z } from 'zod';
2
2
  import type { GameStats } from '../doc-schemas/sport-game.js';
3
3
  import type { BulkEntryKind } from '../types/stats.js';
4
- import type { SportStatEventBody, StatVoidBody } from './scoring.js';
4
+ import type { StatEventSubmission } from '../schemas/scoring.js';
5
+ import type { STAT_CORRECTION_EVENT_TYPE } from '../types/stats.js';
5
6
  import type { GameTeamIds } from './sport-adapter.js';
6
7
  export interface LiveRoundEvent {
7
8
  readonly eventId: string;
@@ -14,7 +15,10 @@ export interface BulkEntryContext {
14
15
  readonly absentRosterPlayerIds: readonly string[];
15
16
  readonly liveRoundEvents: readonly LiveRoundEvent[];
16
17
  }
17
- export type StatEventDraft = SportStatEventBody | StatVoidBody;
18
+ type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;
19
+ export type StatEventDraft = DistributiveOmit<Exclude<StatEventSubmission, {
20
+ type: typeof STAT_CORRECTION_EVENT_TYPE;
21
+ }>, 'eventId' | 'recordedAt'>;
18
22
  export interface BulkEntryForm<K extends BulkEntryKind, Values> {
19
23
  readonly kind: K;
20
24
  readonly formSchema: (context: BulkEntryContext) => z.ZodType<Values, Values>;
@@ -37,4 +41,5 @@ export type BulkEntry = PlayerLinesBulkEntry | TeamScoresBulkEntry | RoundsBulkE
37
41
  export declare const BULK_ENTRY_TOO_LARGE_MESSAGE = "This entry changes more stats than one save can hold. Save part of it, then enter the rest.";
38
42
  export declare const BULK_ENTRY_COUNT_MESSAGE = "Enter a whole number, zero or more.";
39
43
  export declare const bulkEntryCountField: z.ZodCustom<number, number>;
44
+ export {};
40
45
  //# sourceMappingURL=bulk-entry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bulk-entry.d.ts","sourceRoot":"","sources":["../../src/sports/bulk-entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,QAAQ,CAAC,qBAAqB,EAAE,SAAS,MAAM,EAAE,CAAC;IAClD,QAAQ,CAAC,eAAe,EAAE,SAAS,cAAc,EAAE,CAAC;CACrD;AAED,MAAM,MAAM,cAAc,GAAG,kBAAkB,GAAG,YAAY,CAAC;AAE/D,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,aAAa,EAAE,MAAM;IAC5D,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACjB,QAAQ,CAAC,UAAU,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9E,QAAQ,CAAC,YAAY,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,MAAM,CAAC;IAC7D,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,KAAK,SAAS,cAAc,EAAE,CAAC;CAC7F;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;CAC5E;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACnD;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC;CACrE;AAED,MAAM,MAAM,oBAAoB,GAAG,aAAa,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAC;AACnF,MAAM,MAAM,mBAAmB,GAAG,aAAa,CAAC,YAAY,EAAE,gBAAgB,CAAC,CAAC;AAChF,MAAM,MAAM,eAAe,GAAG,aAAa,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;AAEpE,MAAM,MAAM,SAAS,GAAG,oBAAoB,GAAG,mBAAmB,GAAG,eAAe,CAAC;AAErF,eAAO,MAAM,4BAA4B,gGACsD,CAAC;AAEhG,eAAO,MAAM,wBAAwB,wCAAwC,CAAC;AAE9E,eAAO,MAAM,mBAAmB,6BAG/B,CAAC"}
1
+ {"version":3,"file":"bulk-entry.d.ts","sourceRoot":"","sources":["../../src/sports/bulk-entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,QAAQ,CAAC,qBAAqB,EAAE,SAAS,MAAM,EAAE,CAAC;IAClD,QAAQ,CAAC,eAAe,EAAE,SAAS,cAAc,EAAE,CAAC;CACrD;AAED,KAAK,gBAAgB,CAAC,CAAC,EAAE,CAAC,SAAS,WAAW,IAAI,CAAC,SAAS,OAAO,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC;AAEzF,MAAM,MAAM,cAAc,GAAG,gBAAgB,CAC3C,OAAO,CAAC,mBAAmB,EAAE;IAAE,IAAI,EAAE,OAAO,0BAA0B,CAAA;CAAE,CAAC,EACzE,SAAS,GAAG,YAAY,CACzB,CAAC;AAEF,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,aAAa,EAAE,MAAM;IAC5D,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACjB,QAAQ,CAAC,UAAU,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9E,QAAQ,CAAC,YAAY,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,MAAM,CAAC;IAC7D,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,KAAK,SAAS,cAAc,EAAE,CAAC;CAC7F;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;CAC5E;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACnD;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC;CACrE;AAED,MAAM,MAAM,oBAAoB,GAAG,aAAa,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAC;AACnF,MAAM,MAAM,mBAAmB,GAAG,aAAa,CAAC,YAAY,EAAE,gBAAgB,CAAC,CAAC;AAChF,MAAM,MAAM,eAAe,GAAG,aAAa,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;AAEpE,MAAM,MAAM,SAAS,GAAG,oBAAoB,GAAG,mBAAmB,GAAG,eAAe,CAAC;AAErF,eAAO,MAAM,4BAA4B,gGACsD,CAAC;AAEhG,eAAO,MAAM,wBAAwB,wCAAwC,CAAC;AAE9E,eAAO,MAAM,mBAAmB,6BAG/B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"bulk-entry.js","sourceRoot":"","sources":["../../src/sports/bulk-entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AA8CjE,MAAM,CAAC,MAAM,4BAA4B,GACvC,6FAA6F,CAAC;AAEhG,MAAM,CAAC,MAAM,wBAAwB,GAAG,qCAAqC,CAAC;AAE9E,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CACzC,CAAC,KAAK,EAAE,EAAE,CAAC,sBAAsB,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,EAC1D,wBAAwB,CACzB,CAAC","sourcesContent":["import { z } from 'zod';\nimport { enteredStatCountSchema } from '../doc-schemas/atoms.js';\nimport type { GameStats } from '../doc-schemas/sport-game.js';\nimport type { BulkEntryKind } from '../types/stats.js';\nimport type { SportStatEventBody, StatVoidBody } from './scoring.js';\nimport type { GameTeamIds } from './sport-adapter.js';\n\nexport interface LiveRoundEvent {\n readonly eventId: string;\n readonly roundNumber: number;\n}\n\nexport interface BulkEntryContext {\n readonly game: GameTeamIds;\n readonly state: GameStats;\n readonly rosterPlayerIds: readonly string[];\n readonly absentRosterPlayerIds: readonly string[];\n readonly liveRoundEvents: readonly LiveRoundEvent[];\n}\n\nexport type StatEventDraft = SportStatEventBody | StatVoidBody;\n\nexport interface BulkEntryForm<K extends BulkEntryKind, Values> {\n readonly kind: K;\n readonly formSchema: (context: BulkEntryContext) => z.ZodType<Values, Values>;\n readonly formDefaults: (context: BulkEntryContext) => Values;\n readonly toEvents: (values: Values, context: BulkEntryContext) => readonly StatEventDraft[];\n}\n\nexport interface PlayerLinesValues {\n readonly lines: Readonly<Record<string, Readonly<Record<string, number>>>>;\n}\n\nexport interface TeamScoresValues {\n readonly scores: Readonly<Record<string, number>>;\n}\n\nexport interface RoundsValues {\n readonly rounds: readonly Readonly<Record<string, number | null>>[];\n}\n\nexport type PlayerLinesBulkEntry = BulkEntryForm<'playerLines', PlayerLinesValues>;\nexport type TeamScoresBulkEntry = BulkEntryForm<'teamScores', TeamScoresValues>;\nexport type RoundsBulkEntry = BulkEntryForm<'rounds', RoundsValues>;\n\nexport type BulkEntry = PlayerLinesBulkEntry | TeamScoresBulkEntry | RoundsBulkEntry;\n\nexport const BULK_ENTRY_TOO_LARGE_MESSAGE =\n 'This entry changes more stats than one save can hold. Save part of it, then enter the rest.';\n\nexport const BULK_ENTRY_COUNT_MESSAGE = 'Enter a whole number, zero or more.';\n\nexport const bulkEntryCountField = z.custom<number>(\n (value) => enteredStatCountSchema.safeParse(value).success,\n BULK_ENTRY_COUNT_MESSAGE,\n);\n"]}
1
+ {"version":3,"file":"bulk-entry.js","sourceRoot":"","sources":["../../src/sports/bulk-entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAoDjE,MAAM,CAAC,MAAM,4BAA4B,GACvC,6FAA6F,CAAC;AAEhG,MAAM,CAAC,MAAM,wBAAwB,GAAG,qCAAqC,CAAC;AAE9E,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CACzC,CAAC,KAAK,EAAE,EAAE,CAAC,sBAAsB,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,EAC1D,wBAAwB,CACzB,CAAC","sourcesContent":["import { z } from 'zod';\nimport { enteredStatCountSchema } from '../doc-schemas/atoms.js';\nimport type { GameStats } from '../doc-schemas/sport-game.js';\nimport type { BulkEntryKind } from '../types/stats.js';\nimport type { StatEventSubmission } from '../schemas/scoring.js';\nimport type { STAT_CORRECTION_EVENT_TYPE } from '../types/stats.js';\nimport type { GameTeamIds } from './sport-adapter.js';\n\nexport interface LiveRoundEvent {\n readonly eventId: string;\n readonly roundNumber: number;\n}\n\nexport interface BulkEntryContext {\n readonly game: GameTeamIds;\n readonly state: GameStats;\n readonly rosterPlayerIds: readonly string[];\n readonly absentRosterPlayerIds: readonly string[];\n readonly liveRoundEvents: readonly LiveRoundEvent[];\n}\n\ntype DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;\n\nexport type StatEventDraft = DistributiveOmit<\n Exclude<StatEventSubmission, { type: typeof STAT_CORRECTION_EVENT_TYPE }>,\n 'eventId' | 'recordedAt'\n>;\n\nexport interface BulkEntryForm<K extends BulkEntryKind, Values> {\n readonly kind: K;\n readonly formSchema: (context: BulkEntryContext) => z.ZodType<Values, Values>;\n readonly formDefaults: (context: BulkEntryContext) => Values;\n readonly toEvents: (values: Values, context: BulkEntryContext) => readonly StatEventDraft[];\n}\n\nexport interface PlayerLinesValues {\n readonly lines: Readonly<Record<string, Readonly<Record<string, number>>>>;\n}\n\nexport interface TeamScoresValues {\n readonly scores: Readonly<Record<string, number>>;\n}\n\nexport interface RoundsValues {\n readonly rounds: readonly Readonly<Record<string, number | null>>[];\n}\n\nexport type PlayerLinesBulkEntry = BulkEntryForm<'playerLines', PlayerLinesValues>;\nexport type TeamScoresBulkEntry = BulkEntryForm<'teamScores', TeamScoresValues>;\nexport type RoundsBulkEntry = BulkEntryForm<'rounds', RoundsValues>;\n\nexport type BulkEntry = PlayerLinesBulkEntry | TeamScoresBulkEntry | RoundsBulkEntry;\n\nexport const BULK_ENTRY_TOO_LARGE_MESSAGE =\n 'This entry changes more stats than one save can hold. Save part of it, then enter the rest.';\n\nexport const BULK_ENTRY_COUNT_MESSAGE = 'Enter a whole number, zero or more.';\n\nexport const bulkEntryCountField = z.custom<number>(\n (value) => enteredStatCountSchema.safeParse(value).success,\n BULK_ENTRY_COUNT_MESSAGE,\n);\n"]}
@@ -1 +1 @@
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
+ {"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;AAoED,eAAO,MAAM,gBAAgB,EAAE,YAqB9B,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;AAaF,eAAO,MAAM,mBAAmB,EAAE,mBA8BjC,CAAC"}
@@ -52,8 +52,6 @@ const validate = (stateIn, event, roster) => {
52
52
  if (!Number.isInteger(event.value) || event.value === 0)
53
53
  return refuseStatEvent('bulkValueInvalid');
54
54
  const score = teamScoreOf(ownStats(stateIn), event.teamId);
55
- if (event.previousValue !== score)
56
- return refuseStatEvent('bulkStale');
57
55
  if (score + event.value < 0)
58
56
  return refuseStatEvent('bulkValueInvalid');
59
57
  }
@@ -65,8 +63,19 @@ const FOOTBALL_CONTRIBUTION = {
65
63
  zeroLineIsNonScoring: false,
66
64
  deriveLine: (_kind, line) => line,
67
65
  };
66
+ const resolveBulkEntry = (state, submission) => {
67
+ if (submission.type !== FOOTBALL_BULK_EDIT_EVENT_TYPE)
68
+ return null;
69
+ const { target, ...event } = submission;
70
+ const previousValue = teamScoreOf(ownStats(state), event.teamId);
71
+ const value = target - previousValue;
72
+ return value === 0
73
+ ? { kind: 'unchanged' }
74
+ : { kind: 'change', event: { ...event, value, previousValue } };
75
+ };
68
76
  export const FOOTBALL_SCORING = {
69
77
  validate,
78
+ resolveBulkEntry,
70
79
  apply: (state, event) => {
71
80
  if (isStatCorrection(event))
72
81
  return addToScore(state, event.teamId, correctionAmount(event));
@@ -121,15 +130,9 @@ function scoreDrafts(values, context) {
121
130
  const drafts = [];
122
131
  for (const teamId of context.game.teamIds) {
123
132
  const entered = values.scores[teamId];
124
- const previousValue = teamScoreOf(state, teamId);
125
- if (entered === undefined || entered === previousValue)
133
+ if (entered === undefined || entered === teamScoreOf(state, teamId))
126
134
  continue;
127
- drafts.push({
128
- type: FOOTBALL_BULK_EDIT_EVENT_TYPE,
129
- teamId,
130
- value: entered - previousValue,
131
- previousValue,
132
- });
135
+ drafts.push({ type: FOOTBALL_BULK_EDIT_EVENT_TYPE, teamId, target: entered });
133
136
  }
134
137
  return drafts;
135
138
  }
@@ -1 +1 @@
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
+ {"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 (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\nconst resolveBulkEntry: NonNullable<SportScoring['resolveBulkEntry']> = (state, submission) => {\n if (submission.type !== FOOTBALL_BULK_EDIT_EVENT_TYPE) return null;\n const { target, ...event } = submission;\n const previousValue = teamScoreOf(ownStats(state), event.teamId);\n const value = target - previousValue;\n return value === 0\n ? { kind: 'unchanged' }\n : { kind: 'change', event: { ...event, value, previousValue } };\n};\n\nexport const FOOTBALL_SCORING: SportScoring = {\n validate,\n resolveBulkEntry,\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 if (entered === undefined || entered === teamScoreOf(state, teamId)) continue;\n drafts.push({ type: FOOTBALL_BULK_EDIT_EVENT_TYPE, teamId, target: entered });\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":"replay.d.ts","sourceRoot":"","sources":["../../src/sports/replay.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAkC,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAClF,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEvE,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEb,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;CAQ5C;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,SAAS,EAChB,IAAI,EAAE,WAAW,GAChB,SAAS,CAOX;AAED,wBAAgB,aAAa,CAC3B,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,SAAS,EAChB,KAAK,EAAE,aAAa,EACpB,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,aAAa,GAAG,SAAS,GACvD,SAAS,CAWX;AAED,wBAAgB,eAAe,CAC7B,OAAO,EAAE,eAAe,EACxB,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,SAAS,aAAa,EAAE,GAC/B,SAAS,CAYX"}
1
+ {"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../../src/sports/replay.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAkC,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAClF,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEvE,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEb,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;CAQ5C;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,SAAS,EAChB,IAAI,EAAE,WAAW,GAChB,SAAS,CAOX;AA0BD,wBAAgB,aAAa,CAC3B,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,SAAS,EAChB,KAAK,EAAE,aAAa,EACpB,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,aAAa,GAAG,SAAS,GACvD,SAAS,CAWX;AAED,wBAAgB,eAAe,CAC7B,OAAO,EAAE,eAAe,EACxB,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,SAAS,aAAa,EAAE,GAC/B,SAAS,CAYX"}
@@ -16,13 +16,28 @@ export function withGameTeamLines(adapter, state, game) {
16
16
  playerStats: state.playerStats,
17
17
  };
18
18
  }
19
+ const floorLine = (line) => Object.fromEntries(Object.entries(line).map(([field, value]) => [
20
+ field,
21
+ typeof value === 'number'
22
+ ? Math.max(0, value)
23
+ : Array.isArray(value)
24
+ ? value.map((slot) => (typeof slot === 'number' ? Math.max(0, slot) : slot))
25
+ : value,
26
+ ]));
27
+ const floorLines = (lines) => Object.fromEntries(Object.entries(lines).map(([key, line]) => [key, floorLine(line)]));
28
+ function floorAtZero(state) {
29
+ return {
30
+ teamStats: floorLines(state.teamStats),
31
+ playerStats: floorLines(state.playerStats),
32
+ };
33
+ }
19
34
  export function foldStatEvent(adapter, state, event, targetOf) {
20
35
  if (isStatVoid(event)) {
21
36
  const target = targetOf(event.targetEventId);
22
37
  if (!isSportStatEventIn(adapter.eventTypes, target)) {
23
38
  throw new Error(`the void's target ${event.targetEventId} is not a ${adapter.leagueType} event folded before it`);
24
39
  }
25
- return adapter.undo(state, target);
40
+ return floorAtZero(adapter.undo(state, target));
26
41
  }
27
42
  return adapter.apply(state, event);
28
43
  }
@@ -1 +1 @@
1
- {"version":3,"file":"replay.js","sourceRoot":"","sources":["../../src/sports/replay.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAsB,MAAM,cAAc,CAAC;AAGlF,MAAM,OAAO,eAAgB,SAAQ,KAAK;IAC/B,OAAO,CAAS;IAEzB,YAAY,OAAe,EAAE,KAAc;QACzC,KAAK,CACH,cAAc,OAAO,wBAAwB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EACrG,EAAE,KAAK,EAAE,CACV,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF;AAED,MAAM,UAAU,iBAAiB,CAC/B,OAAwB,EACxB,KAAgB,EAChB,IAAiB;IAEjB,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,SAAS,CAAC;QAAE,OAAO,KAAK,CAAC;IACxF,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACvC,OAAO;QACL,SAAS,EAAE,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE;QACrD,WAAW,EAAE,KAAK,CAAC,WAAW;KAClB,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,OAAwB,EACxB,KAAgB,EAChB,KAAoB,EACpB,QAAwD;IAExD,IAAI,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QAC7C,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,EAAE,CAAC;YACpD,MAAM,IAAI,KAAK,CACb,qBAAqB,KAAK,CAAC,aAAa,aAAa,OAAO,CAAC,UAAU,yBAAyB,CACjG,CAAC;QACJ,CAAC;QACD,OAAO,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,OAAwB,EACxB,IAAiB,EACjB,MAAgC;IAEhC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAyB,CAAC;IAChD,IAAI,KAAK,GAAc,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAChD,KAAK,MAAM,KAAK,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;QACtF,IAAI,CAAC;YACH,KAAK,GAAG,aAAa,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;QACjF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,eAAe,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAClD,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["import type { GameStatEvent } from '../doc-schemas/game-stats.js';\nimport type { GameStats } from '../doc-schemas/sport-game.js';\nimport { isSportStatEventIn, isStatVoid, type StatEventBody } from './scoring.js';\nimport type { AnySportAdapter, GameTeamIds } from './sport-adapter.js';\n\nexport class StatReplayError extends Error {\n readonly eventId: string;\n\n constructor(eventId: string, cause: unknown) {\n super(\n `Stat event ${eventId} cannot be replayed: ${cause instanceof Error ? cause.message : String(cause)}`,\n { cause },\n );\n this.name = 'StatReplayError';\n this.eventId = eventId;\n }\n}\n\nexport function withGameTeamLines(\n adapter: AnySportAdapter,\n state: GameStats,\n game: GameTeamIds,\n): GameStats {\n if (game.teamIds.every((teamId) => state.teamStats[teamId] !== undefined)) return state;\n const empty = adapter.emptyStats(game);\n return {\n teamStats: { ...empty.teamStats, ...state.teamStats },\n playerStats: state.playerStats,\n } as GameStats;\n}\n\nexport function foldStatEvent(\n adapter: AnySportAdapter,\n state: GameStats,\n event: StatEventBody,\n targetOf: (eventId: string) => StatEventBody | undefined,\n): GameStats {\n if (isStatVoid(event)) {\n const target = targetOf(event.targetEventId);\n if (!isSportStatEventIn(adapter.eventTypes, target)) {\n throw new Error(\n `the void's target ${event.targetEventId} is not a ${adapter.leagueType} event folded before it`,\n );\n }\n return adapter.undo(state, target);\n }\n return adapter.apply(state, event);\n}\n\nexport function replayGameStats(\n adapter: AnySportAdapter,\n game: GameTeamIds,\n events: readonly GameStatEvent[],\n): GameStats {\n const folded = new Map<string, GameStatEvent>();\n let state: GameStats = adapter.emptyStats(game);\n for (const event of [...events].sort((left, right) => left.sequence - right.sequence)) {\n try {\n state = foldStatEvent(adapter, state, event, (eventId) => folded.get(eventId));\n } catch (error) {\n throw new StatReplayError(event.eventId, error);\n }\n folded.set(event.eventId, event);\n }\n return state;\n}\n"]}
1
+ {"version":3,"file":"replay.js","sourceRoot":"","sources":["../../src/sports/replay.ts"],"names":[],"mappings":"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","sourcesContent":["import type { GameStatEvent } from '../doc-schemas/game-stats.js';\nimport type { GameStats } from '../doc-schemas/sport-game.js';\nimport { isSportStatEventIn, isStatVoid, type StatEventBody } from './scoring.js';\nimport type { AnySportAdapter, GameTeamIds } from './sport-adapter.js';\n\nexport class StatReplayError extends Error {\n readonly eventId: string;\n\n constructor(eventId: string, cause: unknown) {\n super(\n `Stat event ${eventId} cannot be replayed: ${cause instanceof Error ? cause.message : String(cause)}`,\n { cause },\n );\n this.name = 'StatReplayError';\n this.eventId = eventId;\n }\n}\n\nexport function withGameTeamLines(\n adapter: AnySportAdapter,\n state: GameStats,\n game: GameTeamIds,\n): GameStats {\n if (game.teamIds.every((teamId) => state.teamStats[teamId] !== undefined)) return state;\n const empty = adapter.emptyStats(game);\n return {\n teamStats: { ...empty.teamStats, ...state.teamStats },\n playerStats: state.playerStats,\n } as GameStats;\n}\n\nconst floorLine = (line: object): Record<string, unknown> =>\n Object.fromEntries(\n Object.entries(line).map(([field, value]: [string, unknown]) => [\n field,\n typeof value === 'number'\n ? Math.max(0, value)\n : Array.isArray(value)\n ? value.map((slot: unknown) => (typeof slot === 'number' ? Math.max(0, slot) : slot))\n : value,\n ]),\n );\n\nconst floorLines = (lines: object): Record<string, Record<string, unknown>> =>\n Object.fromEntries(\n Object.entries(lines).map(([key, line]: [string, object]) => [key, floorLine(line)]),\n );\n\nfunction floorAtZero(state: GameStats): GameStats {\n return {\n teamStats: floorLines(state.teamStats),\n playerStats: floorLines(state.playerStats),\n } as GameStats;\n}\n\nexport function foldStatEvent(\n adapter: AnySportAdapter,\n state: GameStats,\n event: StatEventBody,\n targetOf: (eventId: string) => StatEventBody | undefined,\n): GameStats {\n if (isStatVoid(event)) {\n const target = targetOf(event.targetEventId);\n if (!isSportStatEventIn(adapter.eventTypes, target)) {\n throw new Error(\n `the void's target ${event.targetEventId} is not a ${adapter.leagueType} event folded before it`,\n );\n }\n return floorAtZero(adapter.undo(state, target));\n }\n return adapter.apply(state, event);\n}\n\nexport function replayGameStats(\n adapter: AnySportAdapter,\n game: GameTeamIds,\n events: readonly GameStatEvent[],\n): GameStats {\n const folded = new Map<string, GameStatEvent>();\n let state: GameStats = adapter.emptyStats(game);\n for (const event of [...events].sort((left, right) => left.sequence - right.sequence)) {\n try {\n state = foldStatEvent(adapter, state, event, (eventId) => folded.get(eventId));\n } catch (error) {\n throw new StatReplayError(event.eventId, error);\n }\n folded.set(event.eventId, event);\n }\n return state;\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  import type { GameStatEvent } from '../doc-schemas/game-stats.js';
2
2
  import type { GameStats } from '../doc-schemas/sport-game.js';
3
+ import type { StatEventSubmission } from '../schemas/scoring.js';
3
4
  import type { StatCounterKey } from '../types/sports.js';
4
5
  import { STAT_CORRECTION_EVENT_TYPE, STAT_VOID_EVENT_TYPE } from '../types/stats.js';
5
6
  import type { FinalizationNotReadyReason, StatEventRecorderField, StatEventRefusalCode, StatEventSnapshotField, StatLineKind } from '../types/stats.js';
@@ -14,8 +15,16 @@ export type StatCorrectionBody = Extract<StatEventBody, {
14
15
  type: typeof STAT_CORRECTION_EVENT_TYPE;
15
16
  }>;
16
17
  export type SportStatEventBody = Exclude<StatEventBody, StatVoidBody | StatCorrectionBody>;
17
- export declare function isStatVoid(event: StatEventBody): event is StatVoidBody;
18
- export declare function isStatCorrection(event: StatEventBody): event is StatCorrectionBody;
18
+ export declare function isStatVoid<E extends {
19
+ readonly type: string;
20
+ }>(event: E): event is Extract<E, {
21
+ type: typeof STAT_VOID_EVENT_TYPE;
22
+ }>;
23
+ export declare function isStatCorrection<E extends {
24
+ readonly type: string;
25
+ }>(event: E): event is Extract<E, {
26
+ type: typeof STAT_CORRECTION_EVENT_TYPE;
27
+ }>;
19
28
  export declare function isSportStatEventIn(eventTypes: readonly string[], event: StatEventBody | undefined): event is SportStatEventBody;
20
29
  export type GameTeamLine = GameStats['teamStats'][string];
21
30
  export type GamePlayerLine = GameStats['playerStats'][string];
@@ -26,6 +35,12 @@ export interface StatEventRoster {
26
35
  readonly teamIds: readonly string[];
27
36
  readonly rosterPlayerIds: ReadonlySet<string>;
28
37
  }
38
+ export type BulkEntryResolution = {
39
+ readonly kind: 'change';
40
+ readonly event: StatEventBody;
41
+ } | {
42
+ readonly kind: 'unchanged';
43
+ };
29
44
  export type StatEventVerdict = {
30
45
  readonly ok: true;
31
46
  } | {
@@ -48,6 +63,7 @@ export interface CorrectableFields {
48
63
  export interface SportScoring {
49
64
  readonly validate: (state: GameStats, event: StatEventBody, roster: StatEventRoster) => StatEventVerdict;
50
65
  readonly apply: (state: GameStats, event: StatEventBody) => GameStats;
66
+ readonly resolveBulkEntry?: (state: GameStats, submission: StatEventSubmission) => BulkEntryResolution | null;
51
67
  readonly undo: (state: GameStats, target: StatEventBody) => GameStats;
52
68
  readonly deriveLine: (kind: StatLineKind, line: StatLineValues) => StatLineValues;
53
69
  readonly correctableFields: CorrectableFields;
@@ -1 +1 @@
1
- {"version":3,"file":"scoring.d.ts","sourceRoot":"","sources":["../../src/sports/scoring.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scoring.d.ts","sourceRoot":"","sources":["../../src/sports/scoring.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"scoring.js","sourceRoot":"","sources":["../../src/sports/scoring.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAwBrF,MAAM,UAAU,UAAU,CAAC,KAAoB;IAC7C,OAAO,KAAK,CAAC,IAAI,KAAK,oBAAoB,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAoB;IACnD,OAAO,KAAK,CAAC,IAAI,KAAK,0BAA0B,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,UAA6B,EAC7B,KAAgC;IAEhC,OAAO,KAAK,KAAK,SAAS,IAAI,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAChE,CAAC;AAeD,MAAM,CAAC,MAAM,mBAAmB,GAAqB,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AAElE,MAAM,UAAU,eAAe,CAAC,IAA0B;IACxD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AAC7B,CAAC;AAKD,MAAM,CAAC,MAAM,kBAAkB,GAA0B,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AA2BzE,MAAM,UAAU,aAAa,CAAC,IAAoB,EAAE,WAA8B;IAChF,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC,CAAC;AAChF,CAAC","sourcesContent":["import type { GameStatEvent } from '../doc-schemas/game-stats.js';\nimport type { GameStats } from '../doc-schemas/sport-game.js';\nimport type { StatCounterKey } from '../types/sports.js';\nimport { STAT_CORRECTION_EVENT_TYPE, STAT_VOID_EVENT_TYPE } from '../types/stats.js';\nimport type {\n FinalizationNotReadyReason,\n StatEventRecorderField,\n StatEventRefusalCode,\n StatEventSnapshotField,\n StatLineKind,\n} from '../types/stats.js';\nimport type { FinalizedGame, GameContribution } from './contribution.js';\nimport type { GameTeamIds } from './sport-adapter.js';\n\ntype DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;\n\nexport type StatEventBody = DistributiveOmit<\n GameStatEvent,\n 'eventId' | 'recordedAt' | StatEventRecorderField | StatEventSnapshotField\n>;\nexport type StatVoidBody = Extract<StatEventBody, { type: typeof STAT_VOID_EVENT_TYPE }>;\nexport type StatCorrectionBody = Extract<\n StatEventBody,\n { type: typeof STAT_CORRECTION_EVENT_TYPE }\n>;\nexport type SportStatEventBody = Exclude<StatEventBody, StatVoidBody | StatCorrectionBody>;\n\nexport function isStatVoid(event: StatEventBody): event is StatVoidBody {\n return event.type === STAT_VOID_EVENT_TYPE;\n}\n\nexport function isStatCorrection(event: StatEventBody): event is StatCorrectionBody {\n return event.type === STAT_CORRECTION_EVENT_TYPE;\n}\n\nexport function isSportStatEventIn(\n eventTypes: readonly string[],\n event: StatEventBody | undefined,\n): event is SportStatEventBody {\n return event !== undefined && eventTypes.includes(event.type);\n}\n\nexport type GameTeamLine = GameStats['teamStats'][string];\nexport type GamePlayerLine = GameStats['playerStats'][string];\n\nexport type StatLineValues = { readonly [field: string]: number | readonly (number | null)[] };\n\nexport interface StatEventRoster {\n readonly teamIds: readonly string[];\n readonly rosterPlayerIds: ReadonlySet<string>;\n}\n\nexport type StatEventVerdict =\n { readonly ok: true } | { readonly ok: false; readonly code: StatEventRefusalCode };\n\nexport const STAT_EVENT_ACCEPTED: StatEventVerdict = { ok: true };\n\nexport function refuseStatEvent(code: StatEventRefusalCode): StatEventVerdict {\n return { ok: false, code };\n}\n\nexport type FinalizationReadiness =\n { readonly ready: true } | { readonly ready: false; readonly reason: FinalizationNotReadyReason };\n\nexport const FINALIZATION_READY: FinalizationReadiness = { ready: true };\n\nexport interface CorrectableFields {\n readonly team: readonly StatCounterKey[];\n readonly player: readonly StatCounterKey[];\n}\n\nexport interface SportScoring {\n readonly validate: (\n state: GameStats,\n event: StatEventBody,\n roster: StatEventRoster,\n ) => StatEventVerdict;\n readonly apply: (state: GameStats, event: StatEventBody) => GameStats;\n readonly undo: (state: GameStats, target: StatEventBody) => GameStats;\n readonly deriveLine: (kind: StatLineKind, line: StatLineValues) => StatLineValues;\n readonly correctableFields: CorrectableFields;\n readonly nextRound: (state: GameStats) => number | null;\n readonly finalizationReadiness: (state: GameStats, game: GameTeamIds) => FinalizationReadiness;\n readonly participation: (\n state: GameStats,\n rosterPlayerIds: readonly string[],\n absentRosterPlayerIds: readonly string[],\n ) => string[];\n readonly contribution: (game: FinalizedGame) => GameContribution;\n}\n\nexport function isAllZeroLine(line: StatLineValues, counterKeys: readonly string[]): boolean {\n return counterKeys.every((key) => line[key] === 0 || line[key] === undefined);\n}\n"]}
1
+ {"version":3,"file":"scoring.js","sourceRoot":"","sources":["../../src/sports/scoring.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAwBrF,MAAM,UAAU,UAAU,CACxB,KAAQ;IAER,OAAO,KAAK,CAAC,IAAI,KAAK,oBAAoB,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,KAAQ;IAER,OAAO,KAAK,CAAC,IAAI,KAAK,0BAA0B,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,UAA6B,EAC7B,KAAgC;IAEhC,OAAO,KAAK,KAAK,SAAS,IAAI,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAChE,CAAC;AAkBD,MAAM,CAAC,MAAM,mBAAmB,GAAqB,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AAElE,MAAM,UAAU,eAAe,CAAC,IAA0B;IACxD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AAC7B,CAAC;AAKD,MAAM,CAAC,MAAM,kBAAkB,GAA0B,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AA+BzE,MAAM,UAAU,aAAa,CAAC,IAAoB,EAAE,WAA8B;IAChF,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC,CAAC;AAChF,CAAC","sourcesContent":["import type { GameStatEvent } from '../doc-schemas/game-stats.js';\nimport type { GameStats } from '../doc-schemas/sport-game.js';\nimport type { StatEventSubmission } from '../schemas/scoring.js';\nimport type { StatCounterKey } from '../types/sports.js';\nimport { STAT_CORRECTION_EVENT_TYPE, STAT_VOID_EVENT_TYPE } from '../types/stats.js';\nimport type {\n FinalizationNotReadyReason,\n StatEventRecorderField,\n StatEventRefusalCode,\n StatEventSnapshotField,\n StatLineKind,\n} from '../types/stats.js';\nimport type { FinalizedGame, GameContribution } from './contribution.js';\nimport type { GameTeamIds } from './sport-adapter.js';\n\ntype DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;\n\nexport type StatEventBody = DistributiveOmit<\n GameStatEvent,\n 'eventId' | 'recordedAt' | StatEventRecorderField | StatEventSnapshotField\n>;\nexport type StatVoidBody = Extract<StatEventBody, { type: typeof STAT_VOID_EVENT_TYPE }>;\nexport type StatCorrectionBody = Extract<\n StatEventBody,\n { type: typeof STAT_CORRECTION_EVENT_TYPE }\n>;\nexport type SportStatEventBody = Exclude<StatEventBody, StatVoidBody | StatCorrectionBody>;\n\nexport function isStatVoid<E extends { readonly type: string }>(\n event: E,\n): event is Extract<E, { type: typeof STAT_VOID_EVENT_TYPE }> {\n return event.type === STAT_VOID_EVENT_TYPE;\n}\n\nexport function isStatCorrection<E extends { readonly type: string }>(\n event: E,\n): event is Extract<E, { type: typeof STAT_CORRECTION_EVENT_TYPE }> {\n return event.type === STAT_CORRECTION_EVENT_TYPE;\n}\n\nexport function isSportStatEventIn(\n eventTypes: readonly string[],\n event: StatEventBody | undefined,\n): event is SportStatEventBody {\n return event !== undefined && eventTypes.includes(event.type);\n}\n\nexport type GameTeamLine = GameStats['teamStats'][string];\nexport type GamePlayerLine = GameStats['playerStats'][string];\n\nexport type StatLineValues = { readonly [field: string]: number | readonly (number | null)[] };\n\nexport interface StatEventRoster {\n readonly teamIds: readonly string[];\n readonly rosterPlayerIds: ReadonlySet<string>;\n}\n\nexport type BulkEntryResolution =\n { readonly kind: 'change'; readonly event: StatEventBody } | { readonly kind: 'unchanged' };\n\nexport type StatEventVerdict =\n { readonly ok: true } | { readonly ok: false; readonly code: StatEventRefusalCode };\n\nexport const STAT_EVENT_ACCEPTED: StatEventVerdict = { ok: true };\n\nexport function refuseStatEvent(code: StatEventRefusalCode): StatEventVerdict {\n return { ok: false, code };\n}\n\nexport type FinalizationReadiness =\n { readonly ready: true } | { readonly ready: false; readonly reason: FinalizationNotReadyReason };\n\nexport const FINALIZATION_READY: FinalizationReadiness = { ready: true };\n\nexport interface CorrectableFields {\n readonly team: readonly StatCounterKey[];\n readonly player: readonly StatCounterKey[];\n}\n\nexport interface SportScoring {\n readonly validate: (\n state: GameStats,\n event: StatEventBody,\n roster: StatEventRoster,\n ) => StatEventVerdict;\n readonly apply: (state: GameStats, event: StatEventBody) => GameStats;\n readonly resolveBulkEntry?: (\n state: GameStats,\n submission: StatEventSubmission,\n ) => BulkEntryResolution | null;\n readonly undo: (state: GameStats, target: StatEventBody) => GameStats;\n readonly deriveLine: (kind: StatLineKind, line: StatLineValues) => StatLineValues;\n readonly correctableFields: CorrectableFields;\n readonly nextRound: (state: GameStats) => number | null;\n readonly finalizationReadiness: (state: GameStats, game: GameTeamIds) => FinalizationReadiness;\n readonly participation: (\n state: GameStats,\n rosterPlayerIds: readonly string[],\n absentRosterPlayerIds: readonly string[],\n ) => string[];\n readonly contribution: (game: FinalizedGame) => GameContribution;\n}\n\nexport function isAllZeroLine(line: StatLineValues, counterKeys: readonly string[]): boolean {\n return counterKeys.every((key) => line[key] === 0 || line[key] === undefined);\n}\n"]}
@@ -2,7 +2,7 @@ import type { GameStatEvent } from '../doc-schemas/game-stats.js';
2
2
  import type { GameStats } from '../doc-schemas/sport-game.js';
3
3
  import type { StatEventSubmission } from '../schemas/scoring.js';
4
4
  import type { RecordedStatEventOutcome, StatEventRefusalCode } from '../types/stats.js';
5
- import { type StatEventRoster } from './scoring.js';
5
+ import { type StatEventBody, type StatEventRoster } from './scoring.js';
6
6
  import type { AnySportAdapter, GameTeamIds } from './sport-adapter.js';
7
7
  export declare function statEventSubmissionId(submission: StatEventSubmission): string;
8
8
  export declare function statEventIdsToRead(submissions: readonly StatEventSubmission[]): string[];
@@ -37,5 +37,6 @@ export interface StatEventBatch {
37
37
  readonly submissions: readonly StatEventSubmission[];
38
38
  readonly storedEvent: (eventId: string) => GameStatEvent | undefined;
39
39
  }
40
+ export declare function resolveStatSubmission(adapter: AnySportAdapter, state: GameStats, submission: StatEventSubmission): StatEventBody | null;
40
41
  export declare function decideStatEventBatch(batch: StatEventBatch): StatEventBatchDecision;
41
42
  //# sourceMappingURL=store-driver.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"store-driver.d.ts","sourceRoot":"","sources":["../../src/sports/store-driver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAE9D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAEjE,OAAO,KAAK,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAExF,OAAO,EAKL,KAAK,eAAe,EACrB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEvE,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,mBAAmB,GAAG,MAAM,CAE7E;AAED,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,SAAS,mBAAmB,EAAE,GAAG,MAAM,EAAE,CAQxF;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACpD;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,wBAAwB,CAAC;IAC3C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,MAAM,sBAAsB,GAC9B;IACE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,SAAS,uBAAuB,EAAE,CAAC;IACrD,QAAQ,CAAC,QAAQ,EAAE,SAAS,iBAAiB,EAAE,CAAC;CACjD,GACD;IACE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEN,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG;QAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;QAAC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/F,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,QAAQ,CAAC,WAAW,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACrD,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,aAAa,GAAG,SAAS,CAAC;CACtE;AA2DD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,cAAc,GAAG,sBAAsB,CA2DlF"}
1
+ {"version":3,"file":"store-driver.d.ts","sourceRoot":"","sources":["../../src/sports/store-driver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAE9D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAEjE,OAAO,KAAK,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAExF,OAAO,EAIL,KAAK,aAAa,EAClB,KAAK,eAAe,EACrB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEvE,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,mBAAmB,GAAG,MAAM,CAE7E;AAED,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,SAAS,mBAAmB,EAAE,GAAG,MAAM,EAAE,CAQxF;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACpD;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,wBAAwB,CAAC;IAC3C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,MAAM,sBAAsB,GAC9B;IACE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,SAAS,uBAAuB,EAAE,CAAC;IACrD,QAAQ,CAAC,QAAQ,EAAE,SAAS,iBAAiB,EAAE,CAAC;CACjD,GACD;IACE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEN,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG;QAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;QAAC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/F,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,QAAQ,CAAC,WAAW,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACrD,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,aAAa,GAAG,SAAS,CAAC;CACtE;AA6DD,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,SAAS,EAChB,UAAU,EAAE,mBAAmB,GAC9B,aAAa,GAAG,IAAI,CAItB;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,cAAc,GAAG,sBAAsB,CAiElF"}