Extensions
Overview
Extensions capture configuration parameters, calculated results, and custom data that are not part of the core TODS specification. They further extend the tournament "time capsule" - ensuring what happened during a tournament can be accurately reconstructed.
Key Concepts
Non-Temporal Data: Unlike Time Items, extensions don't track changes over time
Name-Value Pairs: Each extension has { name, value } structure
Uniqueness: Only one extension with a given name per .extensions array
Hierarchical Scope: Extensions can exist on any TODS document element
Factory Constants: Pre-defined extension names recognized by the factory
Custom Extensions: User-defined extensions for application-specific data
Extension Structure
type Extension = {
name: string; // Unique identifier within the extensions array
value: any; // Any JSON-serializable data
};
// On any TODS element
{
extensions: Extension[];
}
Location in TODS
Extensions can be attached to any element in a tournament record:
- Tournament Record (top level)
- Events
- Draw Definitions
- Structures
- MatchUps
- Participants
- Venues/Courts
Factory-Recognized Extensions
The factory internally uses and recognizes the following extension names (constants):
Configuration & Policy Extensions
appliedPolicies - Policies applied to tournament element
Attached to: tournamentRecords, events, drawDefinitions, structures
Purpose: Captures which policies are "in scope" within the object hierarchy. Policies can be attached programmatically or automatically during draw generation.
{
name: 'appliedPolicies',
value: {
seeding: { seedingProfile: 'WTN', ... },
avoidance: { roundsToSeparate: 3, ... }
}
}
schedulingProfile - Auto scheduler configuration
Attached to: tournamentRecords, events
Purpose: Stores iteratively-edited scheduling profile before running the auto scheduler.
{
name: 'schedulingProfile',
value: [
{
scheduleDate: '2024-06-15',
venues: [{ venueId: 'venue-1', rounds: [...] }]
}
]
}
scheduleLimits - Match daily limits override
Attached to: matchUps
Purpose: Overrides policy-defined daily limits for specific matchUps.
{
name: 'scheduleLimits',
value: {
SINGLES: 2,
DOUBLES: 1,
TOTAL: 2
}
}
scheduleTiming - Match format timing override
Attached to: matchUps
Purpose: Overrides policy-defined timing for specific matchUp formats.
{
name: 'scheduleTiming',
value: {
'SET3-S:6/TB7': {
averageMinutes: 90,
recoveryMinutes: 30
}
}
}
Draw Generation & Structure Extensions
entryProfile - Entry limitations
Attached to: events
Purpose: Stores limitations on accepted entries including qualifiers, wildcards, and maximum draw size.
{
name: 'entryProfile',
value: {
maxDrawSize: 32,
qualifiers: 4,
wildcards: 4
}
}
flightProfile - Flight names and entries
Attached to: events
Purpose: Captures flight configuration when event entries are split into multiple flights before draw generation.
{
name: 'flightProfile',
value: {
flights: [
{ flightNumber: 1, drawEntries: [...] },
{ flightNumber: 2, drawEntries: [...] }
]
}
}
drawProfile - Draw-specific configuration
Attached to: drawDefinitions
Purpose: Stores draw generation parameters and configuration.
{
name: 'drawProfile',
value: {
automated: true,
seedsCount: 8,
drawType: 'ELIMINATION'
}
}
eventProfile - Event-specific configuration
Attached to: events
Purpose: Stores event-level configuration and parameters.
{
name: 'eventProfile',
value: {
category: 'U18',
gender: 'FEMALE',
surfaceType: 'HARD'
}
}
disableLinks - Link disabling for position swaps
Attached to: structures
Purpose: When positioning policies allow draw position swaps in non-MAIN structures, normal progression logic breaks and links must be disabled to avoid data corruption.
{
name: 'disableLinks',
value: true
}
roundTarget - Qualifying feed-in rounds
Attached to: structures, event.entries
Purpose: Supports qualifying structures feeding into different rounds of MAIN structures; also annotates entries for "staggered entry" structures.
{
name: 'roundTarget',
value: {
roundNumber: 2, // Feed into round 2 of main draw
structureId: 'main-structure-id'
}
}
Team Event Extensions
groupingAttribute - Team generation attribute
Attached to: participants (TEAM type)
Purpose: Stores the person/participant attribute used to generate TEAM participants.
{
name: 'groupingAttribute',
value: 'club' // Teams generated by club affiliation
}
lineUps - Team lineup defaults
Attached to: structures
Purpose: Stores most recent lineup for each TEAM progressing through structure. Enables default assignments in hydrated SINGLES/DOUBLES tie matchUps before actual lineups are saved.
{
name: 'lineUps',
value: {
'team-1': [
{ participantId: 'player-1', collectionPosition: 1 },
{ participantId: 'player-2', collectionPosition: 2 }
]
}
}
disableAutoCalc - Manual TEAM scoring
Attached to: matchUps (TEAM type)
Purpose: When manual scoring is enabled for TEAM matchUps, disables automatic calculation of team results.
{
name: 'disableAutoCalc',
value: true
}
Scoring & Results Extensions
delegatedOutcome - Unofficial results
Attached to: matchUps
Purpose: Captures matchUp outcomes sourced from third-party applications for display purposes prior to official result entry.
{
name: 'delegatedOutcome',
value: {
score: {
sets: [
{ side1Score: 6, side2Score: 4, winningSide: 1 },
{ side1Score: 6, side2Score: 2, winningSide: 1 }
]
},
winningSide: 1,
source: 'external-app'
}
}
tally - Round robin results
Attached to: positionAssignments (within structures)
Purpose: Stores calculated participant results for structure positions. Used to determine group order in round robin structures.
{
name: 'tally',
value: {
matchUpsWon: 4,
matchUpsLost: 1,
setsWon: 9,
setsLost: 3,
gamesWon: 54,
gamesLost: 38
}
}
subOrder - Tie-breaking order
Attached to: positionAssignments
Purpose: When ties occur in round robin group order calculation, captures manual determination of order for tied positions.
{
name: 'subOrder',
value: 2 // Second place among tied participants
}
rankingPoints - Points awarded
Attached to: tournamentRecords
Purpose: Enables capturing points generated by event outcomes for real-time display as participants advance through structures.
{
name: 'rankingPoints',
value: {
'player-1': { singles: 250, doubles: 100 },
'player-2': { singles: 150 }
}
}
Scheduling & Venue Extensions
disabled - Resource disabling
Attached to: courts, venues
Purpose: Indicates resource has been disabled and scheduling methods cannot use it.
{
name: 'disabled',
value: true
}
linkedTournamentsIds - Multi-tournament scheduling
Attached to: tournamentRecords
Purpose: When multiple tournaments share venues, captures the association so client applications know to load all tournament records for scheduling.
{
name: 'linkedTournamentsIds',
value: ['tournament-1', 'tournament-2', 'tournament-3']
}
personRequests - Scheduling requests
Attached to: participants, events, tournamentRecords
Purpose: Captures scheduling requests from participants. Consumed by scheduler to accommodate requests or simply display them.
{
name: 'personRequests',
value: [
{
personId: 'person-1',
requestType: 'DO_NOT_SCHEDULE',
date: '2024-06-15',
startTime: '09:00',
endTime: '11:00'
}
]
}
Participant & Registration Extensions
registration - Registration data
Attached to: participants
Purpose: Captures registration-related information and metadata.
{
name: 'registration',
value: {
registrationDate: '2024-05-01',
registrationId: 'REG-12345',
paymentStatus: 'PAID'
}
}
participantRepresentatives - Draw witnesses
Attached to: drawDefinitions
Purpose: Records participant representatives present as witnesses during manual draw creation (traditional "hat draw").
{
name: 'participantRepresentatives',
value: [
{ participantId: 'player-1', representativeName: 'John Coach' },
{ participantId: 'player-2', representativeName: 'Jane Manager' }
]
}
statusDetail - Entry status annotation
Attached to: event.entries
Purpose: Third-party extension (not used by factory) for annotating entry status of participants.
{
name: 'statusDetail',
value: {
status: 'CONFIRMED',
notes: 'Paid in full, medical clearance received'
}
}
eventWithdrawalRequests - Withdrawal tracking
Attached to: events
Purpose: Tracks participant withdrawal requests from events.
{
name: 'eventWithdrawalRequests',
value: [
{
participantId: 'player-1',
requestDate: '2024-06-10',
reason: 'Injury'
}
]
}
activeSuspension - Participant suspension
Attached to: participants
Purpose: Indicates participant has active suspension affecting eligibility.
{
name: 'activeSuspension',
value: {
startDate: '2024-05-01',
endDate: '2024-08-31',
reason: 'Code violation'
}
}
Auditing & System Extensions
factory - Factory version tracking
Attached to: tournamentRecords
Purpose: Captures version of factory (and other TODS processors) that created/mutated the tournament record.
{
name: 'factory',
value: {
version: '2.2.49',
lastModified: '2024-06-15T10:30:00Z'
}
}
drawDeletions - Deleted draw audit
Attached to: tournamentRecords
Purpose: When no external auditing service is available, stores deleted draw definitions for audit purposes.
{
name: 'drawDeletions',
value: [
{
drawId: 'draw-123',
drawName: 'Main Draw',
deletedAt: '2024-06-14T15:00:00Z',
deletedBy: 'user-456'
}
]
}
positionActions - Position action telemetry
Attached to: tournamentRecords
Purpose: When no external auditing service is available, stores position action telemetry.
{
name: 'positionActions',
value: [
{
action: 'ASSIGN_PARTICIPANT',
drawPosition: 1,
participantId: 'player-1',
timestamp: '2024-06-14T14:00:00Z'
}
]
}
tieFormatModifications - Tie format audit
Attached to: tournamentRecords
Purpose: When no external auditing service is available, captures modifications to tie formats during the course of an event.
{
name: 'tieFormatModifications',
value: [
{
tieFormatId: 'format-1',
modifiedAt: '2024-06-15T09:00:00Z',
changes: { collectionDefinitions: [...] }
}
]
}
context - Creation context
Attached to: venues, courts, and other elements
Purpose: Captures context in which a resource was added (metadata about creation).
{
name: 'context',
value: {
createdBy: 'user-123',
createdAt: '2024-06-01T10:00:00Z',
source: 'mobile-app'
}
}
Display & UI Extensions
display - Display preferences
Attached to: events, drawDefinitions, structures
Purpose: Client application-specific storage for display preferences.
{
name: 'display',
value: {
viewMode: 'bracket',
showSeeds: true,
highlightedParticipants: ['player-1', 'player-2']
}
}
Custom Extensions
Applications can define custom extensions for application-specific data not covered by factory constants.
Naming Conventions
Use descriptive, unique names that won't conflict with factory extensions:
// ✓ GOOD - Clear, specific names
{ name: 'broadcastSchedule', value: { ... } }
{ name: 'sponsorData', value: { ... } }
{ name: 'mediaRequirements', value: { ... } }
{ name: 'facilityAccess', value: { ... } }
// ✗ AVOID - Generic or conflicting names
{ name: 'data', value: { ... } }
{ name: 'info', value: { ... } }
{ name: 'config', value: { ... } }
Custom Extension Examples
Broadcast Information:
tournamentEngine.addMatchUpExtension({
matchUpId: 'match-123',
drawId: 'draw-456',
extension: {
name: 'broadcastSchedule',
value: {
channel: 'ESPN',
broadcastTime: '14:00',
commentators: ['John Smith', 'Jane Doe'],
},
},
});
Facility Requirements:
tournamentEngine.addEventExtension({
eventId: 'singles-main',
extension: {
name: 'facilityRequirements',
value: {
courtSurface: 'HARD',
lighting: 'REQUIRED',
spectatorCapacity: 500,
},
},
});
Sponsor Data:
tournamentEngine.addTournamentExtension({
extension: {
name: 'sponsorData',
value: {
titleSponsor: 'Acme Corp',
courtSponsors: {
'court-1': 'Beta LLC',
'court-2': 'Gamma Inc',
},
},
},
});
Working with Extensions
Adding Extensions
// Add to tournament
tournamentEngine.addTournamentExtension({
extension: {
name: 'customData',
value: { ... }
}
});
// Add to event
tournamentEngine.addEventExtension({
eventId: 'singles-main',
extension: {
name: 'eventCustomData',
value: { ... }
}
});
// Add to draw
tournamentEngine.addDrawDefinitionExtension({
drawId: 'draw-123',
extension: {
name: 'drawCustomData',
value: { ... }
}
});
// Add to matchUp
tournamentEngine.addMatchUpExtension({
matchUpId: 'match-456',
drawId: 'draw-123',
extension: {
name: 'matchUpCustomData',
value: { ... }
}
});
Retrieving Extensions
// From tournament record
const tournament = tournamentEngine.getTournament();
const customExt = tournament.extensions?.find((e) => e.name === 'customData');
// From event
const { event } = tournamentEngine.getEvent({ eventId: 'singles-main' });
const eventExt = event.extensions?.find((e) => e.name === 'eventCustomData');
// From draw
const { drawDefinition } = tournamentEngine.getDrawDefinition({ drawId: 'draw-123' });
const drawExt = drawDefinition.extensions?.find((e) => e.name === 'drawCustomData');
// From matchUp (with context)
const { matchUp } = tournamentEngine.findMatchUp({
matchUpId: 'match-456',
inContext: true,
});
const matchUpExt = matchUp.extensions?.find((e) => e.name === 'matchUpCustomData');
Updating Extensions
Extensions are updated by removing the old one and adding the new one (or using update methods):
// Remove extension
tournamentEngine.removeTournamentExtension({
name: 'customData',
});
// Add updated version
tournamentEngine.addTournamentExtension({
extension: {
name: 'customData',
value: updatedValue,
},
});
Extension Hydration
When retrieving data with inContext: true, extensions are automatically converted to underscore-prefixed attributes:
// MatchUp has extension
{
extensions: [
{
name: 'broadcastSchedule',
value: { channel: 'ESPN' },
},
];
}
// When hydrated
const { matchUps } = tournamentEngine.allTournamentMatchUps({
inContext: true,
});
console.log(matchUps[0]._broadcastSchedule); // { channel: 'ESPN' }
console.log(matchUps[0].extensions); // Original array still present
See: MatchUp Context for details on extension hydration.
Extensions vs Time Items
Choose the appropriate mechanism for your data:
| Feature | Extensions | Time Items |
|---|---|---|
| Purpose | Configuration, calculated results, custom data | Temporal data with effective dates |
| History | Single value (current state) | Multiple values over time |
| Timestamps | No timestamps | itemDate and createdAt |
| Use Cases | Policies, profiles, preferences, metadata | Rankings, ratings, schedule changes, status transitions |
| Hydration | Underscore-prefixed attributes | Extracted to .schedule for matchUps |
Example Decision:
- Use Extension: Store broadcast channel assignment (doesn't change over time)
- Use Time Item: Track court assignments (may change, need history)
Best Practices
Uniqueness
Only one extension with a given name per element:
// ✓ CORRECT - One extension per name
{
extensions: [{ name: 'customData', value: { field1: 'value1', field2: 'value2' } }];
}
// ✗ WRONG - Duplicate names
{
extensions: [
{ name: 'customData', value: { field1: 'value1' } },
{ name: 'customData', value: { field2: 'value2' } }, // Duplicate!
];
}
Value Types
Use appropriate JSON-serializable types:
// ✓ GOOD - JSON-serializable
{ name: 'config', value: { enabled: true, count: 5, items: ['a', 'b'] } }
{ name: 'timestamp', value: '2024-06-15T10:00:00Z' }
// ✗ AVOID - Non-serializable
{ name: 'config', value: new Date() } // Date object
{ name: 'callback', value: function() {} } // Function
Factory Constant Names
Avoid using factory-recognized extension names for custom extensions:
// ✗ AVOID - Conflicts with factory constants
{ name: 'appliedPolicies', value: myCustomValue }
{ name: 'schedulingProfile', value: myCustomValue }
// ✓ GOOD - Unique custom names
{ name: 'myApp_policies', value: myCustomValue }
{ name: 'myApp_schedule', value: myCustomValue }
Related Documentation
- Time Items - Temporal data with effective dates
- MatchUp Context - Extension hydration
- Participant Context - Participant extensions
- Policies Overview - Applied policies extension