Skip to main content

Tournament Governor

Governor API transition at 8.0.0

Some methods on this page are candidates for removal from production engine exports at the 8.0.0 boundary. Their implementation references have moved: addNotes, getAllowedDrawTypes, getTournamentTimeItem. Existing 7.x routes remain available; final decisions and external-integrator confirmations are tracked in the transition index.

import { tournamentGovernor } from 'tods-competition-factory';

addExtension​

Adds an extension to a specified element in the tournament record. See examples: Follow By Implementation, 4. Manual Override.

engine.addExtension({
extension, // required - extension object
tournamentId, // optional - target tournament
participantId, // optional - target participant
eventId, // optional - target event
drawId, // optional - target draw
structureId, // optional - target structure
matchUpId, // optional - target matchUp
});

Purpose: Attach custom metadata to tournament elements.

When the element passed is one stored in a loaded record, the change is announced on its topic: the tournament record itself as MODIFY_TOURNAMENT_DETAIL with its extensions, an event as MODIFY_EVENT, a drawDefinition as MODIFY_DRAW_DEFINITION, a structure as MODIFY_DRAW_DEFINITION naming it, a matchUp (a TEAM matchUp's tieMatchUps included) as MODIFY_MATCHUP, a venue as MODIFY_VENUE, a participant as MODIFY_PARTICIPANTS. A copy (what getEvent or getParticipants returns) is not in the record, so writing to it announces nothing; nor does a write that leaves the element's extensions as they were.


addMutationLock​

Acquires a mutation lock on a tournament element. Automatically enables the mutation locks feature gate on the tournament record.

const { success, lockId } = engine.addMutationLock({
scope, // required - lock scope (e.g. 'SCHEDULING', 'SCORING', 'DRAWS')
lockToken, // required - opaque string token
expiresAt, // optional - ISO 8601 UTC string or null for permanent (default: null)
methods, // optional - string[] to restrict to specific methods within scope
drawId, // optional - lock on specific draw
eventId, // optional - lock on specific event
venueId, // optional - lock on specific venue
});

Purpose: Control concurrent access to scoped mutations. See Mutation Locks for details.

The lock is stored as an extension on the element it locks, and the change is announced on that element's topic (modifyDrawDefinition, modifyEvent, modifyVenue). A tournament-level lock, and the feature gate on the tournament record, are root extensions: their change is announced as modifyTournamentDetail with the record's extensions, as addTournamentExtension and removeTournamentExtension announce theirs.


addNotes​

8.0.0 engine-removal candidate. This 7.x route remains available pending review. The full reference has moved to addNotes. Package-export status is a separate decision.

addOnlineResource​

Attach an online resource to specified element. When no other ids are provided, will attach to tournamentRecord.

engine.addOnlineResource({
onlineResource, // required { identifier, resourceType, resourceSubType, ... }
organisationId, // optional
participantId, // optional
personId, // optional
matchUpId, // optional - with drawId
courtId, // optional
venueId, // optional
eventId, // optional
drawId, // optional
});

A matchUp's recording, stream or stats page belongs on the matchUp: pass matchUpId with the drawId that contains it (tieMatchUps included). The resource is merged as on every other element (one with the same name or identifier is replaced), and a modifyMatchUp notice is emitted so subscribers refresh. Public projections (getEventData, published or not) carry it on the matchUp.

Every other element announces the change on its own topic, and only when its resources changed (re-adding a resource it already holds, or removing one it does not hold, announces nothing): a participant or person modifyParticipants, a venue or court modifyVenue, the parent organisation or the tournament itself modifyTournamentDetail. removeOnlineResource does the same.

engine.addOnlineResource({
onlineResource: {
identifier: 'https://…/recording/rec-1',
resourceType: 'URL',
resourceSubType: 'VIDEO',
name: 'matchVideo',
provider: 'MatchTennisApp',
},
matchUpId,
drawId,
});

analyzeDraws​

Analyzes all draws in a tournament to provide structural insights.

const { analysis } = engine.analyzeDraws();

Returns: Analysis of draw structures, including sizes, stages, and completeness. A round robin draw is never listed in canBePruned (7.7.0).


analyzeTournament​

Provides comprehensive analysis of tournament structure and data.

const { analysis } = engine.analyzeTournament();

Returns: Tournament-level metrics including events, participants, draws, and matchUps.

extensionAnomalies​

From 6.32.0, the analysis reports extensions that no reader will ever see.

An extension after the first of a given name is unreachable — every reader resolves with .find() — so the attached policy, timing override or flag simply never takes effect, with no error and no signal.

const { analysis } = engine.analyzeTournament();
analysis.extensionAnomalies;
// [{ element: 'venue', elementId: 'venue-1', name: 'scheduleTiming', count: 3 }]

Present only when there is something to report, matching how missingParticipantIds behaves — so its absence is the ordinary case, not a failure to look.

Records built through the factory cannot reach this state: addExtension maintains one-per-name (it replaces in place and pushes only when absent) and attachPolicies guards again per policyType. The invariant is upheld by the writer rather than validated on the way in, which is precisely why records assembled outside the API — hand-built fixtures, importers, classic-converter, legacy storage — can carry duplicates that vanish silently. This reports them rather than throwing: the condition is rare and always upstream of the factory.

analyzeDraws reports the same anomalies for draw-level extensions.


copyTournamentRecord​

Creates a deep copy of a tournament record.

const { tournamentRecord } = engine.copyTournamentRecord({
tournamentId, // optional - specific tournament
});

Purpose: Clone tournament data for modifications or comparisons.


cleanExpiredMutationLocks​

Proactively removes expired mutation locks from all elements in the tournament.

const { success, removedCount } = engine.cleanExpiredMutationLocks();

Purpose: Housekeeping for time-limited locks. Expired locks are also cleaned lazily when encountered during lock checks.


createTournamentRecord​

Creates a new tournament record.

const { tournamentRecord } = engine.createTournamentRecord({
tournamentName, // optional
startDate, // optional - 'YYYY-MM-DD'
endDate, // optional - 'YYYY-MM-DD'
activeDates, // optional - string[] of 'YYYY-MM-DD' dates within start/end range when play occurs
localTimeZone, // optional - IANA timezone identifier (e.g. 'America/New_York')
tournamentId, // optional - provide specific ID
extensions, // optional - array of extensions (validated)
});

Purpose: Initialize a new tournament.

When activeDates is provided, each date is validated as a proper date string and all activeDates must fall within the startDate/endDate range — otherwise an INVALID_DATE error is returned. If startDate or endDate are not provided, they are automatically derived from the earliest and latest activeDates respectively, ensuring the tournament record always has valid date boundaries. Falsy values in the array are automatically filtered out. See Date and Time Handling for more on activeDates.


getAggregateTeamResults​

Returns aggregated results for team competitions across multiple tournaments.

const { results } = engine.getAggregateTeamResults({
tournamentRecords, // required - array of tournament records
});

Purpose: Compile team standings across competitions.


getAllowedDrawTypes​

8.0.0 engine-removal candidate. This 7.x route remains available pending review. The full reference has moved to getAllowedDrawTypes. Package-export status is a separate decision.

getAllowedMatchUpFormats​

Returns allowed matchUp formats based on event type and configuration.

const { matchUpFormats } = engine.getAllowedMatchUpFormats({
eventType, // optional
});

Purpose: Get valid format options for events.


getAppliedPolicies​

Returns all policies currently applied to the tournament.

const { policies } = engine.getAppliedPolicies();

Purpose: Query active tournament policies.


getCompetitionDateRange​

Returns the date range spanning all tournaments in a competition.

const { startDate, endDate } = engine.getCompetitionDateRange({
tournamentRecords, // required - array of tournament records
});

Purpose: Get overall competition dates.


getCompetitionPenalties​

Returns all penalties across multiple tournaments in a competition. penaltyType is returned as its code: a record written before 7.x stored penalty codes may still hold a display label ('Ball Abuse') until it is run through migrateTournamentRecord or the penalty is modified, and the query returns that label's code without changing the record. A value outside the legacy labels is returned as stored.

const { penalties } = engine.getCompetitionPenalties({
tournamentRecords, // required - array of tournament records
});

Purpose: Aggregate penalties across competition.


getMutationLocks​

Returns all active (non-expired) mutation locks across the entire tournament, including locks on events, draws, and venues.

const { mutationLocks } = engine.getMutationLocks({
scope, // optional - filter by scope
});

Returns: Array of lock objects, each with lockId, lockToken, scope, expiresAt, createdAt, and optional drawId, eventId, venueId identifying where the lock is stored.


getPolicyDefinitions​

Returns attached policy definitions.

const { policyDefinitions } = engine.getPolicyDefinitions();

Purpose: Get tournament policy configurations.


getTournamentInfo​

Returns basic tournament information.

const { tournamentInfo } = engine.getTournamentInfo();

Returns: Tournament name, dates, location, and other metadata.

If a primary venue is designated and has at least one address, tournamentInfo.tournamentAddress will contain that venue's first address. This is always included regardless of the withVenueData option.

tournamentInfo.eventInfo returns a lightweight summary of each event. Each entry includes an entriesCount (the length of event.entries) alongside drawDefinitionCount and the other event metadata, so calendar admin dashboards can render entry totals without loading the full tournament record. Pass usePublishState: true to limit eventInfo to events listed in the tournament's published event IDs.


getTournamentTimeZone​

Resolves the authoritative IANA time zone for a tournament.

const { timeZone, inferred, error } = engine.getTournamentTimeZone();

Resolution order:

  1. tournamentRecord.localTimeZone when set — validated as a real IANA zone (INVALID_TIME_ZONE if not).
  2. Otherwise inferred from venue addresses — a venue is the physical location, so a single distinct address.timeZone across all venues is used and returned with inferred: true.

Returns CONFLICTING_TIME_ZONES when venues specify different zones and no tournament-level localTimeZone is set (the rare border-city case). Venue addresses that name no zone, or an invalid one, are ignored. Returns {} (no timeZone, no error) when no zone can be determined.

Only that last case admits a fallback. The factory's own reads, such as "today" for eligibility, use the UTC day there. A host that prefers its own zone may substitute it in that case only. An error is never replaced by a fallback zone. Zone resolution is separate from the choice of clock; see Clocks and Competition Time.


getTournamentPenalties​

Returns all penalties issued in the tournament. penaltyType is returned as its code: a record written before 7.x stored penalty codes may still hold a display label ('Ball Abuse') until it is run through migrateTournamentRecord or the penalty is modified, and the query returns that label's code without changing the record. A value outside the legacy labels is returned as stored.

const { penalties } = engine.getTournamentPenalties();

Purpose: Query tournament penalties.


getTournamentPersons​

Returns all persons (not just participants) associated with tournament.

const { persons } = engine.getTournamentPersons();

Purpose: Get all individuals including officials, staff, etc.


getTournamentPoints​

Returns points/prize money configuration for tournament.

const { points } = engine.getTournamentPoints();

Purpose: Get tournament points structure.


getTournamentStructures​

Returns all structures across all draws in tournament.

const { structures } = engine.getTournamentStructures();

Purpose: Get all draw structures tournament-wide.


getTournamentTimeItem​

8.0.0 engine-removal candidate. This 7.x route remains available pending review. The full reference has moved to getTournamentTimeItem. Package-export status is a separate decision.

hydrateTournamentRecord​

Currently only accepts the directive hydrateRoundNames: true. Use of eventProfiles is optional and allows granularity in application of round naming policy at the event level. If eventProfiles is used the top level directives and policyDefinitions are not necessary.

engine.hydrateTournamentRecord({
policyDefinitions: POLICY_ROUND_NAMING_DEFAULT,
directives: { hydrateRoundNames: true },
eventProfiles: [
{
directives: { hydrateRoundNames: true },
policyDefinitions: {},
eventId: 'eventId',
},
],
});

orderCollectionDefinitions​

Modify the array order of tieFormat.collectionDefinitions for an event, a drawDefinition, structure, or matchUp.

engine.orderCollectionDefinitions({
orderMap: { collectionId1: 1, collectionId2: 2 },
structureId, // required if modifying tieFormat for a structure
matchUpId, // required if modifying tieFormat for a matchUp
eventId, // required if modifying tieFormat for a event
drawId, // required if modifying tieFormat for a drawDefinition or a structure
});

removeExtension​

Removes an extension from a specified element in the tournament record.

engine.removeExtension({
name, // required - extension name
tournamentId, // optional - target tournament
participantId, // optional - target participant
eventId, // optional - target event
drawId, // optional - target draw
structureId, // optional - target structure
matchUpId, // optional - target matchUp
});

Purpose: Remove custom metadata from tournament elements.

Announced as addExtension is; with discover: true each record whose root extensions changed is announced as MODIFY_TOURNAMENT_DETAIL. Removing a name the element does not carry sends nothing.


removeMutationLock​

Releases a mutation lock. Requires the matching lockToken unless forceRelease: true.

const { success } = engine.removeMutationLock({
lockId, // optional - identify lock by ID
scope, // optional - identify lock by scope (alternative to lockId)
lockToken, // required unless forceRelease
forceRelease, // optional - bypass token check (admin override)
drawId, // optional - target element
eventId, // optional - target element
venueId, // optional - target element
});

Purpose: Release a mutation lock to restore normal access.


removeOnlineResource​

Remove an online resource from specified element. When no other ids are provided, will remove from tournamentRecord.

engine.removeOnlineResource({
onlineResource, // only requires { identifier, resourceType, resourceSubType }
organisationId, // optional
participantId, // optional
personId, // optional
matchUpId, // optional - with drawId; emits modifyMatchUp
courtId, // optional
venueId, // optional
eventId, // optional
drawId, // optional
});

setTournamentCategories​

Define categories to be used in event creation for tournament record.

const categories = [
{
type: eventConstants.AGE,
categoryName: 'U18',
},
{
type: eventConstants.AGE,
categoryName: 'U16',
},
{
type: eventConstants.RATING,
categoryName: 'WTN',
},
];
engine.setTournamentCategories({ categories });

setTournamentName​

const tournamentName = 'CourtHive Challenge';
engine.setTournamentName({
tournamentName,
});

setTournamentNotes​

engine.setTournamentNotes({ notes });

setTournamentStatus​

engine.setTournamentStatus({ status: CANCELLED });

Announced as MODIFY_TOURNAMENT_DETAIL with tournamentStatus; setting the status the record already has sends nothing.


setTournamentDates​

Sets the start date, end date, active dates, and/or weekdays for a tournament. Validates that dates are proper 'YYYY-MM-DD' strings and that activeDates fall within the start/end range. Event dates are coerced to fit within the new tournament dates.

const {
unscheduledMatchUpIds, // matchUpIds unscheduled by a force: true date change
datesRemoved, // dates no longer in the tournament range
datesAdded, // dates newly in the tournament range
} = engine.setTournamentDates({
startDate, // optional — 'YYYY-MM-DD'
endDate, // optional — 'YYYY-MM-DD'
activeDates, // optional — string[] of 'YYYY-MM-DD' dates within start/end range
weekdays, // optional — array of weekday constants [MON, TUE, ...]
force, // optional boolean — unschedule non-completed matchUps that fall outside the new range
});

Scheduling outside the new range​

When the range shrinks so that scheduled matchUps fall outside the new start/end dates, the mutation does not silently discard their placement. Behavior depends on whether those matchUps have been played:

  • Non-completed matchUps — rejected by default with MATCHUPS_SCHEDULED_OUTSIDE_DATES. The rejection carries outOfRangeMatchUpIds and outOfRangeDates. Pass force: true to proceed and unschedule them; the returned unscheduledMatchUpIds lists what was cleared.
  • Completed matchUps — rejected with MATCHUPS_COMPLETED_OUTSIDE_DATES and this block cannot be overridden by force. A match played on a given date forces that date into the tournament range; unscheduling it would erase the record of when it was played. The rejection carries completedOutOfRangeMatchUpIds and completedOutOfRangeDates. To proceed, either widen the range to include those dates or reschedule/clear the completed matchUps' results first.

When both kinds are present, the completed block takes precedence (there is no partial date change).


setTournamentLocalTimeZone​

Sets the canonical IANA timezone for a tournament. Once set, scoring and audit timestamps are recorded in this zone rather than the viewing device's local zone — so a score entered in Sydney and reviewed in Berlin reads the same wall-clock time. Validation uses the JS runtime's Intl.DateTimeFormat IANA database; any string the constructor rejects is returned as INVALID_TIME_ZONE without mutating state.

// Set or change the zone
engine.setTournamentLocalTimeZone({ localTimeZone: 'America/New_York' });

// Clear the zone (any of these work)
engine.setTournamentLocalTimeZone({ localTimeZone: '' });
engine.setTournamentLocalTimeZone({ localTimeZone: null });
engine.setTournamentLocalTimeZone({ localTimeZone: undefined });

No-op when the value is unchanged. On error returns { error: INVALID_TIME_ZONE }.


addTournamentTimeItem​

Adds a timeItem to the tournament record. TimeItems are used to store time-based metadata such as rankings, ratings, publish state, and scheduling data.

engine.addTournamentTimeItem({
timeItem, // required — { itemType, itemValue, ... } time item object
removePriorValues, // optional boolean — remove prior timeItems of the same itemType
duplicateValues, // optional boolean — allow duplicate values
});