tods-competition-factory 7.4.0 → 7.6.0

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 (251) hide show
  1. package/README.md +1 -1
  2. package/dist/esm/acquire/findDrawMatchUp.mjs +10 -1
  3. package/dist/esm/assemblies/engines/async/asyncEngineInvoke.mjs +4 -2
  4. package/dist/esm/assemblies/engines/async/asyncExecutionQueue.mjs +4 -2
  5. package/dist/esm/assemblies/engines/parts/checkMutationLock.mjs +2 -1
  6. package/dist/esm/assemblies/engines/parts/engineStart.mjs +16 -1
  7. package/dist/esm/assemblies/engines/parts/executeMethod.mjs +9 -11
  8. package/dist/esm/assemblies/engines/scoring/ScoringEngine.mjs +29 -24
  9. package/dist/esm/assemblies/engines/sync/engineInvoke.mjs +4 -2
  10. package/dist/esm/assemblies/engines/sync/executionQueue.mjs +4 -2
  11. package/dist/esm/assemblies/generators/drawDefinitions/drawTypes/adHoc/drawMatic/generateCandidate.mjs +3 -2
  12. package/dist/esm/assemblies/generators/drawDefinitions/drawTypes/adHoc/drawMatic/getPairings.mjs +2 -1
  13. package/dist/esm/assemblies/generators/drawDefinitions/generateSeedingScaleItems.mjs +2 -1
  14. package/dist/esm/assemblies/generators/drawDefinitions/getDrawFormat.mjs +1 -0
  15. package/dist/esm/assemblies/generators/drawDefinitions/validateAndDeriveDrawValues.mjs +3 -0
  16. package/dist/esm/assemblies/generators/matchUps/generateScoreString.mjs +2 -1
  17. package/dist/esm/assemblies/generators/matchUps/matchUpScore.mjs +4 -1
  18. package/dist/esm/assemblies/generators/mocks/applyScenarioProfile.mjs +2 -1
  19. package/dist/esm/assemblies/generators/mocks/generateOutcome.mjs +27 -1
  20. package/dist/esm/assemblies/generators/mocks/generateOutcomeFromScoreString.mjs +10 -5
  21. package/dist/esm/assemblies/generators/mocks/generateParticipants.mjs +1 -1
  22. package/dist/esm/assemblies/generators/mocks/generatePersons.mjs +1 -1
  23. package/dist/esm/assemblies/generators/mocks/generateTournamentRecord.mjs +2 -1
  24. package/dist/esm/assemblies/generators/mocks/processLeagueProfiles.mjs +2 -0
  25. package/dist/esm/assemblies/generators/scheduling/utils/generateVirtualCourts.mjs +1 -1
  26. package/dist/esm/assemblies/generators/tournamentRecords/anonymizeTournamentRecord.mjs +4 -3
  27. package/dist/esm/assemblies/generators/tournamentRecords/copyTournamentRecord.mjs +1 -1
  28. package/dist/esm/assemblies/generators/tournamentRecords/createTournamentRecord.mjs +1 -1
  29. package/dist/esm/assemblies/generators/tournamentRecords/generateStatCrew.mjs +1 -1
  30. package/dist/esm/assemblies/generators/venues/generateCourts.mjs +2 -2
  31. package/dist/esm/assemblies/governors/matchUpFormatGovernor/index.mjs +1 -0
  32. package/dist/esm/assemblies/governors/scoreGovernor/help.mjs +1 -0
  33. package/dist/esm/assemblies/governors/scoreGovernor/index.mjs +1 -0
  34. package/dist/esm/assemblies/tools/index.mjs +3 -0
  35. package/dist/esm/constants/errorConditionConstants.mjs +37 -1
  36. package/dist/esm/constants/index.mjs +1 -0
  37. package/dist/esm/constants/matchUpActionConstants.mjs +3 -1
  38. package/dist/esm/constants/matchUpStatusConstants.mjs +0 -1
  39. package/dist/esm/constants/outcomePipelineConstants.mjs +10 -0
  40. package/dist/esm/constants/penaltyConstants.mjs +17 -5
  41. package/dist/esm/constants/scheduleConstants.mjs +5 -1
  42. package/dist/esm/constants/scoreWarningConstants.mjs +8 -0
  43. package/dist/esm/errors/subclasses.mjs +16 -13
  44. package/dist/esm/fixtures/policies/POLICY_POSITION_ACTIONS_DEFAULT.mjs +1 -1
  45. package/dist/esm/fixtures/policies/POLICY_SCORING_DEFAULT.mjs +3 -3
  46. package/dist/esm/forge/unwrap.mjs +2 -1
  47. package/dist/esm/functions/declaration/transitionRecordStatus.mjs +2 -1
  48. package/dist/esm/global/state/globalState.mjs +26 -1
  49. package/dist/esm/helpers/keyValueScore/keyValueSetScore.mjs +10 -9
  50. package/dist/esm/helpers/keyValueScore/processIncompleteSetScore.mjs +14 -37
  51. package/dist/esm/helpers/keyValueScore/scoreAnalysis.mjs +2 -1
  52. package/dist/esm/helpers/matchUpFormatCode/aggregateDecider.mjs +17 -0
  53. package/dist/esm/helpers/matchUpFormatCode/parse.mjs +41 -1
  54. package/dist/esm/helpers/matchUpFormatCode/stringify.mjs +16 -2
  55. package/dist/esm/helpers/scoreRepair/repairScore.mjs +38 -0
  56. package/dist/esm/index.mjs +1 -0
  57. package/dist/esm/mutate/base/unifiedIds.mjs +3 -2
  58. package/dist/esm/mutate/drawDefinitions/addDrawDefinitionTimeItem.mjs +2 -1
  59. package/dist/esm/mutate/drawDefinitions/draft/resolveDraftPositions.mjs +4 -2
  60. package/dist/esm/mutate/drawDefinitions/luckyDrawAdvancement.mjs +5 -3
  61. package/dist/esm/mutate/drawDefinitions/matchUpGovernor/attemptToModifyScore.mjs +2 -2
  62. package/dist/esm/mutate/drawDefinitions/matchUpGovernor/noDownstreamDependencies.mjs +7 -4
  63. package/dist/esm/mutate/drawDefinitions/matchUpGovernor/removeDoubleExit.mjs +3 -1
  64. package/dist/esm/mutate/drawDefinitions/positionGovernor/byePositioning/getBlockSortedRandomDrawPositions.mjs +2 -1
  65. package/dist/esm/mutate/drawDefinitions/positionGovernor/doubleExitAdvancement.mjs +97 -13
  66. package/dist/esm/mutate/drawDefinitions/removeDrawPositionAssignment.mjs +4 -0
  67. package/dist/esm/mutate/entries/destroyPairEntry.mjs +6 -2
  68. package/dist/esm/mutate/events/setEventDates.mjs +2 -2
  69. package/dist/esm/mutate/extensions/addExtension.mjs +2 -1
  70. package/dist/esm/mutate/matchUps/drawPositions/assignDrawPositionBye.mjs +16 -9
  71. package/dist/esm/mutate/matchUps/drawPositions/carryEmptiedSeatsPastByes.mjs +64 -0
  72. package/dist/esm/mutate/matchUps/drawPositions/directLoser.mjs +31 -9
  73. package/dist/esm/mutate/matchUps/drawPositions/directParticipants.mjs +9 -4
  74. package/dist/esm/mutate/matchUps/drawPositions/drawPositionPlacement.mjs +33 -19
  75. package/dist/esm/mutate/matchUps/drawPositions/normalizeDrawPositions.mjs +6 -1
  76. package/dist/esm/mutate/matchUps/drawPositions/positionParticipantAction.mjs +12 -9
  77. package/dist/esm/mutate/matchUps/drawPositions/positionSeeds.mjs +2 -2
  78. package/dist/esm/mutate/matchUps/drawPositions/reconcileFedLoserEligibility.mjs +1 -0
  79. package/dist/esm/mutate/matchUps/drawPositions/relabelLoserExit.mjs +185 -0
  80. package/dist/esm/mutate/matchUps/drawPositions/releaseAdvancedDrawPosition.mjs +20 -11
  81. package/dist/esm/mutate/matchUps/drawPositions/removeDirectedParticipants.mjs +9 -4
  82. package/dist/esm/mutate/matchUps/drawPositions/removeOnwardLoserPlacements.mjs +19 -5
  83. package/dist/esm/mutate/matchUps/lineUps/ensureSideLineUps.mjs +8 -3
  84. package/dist/esm/mutate/matchUps/matchUpStatus/applyWithdrawnExits.mjs +23 -0
  85. package/dist/esm/mutate/matchUps/matchUpStatus/reconcileScoredTimes.mjs +26 -0
  86. package/dist/esm/mutate/matchUps/matchUpStatus/reconcileStaleExitOrigins.mjs +3 -4
  87. package/dist/esm/mutate/matchUps/matchUpStatus/setMatchUpState.mjs +71 -35
  88. package/dist/esm/mutate/matchUps/matchUpStatus/setMatchUpStatus.mjs +85 -8
  89. package/dist/esm/mutate/matchUps/matchUpStatus/settleRederivedDoubleExits.mjs +133 -0
  90. package/dist/esm/mutate/matchUps/matchUpStatus/sideExitProvenance.mjs +31 -8
  91. package/dist/esm/mutate/matchUps/matchUpStatus/sideStatusCodes.mjs +1 -1
  92. package/dist/esm/mutate/matchUps/outcome/decide.mjs +287 -0
  93. package/dist/esm/mutate/matchUps/outcome/differential.mjs +58 -0
  94. package/dist/esm/mutate/matchUps/outcome/direction.mjs +120 -0
  95. package/dist/esm/mutate/matchUps/outcome/refusals.mjs +192 -0
  96. package/dist/esm/mutate/matchUps/outcome/route.mjs +82 -0
  97. package/dist/esm/mutate/matchUps/outcome/view.mjs +343 -0
  98. package/dist/esm/mutate/matchUps/outcome/write.mjs +127 -0
  99. package/dist/esm/mutate/matchUps/schedule/addSchedulingProfileRound.mjs +2 -2
  100. package/dist/esm/mutate/matchUps/schedule/byeScheduling.mjs +11 -1
  101. package/dist/esm/mutate/matchUps/schedule/scheduleItems/addMatchUpScheduledDate.mjs +1 -1
  102. package/dist/esm/mutate/matchUps/schedule/scheduleItems/scheduleItems.mjs +2 -1
  103. package/dist/esm/mutate/matchUps/schedule/scheduleMatchUps/scheduleMatchUps.mjs +2 -2
  104. package/dist/esm/mutate/matchUps/schedule/scheduleProfileGrid.mjs +2 -2
  105. package/dist/esm/mutate/matchUps/schedule/scheduleProfileRounds.mjs +2 -2
  106. package/dist/esm/mutate/matchUps/schedule/scheduledTime.mjs +2 -2
  107. package/dist/esm/mutate/matchUps/schedule/schedulers/jinnScheduler/jinnScheduler.mjs +2 -1
  108. package/dist/esm/mutate/matchUps/schedule/schedulers/proScheduler/proConflicts.mjs +6 -1
  109. package/dist/esm/mutate/matchUps/schedule/schedulers/v2Scheduler/v2Scheduler.mjs +2 -1
  110. package/dist/esm/mutate/matchUps/schedule/setMatchUpCalledAt.mjs +2 -2
  111. package/dist/esm/mutate/matchUps/score/modifyMatchUpScore.mjs +3 -0
  112. package/dist/esm/mutate/matchUps/score/updateTieMatchUpScore.mjs +7 -2
  113. package/dist/esm/mutate/notifications/drawNotifications.mjs +3 -2
  114. package/dist/esm/mutate/officiating/addCertification.mjs +2 -1
  115. package/dist/esm/mutate/officiating/addCertificationRequirement.mjs +2 -1
  116. package/dist/esm/mutate/officiating/addConflictDeclaration.mjs +2 -1
  117. package/dist/esm/mutate/officiating/addEvaluation.mjs +2 -1
  118. package/dist/esm/mutate/officiating/addEvaluationPolicy.mjs +2 -1
  119. package/dist/esm/mutate/officiating/addSuspension.mjs +2 -1
  120. package/dist/esm/mutate/officiating/assignOfficial.mjs +2 -1
  121. package/dist/esm/mutate/officiating/createOfficialRecord.mjs +2 -1
  122. package/dist/esm/mutate/officiating/modifyCertification.mjs +2 -1
  123. package/dist/esm/mutate/officiating/modifyEvaluation.mjs +2 -1
  124. package/dist/esm/mutate/officiating/removeCertification.mjs +2 -1
  125. package/dist/esm/mutate/officiating/removeConflictDeclaration.mjs +2 -1
  126. package/dist/esm/mutate/officiating/removeEvaluation.mjs +2 -1
  127. package/dist/esm/mutate/officiating/removeOfficialAssignment.mjs +2 -1
  128. package/dist/esm/mutate/officiating/removeSuspension.mjs +2 -1
  129. package/dist/esm/mutate/participants/addParticipantOtherId.mjs +3 -2
  130. package/dist/esm/mutate/participants/addPersonOtherId.mjs +3 -2
  131. package/dist/esm/mutate/participants/modifyParticipant.mjs +2 -1
  132. package/dist/esm/mutate/participants/modifyParticipantsPaymentStatus.mjs +2 -1
  133. package/dist/esm/mutate/participants/modifyParticipantsSignInStatus.mjs +2 -1
  134. package/dist/esm/mutate/participants/penalties/addPenalty.mjs +4 -2
  135. package/dist/esm/mutate/participants/penalties/modifyPenalty.mjs +4 -1
  136. package/dist/esm/mutate/participants/penalties/normalizePenaltyType.mjs +11 -0
  137. package/dist/esm/mutate/participants/scaleItems/addScaleItems.mjs +2 -1
  138. package/dist/esm/mutate/practice/addPracticeRegistration.mjs +2 -1
  139. package/dist/esm/mutate/practice/removePracticeRegistration.mjs +2 -1
  140. package/dist/esm/mutate/practice/updatePracticeRegistration.mjs +2 -1
  141. package/dist/esm/mutate/presence/buildAttestation.mjs +2 -1
  142. package/dist/esm/mutate/sanctioning/addEventProposal.mjs +2 -1
  143. package/dist/esm/mutate/sanctioning/addReviewNote.mjs +3 -2
  144. package/dist/esm/mutate/sanctioning/amendments.mjs +5 -4
  145. package/dist/esm/mutate/sanctioning/compliance.mjs +9 -8
  146. package/dist/esm/mutate/sanctioning/conditionallyApprove.mjs +2 -1
  147. package/dist/esm/mutate/sanctioning/createSanctioningRecord.mjs +2 -1
  148. package/dist/esm/mutate/sanctioning/endorsement.mjs +6 -5
  149. package/dist/esm/mutate/sanctioning/meetCondition.mjs +3 -2
  150. package/dist/esm/mutate/sanctioning/openProposalRegistration.mjs +2 -1
  151. package/dist/esm/mutate/sanctioning/removeEventProposal.mjs +2 -1
  152. package/dist/esm/mutate/sanctioning/transitionStatus.mjs +2 -1
  153. package/dist/esm/mutate/sanctioning/updateEventProposal.mjs +2 -1
  154. package/dist/esm/mutate/sanctioning/updateProposal.mjs +2 -1
  155. package/dist/esm/mutate/scoring/addPoint.mjs +29 -18
  156. package/dist/esm/mutate/scoring/createMatchUp.mjs +4 -2
  157. package/dist/esm/mutate/timeItems/addTimeItem.mjs +2 -1
  158. package/dist/esm/mutate/timeItems/publishTournamentInfo.mjs +2 -2
  159. package/dist/esm/mutate/tournaments/mutationLocks/addMutationLock.mjs +2 -1
  160. package/dist/esm/mutate/tournaments/setTournamentDates.mjs +2 -2
  161. package/dist/esm/mutate/venues/addCourt.mjs +2 -2
  162. package/dist/esm/mutate/venues/addCourtGridBooking.mjs +2 -1
  163. package/dist/esm/mutate/venues/addVenueOtherId.mjs +3 -2
  164. package/dist/esm/mutate/venues/updateCourtAvailability.mjs +2 -2
  165. package/dist/esm/package.json.mjs +1 -1
  166. package/dist/esm/query/drawDefinition/getRandomQualifierList.mjs +2 -1
  167. package/dist/esm/query/drawDefinition/hasPropagatedExitDownstream.mjs +2 -2
  168. package/dist/esm/query/drawDefinition/isActiveDownstream.mjs +31 -12
  169. package/dist/esm/query/drawDefinition/matchUpActions/matchUpActions.mjs +47 -5
  170. package/dist/esm/query/entries/categoryValidation.mjs +1 -1
  171. package/dist/esm/query/event/getCategoryAgeDetails.mjs +2 -1
  172. package/dist/esm/query/hierarchical/resolveScoringFormat.mjs +10 -0
  173. package/dist/esm/query/matchUp/activeMatchUp.mjs +1 -1
  174. package/dist/esm/query/matchUp/analyzeMatchUp.mjs +2 -1
  175. package/dist/esm/query/matchUp/analyzeScore.mjs +26 -11
  176. package/dist/esm/query/matchUp/analyzeSet.mjs +38 -26
  177. package/dist/esm/query/matchUp/checkSetIsComplete.mjs +11 -8
  178. package/dist/esm/query/matchUp/getComplement.mjs +14 -20
  179. package/dist/esm/query/matchUp/getDrawPositionWinCount.mjs +1 -1
  180. package/dist/esm/query/matchUp/getProjectedDualWinningSide.mjs +5 -1
  181. package/dist/esm/query/matchUp/matchUpDuration.mjs +2 -1
  182. package/dist/esm/query/matchUp/retainScoreForFormat.mjs +2 -1
  183. package/dist/esm/query/matchUp/tiebreakAtRules.mjs +44 -0
  184. package/dist/esm/query/matchUp/tiebreakSetShape.mjs +51 -0
  185. package/dist/esm/query/matchUps/addGoesTo.mjs +19 -1
  186. package/dist/esm/query/matchUps/addMatchUpContext.mjs +4 -1
  187. package/dist/esm/query/matchUps/competitionScheduleMatchUps.mjs +7 -3
  188. package/dist/esm/query/matchUps/getAllStructureMatchUps.mjs +5 -0
  189. package/dist/esm/query/matchUps/getDrawPositionSides.mjs +28 -1
  190. package/dist/esm/query/matchUps/participantScheduledMatchUps.mjs +2 -2
  191. package/dist/esm/query/matchUps/scheduling/getRounds.mjs +2 -2
  192. package/dist/esm/query/matchUps/scheduling/getSchedulingProfileIssues.mjs +1 -1
  193. package/dist/esm/query/officiating/getOfficialEligibility.mjs +2 -1
  194. package/dist/esm/query/publishing/isEmbargoed.mjs +2 -1
  195. package/dist/esm/query/publishing/tournamentVisibility.mjs +2 -1
  196. package/dist/esm/query/reports/wrappers/wrapCallTimingVarianceReport.mjs +2 -1
  197. package/dist/esm/query/reports/wrappers/wrapCompetitivenessReport.mjs +2 -1
  198. package/dist/esm/query/reports/wrappers/wrapEntryStatusReport.mjs +2 -1
  199. package/dist/esm/query/reports/wrappers/wrapMatchResultsReport.mjs +2 -1
  200. package/dist/esm/query/reports/wrappers/wrapMatchUpStatusReport.mjs +2 -1
  201. package/dist/esm/query/reports/wrappers/wrapParticipantExperienceReport.mjs +2 -1
  202. package/dist/esm/query/reports/wrappers/wrapParticipantResultsReport.mjs +2 -1
  203. package/dist/esm/query/reports/wrappers/wrapParticipantStats.mjs +2 -1
  204. package/dist/esm/query/reports/wrappers/wrapRecoveryTimeReport.mjs +2 -1
  205. package/dist/esm/query/reports/wrappers/wrapSeedingPerformanceReport.mjs +2 -1
  206. package/dist/esm/query/reports/wrappers/wrapStructureReport.mjs +2 -1
  207. package/dist/esm/query/reports/wrappers/wrapVenuesReport.mjs +2 -1
  208. package/dist/esm/query/scoring/getEpisodes.mjs +3 -1
  209. package/dist/esm/query/scoring/getScore.mjs +10 -11
  210. package/dist/esm/query/scoring/getScoreboard.mjs +2 -1
  211. package/dist/esm/query/scoring/getWinner.mjs +3 -1
  212. package/dist/esm/query/scoring/isComplete.mjs +3 -1
  213. package/dist/esm/query/tournaments/cleanExpiredMutationLocks.mjs +2 -1
  214. package/dist/esm/query/tournaments/getCompetitionDateRange.mjs +1 -1
  215. package/dist/esm/query/tournaments/getMutationLocks.mjs +2 -1
  216. package/dist/esm/tools/UUID.mjs +2 -1
  217. package/dist/esm/tools/arrays.mjs +4 -3
  218. package/dist/esm/tools/canonicalJson.mjs +63 -0
  219. package/dist/esm/tools/clock.mjs +39 -0
  220. package/dist/esm/tools/dateTime.mjs +4 -3
  221. package/dist/esm/tools/math.mjs +4 -3
  222. package/dist/esm/tools/parseScoreString.mjs +5 -11
  223. package/dist/esm/tools/plainDate.mjs +3 -2
  224. package/dist/esm/tools/prng.mjs +28 -2
  225. package/dist/esm/tools/scoring/scoringUtilities.mjs +10 -5
  226. package/dist/esm/tools/timeCode.mjs +3 -1
  227. package/dist/esm/tools/timeZone.mjs +2 -1
  228. package/dist/esm/types/tournamentTypes.mjs +6 -0
  229. package/dist/esm/validators/isMatchUpStatus.mjs +8 -0
  230. package/dist/esm/validators/isValidMatchUpFormat.mjs +1 -17
  231. package/dist/esm/validators/officiating/validateCertification.mjs +2 -1
  232. package/dist/esm/validators/sanctioning/validateProposal.mjs +3 -2
  233. package/dist/esm/validators/scoreCompleteness.mjs +31 -0
  234. package/dist/esm/validators/scoring/mcpValidator.mjs +2 -1
  235. package/dist/esm/validators/scoring/pbpValidator.mjs +2 -1
  236. package/dist/esm/validators/scoring/validateMatchUp.mjs +2 -1
  237. package/dist/esm/validators/setCount.mjs +43 -0
  238. package/dist/esm/validators/timedSetWinner.mjs +10 -0
  239. package/dist/esm/validators/validateMatchUpScore.mjs +70 -34
  240. package/dist/esm/validators/validateScheduleScenario.mjs +1 -1
  241. package/dist/esm/validators/validateSchedulingProfile.mjs +1 -1
  242. package/dist/esm/validators/validateScore.mjs +75 -25
  243. package/dist/schema/tournament.schema.json +6 -0
  244. package/dist/tods-competition-factory.d.cts +4394 -850
  245. package/dist/tods-competition-factory.d.mts +4394 -850
  246. package/dist/tods-competition-factory.d.ts +4394 -850
  247. package/dist/tods-competition-factory.development.cjs.js +32221 -29635
  248. package/dist/tods-competition-factory.development.cjs.js.map +1 -1
  249. package/dist/tods-competition-factory.production.cjs.min.js +1 -1
  250. package/dist/tods-competition-factory.production.cjs.min.js.map +1 -1
  251. package/package.json +17 -3
