tods-competition-factory 7.6.0 → 7.7.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 (195) hide show
  1. package/README.md +1 -1
  2. package/dist/esm/acquire/findStructure.mjs +4 -2
  3. package/dist/esm/acquire/structureMembers.mjs +11 -0
  4. package/dist/esm/assemblies/engines/scoring/ScoringEngine.mjs +82 -32
  5. package/dist/esm/assemblies/generators/drawDefinitions/autoSeeding.mjs +4 -2
  6. package/dist/esm/assemblies/generators/drawDefinitions/drawTypes/adHoc/drawMatic/drawMatic.mjs +1 -3
  7. package/dist/esm/assemblies/generators/drawDefinitions/drawTypes/adHoc/drawMatic/generateDrawMaticRound.mjs +5 -4
  8. package/dist/esm/assemblies/generators/drawDefinitions/drawTypes/adHoc/getAvailableMatchUpsCount.mjs +4 -3
  9. package/dist/esm/assemblies/generators/drawDefinitions/drawTypes/adHoc/swiss/generateSwissRound.mjs +3 -2
  10. package/dist/esm/assemblies/generators/drawDefinitions/drawTypes/generateVoluntaryConsolation.mjs +2 -1
  11. package/dist/esm/assemblies/generators/drawDefinitions/drawTypes/luckyDraw.mjs +3 -1
  12. package/dist/esm/assemblies/generators/drawDefinitions/generateAndPopulatePlayoffStructures.mjs +43 -15
  13. package/dist/esm/assemblies/generators/drawDefinitions/generateDrawDefinition/generateNewDrawDefinition.mjs +24 -0
  14. package/dist/esm/assemblies/generators/drawDefinitions/generateDrawDefinition/prepareStage.mjs +8 -3
  15. package/dist/esm/assemblies/generators/drawDefinitions/generateDrawDefinition/qualifyingGeneration.mjs +1 -0
  16. package/dist/esm/assemblies/generators/drawDefinitions/generateDrawDefinition/setUpDrawGeneration.mjs +2 -1
  17. package/dist/esm/assemblies/generators/drawDefinitions/generateDrawStructuresAndLinks.mjs +1 -1
  18. package/dist/esm/assemblies/generators/drawDefinitions/generateSeedingScaleItems.mjs +2 -0
  19. package/dist/esm/assemblies/generators/mocks/applyScenarioProfile.mjs +2 -2
  20. package/dist/esm/assemblies/generators/mocks/generateEventWithDraw.mjs +3 -2
  21. package/dist/esm/assemblies/generators/mocks/generateTournamentRecord.mjs +1 -1
  22. package/dist/esm/assemblies/generators/mocks/processLeagueProfiles.mjs +1 -1
  23. package/dist/esm/assemblies/generators/tournamentRecords/createTournamentRecord.mjs +16 -1
  24. package/dist/esm/functions/declaration/recordStore.mjs +1 -1
  25. package/dist/esm/functions/sorters/structureSort.mjs +15 -4
  26. package/dist/esm/mutate/drawDefinitions/addDrawDefinition.mjs +1 -1
  27. package/dist/esm/mutate/drawDefinitions/addDrawEntries.mjs +1 -1
  28. package/dist/esm/mutate/drawDefinitions/attachStructures.mjs +7 -3
  29. package/dist/esm/mutate/drawDefinitions/automatedPlayoffPositioning.mjs +3 -0
  30. package/dist/esm/mutate/drawDefinitions/automatedPositioning.mjs +19 -15
  31. package/dist/esm/mutate/drawDefinitions/entryGovernor/seedAssignment.mjs +1 -1
  32. package/dist/esm/mutate/drawDefinitions/luckyDrawAdvancement.mjs +10 -9
  33. package/dist/esm/mutate/drawDefinitions/matchUpGovernor/attemptToSetWinningSide.mjs +8 -2
  34. package/dist/esm/mutate/drawDefinitions/matchUpGovernor/noDownstreamDependencies.mjs +2 -0
  35. package/dist/esm/mutate/drawDefinitions/matchUpGovernor/placeQualifier.mjs +6 -4
  36. package/dist/esm/mutate/drawDefinitions/matchUpGovernor/recordSourceSideProvenance.mjs +37 -7
  37. package/dist/esm/mutate/drawDefinitions/matchUpGovernor/removeDoubleExit.mjs +85 -19
  38. package/dist/esm/mutate/drawDefinitions/matchUpGovernor/removeQualifier.mjs +42 -44
  39. package/dist/esm/mutate/drawDefinitions/matchUpGovernor/replaceQualifier.mjs +43 -45
  40. package/dist/esm/mutate/drawDefinitions/matchUpGovernor/unwindDualDoubleExit.mjs +1 -1
  41. package/dist/esm/mutate/drawDefinitions/modifyDrawDefinition.mjs +6 -5
  42. package/dist/esm/mutate/drawDefinitions/positionGovernor/addPositionActionTelemetry.mjs +1 -1
  43. package/dist/esm/mutate/drawDefinitions/positionGovernor/byePositioning/positionByes.mjs +11 -4
  44. package/dist/esm/mutate/drawDefinitions/positionGovernor/doubleExitAdvancement.mjs +238 -85
  45. package/dist/esm/mutate/drawDefinitions/positionGovernor/initializeSeedAssignments.mjs +2 -1
  46. package/dist/esm/mutate/drawDefinitions/positionGovernor/setPositionAssignments.mjs +3 -4
  47. package/dist/esm/mutate/drawDefinitions/positionPagePlayoff.mjs +49 -0
  48. package/dist/esm/mutate/drawDefinitions/pruneDrawDefinition.mjs +5 -2
  49. package/dist/esm/mutate/drawDefinitions/remapDrawDefinitionMatchUpIds.mjs +7 -5
  50. package/dist/esm/mutate/drawDefinitions/resetDrawDefinition.mjs +2 -1
  51. package/dist/esm/mutate/drawDefinitions/resetQualifyingStructure.mjs +12 -4
  52. package/dist/esm/mutate/drawDefinitions/structureGovernor/adHocRounds/checkRoundsArgs.mjs +2 -1
  53. package/dist/esm/mutate/drawDefinitions/structureGovernor/removeStructure.mjs +13 -4
  54. package/dist/esm/mutate/drawDefinitions/structureGovernor/renameStructures.mjs +2 -1
  55. package/dist/esm/mutate/events/deleteDrawDefinitions.mjs +1 -1
  56. package/dist/esm/mutate/events/modifyEvent.mjs +1 -2
  57. package/dist/esm/mutate/events/modifyEventPublishStatus.mjs +3 -1
  58. package/dist/esm/mutate/ladder/addLadderParticipant.mjs +4 -2
  59. package/dist/esm/mutate/ladder/applyLadderMovement.mjs +3 -2
  60. package/dist/esm/mutate/ladder/applyLapseConsequence.mjs +2 -1
  61. package/dist/esm/mutate/ladder/issueChallenge.mjs +5 -3
  62. package/dist/esm/mutate/ladder/mirrorStandingToScale.mjs +1 -1
  63. package/dist/esm/mutate/ladder/refreshLadderRatings.mjs +2 -1
  64. package/dist/esm/mutate/ladder/removeLadderParticipant.mjs +7 -19
  65. package/dist/esm/mutate/ladder/reportResult.mjs +5 -3
  66. package/dist/esm/mutate/ladder/respondToChallenge.mjs +4 -2
  67. package/dist/esm/mutate/matchUps/drawPositions/adHocPositionSwap.mjs +2 -1
  68. package/dist/esm/mutate/matchUps/drawPositions/assignDrawPositionBye.mjs +128 -15
  69. package/dist/esm/mutate/matchUps/drawPositions/assignMatchUpSideParticipant.mjs +3 -2
  70. package/dist/esm/mutate/matchUps/drawPositions/directLoser.mjs +10 -6
  71. package/dist/esm/mutate/matchUps/drawPositions/directParticipants.mjs +4 -3
  72. package/dist/esm/mutate/matchUps/drawPositions/drawPositionPlacement.mjs +54 -4
  73. package/dist/esm/mutate/matchUps/drawPositions/normalizeDrawPositions.mjs +1 -6
  74. package/dist/esm/mutate/matchUps/drawPositions/positionClear.mjs +146 -33
  75. package/dist/esm/mutate/matchUps/drawPositions/positionQualifiers.mjs +4 -3
  76. package/dist/esm/mutate/matchUps/drawPositions/positionUnseededParticipants.mjs +2 -1
  77. package/dist/esm/mutate/matchUps/drawPositions/propagateUnfillableLoserBye.mjs +10 -5
  78. package/dist/esm/mutate/matchUps/drawPositions/relabelLoserExit.mjs +113 -20
  79. package/dist/esm/mutate/matchUps/drawPositions/releaseAdvancedDrawPosition.mjs +21 -5
  80. package/dist/esm/mutate/matchUps/drawPositions/releaseLinkedWinnerAdvancement.mjs +44 -9
  81. package/dist/esm/mutate/matchUps/drawPositions/removeDirectedParticipants.mjs +21 -12
  82. package/dist/esm/mutate/matchUps/drawPositions/removeOnwardLoserPlacements.mjs +3 -3
  83. package/dist/esm/mutate/matchUps/drawPositions/removeSubsequentRoundsParticipant.mjs +35 -10
  84. package/dist/esm/mutate/matchUps/drawPositions/setMatchUpDrawPositions.mjs +61 -0
  85. package/dist/esm/mutate/matchUps/drawPositions/swapWinnerLoser.mjs +28 -10
  86. package/dist/esm/mutate/matchUps/lineUps/applyLineUps.mjs +2 -2
  87. package/dist/esm/mutate/matchUps/lineUps/assignTieMatchUpParticipant.mjs +3 -2
  88. package/dist/esm/mutate/matchUps/lineUps/replaceTieMatchUpParticipant.mjs +6 -3
  89. package/dist/esm/mutate/matchUps/matchUpStatus/applyWithdrawnExits.mjs +28 -3
  90. package/dist/esm/mutate/matchUps/matchUpStatus/reconcileDecider.mjs +7 -2
  91. package/dist/esm/mutate/matchUps/matchUpStatus/reconcileLinkAdvancements.mjs +37 -0
  92. package/dist/esm/mutate/matchUps/matchUpStatus/reconcileStaleExitOrigins.mjs +83 -6
  93. package/dist/esm/mutate/matchUps/matchUpStatus/setMatchUpState.mjs +13 -12
  94. package/dist/esm/mutate/matchUps/matchUpStatus/setMatchUpStatus.mjs +27 -15
  95. package/dist/esm/mutate/matchUps/matchUpStatus/settleRederivedDoubleExits.mjs +60 -3
  96. package/dist/esm/mutate/matchUps/matchUpStatus/sideExitProvenance.mjs +45 -11
  97. package/dist/esm/mutate/matchUps/outcome/decide.mjs +39 -7
  98. package/dist/esm/mutate/matchUps/outcome/direction.mjs +15 -0
  99. package/dist/esm/mutate/matchUps/outcome/refusals.mjs +3 -0
  100. package/dist/esm/mutate/matchUps/outcome/view.mjs +45 -26
  101. package/dist/esm/mutate/matchUps/resetScorecard.mjs +5 -8
  102. package/dist/esm/mutate/matchUps/schedule/clearMatchUpSchedule.mjs +1 -1
  103. package/dist/esm/mutate/matchUps/schedule/scheduleItems/scheduleItems.mjs +2 -2
  104. package/dist/esm/mutate/officiating/transitionEvaluationStatus.mjs +1 -1
  105. package/dist/esm/mutate/publishing/unPublishEvent.mjs +5 -3
  106. package/dist/esm/mutate/structures/removeRoundMatchUps.mjs +2 -1
  107. package/dist/esm/mutate/structures/setSubOrder.mjs +4 -3
  108. package/dist/esm/mutate/structures/updateParticipantResults.mjs +4 -3
  109. package/dist/esm/mutate/timeItems/publishOrderOfPlay.mjs +1 -0
  110. package/dist/esm/mutate/timeItems/publishParticipants.mjs +1 -0
  111. package/dist/esm/mutate/tournaments/migrateTournamentRecord.mjs +17 -4
  112. package/dist/esm/mutate/tournaments/setTournamentDates.mjs +1 -1
  113. package/dist/esm/mutate/venues/courtAvailability.mjs +1 -1
  114. package/dist/esm/package.json.mjs +1 -1
  115. package/dist/esm/query/drawDefinition/avoidance/getSwapOptions.mjs +4 -1
  116. package/dist/esm/query/drawDefinition/getByeCrossings.mjs +49 -0
  117. package/dist/esm/query/drawDefinition/getByesData.mjs +4 -3
  118. package/dist/esm/query/drawDefinition/getContainedStructures.mjs +5 -2
  119. package/dist/esm/query/drawDefinition/getDrawData.mjs +6 -2
  120. package/dist/esm/query/drawDefinition/getDrawInconsistencies.mjs +5 -4
  121. package/dist/esm/query/drawDefinition/getLuckyDrawRoundStatus.mjs +7 -5
  122. package/dist/esm/query/drawDefinition/getMatchUpFormatVariance.mjs +5 -4
  123. package/dist/esm/query/drawDefinition/getSourceRounds.mjs +1 -1
  124. package/dist/esm/query/drawDefinition/getStageDrawPositions.mjs +3 -3
  125. package/dist/esm/query/drawDefinition/getStructureCompleteness.mjs +5 -4
  126. package/dist/esm/query/drawDefinition/getStructureInconsistencies.mjs +105 -3
  127. package/dist/esm/query/drawDefinition/getUnearnedLinkAdvancements.mjs +44 -0
  128. package/dist/esm/query/drawDefinition/isActiveDownstream.mjs +27 -11
  129. package/dist/esm/query/drawDefinition/isLucky.mjs +5 -4
  130. package/dist/esm/query/drawDefinition/linkGetter.mjs +7 -8
  131. package/dist/esm/query/drawDefinition/matchUpActions/adHocMatchUpActions.mjs +3 -2
  132. package/dist/esm/query/drawDefinition/matchUpActions/collectionMatchUpActions.mjs +3 -3
  133. package/dist/esm/query/drawDefinition/matchUpActions/matchUpActions.mjs +9 -5
  134. package/dist/esm/query/drawDefinition/positionActions/getValidAlternatesAction.mjs +4 -1
  135. package/dist/esm/query/drawDefinition/positionActions/getValidLuckyLoserAction.mjs +4 -1
  136. package/dist/esm/query/drawDefinition/processAccessors.mjs +1 -1
  137. package/dist/esm/query/drawDefinition/seedGetter.mjs +2 -1
  138. package/dist/esm/query/drawDefinition/stageGetter.mjs +3 -2
  139. package/dist/esm/query/drawDefinitions/swiss/getSwissChart.mjs +2 -1
  140. package/dist/esm/query/drawDefinitions/swiss/getSwissStandings.mjs +2 -1
  141. package/dist/esm/query/event/extractEventInfo.mjs +3 -1
  142. package/dist/esm/query/event/getEventInconsistencies.mjs +5 -4
  143. package/dist/esm/query/ladder/getChallengeState.mjs +10 -2
  144. package/dist/esm/query/ladder/getLadderStanding.mjs +2 -1
  145. package/dist/esm/query/ladder/getLapses.mjs +6 -4
  146. package/dist/esm/query/ladder/getResultAttestation.mjs +3 -2
  147. package/dist/esm/query/ladder/resolveLadderContext.mjs +3 -1
  148. package/dist/esm/query/matchUp/addUpcomingMatchUps.mjs +2 -0
  149. package/dist/esm/query/matchUp/analyzeScore.mjs +1 -1
  150. package/dist/esm/query/matchUp/positionTargets.mjs +37 -14
  151. package/dist/esm/query/matchUp/retainScoreForFormat.mjs +1 -1
  152. package/dist/esm/query/matchUps/addGoesTo.mjs +14 -10
  153. package/dist/esm/query/matchUps/drawMatchUps.mjs +9 -7
  154. package/dist/esm/query/matchUps/getAllStructureMatchUps.mjs +2 -1
  155. package/dist/esm/query/matchUps/getDownstreamStructureIds.mjs +4 -1
  156. package/dist/esm/query/matchUps/getDrawPositionSides.mjs +6 -4
  157. package/dist/esm/query/matchUps/getMatchUpDependencies.mjs +12 -7
  158. package/dist/esm/query/matchUps/getMatchUpsMap.mjs +5 -2
  159. package/dist/esm/query/matchUps/getRoundContextProfile.mjs +2 -1
  160. package/dist/esm/query/matchUps/getTargetMatchUp.mjs +7 -1
  161. package/dist/esm/query/matchUps/scheduling/getMatchUpReadiness.mjs +12 -4
  162. package/dist/esm/query/matchUps/scheduling/getParticipantRest.mjs +39 -3
  163. package/dist/esm/query/matchUps/scheduling/getScheduledRoundsDetails.mjs +6 -2
  164. package/dist/esm/query/officiating/getMatchUpOfficialConflicts.mjs +1 -1
  165. package/dist/esm/query/officiating/getOfficialEligibility.mjs +1 -1
  166. package/dist/esm/query/participants/getParticipantEntries.mjs +4 -3
  167. package/dist/esm/query/participants/getParticipantSchedules.mjs +1 -1
  168. package/dist/esm/query/readModel/cast.mjs +2 -1
  169. package/dist/esm/query/readModel/progressionEdges.mjs +3 -2
  170. package/dist/esm/query/readModel/readModelRows.mjs +9 -2
  171. package/dist/esm/query/reports/recoveryTimeline.mjs +5 -5
  172. package/dist/esm/query/reports/wrappers/wrapCallTimingVarianceReport.mjs +2 -1
  173. package/dist/esm/query/reports/wrappers/wrapCompetitivenessReport.mjs +2 -1
  174. package/dist/esm/query/reports/wrappers/wrapMatchResultsReport.mjs +2 -1
  175. package/dist/esm/query/reports/wrappers/wrapMatchUpStatusReport.mjs +2 -1
  176. package/dist/esm/query/reports/wrappers/wrapParticipantResultsReport.mjs +4 -3
  177. package/dist/esm/query/reports/wrappers/wrapRecoveryTimeReport.mjs +1 -1
  178. package/dist/esm/query/scales/getAggregateTeamResults.mjs +1 -1
  179. package/dist/esm/query/structure/getStructureSeedAssignments.mjs +2 -1
  180. package/dist/esm/query/tournaments/analyzeDraws.mjs +4 -1
  181. package/dist/esm/query/tournaments/getTournamentInconsistencies.mjs +1 -1
  182. package/dist/esm/query/tournaments/getTournamentInfo.mjs +15 -2
  183. package/dist/esm/validators/scoring/mcpParser.mjs +37 -2
  184. package/dist/esm/validators/scoring/mcpValidator.mjs +132 -22
  185. package/dist/esm/validators/validatePlayoffGroups.mjs +1 -1
  186. package/dist/esm/validators/validateSchedulingProfile.mjs +7 -3
  187. package/dist/schema/tournament.schema.json +16 -1
  188. package/dist/tods-competition-factory.d.cts +309 -142
  189. package/dist/tods-competition-factory.d.mts +309 -142
  190. package/dist/tods-competition-factory.d.ts +309 -142
  191. package/dist/tods-competition-factory.development.cjs.js +3809 -2054
  192. package/dist/tods-competition-factory.development.cjs.js.map +1 -1
  193. package/dist/tods-competition-factory.production.cjs.min.js +1 -1
  194. package/dist/tods-competition-factory.production.cjs.min.js.map +1 -1
  195. package/package.json +27 -23
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
- **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.
145
+ **15,800+ 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,6 +1,7 @@
1
1
  import { checkRequiredParameters } from '../helpers/parameters/checkRequiredParameters.mjs';
