tods-competition-factory 7.3.1 → 7.4.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.
- package/dist/esm/mutate/drawDefinitions/matchUpGovernor/noDownstreamDependencies.mjs +4 -0
- package/dist/esm/mutate/drawDefinitions/matchUpGovernor/removeDoubleExit.mjs +3 -17
- package/dist/esm/mutate/drawDefinitions/matchUpGovernor/unwindDualDoubleExit.mjs +13 -0
- package/dist/esm/mutate/drawDefinitions/positionGovernor/doubleExitAdvancement.mjs +7 -0
- package/dist/esm/mutate/matchUps/drawPositions/assignDrawPositionBye.mjs +62 -34
- package/dist/esm/mutate/matchUps/drawPositions/positionClear.mjs +47 -13
- package/dist/esm/mutate/matchUps/matchUpStatus/reconcileDecider.mjs +4 -1
- package/dist/esm/mutate/matchUps/matchUpStatus/setMatchUpState.mjs +11 -3
- package/dist/esm/mutate/venues/modifyCourt.mjs +1 -1
- package/dist/esm/package.json.mjs +1 -1
- package/dist/esm/query/matchUp/checkSetIsComplete.mjs +13 -4
- package/dist/esm/query/matchUp/getComplement.mjs +1 -1
- package/dist/esm/validators/validateMatchUpScore.mjs +14 -0
- package/dist/schema/tournament.schema.json +4421 -0
- package/dist/tods-competition-factory.d.cts +2 -0
- package/dist/tods-competition-factory.d.mts +2 -0
- package/dist/tods-competition-factory.d.ts +2 -0
- package/dist/tods-competition-factory.development.cjs.js +356 -257
- package/dist/tods-competition-factory.development.cjs.js.map +1 -1
- package/dist/tods-competition-factory.production.cjs.min.js +1 -1
- package/dist/tods-competition-factory.production.cjs.min.js.map +1 -1
- package/package.json +5 -3
|
@@ -8,6 +8,7 @@ import { checkConnectedStructures } from './checkConnectedStructures.mjs';
|
|
|
8
8
|
import { attemptToSetWinningSide } from './attemptToSetWinningSide.mjs';
|
|
9
9
|
import { decorateResult } from '../../../functions/global/decorateResult.mjs';
|
|
10
10
|
import { attemptToModifyScore } from './attemptToModifyScore.mjs';
|
|
11
|
+
import { unwindDualDoubleExit } from './unwindDualDoubleExit.mjs';
|
|
11
12
|
import { pushGlobalLog } from '../../../functions/global/globalLog.mjs';
|
|
12
13
|
import { isExit, isDoubleExit } from '../../../validators/isExit.mjs';
|
|
13
14
|
import { removeDoubleExit } from './removeDoubleExit.mjs';
|
|
@@ -25,6 +26,9 @@ function noDownstreamDependencies(params) {
|
|
|
25
26
|
if (result.error)
|
|
26
27
|
return decorateResult({ result, stack });
|
|
27
28
|
}
|
|
29
|
+
const dualUnwound = unwindDualDoubleExit(params);
|
|
30
|
+
if (dualUnwound.error)
|
|
31
|
+
return decorateResult({ result: dualUnwound, stack });
|
|
28
32
|
const doubleExit = isDoubleExit(matchUpStatus);
|
|
29
33
|
const preserveScoreStatuses = [IN_PROGRESS, SUSPENDED, CANCELLED, ABANDONED, INCOMPLETE];
|
|
30
34
|
const scoreWithNoWinningSide = checkScoreHasValue({ score }) &&
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
import { withdrawByeClaim, withdrawByeClaimsFrom, byeClaimSurvives, getSideExitProvenance, retainPolicyCodes, setSideExitProvenance, retainForeignProvenance, deriveExitStateFromProvenance } from '../../matchUps/matchUpStatus/sideExitProvenance.mjs';
|
|
2
1
|
import { removeDirectedBye, removeDirectedWinner } from '../../matchUps/drawPositions/removeDirectedParticipants.mjs';
|
|
3
2
|
import { propagatesByeOnDoubleExit } from '../../matchUps/drawPositions/propagatesByeOnDoubleExit.mjs';
|
|
4
3
|
import { getPairedPreviousMatchUp } from '../../../query/matchUps/getPairedPreviousMatchup.mjs';
|
|
5
4
|
import { modifyMatchUpScore } from '../../matchUps/score/modifyMatchUpScore.mjs';
|
|
6
5
|
import { decorateResult } from '../../../functions/global/decorateResult.mjs';
|
|
7
|
-
import { chunkArray, intersection, overlap } from '../../../tools/arrays.mjs';
|
|
8
6
|
import { positionTargets } from '../../../query/matchUp/positionTargets.mjs';
|
|
9
7
|
import { pushGlobalLog } from '../../../functions/global/globalLog.mjs';
|
|
10
8
|
import { findStructure } from '../../../acquire/findStructure.mjs';
|
|
9
|
+
import { intersection, overlap } from '../../../tools/arrays.mjs';
|
|
11
10
|
import { isDoubleExit } from '../../../validators/isExit.mjs';
|
|
12
|
-
import {
|
|
11
|
+
import { withdrawByeClaim, withdrawByeClaimsFrom, byeClaimSurvives, getSideExitProvenance, retainPolicyCodes, setSideExitProvenance, retainForeignProvenance, deriveExitStateFromProvenance } from '../../matchUps/matchUpStatus/sideExitProvenance.mjs';
|
|
13
12
|
import { SUCCESS } from '../../../constants/resultConstants.mjs';
|
|
14
13
|
import { completedMatchUpStatuses } from '../../../constants/matchUpStatusConstants.mjs';
|
|
15
|
-
import { BYE,
|
|
14
|
+
import { BYE, DOUBLE_WALKOVER, DOUBLE_DEFAULT, DEFAULTED, WALKOVER, TO_BE_PLAYED } from '../../../constants/matchUpStatusValues.mjs';
|
|
16
15
|
|
|
17
16
|
const keyColors = {
|
|
18
17
|
drawPositionToRemove: 'green',
|
|
@@ -61,19 +60,6 @@ function removeDoubleExit(params) {
|
|
|
61
60
|
const claimSideNumber = (rawLoserMatchUp?.drawPositions ?? []).indexOf(loserTargetDrawPosition) + 1 || 1;
|
|
62
61
|
withdrawByeClaim({ matchUp: rawLoserMatchUp, sideNumber: claimSideNumber, claimantMatchUpId: matchUpId });
|
|
63
62
|
withdrawByeClaimsFrom({ matchUps: matchUpsMap?.drawMatchUps, claimantMatchUpId: matchUpId });
|
|
64
|
-
const isFMLC = targetData?.targetLinks?.loserTargetLink?.linkCondition === FIRST_MATCHUP;
|
|
65
|
-
if (byePropagatedToLoserMatchUp && isFMLC) {
|
|
66
|
-
const roundMatchUps = inContextDrawMatchUps.filter(({ roundNumber, structureId }) => structureId === matchUp.structureId && roundNumber === 1);
|
|
67
|
-
const roundPositions = roundMatchUps.map(({ roundPosition }) => roundPosition);
|
|
68
|
-
const pairedPositions = chunkArray(roundPositions.toSorted((a, b) => a - b), 2).find((chunk) => chunk.includes(matchUp.roundPosition));
|
|
69
|
-
const pairedMatchUpStatuses = roundMatchUps
|
|
70
|
-
.filter(({ roundPosition }) => pairedPositions.includes(roundPosition))
|
|
71
|
-
?.map(({ matchUpStatus }) => matchUpStatus);
|
|
72
|
-
const pairedMatchUpIsDoubleExit = pairedMatchUpStatuses.every((matchUpStatus) => [DOUBLE_DEFAULT, DOUBLE_WALKOVER].includes(matchUpStatus));
|
|
73
|
-
if (pairedMatchUpIsDoubleExit) {
|
|
74
|
-
return decorateResult({ result: { ...SUCCESS }, stack });
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
63
|
if (loserMatchUp && (loserMatchUp.matchUpStatus !== BYE || byePropagatedToLoserMatchUp)) {
|
|
78
64
|
const inContextLoserMatchUp = inContextDrawMatchUps.find(({ matchUpId }) => matchUpId === loserMatchUp.matchUpId);
|
|
79
65
|
const { structure: loserStructure } = findStructure({
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { removeDoubleExit } from './removeDoubleExit.mjs';
|
|
2
|
+
import { isDoubleExit } from '../../../validators/isExit.mjs';
|
|
3
|
+
import { SUCCESS } from '../../../constants/resultConstants.mjs';
|
|
4
|
+
|
|
5
|
+
function unwindDualDoubleExit(params) {
|
|
6
|
+
const { isCollectionMatchUp, dualMatchUp, matchUpsMap } = params;
|
|
7
|
+
if (!isCollectionMatchUp || !isDoubleExit(dualMatchUp?.matchUpStatus))
|
|
8
|
+
return { ...SUCCESS };
|
|
9
|
+
const rawDual = matchUpsMap?.drawMatchUps?.find((candidate) => candidate.matchUpId === dualMatchUp.matchUpId) ?? dualMatchUp;
|
|
10
|
+
return removeDoubleExit({ ...params, matchUp: rawDual });
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export { unwindDualDoubleExit };
|
|
@@ -773,6 +773,13 @@ function getExitArrivalSideNumber({ inContextDrawMatchUps, nextWinnerMatchUp, so
|
|
|
773
773
|
if (nextWinnerMatchUp.feedRound) {
|
|
774
774
|
return sourceMatchUp.structureId === nextWinnerMatchUp.structureId ? 2 : 1;
|
|
775
775
|
}
|
|
776
|
+
const sourcePositions = (sourceMatchUp.drawPositions ?? []).filter(Boolean);
|
|
777
|
+
const targetPositions = (nextWinnerMatchUp.drawPositions ?? []).filter(Boolean);
|
|
778
|
+
if (targetPositions.length === 2) {
|
|
779
|
+
const seated = nextWinnerMatchUp.sides?.find((side) => side?.sideNumber && side.drawPosition && sourcePositions.includes(side.drawPosition));
|
|
780
|
+
if (seated)
|
|
781
|
+
return seated.sideNumber;
|
|
782
|
+
}
|
|
776
783
|
const feeders = inContextDrawMatchUps
|
|
777
784
|
.filter(({ winnerMatchUpId, loserMatchUpId }) => winnerMatchUpId === nextWinnerMatchUp.matchUpId || loserMatchUpId === nextWinnerMatchUp.matchUpId)
|
|
778
785
|
.sort((a, b) => a.roundPosition - b.roundPosition);
|
|
@@ -172,6 +172,22 @@ function assignDrawPositionBye({ byeFromPropagation, provisionalPositioning, pre
|
|
|
172
172
|
if (result.error)
|
|
173
173
|
return result;
|
|
174
174
|
}
|
|
175
|
+
else if (matchUp &&
|
|
176
|
+
roundNumber &&
|
|
177
|
+
roundNumber > (getInitialRoundNumber({ drawPosition, matchUps }).initialRoundNumber ?? 0)) {
|
|
178
|
+
const result = assignByeToLoserTarget({
|
|
179
|
+
byeFromPropagation,
|
|
180
|
+
inContextDrawMatchUps,
|
|
181
|
+
preserveScheduling,
|
|
182
|
+
tournamentRecord,
|
|
183
|
+
drawDefinition,
|
|
184
|
+
matchUpsMap,
|
|
185
|
+
matchUp,
|
|
186
|
+
event,
|
|
187
|
+
});
|
|
188
|
+
if (result?.error)
|
|
189
|
+
return result;
|
|
190
|
+
}
|
|
175
191
|
modifyPositionAssignmentsNotice({
|
|
176
192
|
tournamentId: tournamentRecord?.tournamentId,
|
|
177
193
|
drawDefinition,
|
|
@@ -422,11 +438,6 @@ function advanceWinner({ sourceRoundPosition, byeFromPropagation, drawPositionTo
|
|
|
422
438
|
context: stack,
|
|
423
439
|
drawDefinition,
|
|
424
440
|
});
|
|
425
|
-
const { targetLinks: { loserTargetLink }, targetMatchUps: { loserMatchUp, loserTargetDrawPosition }, } = positionTargets({
|
|
426
|
-
matchUpId: winnerMatchUp.matchUpId,
|
|
427
|
-
inContextDrawMatchUps,
|
|
428
|
-
drawDefinition,
|
|
429
|
-
});
|
|
430
441
|
if (pairedDrawPositionIsBye || drawPositionIsBye) {
|
|
431
442
|
const advancingDrawPosition = pairedDrawPositionIsBye ? drawPositionToAdvance : pairedDrawPosition;
|
|
432
443
|
if (advancingDrawPosition) {
|
|
@@ -440,38 +451,55 @@ function advanceWinner({ sourceRoundPosition, byeFromPropagation, drawPositionTo
|
|
|
440
451
|
matchUpsMap,
|
|
441
452
|
});
|
|
442
453
|
}
|
|
443
|
-
else if (drawPositionIsBye
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
else {
|
|
457
|
-
const sourceStructureRoundPosition = winnerMatchUp.roundPosition;
|
|
458
|
-
const targetDrawPositionIndex = 1 - (sourceStructureRoundPosition % 2);
|
|
459
|
-
const targetDrawPosition = loserMatchUp.drawPositions[targetDrawPositionIndex];
|
|
460
|
-
const result = assignDrawPositionBye({
|
|
461
|
-
byeFromPropagation,
|
|
462
|
-
structureId: loserTargetLink.target.structureId,
|
|
463
|
-
drawPosition: targetDrawPosition,
|
|
464
|
-
preserveScheduling,
|
|
465
|
-
tournamentRecord,
|
|
466
|
-
drawDefinition,
|
|
467
|
-
event,
|
|
468
|
-
});
|
|
469
|
-
if (result.error)
|
|
470
|
-
return result;
|
|
471
|
-
}
|
|
454
|
+
else if (drawPositionIsBye) {
|
|
455
|
+
const result = assignByeToLoserTarget({
|
|
456
|
+
matchUp: winnerMatchUp,
|
|
457
|
+
byeFromPropagation,
|
|
458
|
+
inContextDrawMatchUps,
|
|
459
|
+
preserveScheduling,
|
|
460
|
+
tournamentRecord,
|
|
461
|
+
drawDefinition,
|
|
462
|
+
matchUpsMap,
|
|
463
|
+
event,
|
|
464
|
+
});
|
|
465
|
+
if (result?.error)
|
|
466
|
+
return result;
|
|
472
467
|
}
|
|
473
468
|
}
|
|
474
469
|
}
|
|
470
|
+
function assignByeToLoserTarget({ byeFromPropagation, inContextDrawMatchUps, preserveScheduling, tournamentRecord, drawDefinition, matchUpsMap, matchUp, event, }) {
|
|
471
|
+
const { targetLinks: { loserTargetLink }, targetMatchUps: { loserMatchUp, loserTargetDrawPosition }, } = positionTargets({
|
|
472
|
+
matchUpId: matchUp.matchUpId,
|
|
473
|
+
inContextDrawMatchUps,
|
|
474
|
+
drawDefinition,
|
|
475
|
+
});
|
|
476
|
+
if (!loserTargetLink || !loserMatchUp)
|
|
477
|
+
return { ...SUCCESS };
|
|
478
|
+
if (loserMatchUp.feedRound) {
|
|
479
|
+
return assignFedDrawPositionBye({
|
|
480
|
+
byeFromPropagation,
|
|
481
|
+
loserTargetDrawPosition,
|
|
482
|
+
preserveScheduling,
|
|
483
|
+
tournamentRecord,
|
|
484
|
+
loserTargetLink,
|
|
485
|
+
drawDefinition,
|
|
486
|
+
loserMatchUp,
|
|
487
|
+
matchUpsMap,
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
const sourceStructureRoundPosition = matchUp.roundPosition;
|
|
491
|
+
const targetDrawPositionIndex = 1 - (sourceStructureRoundPosition % 2);
|
|
492
|
+
const targetDrawPosition = loserMatchUp.drawPositions[targetDrawPositionIndex];
|
|
493
|
+
return assignDrawPositionBye({
|
|
494
|
+
byeFromPropagation,
|
|
495
|
+
structureId: loserTargetLink.target.structureId,
|
|
496
|
+
drawPosition: targetDrawPosition,
|
|
497
|
+
preserveScheduling,
|
|
498
|
+
tournamentRecord,
|
|
499
|
+
drawDefinition,
|
|
500
|
+
event,
|
|
501
|
+
});
|
|
502
|
+
}
|
|
475
503
|
function arrivalIntoProvenanceOnlyExit({ existingDrawPositions, drawPositionToAdvance, pairedDrawPositionIsBye, sourceRoundPosition, drawPositionIsBye, holdsParticipant, inContextMatchUp, matchUp, ...context }) {
|
|
476
504
|
const exitSides = Object.keys(matchUp.sideExitProvenance ?? {}).map(Number);
|
|
477
505
|
const applies = isExit(matchUp.matchUpStatus) &&
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { retainByeClaimsOnly, clearSideExitProvenance } from '../matchUpStatus/sideExitProvenance.mjs';
|
|
2
1
|
import { modifyRoundRobinMatchUpsStatus } from '../matchUpStatus/modifyRoundRobinMatchUpsStatus.mjs';
|
|
3
2
|
import { modifyPositionAssignmentsNotice, modifyMatchUpNotice } from '../../notifications/drawNotifications.mjs';
|
|
4
3
|
import { structureAssignedDrawPositions, getPositionAssignments } from '../../../query/drawDefinition/positionsGetter.mjs';
|
|
4
|
+
import { releaseLinkedWinnerAdvancement } from './releaseLinkedWinnerAdvancement.mjs';
|
|
5
5
|
import { getStructureDrawPositionProfiles } from '../../../query/structure/getStructureDrawPositionProfiles.mjs';
|
|
6
6
|
import { normalizeDrawPositions } from './normalizeDrawPositions.mjs';
|
|
7
7
|
import { getAllStructureMatchUps } from '../../../query/matchUps/getAllStructureMatchUps.mjs';
|
|
@@ -15,6 +15,7 @@ import { pushGlobalLog } from '../../../functions/global/globalLog.mjs';
|
|
|
15
15
|
import { findStructure } from '../../../acquire/findStructure.mjs';
|
|
16
16
|
import { ensureInt } from '../../../tools/ensureInt.mjs';
|
|
17
17
|
import { overlap } from '../../../tools/arrays.mjs';
|
|
18
|
+
import { clearSideExitProvenance, deriveExitStateFromProvenance, retainByeClaimsOnly } from '../matchUpStatus/sideExitProvenance.mjs';
|
|
18
19
|
import { BYE, TO_BE_PLAYED } from '../../../constants/matchUpStatusValues.mjs';
|
|
19
20
|
import { CONTAINER, DRAW } from '../../../constants/drawDefinitionConstants.mjs';
|
|
20
21
|
import { SUCCESS } from '../../../constants/resultConstants.mjs';
|
|
@@ -80,7 +81,6 @@ function drawPositionRemovals({ inContextDrawMatchUps, tournamentRecord, drawDef
|
|
|
80
81
|
drawDefinition,
|
|
81
82
|
structure,
|
|
82
83
|
}).positionAssignments ?? [];
|
|
83
|
-
const withdrawsPropagatedBye = positionAssignments.some((assignment) => assignment.drawPosition === drawPosition && assignment.bye && assignment.byeFromPropagation);
|
|
84
84
|
const drawPositionCleared = positionAssignments.some((assignment) => {
|
|
85
85
|
if (assignment.drawPosition === drawPosition) {
|
|
86
86
|
delete assignment.participantId;
|
|
@@ -123,7 +123,6 @@ function drawPositionRemovals({ inContextDrawMatchUps, tournamentRecord, drawDef
|
|
|
123
123
|
return;
|
|
124
124
|
}
|
|
125
125
|
removeSubsequentRoundsParticipant({
|
|
126
|
-
withdrawsPropagatedBye,
|
|
127
126
|
inContextDrawMatchUps,
|
|
128
127
|
targetDrawPosition,
|
|
129
128
|
tournamentRecord,
|
|
@@ -133,7 +132,6 @@ function drawPositionRemovals({ inContextDrawMatchUps, tournamentRecord, drawDef
|
|
|
133
132
|
matchUpsMap,
|
|
134
133
|
});
|
|
135
134
|
removeDrawPosition({
|
|
136
|
-
withdrawsPropagatedBye,
|
|
137
135
|
inContextDrawMatchUps,
|
|
138
136
|
positionAssignments,
|
|
139
137
|
tournamentRecord,
|
|
@@ -147,7 +145,7 @@ function drawPositionRemovals({ inContextDrawMatchUps, tournamentRecord, drawDef
|
|
|
147
145
|
});
|
|
148
146
|
return { tasks, drawPositionCleared, positionAssignments };
|
|
149
147
|
}
|
|
150
|
-
function removeSubsequentRoundsParticipant({
|
|
148
|
+
function removeSubsequentRoundsParticipant({ inContextDrawMatchUps, targetDrawPosition, tournamentRecord, drawDefinition, matchUpsMap, roundNumber, structureId, }) {
|
|
151
149
|
const { structure } = findStructure({ drawDefinition, structureId });
|
|
152
150
|
if (!structure)
|
|
153
151
|
return { error: STRUCTURE_NOT_FOUND };
|
|
@@ -169,7 +167,6 @@ function removeSubsequentRoundsParticipant({ withdrawsPropagatedBye, inContextDr
|
|
|
169
167
|
}).positionAssignments ?? [];
|
|
170
168
|
relevantMatchUps?.forEach((matchUp) => removeDrawPosition({
|
|
171
169
|
drawPosition: targetDrawPosition,
|
|
172
|
-
withdrawsPropagatedBye,
|
|
173
170
|
targetMatchUp: matchUp,
|
|
174
171
|
inContextDrawMatchUps,
|
|
175
172
|
positionAssignments,
|
|
@@ -180,7 +177,7 @@ function removeSubsequentRoundsParticipant({ withdrawsPropagatedBye, inContextDr
|
|
|
180
177
|
}));
|
|
181
178
|
return { ...SUCCESS };
|
|
182
179
|
}
|
|
183
|
-
function removeDrawPosition({
|
|
180
|
+
function removeDrawPosition({ inContextDrawMatchUps, positionAssignments, tournamentRecord, drawDefinition, targetMatchUp, drawPosition, matchUpsMap, structure, event, }) {
|
|
184
181
|
const stack = 'removeDrawPosition';
|
|
185
182
|
const initialDrawPositions = targetMatchUp.drawPositions?.slice();
|
|
186
183
|
const initialMatchUpStatus = targetMatchUp.matchUpStatus;
|
|
@@ -192,13 +189,26 @@ function removeDrawPosition({ withdrawsPropagatedBye, inContextDrawMatchUps, pos
|
|
|
192
189
|
drawPosition,
|
|
193
190
|
matchUps,
|
|
194
191
|
});
|
|
195
|
-
const keepsByeAdvancement =
|
|
196
|
-
|
|
192
|
+
const keepsByeAdvancement = advancedByOpponentsBye({
|
|
193
|
+
matchUps: matchUps ?? [],
|
|
194
|
+
positionAssignments,
|
|
195
|
+
targetMatchUp,
|
|
196
|
+
drawPosition,
|
|
197
|
+
});
|
|
197
198
|
if (!keepsByeAdvancement &&
|
|
198
199
|
targetMatchUp.roundNumber &&
|
|
199
200
|
initialRoundNumber &&
|
|
200
201
|
targetMatchUp.roundNumber > initialRoundNumber) {
|
|
201
202
|
targetMatchUp.drawPositions = normalizeDrawPositions((targetMatchUp.drawPositions ?? []).map((currentDrawPosition) => currentDrawPosition === drawPosition ? undefined : currentDrawPosition));
|
|
203
|
+
releaseLinkedWinnerAdvancement({
|
|
204
|
+
roundNumber: targetMatchUp.roundNumber,
|
|
205
|
+
structureId: structure.structureId,
|
|
206
|
+
tournamentRecord,
|
|
207
|
+
drawDefinition,
|
|
208
|
+
drawPosition,
|
|
209
|
+
matchUpsMap,
|
|
210
|
+
event,
|
|
211
|
+
});
|
|
202
212
|
}
|
|
203
213
|
handleTeamPositionRemoval({
|
|
204
214
|
inContextDrawMatchUps,
|
|
@@ -313,11 +323,18 @@ function updateMatchUpStatusAfterRemoval({ initialDrawPositions, initialMatchUpS
|
|
|
313
323
|
targetMatchUp.winningSide = undefined;
|
|
314
324
|
if (targetMatchUp.matchUpStatusCodes?.length)
|
|
315
325
|
targetMatchUp.matchUpStatusCodes = [];
|
|
316
|
-
const retainedClaims = retainByeClaimsOnly(targetMatchUp.sideExitProvenance);
|
|
317
|
-
clearSideExitProvenance(targetMatchUp);
|
|
318
|
-
if (retainedClaims)
|
|
319
|
-
targetMatchUp.sideExitProvenance = retainedClaims;
|
|
320
326
|
const removedDrawPosition = initialDrawPositions?.find((position) => !targetMatchUp.drawPositions?.includes(position));
|
|
327
|
+
const clearedIndex = initialDrawPositions?.indexOf(drawPosition) ?? -1;
|
|
328
|
+
const clearedSideNumber = clearedIndex >= 0 ? clearedIndex + 1 : undefined;
|
|
329
|
+
const retained = retainProvenanceBesideRemoval(targetMatchUp.sideExitProvenance, clearedSideNumber);
|
|
330
|
+
clearSideExitProvenance(targetMatchUp);
|
|
331
|
+
if (retained)
|
|
332
|
+
targetMatchUp.sideExitProvenance = retained;
|
|
333
|
+
const rederived = !matchUpContainsBye && retained ? deriveExitStateFromProvenance(retained) : undefined;
|
|
334
|
+
if (rederived) {
|
|
335
|
+
targetMatchUp.matchUpStatus = rederived.matchUpStatus;
|
|
336
|
+
targetMatchUp.winningSide = rederived.winningSide;
|
|
337
|
+
}
|
|
321
338
|
const noChange = initialDrawPositions?.includes(drawPosition) &&
|
|
322
339
|
initialMatchUpStatus === targetMatchUp.matchUpStatus &&
|
|
323
340
|
initialWinningSide === targetMatchUp.winningSide;
|
|
@@ -340,6 +357,23 @@ function updateMatchUpStatusAfterRemoval({ initialDrawPositions, initialMatchUpS
|
|
|
340
357
|
}
|
|
341
358
|
return matchUpContainsBye;
|
|
342
359
|
}
|
|
360
|
+
function retainProvenanceBesideRemoval(provenance, clearedSideNumber) {
|
|
361
|
+
if (!provenance)
|
|
362
|
+
return undefined;
|
|
363
|
+
if (!clearedSideNumber)
|
|
364
|
+
return retainByeClaimsOnly(provenance);
|
|
365
|
+
const retained = {};
|
|
366
|
+
for (const sideNumber of [1, 2]) {
|
|
367
|
+
const entry = provenance[sideNumber];
|
|
368
|
+
if (!entry)
|
|
369
|
+
continue;
|
|
370
|
+
if (sideNumber !== clearedSideNumber)
|
|
371
|
+
retained[sideNumber] = { ...entry };
|
|
372
|
+
else if (entry.byeClaims?.length)
|
|
373
|
+
retained[sideNumber] = { byeClaims: [...entry.byeClaims] };
|
|
374
|
+
}
|
|
375
|
+
return Object.keys(retained).length ? retained : undefined;
|
|
376
|
+
}
|
|
343
377
|
function handleLoserMatchUpRemoval({ loserMatchUpDrawPositionIndex, inContextDrawMatchUps, tournamentRecord, drawDefinition, loserMatchUp, matchUpsMap, event, stack, }) {
|
|
344
378
|
const { drawPositions, roundNumber } = loserMatchUp;
|
|
345
379
|
if (roundNumber === 1) {
|
|
@@ -4,7 +4,8 @@ import { getAllDrawMatchUps } from '../../../query/matchUps/drawMatchUps.mjs';
|
|
|
4
4
|
import { positionTargets } from '../../../query/matchUp/positionTargets.mjs';
|
|
5
5
|
import { getMatchUpsMap } from '../../../query/matchUps/getMatchUpsMap.mjs';
|
|
6
6
|
import { findStructure } from '../../../acquire/findStructure.mjs';
|
|
7
|
-
import {
|
|
7
|
+
import { isAnyExit } from '../../../validators/isExit.mjs';
|
|
8
|
+
import { BYE, DEAD_RUBBER, TO_BE_PLAYED } from '../../../constants/matchUpStatusValues.mjs';
|
|
8
9
|
import { WINNER, LOSER } from '../../../constants/drawDefinitionConstants.mjs';
|
|
9
10
|
import { SUCCESS } from '../../../constants/resultConstants.mjs';
|
|
10
11
|
|
|
@@ -38,6 +39,8 @@ function reconcileDecider({ winningSideBefore, tournamentRecord, drawDefinition,
|
|
|
38
39
|
const holdsAResult = !!decider.winningSide || !!decider.score?.sets?.length;
|
|
39
40
|
if (!holdsAResult && decider.matchUpStatus === matchUpStatus)
|
|
40
41
|
return { ...SUCCESS };
|
|
42
|
+
if (!holdsAResult && (decider.matchUpStatus === BYE || isAnyExit(decider.matchUpStatus)))
|
|
43
|
+
return { ...SUCCESS };
|
|
41
44
|
return modifyMatchUpScore({
|
|
42
45
|
matchUpId: decider.matchUpId,
|
|
43
46
|
removeWinningSide: true,
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { feedEligibilityChange } from './feedEligibilityGuard.mjs';
|
|
2
1
|
import { noDownstreamDependencies } from '../../drawDefinitions/matchUpGovernor/noDownstreamDependencies.mjs';
|
|
3
2
|
import { generateTieMatchUpScore } from '../../../assemblies/generators/tieMatchUpScore/generateTieMatchUpScore.mjs';
|
|
4
3
|
import { isNonDirectingMatchUpStatus, isDirectingMatchUpStatus } from '../../../query/matchUp/checkStatusType.mjs';
|
|
5
4
|
import { addMatchUpScheduleItems } from '../schedule/scheduleItems/scheduleItems.mjs';
|
|
6
5
|
import { hasPropagatedExitDownstream } from '../../../query/drawDefinition/hasPropagatedExitDownstream.mjs';
|
|
6
|
+
import { feedEligibilityChange } from './feedEligibilityGuard.mjs';
|
|
7
7
|
import { getProjectedDualWinningSide } from '../../../query/matchUp/getProjectedDualWinningSide.mjs';
|
|
8
|
-
import { updateTieMatchUpScore } from '../score/updateTieMatchUpScore.mjs';
|
|
9
|
-
import { getMatchUpStatusScopeViolation } from '../../../query/matchUps/getMatchUpStatusScopeViolation.mjs';
|
|
10
8
|
import { setFirstClassOrExtension } from '../../extensions/setFirstClassOrExtension.mjs';
|
|
9
|
+
import { updateTieMatchUpScore } from '../score/updateTieMatchUpScore.mjs';
|
|
11
10
|
import { isMatchUpEventType } from '../../../helpers/matchUpEventTypes/isMatchUpEventType.mjs';
|
|
12
11
|
import { resolveTieFormat } from '../../../query/hierarchical/tieFormats/resolveTieFormat.mjs';
|
|
13
12
|
import { swapWinnerLoser } from '../drawPositions/swapWinnerLoser.mjs';
|
|
@@ -29,6 +28,7 @@ import { isAdHoc } from '../../../query/drawDefinition/isAdHoc.mjs';
|
|
|
29
28
|
import { findStructure } from '../../../acquire/findStructure.mjs';
|
|
30
29
|
import { isDoubleExit } from '../../../validators/isExit.mjs';
|
|
31
30
|
import { isObject } from '../../../tools/objects.mjs';
|
|
31
|
+
import { getMatchUpStatusScopeViolation } from '../../../query/matchUps/getMatchUpStatusScopeViolation.mjs';
|
|
32
32
|
import { POLICY_TYPE_PROGRESSION, POLICY_TYPE_SCORING } from '../../../constants/policyConstants.mjs';
|
|
33
33
|
import { DISABLE_AUTO_CALC } from '../../../constants/extensionConstants.mjs';
|
|
34
34
|
import { QUALIFYING } from '../../../constants/drawDefinitionConstants.mjs';
|
|
@@ -388,6 +388,14 @@ function resolveAndApplyOutcome({ params, isTeam, dualWinningSideChange, activeD
|
|
|
388
388
|
winningSide,
|
|
389
389
|
});
|
|
390
390
|
const hasPropagated = !!matchUp.winningSide || isDoubleExit(matchUp.matchUpStatus);
|
|
391
|
+
if (params.isCollectionMatchUp && isDoubleExit(params.dualMatchUp?.matchUpStatus) && activeDownstream) {
|
|
392
|
+
return decorateResult({
|
|
393
|
+
result: { error: CANNOT_CHANGE_OUTCOME },
|
|
394
|
+
info: 'the dual holds a double exit whose produced result has been played on',
|
|
395
|
+
context: { dualMatchUpId: params.dualMatchUp.matchUpId, dualMatchUpStatus: params.dualMatchUp.matchUpStatus },
|
|
396
|
+
stack,
|
|
397
|
+
});
|
|
398
|
+
}
|
|
391
399
|
let result;
|
|
392
400
|
if (!activeDownstream || !hasPropagated) {
|
|
393
401
|
result = noDownstreamDependencies(params);
|
|
@@ -39,7 +39,7 @@ function courtModification({ tournamentRecord, disableNotice, venueMatchUps, mod
|
|
|
39
39
|
return { error: INVALID_OBJECT };
|
|
40
40
|
const result = findCourt({ tournamentRecord, courtId });
|
|
41
41
|
if (result.error)
|
|
42
|
-
return result;
|
|
42
|
+
return { error: result.error };
|
|
43
43
|
const { venue, court } = result;
|
|
44
44
|
const validAttributes = Object.keys(courtTemplate()).filter((attribute) => attribute !== 'courtId');
|
|
45
45
|
const validModificationAttributes = Object.keys(modifications).filter((attribute) => validAttributes.includes(attribute));
|
|
@@ -15,10 +15,9 @@ function checkSetIsComplete({ ignoreTiebreak = false, matchUpScoringFormat, matc
|
|
|
15
15
|
const requiresTiebreak = isTiebreakSet ||
|
|
16
16
|
(side1Score >= setTo && side2Score >= setTo) ||
|
|
17
17
|
(tiebreakAt && tiebreakAt < setTo && (side1Score === tiebreakAt || side2Score === tiebreakAt));
|
|
18
|
-
const
|
|
19
|
-
(
|
|
20
|
-
|
|
21
|
-
(leadingSide === 2 && set.side2TiebreakScore > set.side1TiebreakScore)));
|
|
18
|
+
const leaderHoldsTiebreak = (leadingSide === 1 && set.side1TiebreakScore > set.side2TiebreakScore) ||
|
|
19
|
+
(leadingSide === 2 && set.side2TiebreakScore > set.side1TiebreakScore);
|
|
20
|
+
const tiebreakIsValid = ignoreTiebreak || (requiresTiebreak && leaderHoldsTiebreak && tiebreakReachesTarget(set, setFormat, isTiebreakSet));
|
|
22
21
|
const declaredWinBy = setFormat.winBy;
|
|
23
22
|
const winMargin = (!requiresTiebreak && setFormat.NoAD) || requiresTiebreak || (isTiebreakSet && setFormat.tiebreakFormat?.NoAD)
|
|
24
23
|
? 1
|
|
@@ -28,6 +27,16 @@ function checkSetIsComplete({ ignoreTiebreak = false, matchUpScoringFormat, matc
|
|
|
28
27
|
return !!(((isTimedSet && hasScore) || validNormalSetScore || isTiebreakSet) &&
|
|
29
28
|
(!requiresTiebreak || tiebreakIsValid));
|
|
30
29
|
}
|
|
30
|
+
function tiebreakReachesTarget(set, setFormat, isTiebreakSet) {
|
|
31
|
+
const tiebreakFormat = isTiebreakSet ? setFormat?.tiebreakSet : setFormat?.tiebreakFormat;
|
|
32
|
+
const tiebreakTo = tiebreakFormat?.tiebreakTo;
|
|
33
|
+
if (typeof tiebreakTo !== 'number')
|
|
34
|
+
return true;
|
|
35
|
+
const high = Math.max(set.side1TiebreakScore ?? 0, set.side2TiebreakScore ?? 0);
|
|
36
|
+
const low = Math.min(set.side1TiebreakScore ?? 0, set.side2TiebreakScore ?? 0);
|
|
37
|
+
const margin = tiebreakFormat?.NoAD ? 1 : Math.min(2, tiebreakTo);
|
|
38
|
+
return high >= tiebreakTo && high - low >= margin;
|
|
39
|
+
}
|
|
31
40
|
function getLeadingSide({ set }) {
|
|
32
41
|
if (set.side1Score || set.side2Score) {
|
|
33
42
|
if (set.side1Score > set.side2Score)
|
|
@@ -76,7 +76,7 @@ const getMaxSetScore = (params) => {
|
|
|
76
76
|
if (tiebreakAt < setTo)
|
|
77
77
|
return setTo;
|
|
78
78
|
const ceiling = setTo + 1;
|
|
79
|
-
if (opponentScore !== undefined && opponentScore < setTo)
|
|
79
|
+
if (opponentScore !== undefined && opponentScore < setTo - 1)
|
|
80
80
|
return setTo;
|
|
81
81
|
return ceiling;
|
|
82
82
|
};
|
|
@@ -144,6 +144,17 @@ function parseSetScores(set, isTiebreakOnlyFormat, hasTiebreakScores) {
|
|
|
144
144
|
const side2Score = isTiebreakOnlyFormat && hasTiebreakScores ? side2TiebreakScore : set.side2Score || set.side2 || 0;
|
|
145
145
|
return { side1Score, side2Score, side1TiebreakScore, side2TiebreakScore };
|
|
146
146
|
}
|
|
147
|
+
function validateTiebreakWinner(side1Score, side2Score, side1TiebreakScore, side2TiebreakScore) {
|
|
148
|
+
if (side1TiebreakScore === undefined || side2TiebreakScore === undefined)
|
|
149
|
+
return undefined;
|
|
150
|
+
if (side1Score === side2Score || side1TiebreakScore === side2TiebreakScore)
|
|
151
|
+
return undefined;
|
|
152
|
+
const gamesWinner = side1Score > side2Score ? 1 : 2;
|
|
153
|
+
const pointsWinner = side1TiebreakScore > side2TiebreakScore ? 1 : 2;
|
|
154
|
+
if (gamesWinner === pointsWinner)
|
|
155
|
+
return undefined;
|
|
156
|
+
return { isValid: false, error: `Set winner must win the tiebreak: side ${gamesWinner} won the set` };
|
|
157
|
+
}
|
|
147
158
|
function validateTiebreakSet(winnerScore, loserScore, setTo, tiebreakAt, side1TiebreakScore, side2TiebreakScore, tiebreakFormat) {
|
|
148
159
|
if (setTo && tiebreakAt) {
|
|
149
160
|
const validation = validateTiebreakSetGames(winnerScore, loserScore, setTo, tiebreakAt);
|
|
@@ -226,6 +237,9 @@ function validateSetScore(set, matchUpFormat, isDecidingSet, allowIncomplete) {
|
|
|
226
237
|
const isImplicitTiebreak = setTo && winnerScore === setTo + 1 && loserScore === setTo;
|
|
227
238
|
const hasTiebreak = hasExplicitTiebreak || isImplicitTiebreak;
|
|
228
239
|
if (hasTiebreak) {
|
|
240
|
+
const contradiction = validateTiebreakWinner(side1Score, side2Score, side1TiebreakScore, side2TiebreakScore);
|
|
241
|
+
if (contradiction)
|
|
242
|
+
return contradiction;
|
|
229
243
|
return validateTiebreakSet(winnerScore, loserScore, setTo, tiebreakAt, side1TiebreakScore, side2TiebreakScore, tiebreakFormat);
|
|
230
244
|
}
|
|
231
245
|
return validateRegularSet({ side1: side1Score, side2: side2Score, winner: winnerScore, loser: loserScore, diff: scoreDiff }, { setTo, tiebreakAt, NoAD: setFormat.NoAD, winBy: setFormat.winBy }, allowIncomplete);
|