package/README.md CHANGED
@@ -142,7 +142,7 @@ Full documentation with interactive examples: **[courthive.github.io/competition
142
142
 
143
143
  ## Testing
144
144
 
145
- **13,700+ tests** covering draw generation and exit propagation, scheduling, scoring, participants and entries, team competition, publishing and embargo, ranking points and ratings, officiating and sanctioning.
145
+ **15,000+ tests** covering draw generation and exit propagation, scheduling, scoring, participants and entries, team competition, publishing and embargo, ranking points and ratings, officiating and sanctioning.
146
146
 
147
147
  Beyond conventional unit and integration tests, the suite includes relational property suites (do/undo, idempotence, monotonicity), agreement oracles, and an at-scale randomized sweep with delta-debugging for exit propagation — added because full branch coverage of a propagation guard proved compatible with hundreds of wrong answers.
148
148
 
@@ -1,3 +1,4 @@
1
+ import { getMatchUpsMap, getMappedStructureMatchUps } from '../query/matchUps/getMatchUpsMap.mjs';
1
2
  import { checkRequiredParameters } from '../helpers/parameters/checkRequiredParameters.mjs';
2
3
  import { getAllStructureMatchUps } from '../query/matchUps/getAllStructureMatchUps.mjs';
3
4
  import { getContextContent } from '../query/hierarchical/getContextContent.mjs';
@@ -10,11 +11,15 @@ function findDrawMatchUp(params) {
10
11
  const paramsCheck = checkRequiredParameters(params, [{ [DRAW_DEFINITION]: true, [MATCHUP_ID]: true }]);
11
12
  if (paramsCheck.error)
12
13
  return paramsCheck;
13
- const { tournamentParticipants, participantsProfile, afterRecoveryTimes, contextProfile, drawDefinition, matchUpsMap, matchUpId, inContext, context, event, } = params;
14
+ const { tournamentParticipants, participantsProfile, afterRecoveryTimes, contextProfile, drawDefinition, matchUpId, inContext, context, event, } = params;
14
15
  const { structures = [] } = getDrawStructures({ drawDefinition });
15
16
  const contextContent = params.contextContent ?? (contextProfile && getContextContent({ contextProfile, drawDefinition }));
17
+ const matchUpsMap = params.matchUpsMap ?? getMatchUpsMap({ drawDefinition });
16
18
  for (const structure of structures) {
19
+ if (!structureHoldsMatchUp({ structureId: structure.structureId, matchUpsMap, matchUpId }))
20
+ continue;
17
21
  const { matchUps } = getAllStructureMatchUps({
22
+ hydrateMatchUpId: inContext ? matchUpId : undefined,
18
23
  tournamentParticipants,
19
24
  participantsProfile,
20
25
  afterRecoveryTimes,
@@ -33,5 +38,9 @@ function findDrawMatchUp(params) {
33
38
  }
34
39
  return { error: MATCHUP_NOT_FOUND };
35
40
  }
41
+ function structureHoldsMatchUp({ matchUpsMap, structureId, matchUpId, }) {
42
+ return getMappedStructureMatchUps({ matchUpsMap, structureId }).some((matchUp) => matchUp.matchUpId === matchUpId ||
43
+ matchUp.tieMatchUps?.some((tieMatchUp) => tieMatchUp.matchUpId === matchUpId));
44
+ }
36
45
 
37
46
  export { findDrawMatchUp };
@@ -1,12 +1,13 @@
1
1
  import { getMutationStatus } from '../parts/getMutationStatus.mjs';
2
2
  import { logMethodNotFound } from '../parts/logMethodNotFound.mjs';
3
- import { getTournamentRecords, deleteNotices } from '../../../global/state/globalState.mjs';
3
+ import { getTournamentRecords, deleteNotices, getInvokeObserver } from '../../../global/state/globalState.mjs';
4
4
  import { executeFunction } from '../parts/executeMethod.mjs';
5
5
  import { notifySubscribersAsync } from '../../../global/state/notifySubscribers.mjs';
6
6
  import { setState } from '../parts/stateMethods.mjs';
7
7
  import { isObject, isFunction, isString } from '../../../tools/objects.mjs';
8
8
  import { getMethods } from '../../../global/state/syncGlobalState.mjs';
9
9
  import { makeDeepCopy } from '../../../tools/makeDeepCopy.mjs';
10
+ import { nowMs } from '../../../tools/clock.mjs';
10
11
  import { INVALID_VALUES, METHOD_NOT_FOUND } from '../../../constants/errorConditionConstants.mjs';
11
12
 
12
13
  async function asyncEngineInvoke(engine, args) {
@@ -32,7 +33,7 @@ async function asyncEngineInvoke(engine, args) {
32
33
  const result = (await executeFunction(engine, method, params, methodName, 'async')) ?? {};
33
34
  if (result?.error && snapshot)
34
35
  setState(snapshot);
35
- const timeStamp = Date.now();
36
+ const timeStamp = nowMs();
36
37
  const mutationStatus = getMutationStatus({ timeStamp });
37
38
  const notify = result?.success && params?.delayNotify !== true && params?.doNotNotify !== true;
38
39
  if (notify)
@@ -43,6 +44,7 @@ async function asyncEngineInvoke(engine, args) {
43
44
  });
44
45
  if (notify || !result?.success || params?.doNotNotify)
45
46
  deleteNotices();
47
+ getInvokeObserver()?.({ phase: 'after', methodName, engineType: 'async', params, result });
46
48
  return result;
47
49
  }
48
50
 
@@ -1,10 +1,11 @@
1
- import { getMethods, getTournamentRecords, deleteNotices } from '../../../global/state/globalState.mjs';
1
+ import { getMethods, getTournamentRecords, deleteNotices, getInvokeObserver } from '../../../global/state/globalState.mjs';
2
2
  import { getMutationStatus } from '../parts/getMutationStatus.mjs';
3
3
  import { logMethodNotFound } from '../parts/logMethodNotFound.mjs';
4
4
  import { executeFunction } from '../parts/executeMethod.mjs';
5
5
  import { notifySubscribersAsync } from '../../../global/state/notifySubscribers.mjs';
6
6
  import { setState } from '../parts/stateMethods.mjs';
7
7
  import { makeDeepCopy } from '../../../tools/makeDeepCopy.mjs';
8
+ import { nowMs } from '../../../tools/clock.mjs';
8
9
  import { INVALID_VALUES } from '../../../constants/errorConditionConstants.mjs';
9
10
 
10
11
  async function asyncExecutionQueue(engine, directives, rollbackOnError) {
@@ -30,6 +31,7 @@ async function asyncExecutionQueue(engine, directives, rollbackOnError) {
30
31
  }
31
32
  }
32
33
  const result = executeFunction(engine, methods[methodName], params, methodName, 'async');
34
+ getInvokeObserver()?.({ phase: 'after', methodName, engineType: 'async', params, result });
33
35
  if (result?.error) {
34
36
  if (snapshot)
35
37
  setState(snapshot);
@@ -37,7 +39,7 @@ async function asyncExecutionQueue(engine, directives, rollbackOnError) {
37
39
  }
38
40
  results.push({ ...result, methodName });
39
41
  }
40
- const timeStamp = Date.now();
42
+ const timeStamp = nowMs();
41
43
  const mutationStatus = getMutationStatus({ timeStamp });
42
44
  await notifySubscribersAsync({ directives, mutationStatus, timeStamp });
43
45
  deleteNotices();
@@ -2,6 +2,7 @@ import { methodScopeMap } from '../../../constants/mutationLockScopeMap.mjs';
2
2
  import { addExtension } from '../../../mutate/extensions/addExtension.mjs';
3
3
  import { findExtension } from '../../../acquire/findExtension.mjs';
4
4
  import { findVenue } from '../../../query/venues/findVenue.mjs';
5
+ import { nowIso } from '../../../tools/clock.mjs';
5
6
  import { MUTATION_LOCKED } from '../../../constants/errorConditionConstants.mjs';
6
7
  import { MUTATION_LOCKS } from '../../../constants/extensionConstants.mjs';
7
8
 
@@ -15,7 +16,7 @@ function checkMutationLock(methodName, params, tournamentRecord) {
15
16
  if (!scope)
16
17
  return undefined;
17
18
  const lockToken = params.lockToken;
18
- const now = new Date().toISOString();
19
+ const now = nowIso();
19
20
  const elements = [];
20
21
  if (params.drawDefinition) {
21
22
  elements.push({ element: params.drawDefinition, label: `draw:${params.drawDefinition.drawId}` });
@@ -8,9 +8,11 @@ import { createTournamentRecord } from '../../generators/tournamentRecords/creat
8
8
  import { methodImporter } from './methodImporter.mjs';
9
9
  import { processResult } from './processResult.mjs';
10
10
  import { factoryVersion } from '../../../functions/global/factoryVersion.mjs';
11
+ import { setRandomSource } from '../../../tools/prng.mjs';
11
12
  import { getTournament, getState, setTournamentRecord, setState, removeUnlinkedTournamentRecords } from './stateMethods.mjs';
12
- import { setTournamentRecords, getDevContext, getSchemaWriteMode, getSaveDrawDeletions, getAuditAuthorityServer, setTournamentId, setDeepCopy, getTournamentId, removeTournamentRecord, setDevContext, setSchemaWriteMode, setSaveDrawDeletions, setAuditAuthorityServer } from '../../../global/state/globalState.mjs';
13
+ import { setTournamentRecords, getDevContext, getSchemaWriteMode, getOutcomePipeline, getSaveDrawDeletions, getAuditAuthorityServer, setTournamentId, setDeepCopy, getTournamentId, removeTournamentRecord, setDevContext, setSchemaWriteMode, setOutcomePipeline, setSaveDrawDeletions, setAuditAuthorityServer } from '../../../global/state/globalState.mjs';
13
14
  import { SUCCESS } from '../../../constants/resultConstants.mjs';
15
+ import { setClock } from '../../../tools/clock.mjs';
14
16
 
15
17
  function engineStart(engine, engineInvoke) {
16
18
  engine.importMethods = (methods, collections, depth, global) => methodImporter(engine, engineInvoke, methods, collections, depth, global);
@@ -34,6 +36,11 @@ function engineStart(engine, engineInvoke) {
34
36
  return processResult(engine, result);
35
37
  };
36
38
  engine.getSchemaWriteMode = () => getSchemaWriteMode();
39
+ engine.outcomePipeline = (mode) => {
40
+ const result = setOutcomePipeline(mode);
41
+ return processResult(engine, result);
42
+ };
43
+ engine.getOutcomePipeline = () => getOutcomePipeline();
37
44
  engine.saveDrawDeletions = (flag) => {
38
45
  const result = setSaveDrawDeletions(flag);
39
46
  return processResult(engine, result);
@@ -44,6 +51,14 @@ function engineStart(engine, engineInvoke) {
44
51
  return processResult(engine, result);
45
52
  };
46
53
  engine.getAuditAuthorityServer = () => getAuditAuthorityServer();
54
+ engine.setRandomSource = (source) => {
55
+ const result = setRandomSource(source);
56
+ return processResult(engine, result);
57
+ };
58
+ engine.setClock = (clock) => {
59
+ const result = setClock(clock);
60
+ return processResult(engine, result);
61
+ };
47
62
  engine.newTournamentRecord = (params = {}) => {
48
63
  const result = createTournamentRecord(params);
49
64
  const tournamentId = result.tournamentId;
@@ -2,7 +2,7 @@ import { engineLogging } from '../../../global/state/engineLogging.mjs';
2
2
  import { checkMutationLock } from './checkMutationLock.mjs';
3
3
  import { paramsMiddleware } from './paramsMiddleware.mjs';
4
4
  import { makeDeepCopy } from '../../../tools/makeDeepCopy.mjs';
5
- import { getTournamentId, getTournamentRecord, getTournamentRecords, getDevContext, handleCaughtError } from '../../../global/state/globalState.mjs';
5
+ import { getTournamentId, getTournamentRecord, getTournamentRecords, getDevContext, handleCaughtError, getInvokeObserver } from '../../../global/state/globalState.mjs';
6
6
 
7
7
  function executeFunction(engine, method, params, methodName, engineType, options) {
8
8
  delete engine.success;
@@ -17,22 +17,20 @@ function executeFunction(engine, method, params, methodName, engineType, options
17
17
  }) ||
18
18
  getTournamentRecords();
19
19
  const paramsToLog = params ? makeDeepCopy(params, undefined, true) : undefined;
20
+ getInvokeObserver()?.({ phase: 'before', methodName, engineType, params: paramsToLog });
21
+ const result = resolve({ method, params, methodName, tournamentRecords, tournamentRecord });
22
+ const elapsed = Date.now() - start;
23
+ engineLogging({ result, methodName, elapsed, params: paramsToLog, engineType, dryRun: options?.dryRun });
24
+ return result;
25
+ }
26
+ function resolve({ method, params, methodName, tournamentRecords, tournamentRecord }) {
20
27
  const augmentedParams = params ? paramsMiddleware(tournamentRecords, params) : undefined;
21
28
  if (augmentedParams?.error)
22
29
  return augmentedParams;
23
30
  const lockError = checkMutationLock(methodName, augmentedParams, tournamentRecord);
24
31
  if (lockError)
25
32
  return lockError;
26
- const result = invoke({
27
- params: augmentedParams,
28
- tournamentRecords,
29
- tournamentRecord,
30
- methodName,
31
- method,
32
- });
33
- const elapsed = Date.now() - start;
34
- engineLogging({ result, methodName, elapsed, params: paramsToLog, engineType, dryRun: options?.dryRun });
35
- return result;
33
+ return invoke({ params: augmentedParams, tournamentRecords, tournamentRecord, methodName, method });
36
34
  }
37
35
  function invoke({ tournamentRecords, tournamentRecord, params, methodName, method }) {
38
36
  if (getDevContext()) {
@@ -1,3 +1,4 @@
1
+ import { openSetNumber, finalSetGoverns } from '../../../helpers/matchUpFormatCode/aggregateDecider.mjs';
1
2
  import { calculateMatchStatistics } from '../../../query/scoring/statistics/standalone.mjs';
2
3
  import { toStatObjects } from '../../../query/scoring/statistics/toStatObjects.mjs';
3
4
  import { resolveSetType } from '../../../tools/scoring/scoringUtilities.mjs';
@@ -8,7 +9,9 @@ import { getEpisodes } from '../../../query/scoring/getEpisodes.mjs';
8
9
  import { isComplete } from '../../../query/scoring/isComplete.mjs';
9
10
  import { getWinner } from '../../../query/scoring/getWinner.mjs';
10
11
  import { getScore } from '../../../query/scoring/getScore.mjs';
12
+ import { nowIso } from '../../../tools/clock.mjs';
11
13
  import { addPoint, deriveServer, deriveServerBase, checkStandardSetWon, checkAndFinalizeMatch } from '../../../mutate/scoring/addPoint.mjs';
14
+ import { COMPLETED, TO_BE_PLAYED, IN_PROGRESS } from '../../../constants/matchUpStatusValues.mjs';
12
15
 
13
16
  class ScoringEngine {
14
17
  constructor(options = {}) {
@@ -49,7 +52,7 @@ class ScoringEngine {
49
52
  const pointIndex = this.state.history?.points.length || 0;
50
53
  const prevTotalGames = this.state.score.sets.reduce((sum, s) => sum + (s.side1Score || 0) + (s.side2Score || 0), 0);
51
54
  const prevCompletedSets = this.state.score.sets.filter((s) => s.winningSide !== undefined).length;
52
- const prevComplete = this.state.matchUpStatus === 'COMPLETED';
55
+ const prevComplete = this.state.matchUpStatus === COMPLETED;
53
56
  const activePlayersSnapshot = this.hasLineUp() ? this.getActivePlayers() : undefined;
54
57
  this.state = addPoint(this.state, options, {
55
58
  pointMultipliers: this.pointMultipliers,
@@ -81,7 +84,7 @@ class ScoringEngine {
81
84
  result: options.result,
82
85
  penaltyType: options.penaltyType,
83
86
  },
84
- timestamp: options.timestamp || new Date().toISOString(),
87
+ timestamp: options.timestamp || nowIso(),
85
88
  pointIndex,
86
89
  });
87
90
  this.redoStack = [];
@@ -98,7 +101,7 @@ class ScoringEngine {
98
101
  const setWinner = completedSet?.winningSide === 1 ? 0 : 1;
99
102
  this.eventHandlers.onSetComplete?.({ ...ctx, setWinner });
100
103
  }
101
- if (!prevComplete && this.state.matchUpStatus === 'COMPLETED') {
104
+ if (!prevComplete && this.state.matchUpStatus === COMPLETED) {
102
105
  const matchWinner = this.state.winningSide === 1 ? 0 : 1;
103
106
  this.eventHandlers.onMatchComplete?.({ ...ctx, matchWinner });
104
107
  }
@@ -111,7 +114,7 @@ class ScoringEngine {
111
114
  this.state.history.entries.push({
112
115
  type: 'set',
113
116
  data: { ...options, winningSide },
114
- timestamp: options.timestamp || new Date().toISOString(),
117
+ timestamp: options.timestamp || nowIso(),
115
118
  });
116
119
  this.redoStack = [];
117
120
  }
@@ -121,7 +124,7 @@ class ScoringEngine {
121
124
  this.state.history.entries.push({
122
125
  type: 'game',
123
126
  data: options,
124
- timestamp: options.timestamp || new Date().toISOString(),
127
+ timestamp: options.timestamp || nowIso(),
125
128
  });
126
129
  this.redoStack = [];
127
130
  }
@@ -132,7 +135,7 @@ class ScoringEngine {
132
135
  this.state.history.entries.push({
133
136
  type: 'endSegment',
134
137
  data: { setNumber: currentSetIndex + 1, ...options },
135
- timestamp: options?.timestamp || new Date().toISOString(),
138
+ timestamp: options?.timestamp || nowIso(),
136
139
  });
137
140
  }
138
141
  setInitialScore(options) {
@@ -143,10 +146,10 @@ class ScoringEngine {
143
146
  this.state.history.entries.push({
144
147
  type: 'setInitialScore',
145
148
  data: options,
146
- timestamp: new Date().toISOString(),
149
+ timestamp: nowIso(),
147
150
  });
148
- if (this.state.matchUpStatus === 'TO_BE_PLAYED') {
149
- this.state.matchUpStatus = 'IN_PROGRESS';
151
+ if (this.state.matchUpStatus === TO_BE_PLAYED) {
152
+ this.state.matchUpStatus = IN_PROGRESS;
150
153
  }
151
154
  }
152
155
  undo(count = 1) {
@@ -257,7 +260,7 @@ class ScoringEngine {
257
260
  if (set.winningSide === 2)
258
261
  setsWon[1]++;
259
262
  });
260
- const setType = resolveSetType(this.cachedFormatStructure, setsWon);
263
+ const setType = resolveSetType(this.cachedFormatStructure, setsWon, openSetNumber(this.state.score.sets));
261
264
  return deriveServer(this.state, this.cachedFormatStructure, setType);
262
265
  }
263
266
  setServer(side, options) {
@@ -270,7 +273,7 @@ class ScoringEngine {
270
273
  if (set.winningSide === 2)
271
274
  setsWon[1]++;
272
275
  });
273
- const setType = resolveSetType(this.cachedFormatStructure, setsWon);
276
+ const setType = resolveSetType(this.cachedFormatStructure, setsWon, openSetNumber(this.state.score.sets));
274
277
  const baseServer = deriveServerBase(this.state, this.cachedFormatStructure, setType);
275
278
  this.state.serverFlip = side !== baseServer;
276
279
  if (options?.recordEntry === false)
@@ -279,7 +282,7 @@ class ScoringEngine {
279
282
  this.state.history.entries.push({
280
283
  type: 'setServer',
281
284
  data: { side },
282
- timestamp: new Date().toISOString(),
285
+ timestamp: nowIso(),
283
286
  });
284
287
  this.redoStack = [];
285
288
  }
@@ -375,7 +378,7 @@ class ScoringEngine {
375
378
  this.state.history.entries.push({
376
379
  type: 'substitution',
377
380
  data: subEvent,
378
- timestamp: options.timestamp || new Date().toISOString(),
381
+ timestamp: options.timestamp || nowIso(),
379
382
  });
380
383
  this.redoStack = [];
381
384
  }
@@ -568,8 +571,8 @@ class ScoringEngine {
568
571
  if (side2TiebreakScore !== undefined)
569
572
  newSet.side2TiebreakScore = side2TiebreakScore;
570
573
  this.state.score.sets.push(newSet);
571
- if (this.state.matchUpStatus === 'TO_BE_PLAYED') {
572
- this.state.matchUpStatus = 'IN_PROGRESS';
574
+ if (this.state.matchUpStatus === TO_BE_PLAYED) {
575
+ this.state.matchUpStatus = IN_PROGRESS;
573
576
  }
574
577
  if (winningSide !== undefined) {
575
578
  this.checkMatchCompletion();
@@ -602,8 +605,8 @@ class ScoringEngine {
602
605
  currentSet.side1TiebreakScore = tiebreakScore[0];
603
606
  currentSet.side2TiebreakScore = tiebreakScore[1];
604
607
  }
605
- if (this.state.matchUpStatus === 'TO_BE_PLAYED') {
606
- this.state.matchUpStatus = 'IN_PROGRESS';
608
+ if (this.state.matchUpStatus === TO_BE_PLAYED) {
609
+ this.state.matchUpStatus = IN_PROGRESS;
607
610
  }
608
611
  this.checkSetCompletion(currentSet);
609
612
  if (currentSet.winningSide !== undefined) {
@@ -652,7 +655,8 @@ class ScoringEngine {
652
655
  if (set.winningSide === 2)
653
656
  setsWon[1]++;
654
657
  });
655
- const isDecidingSet = setsWon[0] === setsToWin - 1 && setsWon[1] === setsToWin - 1;
658
+ const setNumber = currentSet.setNumber ?? this.state.score.sets.indexOf(currentSet) + 1;
659
+ const isDecidingSet = finalSetGoverns(formatStructure, setNumber, setsWon[0] === setsToWin - 1 && setsWon[1] === setsToWin - 1);
656
660
  const activeSetFormat = isDecidingSet && formatStructure.finalSetFormat ? formatStructure.finalSetFormat : formatStructure.setFormat;
657
661
  if (!activeSetFormat)
658
662
  return;
@@ -729,8 +733,8 @@ class ScoringEngine {
729
733
  }
730
734
  if (this.initialScore) {
731
735
  this.applyInitialScore(newState, this.initialScore);
732
- if (newState.matchUpStatus === 'TO_BE_PLAYED') {
733
- newState.matchUpStatus = 'IN_PROGRESS';
736
+ if (newState.matchUpStatus === TO_BE_PLAYED) {
737
+ newState.matchUpStatus = IN_PROGRESS;
734
738
  }
735
739
  }
736
740
  newState.history = { points: [], entries: [...entries] };
@@ -744,7 +748,7 @@ class ScoringEngine {
744
748
  if (set.winningSide === 2)
745
749
  setsWon[1]++;
746
750
  });
747
- const setType = resolveSetType(this.cachedFormatStructure, setsWon);
751
+ const setType = resolveSetType(this.cachedFormatStructure, setsWon, openSetNumber(this.state.score.sets));
748
752
  const baseServer = deriveServerBase(this.state, this.cachedFormatStructure, setType);
749
753
  this.state.serverFlip = data.side !== baseServer;
750
754
  }
@@ -757,8 +761,8 @@ class ScoringEngine {
757
761
  });
758
762
  if (this.initialScore) {
759
763
  this.applyInitialScore(newState, this.initialScore);
760
- if (newState.matchUpStatus === 'TO_BE_PLAYED') {
761
- newState.matchUpStatus = 'IN_PROGRESS';
764
+ if (newState.matchUpStatus === TO_BE_PLAYED) {
765
+ newState.matchUpStatus = IN_PROGRESS;
762
766
  }
763
767
  }
764
768
  for (const point of currentPoints) {
@@ -791,7 +795,8 @@ class ScoringEngine {
791
795
  }
792
796
  if (setData.winningSide === undefined) {
793
797
  const fs = this.cachedFormatStructure;
794
- const setFormat = fs?.finalSetFormat && i === (fs.bestOf ?? fs.exactly ?? 3) - 1 ? fs.finalSetFormat : fs?.setFormat;
798
+ const isFinalSet = finalSetGoverns(fs, i + 1, i === (fs?.bestOf ?? fs?.exactly ?? 3) - 1);
799
+ const setFormat = fs?.finalSetFormat && isFinalSet ? fs.finalSetFormat : fs?.setFormat;
795
800
  const setTo = setFormat?.setTo;
796
801
  if (setTo && (setData.side1Score >= setTo || setData.side2Score >= setTo)) {
797
802
  if (setData.side1Score > setData.side2Score)
@@ -1,6 +1,6 @@
1
1
  import { getMutationStatus } from '../parts/getMutationStatus.mjs';
2
2
  import { logMethodNotFound } from '../parts/logMethodNotFound.mjs';
3
- import { getTournamentRecords, deleteNotices } from '../../../global/state/globalState.mjs';
3
+ import { getTournamentRecords, deleteNotices, getInvokeObserver } from '../../../global/state/globalState.mjs';
4
4
  import { executeFunction } from '../parts/executeMethod.mjs';
5
5
  import { notifySubscribers } from '../../../global/state/notifySubscribers.mjs';
6
6
  import { setState } from '../parts/stateMethods.mjs';
@@ -9,6 +9,7 @@ import { getMethods } from '../../../global/state/syncGlobalState.mjs';
9
9
  import { makeDeepCopy } from '../../../tools/makeDeepCopy.mjs';
10
10
  import { createSeededRandom } from '../../../tools/prng.mjs';
11
11
  import { INVALID_VALUES, METHOD_NOT_FOUND } from '../../../constants/errorConditionConstants.mjs';
12
+ import { nowMs } from '../../../tools/clock.mjs';
12
13
 
13
14
  function engineInvoke(engine, args) {
14
15
  if (!isObject(args))
@@ -37,7 +38,7 @@ function engineInvoke(engine, args) {
37
38
  const result = executeFunction(engine, method, params, methodName, 'sync') ?? {};
38
39
  if (result?.error && snapshot)
39
40
  setState(snapshot);
40
- const timeStamp = Date.now();
41
+ const timeStamp = nowMs();
41
42
  const mutationStatus = getMutationStatus({ timeStamp });
42
43
  const notify = result?.success && params?.delayNotify !== true && params?.doNotNotify !== true;
43
44
  if (notify)
@@ -48,6 +49,7 @@ function engineInvoke(engine, args) {
48
49
  });
49
50
  if (notify || !result?.success || params?.doNotNotify)
50
51
  deleteNotices();
52
+ getInvokeObserver()?.({ phase: 'after', methodName, engineType: 'sync', params, result });
51
53
  return result;
52
54
  }
53
55
 
@@ -1,4 +1,4 @@
1
- import { getMethods, getTournamentRecords, deleteNotices } from '../../../global/state/globalState.mjs';
1
+ import { getMethods, getTournamentRecords, deleteNotices, getInvokeObserver } from '../../../global/state/globalState.mjs';
2
2
  import { getMutationStatus } from '../parts/getMutationStatus.mjs';
3
3
  import { logMethodNotFound } from '../parts/logMethodNotFound.mjs';
4
4
  import { executeFunction } from '../parts/executeMethod.mjs';
@@ -7,6 +7,7 @@ import { setState } from '../parts/stateMethods.mjs';
7
7
  import { makeDeepCopy } from '../../../tools/makeDeepCopy.mjs';
8
8
  import { createSeededRandom } from '../../../tools/prng.mjs';
9
9
  import { INVALID_VALUES } from '../../../constants/errorConditionConstants.mjs';
10
+ import { nowMs } from '../../../tools/clock.mjs';
10
11
 
11
12
  function executionQueue(engine, directives, rollbackOnError) {
12
13
  if (!Array.isArray(directives))
@@ -37,6 +38,7 @@ function executionQueue(engine, directives, rollbackOnError) {
37
38
  }
38
39
  }
39
40
  const result = executeFunction(engine, methods[methodName], params, methodName, 'sync');
41
+ getInvokeObserver()?.({ phase: 'after', methodName, engineType: 'sync', params, result });
40
42
  if (result?.error) {
41
43
  if (snapshot)
42
44
  setState(snapshot);
@@ -44,7 +46,7 @@ function executionQueue(engine, directives, rollbackOnError) {
44
46
  }
45
47
  results.push({ ...result, methodName });
46
48
  }
47
- const timeStamp = Date.now();
49
+ const timeStamp = nowMs();
48
50
  const mutationStatus = getMutationStatus({ timeStamp });
49
51
  notifySubscribers({ directives, mutationStatus, timeStamp });
50
52
  deleteNotices();
@@ -1,8 +1,9 @@
1
1
  import { randomPop, chunkArray, shuffleArray } from '../../../../../../tools/arrays.mjs';
2
2
  import { stringSort } from '../../../../../../functions/sorters/stringSort.mjs';
3
+ import { randomSource } from '../../../../../../tools/prng.mjs';
3
4
 
4
5
  function generateCandidate({ maxIterations = 4000, valueSortedPairings, individualIdsMap, pairingValues, valueObjects, deltaObjects, random, }) {
5
- const rng = random ?? Math.random;
6
+ const rng = random ?? randomSource();
6
7
  const pairingValueMap = Object.assign({}, ...valueSortedPairings.map((rm) => ({ [rm.pairing]: rm.value })));
7
8
  const actors = Object.keys(pairingValues);
8
9
  let proposedCandidates = [];
@@ -89,7 +90,7 @@ function candidateHash(candidate) {
89
90
  .join('/');
90
91
  }
91
92
  function roundCandidate({ valueSortedPairings, stipulated = [], individualIdsMap, pairingValueMap, deltaObjects, valueObjects, actorsCount, random, }) {
92
- const rng = random ?? Math.random;
93
+ const rng = random ?? randomSource();
93
94
  const occupants = (participantId) => {
94
95
  const individualIds = individualIdsMap?.[participantId];
95
96
  return individualIds?.length ? individualIds : [participantId];
@@ -1,6 +1,7 @@
1
1
  import { getParticipantPairingValues } from './getParticipantPairingValues.mjs';
2
2
  import { generateCandidate } from './generateCandidate.mjs';
3
3
  import { getSideRatings } from './getSideRatings.mjs';
4
+ import { randomSource } from '../../../../../../tools/prng.mjs';
4
5
 
5
6
  function getPairings(params) {
6
7
  const { tournamentParticipants, adHocRatings = {}, possiblePairings, individualIdsMap, uniquePairings, maxIterations, minimizeDelta, deltaObjects, valueObjects, eventType, scaleName, salted, } = params;
@@ -13,7 +14,7 @@ function getPairings(params) {
13
14
  pairing,
14
15
  });
15
16
  const salting = (typeof salted === 'number' && salted) || 0.5;
16
- const rng = params.random ?? Math.random;
17
+ const rng = params.random ?? randomSource();
17
18
  const salt = (salted && (Math.round(rng()) ? salting : salting * -1)) || 0;
18
19
  const ratingsDifference = Math.abs(ratings[0] - ratings[1]) + salt;
19
20
  const pairingDelta = Math.abs(ratings[0] - ratings[1]);
@@ -1,12 +1,13 @@
1
1
  import { MISSING_VALUE } from '../../../constants/errorConditionConstants.mjs';
2
2
  import { SEEDING } from '../../../constants/scaleConstants.mjs';
3
+ import { nowIso } from '../../../tools/clock.mjs';
3
4
 
4
5
  function generateSeedingScaleItems({ scaleAttributes, scaledEntries, stageEntries, seedsCount, scaleName, }) {
5
6
  if (!scaleAttributes)
6
7
  return { error: MISSING_VALUE, info: 'missing scaleAttributes' };
7
8
  const seededEntries = Object.assign({}, ...(scaledEntries ?? []).slice(0, seedsCount).map(({ participantId }, index) => ({ [participantId]: index + 1 })));
8
9
  scaleName = scaleName || scaleAttributes.scaleName;
9
- const scaleDate = new Date().toISOString();
10
+ const scaleDate = nowIso();
10
11
  const scaleItemsWithParticipantIds = stageEntries.map(({ participantId }) => {
11
12
  const scaleItem = {
12
13
  scaleValue: seededEntries[participantId],
@@ -5,6 +5,7 @@ import { TEAM } from '../../../constants/eventConstants.mjs';
5
5
  import { MAIN } from '../../../constants/drawDefinitionConstants.mjs';
6
6
  import { FORMAT_STANDARD } from '../../../fixtures/scoring/matchUpFormats/index.mjs';
7
7
  import '../../../helpers/keyValueScore/constants.mjs';
8
+ import '../../../validators/isMatchUpStatus.mjs';
8
9
 
9
10
  function getDrawFormat(params) {
10
11
  const { existingDrawDefinition, hydrateCollections, enforceGender, tieFormatName, tieFormatId, matchUpType, eventType, isMock, event, } = params;
@@ -15,7 +15,10 @@ import '../../../query/formatWizard/suggestFormatPlans.mjs';
15
15
  import '../../../types/tournamentTypes.mjs';
16
16
  import { isAdHocType } from '../../../query/drawDefinition/isAdHocType.mjs';
17
17
  import '../../../query/matchUp/isScheduleLocked.mjs';
18
+ import '../../../mutate/matchUps/schedule/byeScheduling.mjs';
18
19
  import '../../../query/matchUp/predictMatchUpCompetitiveBands.mjs';
20
+ import '../../../mutate/matchUps/matchUpStatus/sideStatusCodes.mjs';
21
+ import '../../../validators/isMatchUpStatus.mjs';
19
22
  import '../../../functions/sorters/roundSort.mjs';
20
23
  import '../../../query/drawDefinition/predictDrawCompetitiveBands.mjs';
21
24
  import { getDrawTypeCoercion } from './getDrawTypeCoercion.mjs';
@@ -1,3 +1,4 @@
1
+ import { finalSetGoverns } from '../../../helpers/matchUpFormatCode/aggregateDecider.mjs';
1
2
  import { parse } from '../../../helpers/matchUpFormatCode/parse.mjs';
2
3
  import { isNumeric } from '../../../tools/math.mjs';
3
4
  import { MISSING_VALUE } from '../../../constants/errorConditionConstants.mjs';
@@ -23,7 +24,7 @@ function generateScoreString(params) {
23
24
  return `${outcomeString} ${setScores}`;
24
25
  return `${setScores} ${outcomeString}`;
25
26
  function setString(currentSet) {
26
- const isFinalSet = bestOf && currentSet.setNumber === bestOf;
27
+ const isFinalSet = finalSetGoverns(parsedFormat, currentSet.setNumber, !!bestOf && currentSet.setNumber === bestOf);
27
28
  const format = isFinalSet && finalSetFormat ? finalSetFormat : setFormat;
28
29
  const hasGameScores = (set) => isNumeric(set?.side1Score) || isNumeric(set?.side2Score);
29
30
  const hasTiebreakScores = (set) => isNumeric(set?.side1TiebreakScore) || isNumeric(set?.side2TiebreakScore);
@@ -1,10 +1,13 @@
1
+ import { withPointsInTiebreakFields, formatForSet } from '../../../query/matchUp/tiebreakSetShape.mjs';
1
2
  import { generateScoreString } from './generateScoreString.mjs';
3
+ import { parse } from '../../../helpers/matchUpFormatCode/parse.mjs';
2
4
 
3
5
  function matchUpScore(params) {
4
6
  const { matchUpFormat, matchUpStatus, winningSide, score, setTBlast } = params;
5
7
  if (!score)
6
8
  return { sets: [] };
7
- const sets = score.sets ?? [];
9
+ const parsedFormat = score.sets?.length && matchUpFormat ? parse(matchUpFormat) : undefined;
10
+ const sets = (score.sets ?? []).map((set) => withPointsInTiebreakFields(set, formatForSet(parsedFormat, set?.setNumber)));
8
11
  let scoreStringSide1 = generateScoreString({
9
12
  winnerFirst: false,
10
13
  matchUpFormat,
@@ -2,6 +2,7 @@ import { addMatchUpScheduledDate } from '../../../mutate/matchUps/schedule/sched
2
2
  import { addMatchUpScheduledTime } from '../../../mutate/matchUps/schedule/scheduledTime.mjs';
3
3
  import { scheduleProfileGrid } from '../../../mutate/matchUps/schedule/scheduleProfileGrid.mjs';
4
4
  import { decorateResult } from '../../../functions/global/decorateResult.mjs';
5
+ import { now } from '../../../tools/clock.mjs';
5
6
  import { extractDate } from '../../../tools/plainDate.mjs';
6
7
  import { allTournamentMatchUps } from '../../../query/matchUps/getAllTournamentMatchUps.mjs';
7
8
  import { INVALID_VALUES, INVALID_DATE } from '../../../constants/errorConditionConstants.mjs';
@@ -32,7 +33,7 @@ function applyScenarioProfile({ tournamentRecord, scenarioProfile }) {
32
33
  const { anchor = 'NOW', minutesBeforeAnchor = 0, assignCourts } = scenarioProfile;
33
34
  if (typeof minutesBeforeAnchor !== 'number' || !isFinite(minutesBeforeAnchor))
34
35
  return { error: INVALID_VALUES };
35
- const anchorDate = anchor === 'NOW' ? new Date() : new Date(anchor);
36
+ const anchorDate = anchor === 'NOW' ? now() : new Date(anchor);
36
37
  if (isNaN(anchorDate.getTime()))
37
38
  return { error: INVALID_VALUES };
38
39
  const matchUps = allTournamentMatchUps({ tournamentRecord }).matchUps ?? [];
@@ -1,3 +1,4 @@
1
+ import { finalSetGoverns, aggregateDeciderSetNumber } from '../../../helpers/matchUpFormatCode/aggregateDecider.mjs';
1
2
  import { getSetComplement, getTiebreakComplement } from '../../../query/matchUp/getComplement.mjs';
2
3
  import { matchUpScore } from '../matchUps/matchUpScore.mjs';
3
4
  import { isValidMatchUpFormat } from '../../../validators/isValidMatchUpFormat.mjs';
@@ -157,7 +158,7 @@ function generateScoredOutcome({ pointsPerMinute, matchUpFormat, matchUpStatus,
157
158
  let weightedWinningSide;
158
159
  const setsToGenerate = generateRange(1, (exactly ?? bestOf) + 1);
159
160
  for (const setNumber of setsToGenerate) {
160
- const isFinalSet = setNumber === (exactly ?? bestOf);
161
+ const isFinalSet = finalSetGoverns(parsedFormat, setNumber, setNumber === (exactly ?? bestOf));
161
162
  const { set, incomplete, winningSideNumber } = generateSet({
162
163
  setFormat: (isFinalSet && finalSetFormat) || setFormat,
163
164
  incomplete: incompleteAt === setNumber,
@@ -176,6 +177,22 @@ function generateScoredOutcome({ pointsPerMinute, matchUpFormat, matchUpStatus,
176
177
  if (analysis.calculatedWinningSide && !parsedFormat?.aggregate)
177
178
  break;
178
179
  }
180
+ const deciderSetNumber = aggregateDeciderSetNumber(parsedFormat);
181
+ if (deciderSetNumber && finalSetFormat && !weightedWinningSide && aggregateIsLevel(sets)) {
182
+ const { set } = generateSet({
183
+ weightedRange: winningSide ? [winningSide - 1] : weightedRange,
184
+ setFormat: finalSetFormat,
185
+ setNumber: deciderSetNumber,
186
+ incomplete: false,
187
+ pointsPerMinute,
188
+ matchUpStatus,
189
+ random,
190
+ });
191
+ if (winningSide && set.winningSide !== winningSide)
192
+ swapSides(set);
193
+ sets.push(set);
194
+ weightedWinningSide = set.winningSide;
195
+ }
179
196
  const matchUpWinningSide = determineMatchUpWinningSide({
180
197
  weightedWinningSide,
181
198
  matchUpFormat,
@@ -196,6 +213,15 @@ function generateScoredOutcome({ pointsPerMinute, matchUpFormat, matchUpStatus,
196
213
  };
197
214
  return { outcome };
198
215
  }
216
+ function swapSides(set) {
217
+ [set.side1Score, set.side2Score] = [set.side2Score, set.side1Score];
218
+ [set.side1TiebreakScore, set.side2TiebreakScore] = [set.side2TiebreakScore, set.side1TiebreakScore];
219
+ set.winningSide = set.winningSide === 1 ? 2 : 1;
220
+ }
221
+ function aggregateIsLevel(sets) {
222
+ const total = (side) => sets.reduce((sum, set) => sum + (set[`side${side}Score`] ?? 0), 0);
223
+ return sets.length > 0 && total(1) === total(2);
224
+ }
199
225
  function determineMatchUpWinningSide({ weightedWinningSide, matchUpFormat, parsedFormat, winningSide, random, sets }) {
200
226
  if (weightedWinningSide) {
201
227
  return winningSide || weightedWinningSide;