2
2
  import { firstClassOrExtension } from './firstClassOrExtension.mjs';
3
3
  import { structureSort } from '../functions/sorters/structureSort.mjs';
4
+ import { structuresOf } from './structureMembers.mjs';
4
5
  import { MISSING_DRAW_DEFINITION, MISSING_STRUCTURES, STRUCTURE_NOT_FOUND } from '../constants/errorConditionConstants.mjs';
5
6
  import { STRUCTURE_ID, DRAW_DEFINITION } from '../constants/attributeConstants.mjs';
6
7
  import { validStages, ITEM } from '../constants/drawDefinitionConstants.mjs';
@@ -13,13 +14,14 @@ function findStructure(params) {
13
14
  const { drawDefinition, structureId } = params;
14
15
  const { structures } = getDrawStructures({ drawDefinition });
15
16
  const allStructures = structures?.flatMap((structure) => {
16
- return structure.structures ? [...structure.structures].concat(structure) : structure;
17
+ const contained = structuresOf(structure);
18
+ return contained ? [...contained].concat(structure) : structure;
17
19
  });
18
20
  const structure = allStructures?.find((structure) => structure.structureId === structureId);
19
21
  if (!structure)
20
22
  return { error: STRUCTURE_NOT_FOUND };
21
23
  const containingStructure = structure.structureType === ITEM
22
- ? allStructures?.find((s) => s.structures?.some((s) => s.structureId === structureId))
24
+ ? allStructures?.find((s) => structuresOf(s)?.some((s) => s.structureId === structureId))
23
25
  : undefined;
24
26
  return { structure, containingStructure };
25
27
  }
@@ -0,0 +1,11 @@
1
+ function matchUpsOf(structure) {
2
+ return structure && 'matchUps' in structure ? structure.matchUps : undefined;
3
+ }
4
+ function positionAssignmentsOf(structure) {
5
+ return structure && 'positionAssignments' in structure ? structure.positionAssignments : undefined;
6
+ }
7
+ function structuresOf(structure) {
8
+ return structure && 'structures' in structure ? structure.structures : undefined;
9
+ }
10
+
11
+ export { matchUpsOf, positionAssignmentsOf, structuresOf };
@@ -13,6 +13,25 @@ import { nowIso } from '../../../tools/clock.mjs';
13
13
  import { addPoint, deriveServer, deriveServerBase, checkStandardSetWon, checkAndFinalizeMatch } from '../../../mutate/scoring/addPoint.mjs';
14
14
  import { COMPLETED, TO_BE_PLAYED, IN_PROGRESS } from '../../../constants/matchUpStatusValues.mjs';
15
15
 
16
+ function deriveInitialLineUps(matchUp) {
17
+ const sides = (matchUp.sides ?? []).filter((side) => side.lineUp?.length);
18
+ if (!sides.length)
19
+ return undefined;
20
+ const lineUps = {};
21
+ for (const side of sides)
22
+ lineUps[side.sideNumber] = side.lineUp.map((tc) => ({ ...tc }));
23
+ const entries = matchUp.history?.entries;
24
+ const substitutions = entries
25
+ ? entries.filter((e) => e.type === 'substitution').map((e) => e.data)
26
+ : (matchUp.history?.substitutions ?? []);
27
+ for (const sub of [...substitutions].reverse()) {
28
+ const lineUp = lineUps[sub.sideNumber];
29
+ const index = lineUp?.findIndex((tc) => tc.participantId === sub.inParticipantId) ?? -1;
30
+ if (index !== -1)
31
+ lineUp[index] = { ...lineUp[index], participantId: sub.outParticipantId };
32
+ }
33
+ return lineUps;
34
+ }
16
35
  class ScoringEngine {
17
36
  constructor(options = {}) {
18
37
  this.pointMultipliers = [];
@@ -43,6 +62,7 @@ class ScoringEngine {
43
62
  this.isDoubles = matchUp.matchUpType === 'DOUBLES';
44
63
  this.redoStack = [];
45
64
  this.initialScore = undefined;
65
+ this.initialLineUps = deriveInitialLineUps(matchUp);
46
66
  this.cacheFormatStructure();
47
67
  }
48
68
  getState() {
@@ -53,18 +73,15 @@ class ScoringEngine {
53
73
  const prevTotalGames = this.state.score.sets.reduce((sum, s) => sum + (s.side1Score || 0) + (s.side2Score || 0), 0);
54
74
  const prevCompletedSets = this.state.score.sets.filter((s) => s.winningSide !== undefined).length;
55
75
  const prevComplete = this.state.matchUpStatus === COMPLETED;
56
- const activePlayersSnapshot = this.hasLineUp() ? this.getActivePlayers() : undefined;
76
+ const activePlayers = this.activePlayersForPoint();
57
77
  this.state = addPoint(this.state, options, {
58
78
  pointMultipliers: this.pointMultipliers,
59
79
  });
60
- if (activePlayersSnapshot) {
80
+ if ((this.state.history?.points.length || 0) === pointIndex)
81
+ return;
82
+ if (activePlayers) {
61
83
  const lastPoint = this.state.history.points[this.state.history.points.length - 1];
62
- if (this.isDoubles) {
63
- lastPoint.activePlayers = [activePlayersSnapshot.side1, activePlayersSnapshot.side2];
64
- }
65
- else {
66
- lastPoint.activePlayers = [activePlayersSnapshot.side1[0] || '', activePlayersSnapshot.side2[0] || ''];
67
- }
84
+ lastPoint.activePlayers = activePlayers;
68
85
  }
69
86
  if (options.penaltyType) {
70
87
  const lastPoint = this.state.history.points[this.state.history.points.length - 1];
@@ -393,6 +410,12 @@ class ScoringEngine {
393
410
  hasLineUp() {
394
411
  return this.state.sides.some((s) => s.lineUp && s.lineUp.length > 0);
395
412
  }
413
+ activePlayersForPoint() {
414
+ if (!this.hasLineUp())
415
+ return undefined;
416
+ const { side1, side2 } = this.getActivePlayers();
417
+ return this.isDoubles ? [side1, side2] : [side1[0] || '', side2[0] || ''];
418
+ }
396
419
  setPointMultipliers(multipliers) {
397
420
  this.pointMultipliers = multipliers;
398
421
  }
@@ -445,8 +468,14 @@ class ScoringEngine {
445
468
  const point = this.state.history?.points[pointIndex];
446
469
  if (point) {
447
470
  Object.assign(point, metadata);
471
+ const entry = this.pointEntry(pointIndex);
472
+ if (entry)
473
+ Object.assign(entry.data, metadata);
448
474
  }
449
475
  }
476
+ pointEntry(pointIndex) {
477
+ return this.state.history?.entries?.find((e) => e.type === 'point' && e.pointIndex === pointIndex);
478
+ }
450
479
  markHardBoundary(options) {
451
480
  const set = this.state.score.sets[options.setIndex];
452
481
  if (!set)
@@ -463,32 +492,32 @@ class ScoringEngine {
463
492
  return;
464
493
  const shouldRecalculate = options?.recalculate !== false;
465
494
  const point = points[pointIndex];
466
- if (newData.winner !== undefined)
467
- point.winner = newData.winner;
468
- if (newData.server !== undefined)
469
- point.server = newData.server;
470
- if (newData.timestamp !== undefined)
471
- point.timestamp = newData.timestamp;
472
- if (newData.rallyLength !== undefined)
473
- point.rallyLength = newData.rallyLength;
474
- if (newData.wrongSide !== undefined)
475
- point.wrongSide = newData.wrongSide;
476
- if (newData.wrongServer !== undefined)
477
- point.wrongServer = newData.wrongServer;
478
- if (newData.penaltyPoint !== undefined)
479
- point.penaltyPoint = newData.penaltyPoint;
495
+ const entry = this.pointEntry(pointIndex);
496
+ const edits = Object.fromEntries(Object.entries(newData).filter(([, value]) => value !== undefined));
497
+ const pointEdits = { ...edits };
498
+ const entryEdits = { ...edits };
499
+ if (edits.winner !== undefined) {
500
+ pointEdits.winningSide = edits.winner + 1;
501
+ entryEdits.winningSide = undefined;
502
+ }
503
+ else if (edits.winningSide !== undefined) {
504
+ pointEdits.winner = edits.winningSide - 1;
505
+ entryEdits.winner = undefined;
506
+ }
507
+ if (edits.server !== undefined) {
508
+ pointEdits.serverSideNumber = edits.server + 1;
509
+ entryEdits.serverSideNumber = undefined;
510
+ }
511
+ else if (edits.serverSideNumber !== undefined) {
512
+ pointEdits.server = edits.serverSideNumber - 1;
513
+ entryEdits.server = undefined;
514
+ }
515
+ Object.assign(point, pointEdits);
516
+ if (entry)
517
+ Object.assign(entry.data, entryEdits);
480
518
  if (!shouldRecalculate)
481
519
  return;
482
520
  const entries = this.state.history?.entries;
483
- if (entries) {
484
- const pointEntry = entries.find((e) => e.type === 'point' && e.pointIndex === pointIndex);
485
- if (pointEntry) {
486
- if (newData.winner !== undefined)
487
- pointEntry.data.winner = newData.winner;
488
- if (newData.server !== undefined)
489
- pointEntry.data.server = newData.server;
490
- }
491
- }
492
521
  if (entries && entries.length > 0) {
493
522
  this.rebuildFromEntries();
494
523
  }
@@ -691,12 +720,19 @@ class ScoringEngine {
691
720
  this.state = newState;
692
721
  for (const entry of entries) {
693
722
  switch (entry.type) {
694
- case 'point':
723
+ case 'point': {
724
+ const activePlayers = this.activePlayersForPoint();
725
+ const pointCount = this.state.history?.points.length || 0;
695
726
  this.state = addPoint(this.state, entry.data, {
696
727
  pointMultipliers: this.pointMultipliers,
697
728
  });
698
729
  this.state.history.entries = newState.history.entries;
730
+ const points = this.state.history.points;
731
+ const played = points.length > pointCount ? points.at(-1) : undefined;
732
+ if (activePlayers && played)
733
+ played.activePlayers = activePlayers;
699
734
  break;
735
+ }
700
736
  case 'set':
701
737
  this.applyAddSet(entry.data);
702
738
  break;
@@ -759,6 +795,11 @@ class ScoringEngine {
759
795
  matchUpId: this.matchUpId,
760
796
  isDoubles: this.isDoubles,
761
797
  });
798
+ for (const side of newState.sides) {
799
+ const lineUp = this.state.sides.find((s) => s.sideNumber === side.sideNumber)?.lineUp;
800
+ if (lineUp)
801
+ side.lineUp = lineUp.map((tc) => ({ ...tc }));
802
+ }
762
803
  if (this.initialScore) {
763
804
  this.applyInitialScore(newState, this.initialScore);
764
805
  if (newState.matchUpStatus === TO_BE_PLAYED) {
@@ -766,15 +807,24 @@ class ScoringEngine {
766
807
  }
767
808
  }
768
809
  for (const point of currentPoints) {
810
+ const pointCount = newState.history?.points.length || 0;
769
811
  newState = addPoint(newState, {
770
812
  winner: point.winner,
771
813
  server: point.server,
772
814
  timestamp: point.timestamp,
773
815
  rallyLength: point.rallyLength,
774
816
  result: point.result,
817
+ penaltyType: point.penaltyType,
818
+ penaltyPoint: point.penaltyPoint,
819
+ wrongSide: point.wrongSide,
820
+ wrongServer: point.wrongServer,
775
821
  }, {
776
822
  pointMultipliers: this.pointMultipliers,
777
823
  });
824
+ const replayed = newState.history?.points ?? [];
825
+ const played = replayed.length > pointCount ? replayed.at(-1) : undefined;
826
+ if (point.activePlayers && played)
827
+ played.activePlayers = point.activePlayers;
778
828
  }
779
829
  this.state = newState;
780
830
  }
@@ -25,14 +25,16 @@ function autoSeeding({ tournamentRecord, drawDefinition, policyDefinitions, scal
25
25
  entries,
26
26
  stage,
27
27
  }).scaledEntries ?? [];
28
- const { scaleItemsWithParticipantIds } = generateSeedingScaleItems({
28
+ const seedingResult = generateSeedingScaleItems({
29
29
  scaleAttributes,
30
30
  scaledEntries,
31
31
  stageEntries,
32
32
  seedsCount,
33
33
  scaleName,
34
34
  });
35
- return { scaleItemsWithParticipantIds };
35
+ if (seedingResult.error)
36
+ return seedingResult;
37
+ return { scaleItemsWithParticipantIds: seedingResult.scaleItemsWithParticipantIds };
36
38
  }
37
39
 
38
40
  export { autoSeeding };
@@ -50,8 +50,6 @@ function drawMatic(params) {
50
50
  return { ...SUCCESS, matchUps, roundResults };
51
51
  }
52
52
  function getAdHocStructure(params) {
53
- if (params.structureId)
54
- return params.structureId;
55
53
  const drawDefinition = params.drawDefinition;
56
54
  const targetStructure = drawDefinition?.structures
57
55
  ?.filter((structure) => structure.stageSequence === 1)
@@ -60,7 +58,7 @@ function getAdHocStructure(params) {
60
58
  const structureIsAdHoc = isAdHoc({ structure });
61
59
  return structureIsAdHoc && orderNumber > (stageOrder[targetStructure?.stage] || 1) ? structure : targetStructure;
62
60
  }, undefined);
63
- const structure = drawDefinition?.structures?.find((structure) => structure.structureId === targetStructure?.structureId);
61
+ const structure = drawDefinition?.structures?.find((structure) => structure.structureId === (params.structureId ?? targetStructure?.structureId));
64
62
  if (!structure)
65
63
  return { error: STRUCTURE_NOT_FOUND };
66
64
  const structureIsAdHoc = isAdHoc({ structure });
@@ -1,9 +1,10 @@
1
1
  import { getCompetitionPolicy } from '../../../../../../query/drawDefinition/competition/getCompetitionPolicy.mjs';
2
2
  import { getCompetitionState } from '../../../../../../query/drawDefinition/competition/getCompetitionState.mjs';
3
3
  import { buildIndividualIdsMap } from '../../../../../../query/participants/individualParticipantIds.mjs';
4
- import { generateDynamicRatings } from '../../../../scales/generateDynamicRatings.mjs';
5
4
  import { generateAdHocMatchUps } from '../generateAdHocMatchUps.mjs';
5
+ import { generateDynamicRatings } from '../../../../scales/generateDynamicRatings.mjs';
6
6
  import { findStructure } from '../../../../../../acquire/findStructure.mjs';
7
+ import { matchUpsOf } from '../../../../../../acquire/structureMembers.mjs';
7
8
  import { getPairingsData } from './getPairingsData.mjs';
8
9
  import { getEncounters } from './getEncounters.mjs';
9
10
  import { getPairings } from './getPairings.mjs';
@@ -27,7 +28,7 @@ function generateDrawMaticRound(params) {
27
28
  return { error: MISSING_STRUCTURE };
28
29
  if (!participantIds?.length)
29
30
  return { error: MISSING_PARTICIPANT_IDS };
30
- const consideredMatchUps = [...(iterationMatchUps ?? []), ...(structure?.matchUps ?? [])];
31
+ const consideredMatchUps = [...(iterationMatchUps ?? []), ...(matchUpsOf(structure) ?? [])];
31
32
  const { encounters } = getEncounters({ matchUps: consideredMatchUps });
32
33
  const tournamentParticipants = tournamentRecord?.participants ?? [];
33
34
  let modifiedScaleValues = {};
@@ -43,10 +44,10 @@ function generateDrawMaticRound(params) {
43
44
  }
44
45
  }
45
46
  else if (dynamicRatings) {
46
- const roundNumbers = unique(structure?.matchUps ? structure.matchUps.map(({ roundNumber }) => roundNumber) : []);
47
+ const roundNumbers = unique(matchUpsOf(structure)?.map(({ roundNumber }) => roundNumber) ?? []);
47
48
  const lastRoundNumber = Math.max(...roundNumbers, 0);
48
49
  if (lastRoundNumber) {
49
- const matchUpIds = structure?.matchUps
50
+ const matchUpIds = matchUpsOf(structure)
50
51
  ?.filter(({ roundNumber }) => roundNumber === lastRoundNumber)
51
52
  .map(({ matchUpId }) => matchUpId);
52
53
  const result = generateDynamicRatings({
@@ -1,4 +1,5 @@
1
1
  import { checkRequiredParameters } from '../../../../../helpers/parameters/checkRequiredParameters.mjs';
2
+ import { matchUpsOf, structuresOf } from '../../../../../acquire/structureMembers.mjs';
2
3
  import { MISSING_STRUCTURE_ID, STRUCTURE_NOT_FOUND, INVALID_STRUCTURE } from '../../../../../constants/errorConditionConstants.mjs';
3
4
  import { STRUCTURE_SELECTED_STATUSES } from '../../../../../constants/entryStatusConstants.mjs';
4
5
  import { ROUND_OUTCOME } from '../../../../../constants/drawDefinitionConstants.mjs';
@@ -16,13 +17,13 @@ function getAvailableMatchUpsCount(params) {
16
17
  if (!structure)
17
18
  return { error: STRUCTURE_NOT_FOUND };
18
19
  let structureHasRoundPositions;
19
- const existingMatchUps = structure.matchUps ?? [];
20
+ const existingMatchUps = matchUpsOf(structure) ?? [];
20
21
  const lastRoundNumber = existingMatchUps?.reduce((roundNumber, matchUp) => {
21
22
  if (matchUp.roundPosition)
22
23
  structureHasRoundPositions = true;
23
24
  return (matchUp?.roundNumber || 0) > roundNumber ? matchUp.roundNumber : roundNumber;
24
25
  }, 0);
25
- if (structure.structures || structureHasRoundPositions || structure.finishingPosition === ROUND_OUTCOME) {
26
+ if (structuresOf(structure) || structureHasRoundPositions || structure.finishingPosition === ROUND_OUTCOME) {
26
27
  return { error: INVALID_STRUCTURE };
27
28
  }
28
29
  const selectedEntries = drawDefinition?.entries?.filter((entry) => {
@@ -31,7 +32,7 @@ function getAvailableMatchUpsCount(params) {
31
32
  }) ?? [];
32
33
  const roundMatchUpsCount = Math.floor(selectedEntries?.length / 2) || 1;
33
34
  const targetRoundNumber = roundNumber ?? lastRoundNumber ?? 1;
34
- const existingRoundMatchUps = structure.matchUps?.filter((matchUp) => matchUp.roundNumber === targetRoundNumber)?.length ?? 0;
35
+ const existingRoundMatchUps = matchUpsOf(structure)?.filter((matchUp) => matchUp.roundNumber === targetRoundNumber)?.length ?? 0;
35
36
  const maxRemaining = roundMatchUpsCount - existingRoundMatchUps;
36
37
  const availableMatchUpsCount = maxRemaining > 0 ? maxRemaining : 0;
37
38
  return { ...SUCCESS, availableMatchUpsCount, lastRoundNumber, roundMatchUpsCount };
@@ -7,6 +7,7 @@ import { getParticipantIds } from '../drawMatic/getParticipantIds.mjs';
7
7
  import { generateAdHocMatchUps } from '../generateAdHocMatchUps.mjs';
8
8
  import { getAdHocRatings } from '../drawMatic/getAdHocRatings.mjs';
9
9
  import { generateSwissPairings } from './swissPairing.mjs';
10
+ import { positionAssignmentsOf, matchUpsOf } from '../../../../../../acquire/structureMembers.mjs';
10
11
  import { findStructure } from '../../../../../../acquire/findStructure.mjs';
11
12
  import { findExtension } from '../../../../../../acquire/findExtension.mjs';
12
13
  import { WINNER, QUALIFYING } from '../../../../../../constants/drawDefinitionConstants.mjs';
@@ -28,7 +29,7 @@ function generateSwissRound(params) {
28
29
  if (idsResult.error)
29
30
  return idsResult;
30
31
  const entryParticipantIds = idsResult.participantIds ?? [];
31
- const positionParticipantIds = structure.positionAssignments
32
+ const positionParticipantIds = positionAssignmentsOf(structure)
32
33
  ?.map((pa) => pa.participantId)
33
34
  .filter((pid) => !!pid) ?? [];
34
35
  const qualifierWinnerIds = getQualifierWinners({ drawDefinition, targetStructureId: structure.structureId });
@@ -63,7 +64,7 @@ function generateSwissRound(params) {
63
64
  }
64
65
  const swissPolicy = params.swissPolicy ??
65
66
  findExtension({ element: drawDefinition, name: 'swissPolicy' })?.extension?.value;
66
- const existingMatchUps = structure.matchUps ?? [];
67
+ const existingMatchUps = matchUpsOf(structure) ?? [];
67
68
  const { participantIdPairings, byeParticipantId } = generateSwissPairings({
68
69
  allowDraws: swissPolicy?.allowDraws,
69
70
  matchUps: existingMatchUps,
@@ -49,7 +49,8 @@ function generateVoluntaryConsolation(params) {
49
49
  const structureCount = stageStructures.length;
50
50
  if (structureCount > 1)
51
51
  return { error: STAGE_SEQUENCE_LIMIT };
52
- if (stageStructures?.[0]?.matchUps?.length)
52
+ const existingMatchUps = getAllStructureMatchUps({ structure: stageStructures?.[0] }).matchUps;
53
+ if (existingMatchUps.length)
53
54
  return { error: EXISTING_STRUCTURE };
54
55
  const structureId = stageStructures?.[0]?.structureId;
55
56
  Object.assign(params, definedAttributes({
@@ -70,7 +70,9 @@ function luckyRoundProfiles(drawSize) {
70
70
  const intDrawSize = ensureInt(drawSize);
71
71
  let participantsCount = intDrawSize % 2 ? intDrawSize + 1 : intDrawSize;
72
72
  const preFeedRound = !!(Math.ceil(participantsCount / 2) % 2);
73
- const rounds = [{ participantsCount, preFeedRound }];
73
+ const rounds = [
74
+ { participantsCount, preFeedRound },
75
+ ];
74
76
  while (participantsCount > 2) {
75
77
  const nextRound = Math.ceil(participantsCount / 2);
76
78
  const nextIsFinal = nextRound === 1;
@@ -5,6 +5,7 @@ import { generatePlayoffStructures } from './drawTypes/playoffStructures.mjs';
5
5
  import { directParticipants } from '../../../mutate/matchUps/drawPositions/directParticipants.mjs';
6
6
  import { resolveTieFormat } from '../../../query/hierarchical/tieFormats/resolveTieFormat.mjs';
7
7
  import { getAllStructureMatchUps } from '../../../query/matchUps/getAllStructureMatchUps.mjs';
8
+ import { getSideDrawPosition } from '../../../query/matchUps/getDrawPositionSides.mjs';
8
9
  import { isLuckyBasedDraw } from '../../../query/drawDefinition/isLuckyBasedDraw.mjs';
9
10
  import { matchUpCompletion } from '../../../query/matchUp/checkMatchUpIsComplete.mjs';
10
11
  import { processPlayoffGroups } from './drawTypes/processPlayoffGroups.mjs';
@@ -14,6 +15,7 @@ import { getAllDrawMatchUps } from '../../../query/matchUps/drawMatchUps.mjs';
14
15
  import { positionTargets } from '../../../query/matchUp/positionTargets.mjs';
15
16
  import { getMatchUpId } from '../../../functions/global/extractors.mjs';
16
17
  import { pushGlobalLog } from '../../../functions/global/globalLog.mjs';
18
+ import { structuresOf } from '../../../acquire/structureMembers.mjs';
17
19
  import { findStructure } from '../../../acquire/findStructure.mjs';
18
20
  import { addGoesTo } from '../../../query/matchUps/addGoesTo.mjs';
19
21
  import { generateTieMatchUps } from './tieMatchUps.mjs';
@@ -162,7 +164,7 @@ function generateAndPopulatePlayoffStructures(params) {
162
164
  });
163
165
  }
164
166
  }
165
- advanceCompletedMatchUps({
167
+ const advanced = advanceCompletedMatchUps({
166
168
  inContextDrawMatchUps,
167
169
  sourceStructureId,
168
170
  tournamentRecord,
@@ -171,13 +173,17 @@ function generateAndPopulatePlayoffStructures(params) {
171
173
  structure,
172
174
  event,
173
175
  });
174
- advanceByeMatchUps({
176
+ if (advanced?.error)
177
+ return decorateResult({ result: advanced, stack });
178
+ const byesAdvanced = advanceByeMatchUps({
175
179
  inContextDrawMatchUps,
176
180
  sourceStructureId,
177
181
  tournamentRecord,
178
182
  drawDefinition,
179
183
  event,
180
184
  });
185
+ if (byesAdvanced?.error)
186
+ return decorateResult({ result: byesAdvanced, stack });
181
187
  const matchUpModifications = buildMatchUpModifications({
182
188
  inContextDrawMatchUps,
183
189
  sourceStructureId,
@@ -187,6 +193,8 @@ function generateAndPopulatePlayoffStructures(params) {
187
193
  params,
188
194
  stack,
189
195
  });
196
+ if (!Array.isArray(matchUpModifications))
197
+ return decorateResult({ result: matchUpModifications, stack });
190
198
  return {
191
199
  structures: newStructures,
192
200
  matchUpModifications,
@@ -212,7 +220,7 @@ function resolvePlayoffParams(params, stack) {
212
220
  if (!structure) {
213
221
  return { error: true, earlyReturn: decorateResult({ result: { error: STRUCTURE_NOT_FOUND }, stack }) };
214
222
  }
215
- if (structure.structureType === CONTAINER || structure.structures) {
223
+ if (structure.structureType === CONTAINER || structuresOf(structure)) {
216
224
  return {
217
225
  error: true,
218
226
  earlyReturn: generateAndPopulateRRplayoffStructures({
@@ -291,7 +299,7 @@ function generatePositionBasedPlayoffs({ sourceStructureId, tournamentRecord, dr
291
299
  inContext: true,
292
300
  drawDefinition,
293
301
  });
294
- advanceCompletedMatchUps({
302
+ const advanced = advanceCompletedMatchUps({
295
303
  inContextDrawMatchUps,
296
304
  sourceStructureId,
297
305
  tournamentRecord,
@@ -300,13 +308,17 @@ function generatePositionBasedPlayoffs({ sourceStructureId, tournamentRecord, dr
300
308
  structure,
301
309
  event,
302
310
  });
303
- advanceByeMatchUps({
311
+ if (advanced?.error)
312
+ return decorateResult({ result: advanced, stack });
313
+ const byesAdvanced = advanceByeMatchUps({
304
314
  inContextDrawMatchUps,
305
315
  sourceStructureId,
306
316
  tournamentRecord,
307
317
  drawDefinition,
308
318
  event,
309
319
  });
320
+ if (byesAdvanced?.error)
321
+ return decorateResult({ result: byesAdvanced, stack });
310
322
  return {
311
323
  structures: playoffStructures,
312
324
  links: playoffLinks,
@@ -337,13 +349,15 @@ function computePlayoffDrawSize({ finishingPositionsCount, isLuckyDraw, roundNum
337
349
  }
338
350
  function advanceCompletedMatchUps({ inContextDrawMatchUps, sourceStructureId, tournamentRecord, drawDefinition, matchUpsMap, structure, event, }) {
339
351
  const completedMatchUps = inContextDrawMatchUps?.filter((matchUp) => matchUpCompletion(matchUp) && matchUp.structureId === sourceStructureId);
340
- completedMatchUps?.forEach((matchUp) => {
352
+ for (const matchUp of completedMatchUps ?? []) {
341
353
  const { matchUpId, score, winningSide } = matchUp;
342
354
  const targetData = positionTargets({
343
355
  inContextDrawMatchUps,
344
356
  drawDefinition,
345
357
  matchUpId,
346
358
  });
359
+ if (targetData.error)
360
+ return targetData;
347
361
  const result = directParticipants({
348
362
  inContextDrawMatchUps,
349
363
  tournamentRecord,
@@ -359,21 +373,31 @@ function advanceCompletedMatchUps({ inContextDrawMatchUps, sourceStructureId, to
359
373
  });
360
374
  if (result.error)
361
375
  pushGlobalLog({ method: 'generateAndPopulatePlayoffStructures', error: result.error });
362
- });
376
+ }
377
+ return undefined;
363
378
  }
364
- function advanceByeMatchUps({ inContextDrawMatchUps, sourceStructureId, tournamentRecord, drawDefinition, event }) {
379
+ function advanceByeMatchUps({ inContextDrawMatchUps, sourceStructureId, tournamentRecord, drawDefinition, event, }) {
365
380
  const byeMatchUps = inContextDrawMatchUps?.filter((matchUp) => matchUp.matchUpStatus === BYE && matchUp.structureId === sourceStructureId);
366
- byeMatchUps?.forEach((matchUp) => {
381
+ for (const matchUp of byeMatchUps ?? []) {
367
382
  const { matchUpId } = matchUp;
368
383
  const targetData = positionTargets({
369
384
  inContextDrawMatchUps,
370
385
  drawDefinition,
371
386
  matchUpId,
372
387
  });
388
+ if (targetData.error)
389
+ return targetData;
373
390
  const { targetLinks: { loserTargetLink }, targetMatchUps: { loserMatchUpDrawPositionIndex, loserMatchUp }, } = targetData;
374
- if (loserTargetLink && loserMatchUp) {
391
+ if (loserTargetLink && loserMatchUp?.drawPositions && loserMatchUpDrawPositionIndex !== undefined) {
375
392
  const targetStructureId = loserTargetLink.target.structureId;
376
- const targetDrawPosition = loserMatchUp.drawPositions[loserMatchUpDrawPositionIndex];
393
+ const targetDrawPosition = getSideDrawPosition({
394
+ sideNumber: loserMatchUpDrawPositionIndex + 1,
395
+ structureId: targetStructureId,
396
+ matchUp: loserMatchUp,
397
+ drawDefinition,
398
+ });
399
+ if (!targetDrawPosition)
400
+ continue;
377
401
  const result = assignDrawPositionBye({
378
402
  drawPosition: targetDrawPosition,
379
403
  structureId: targetStructureId,
@@ -384,15 +408,19 @@ function advanceByeMatchUps({ inContextDrawMatchUps, sourceStructureId, tourname
384
408
  if (result.error)
385
409
  pushGlobalLog({ method: 'generateAndPopulatePlayoffStructures', error: result.error });
386
410
  }
387
- });
411
+ }
412
+ return undefined;
388
413
  }
389
414
  function buildMatchUpModifications({ inContextDrawMatchUps, sourceStructureId, tournamentRecord, drawDefinition, matchUpsMap, params, stack, }) {
390
415
  const matchUpModifications = [];
391
- const goesToMap = addGoesTo({
416
+ const goesTo = addGoesTo({
392
417
  inContextDrawMatchUps,
393
418
  drawDefinition,
394
419
  matchUpsMap,
395
- }).goesToMap;
420
+ });
421
+ if (goesTo.error)
422
+ return goesTo;
423
+ const goesToMap = goesTo.goesToMap;
396
424
  const { structure: sourceStructure } = findStructure({
397
425
  drawDefinition: params.drawDefinition,
398
426
  structureId: sourceStructureId,
@@ -427,4 +455,4 @@ function buildMatchUpModifications({ inContextDrawMatchUps, sourceStructureId, t
427
455
  return matchUpModifications;
428
456
  }
429
457
 
430
- export { generateAndPopulatePlayoffStructures };
458
+ export { advanceByeMatchUps, generateAndPopulatePlayoffStructures };
@@ -1,13 +1,18 @@
1
1
  import { generateDrawTypeAndModifyDrawDefinition } from '../generateDrawTypeAndModifyDrawDefinition.mjs';
2
2
  import { generateQualifyingLink } from '../links/generateQualifyingLink.mjs';
3
3
  import { addDrawEntry } from '../../../../mutate/drawDefinitions/entryGovernor/addDrawEntries.mjs';
4
+ import { getQualifiersCount } from '../../../../query/drawDefinition/getQualifiersCount.mjs';
4
5
  import { isLuckyBasedDraw } from '../../../../query/drawDefinition/isLuckyBasedDraw.mjs';
5
6
  import structureTemplate from '../../templates/structureTemplate.mjs';
7
+ import { getStageEntries } from '../../../../query/drawDefinition/stageGetter.mjs';
8
+ import { decorateResult } from '../../../../functions/global/decorateResult.mjs';
6
9
  import { isAdHocType } from '../../../../query/drawDefinition/isAdHocType.mjs';
7
10
  import { constantToString } from '../../../../tools/strings.mjs';
8
11
  import { generateAdHoc } from './generateAdHoc.mjs';
9
12
  import { prepareStage } from './prepareStage.mjs';
10
13
  import { ensureInt } from '../../../../tools/ensureInt.mjs';
14
+ import { DIRECT_ENTRY_STATUSES } from '../../../../constants/entryStatusConstants.mjs';
15
+ import { INSUFFICIENT_DRAW_POSITIONS } from '../../../../constants/errorConditionConstants.mjs';
11
16
  import { MAIN, QUALIFYING, POSITION } from '../../../../constants/drawDefinitionConstants.mjs';
12
17
  import { WITHDRAWN } from '../../../../constants/entryStatusValues.mjs';
13
18
 
@@ -60,6 +65,9 @@ function generateNewDrawDefinition(params) {
60
65
  drawDefinition.links ??= [];
61
66
  drawDefinition.links.push(link);
62
67
  }
68
+ const fitResult = checkMainFits({ ...params, drawDefinition, mainStructureId });
69
+ if (fitResult.error)
70
+ return fitResult;
63
71
  const seedsCount = isLuckyBasedDraw(drawType) ? 0 : ensureInt(params.seedsCount ?? 0);
64
72
  const structureResult = prepareStage({
65
73
  ...params,
@@ -112,5 +120,21 @@ function addEntries(params) {
112
120
  }
113
121
  return { error: undefined };
114
122
  }
123
+ function checkMainFits(params) {
124
+ const { drawDefinition, mainStructureId, qualifyingOnly, drawSize, drawType, qualifiersCount } = params;
125
+ if (!drawDefinition || !mainStructureId || qualifyingOnly || !drawSize || isAdHocType(drawType))
126
+ return {};
127
+ const stageScope = { stage: MAIN, stageSequence: 1, drawDefinition };
128
+ const directEntries = getStageEntries({ ...stageScope, entryStatuses: DIRECT_ENTRY_STATUSES });
129
+ const derivedQualifiersCount = getQualifiersCount({ ...stageScope, structureId: mainStructureId }).qualifiersCount;
130
+ const reservedQualifiersCount = derivedQualifiersCount || qualifiersCount || 0;
131
+ if (directEntries.length + reservedQualifiersCount <= drawSize)
132
+ return {};
133
+ return decorateResult({
134
+ context: { drawSize, directEntriesCount: directEntries.length, qualifiersCount: reservedQualifiersCount },
135
+ result: { error: INSUFFICIENT_DRAW_POSITIONS },
136
+ stack: 'generateNewDrawDefinition',
137
+ });
138
+ }
115
139
 
116
140
  export { generateNewDrawDefinition };