@kaiord/core / extractWorkout
Function: extractWorkout()
extractWorkout(
krd):object
Defined in: packages/core/src/domain/validation/extract-workout.ts:17
Extracts and validates the structured workout from a KRD object.
Checks that the KRD type is "structured_workout" and validates the workout in extensions.structured_workout against workoutSchema.
Parameters
krd
KRD object to extract workout from
events?
object[] = ...
extensions?
{[key: string]: unknown; course?: unknown; course_points?: unknown; fit?: unknown; health?: {[key: string]: unknown; bodyComposition?: { bmi?: number; bodyFatPercent?: number; bodyWaterPercent?: number; boneMassKilograms?: number; externalId?: string; kaiordRecordId?: string; kind: "bodyComposition"; leanMassKilograms?: number; measuredAt: string; sourceBridgeId?: string; version: string; }; daily?: { activeCalories: number; date: string; externalId?: string; floorsClimbed?: number; intensityMinutes: { moderate: number; vigorous: number; }; kaiordRecordId?: string; kind: "daily"; restingCalories: number; sourceBridgeId?: string; steps: number; version: string; }; hrv?: { externalId?: string; kaiordRecordId?: string; kind: "hrv"; measuredAt: string; measurementWindow: "overnight" | "spot"; rMSSD: number; score?: number; sourceBridgeId?: string; version: string; }; sleep?: { endTime: string; externalId?: string; kaiordRecordId?: string; kind: "sleep"; restingHeartRate?: number; score?: number; sourceBridgeId?: string; stages: object[]; startTime: string; totalDurationSeconds: number; version: string; }; stress?: { averageLevel: number; endTime: string; externalId?: string; kaiordRecordId?: string; kind: "stress"; peakLevel: number; sourceBridgeId?: string; startTime: string; version: string; }; weight?: { externalId?: string; kaiordRecordId?: string; kind: "weight"; measuredAt: string; sourceBridgeId?: string; version: string; weightKilograms: number; }; }; structured_workout?: unknown; } = ...
extensions.course?
unknown = ...
extensions.course_points?
unknown = ...
extensions.fit?
unknown = ...
extensions.health?
{[key: string]: unknown; bodyComposition?: { bmi?: number; bodyFatPercent?: number; bodyWaterPercent?: number; boneMassKilograms?: number; externalId?: string; kaiordRecordId?: string; kind: "bodyComposition"; leanMassKilograms?: number; measuredAt: string; sourceBridgeId?: string; version: string; }; daily?: { activeCalories: number; date: string; externalId?: string; floorsClimbed?: number; intensityMinutes: { moderate: number; vigorous: number; }; kaiordRecordId?: string; kind: "daily"; restingCalories: number; sourceBridgeId?: string; steps: number; version: string; }; hrv?: { externalId?: string; kaiordRecordId?: string; kind: "hrv"; measuredAt: string; measurementWindow: "overnight" | "spot"; rMSSD: number; score?: number; sourceBridgeId?: string; version: string; }; sleep?: { endTime: string; externalId?: string; kaiordRecordId?: string; kind: "sleep"; restingHeartRate?: number; score?: number; sourceBridgeId?: string; stages: object[]; startTime: string; totalDurationSeconds: number; version: string; }; stress?: { averageLevel: number; endTime: string; externalId?: string; kaiordRecordId?: string; kind: "stress"; peakLevel: number; sourceBridgeId?: string; startTime: string; version: string; }; weight?: { externalId?: string; kaiordRecordId?: string; kind: "weight"; measuredAt: string; sourceBridgeId?: string; version: string; weightKilograms: number; }; } = ...
extensions.health.bodyComposition?
{ bmi?: number; bodyFatPercent?: number; bodyWaterPercent?: number; boneMassKilograms?: number; externalId?: string; kaiordRecordId?: string; kind: "bodyComposition"; leanMassKilograms?: number; measuredAt: string; sourceBridgeId?: string; version: string; } = ...
extensions.health.bodyComposition.bmi?
number = ...
extensions.health.bodyComposition.bodyFatPercent?
number = ...
extensions.health.bodyComposition.bodyWaterPercent?
number = ...
extensions.health.bodyComposition.boneMassKilograms?
number = ...
extensions.health.bodyComposition.externalId?
string = ...
extensions.health.bodyComposition.kaiordRecordId?
string = ...
extensions.health.bodyComposition.kind
"bodyComposition" = ...
extensions.health.bodyComposition.leanMassKilograms?
number = ...
extensions.health.bodyComposition.measuredAt
string = ...
extensions.health.bodyComposition.sourceBridgeId?
string = ...
extensions.health.bodyComposition.version
string = healthVersionSchema
extensions.health.daily?
{ activeCalories: number; date: string; externalId?: string; floorsClimbed?: number; intensityMinutes: { moderate: number; vigorous: number; }; kaiordRecordId?: string; kind: "daily"; restingCalories: number; sourceBridgeId?: string; steps: number; version: string; } = ...
extensions.health.daily.activeCalories
number = ...
extensions.health.daily.date
string = ...
extensions.health.daily.externalId?
string = ...
extensions.health.daily.floorsClimbed?
number = ...
extensions.health.daily.intensityMinutes
{ moderate: number; vigorous: number; } = ...
extensions.health.daily.intensityMinutes.moderate
number = ...
extensions.health.daily.intensityMinutes.vigorous
number = ...
extensions.health.daily.kaiordRecordId?
string = ...
extensions.health.daily.kind
"daily" = ...
extensions.health.daily.restingCalories
number = ...
extensions.health.daily.sourceBridgeId?
string = ...
extensions.health.daily.steps
number = ...
extensions.health.daily.version
string = healthVersionSchema
extensions.health.hrv?
{ externalId?: string; kaiordRecordId?: string; kind: "hrv"; measuredAt: string; measurementWindow: "overnight" | "spot"; rMSSD: number; score?: number; sourceBridgeId?: string; version: string; } = ...
extensions.health.hrv.externalId?
string = ...
extensions.health.hrv.kaiordRecordId?
string = ...
extensions.health.hrv.kind
"hrv" = ...
extensions.health.hrv.measuredAt
string = ...
extensions.health.hrv.measurementWindow
"overnight" | "spot" = ...
extensions.health.hrv.rMSSD
number = ...
extensions.health.hrv.score?
number = ...
extensions.health.hrv.sourceBridgeId?
string = ...
extensions.health.hrv.version
string = healthVersionSchema
extensions.health.sleep?
{ endTime: string; externalId?: string; kaiordRecordId?: string; kind: "sleep"; restingHeartRate?: number; score?: number; sourceBridgeId?: string; stages: object[]; startTime: string; totalDurationSeconds: number; version: string; } = ...
extensions.health.sleep.endTime
string = ...
extensions.health.sleep.externalId?
string = ...
extensions.health.sleep.kaiordRecordId?
string = ...
extensions.health.sleep.kind
"sleep" = ...
extensions.health.sleep.restingHeartRate?
number = ...
extensions.health.sleep.score?
number = ...
extensions.health.sleep.sourceBridgeId?
string = ...
extensions.health.sleep.stages
object[] = ...
extensions.health.sleep.startTime
string = ...
extensions.health.sleep.totalDurationSeconds
number = ...
extensions.health.sleep.version
string = healthVersionSchema
extensions.health.stress?
{ averageLevel: number; endTime: string; externalId?: string; kaiordRecordId?: string; kind: "stress"; peakLevel: number; sourceBridgeId?: string; startTime: string; version: string; } = ...
extensions.health.stress.averageLevel
number = ...
extensions.health.stress.endTime
string = ...
extensions.health.stress.externalId?
string = ...
extensions.health.stress.kaiordRecordId?
string = ...
extensions.health.stress.kind
"stress" = ...
extensions.health.stress.peakLevel
number = ...
extensions.health.stress.sourceBridgeId?
string = ...
extensions.health.stress.startTime
string = ...
extensions.health.stress.version
string = healthVersionSchema
extensions.health.weight?
{ externalId?: string; kaiordRecordId?: string; kind: "weight"; measuredAt: string; sourceBridgeId?: string; version: string; weightKilograms: number; } = ...
extensions.health.weight.externalId?
string = ...
extensions.health.weight.kaiordRecordId?
string = ...
extensions.health.weight.kind
"weight" = ...
extensions.health.weight.measuredAt
string = ...
extensions.health.weight.sourceBridgeId?
string = ...
extensions.health.weight.version
string = healthVersionSchema
extensions.health.weight.weightKilograms
number = ...
extensions.structured_workout?
unknown = ...
laps?
object[] = ...
metadata
{ created: string; manufacturer?: string; product?: string; serialNumber?: string; sport?: string; subSport?: string; } = krdMetadataSchema
metadata.created
string = ...
metadata.manufacturer?
string = ...
metadata.product?
string = ...
metadata.serialNumber?
string = ...
metadata.sport?
string = ...
See
sportSchema for known sport values. Accepts custom strings for forward compatibility.
Optional at the metadata level because health-metric KRD types (sleep, weight, HRV, daily wellness, body composition, stress) have no associated sport. A conditional refinement on krdSchema still requires sport for the three legacy workout/activity/course types so v1.x consumers see no change for those.
metadata.subSport?
string = ...
records?
object[] = ...
sessions?
object[] = ...
type
"structured_workout" | "recorded_activity" | "course" | "sleep_record" | "weight_measurement" | "hrv_summary" | "daily_wellness" | "body_composition" | "stress_episode" = fileTypeSchema
version
string = ...
Returns
Validated Workout object
extensions?
optionalextensions?:Record<string,unknown>
name?
optionalname?:string
poolLength?
optionalpoolLength?:number
meters — always normalized to meters on ingest
poolLengthUnit?
optionalpoolLengthUnit?:"meters"
sport
sport:
"generic"|"running"|"cycling"|"transition"|"fitness_equipment"|"swimming"|"basketball"|"soccer"|"tennis"|"american_football"|"training"|"walking"|"cross_country_skiing"|"alpine_skiing"|"snowboarding"|"rowing"|"mountaineering"|"hiking"|"multisport"|"paddling"|"flying"|"e_biking"|"motorcycling"|"boating"|"driving"|"golf"|"hang_gliding"|"horseback_riding"|"hunting"|"fishing"|"inline_skating"|"rock_climbing"|"sailing"|"ice_skating"|"sky_diving"|"snowshoeing"|"snowmobiling"|"stand_up_paddleboarding"|"surfing"|"wakeboarding"|"water_skiing"|"kayaking"|"rafting"|"windsurfing"|"kitesurfing"|"tactical"|"jumpmaster"|"boxing"|"floor_climbing"|"baseball"|"diving"|"shooting"|"winter_sport"|"grinding"|"hiit"|"video_gaming"|"racket"|"wheelchair_push_walk"|"wheelchair_push_run"|"meditation"|"para_sport"|"disc_golf"|"team_sport"|"cricket"|"rugby"|"hockey"|"lacrosse"|"volleyball"|"water_tubing"|"wakesurfing"|"water_sport"|"archery"|"mixed_martial_arts"|"motor_sports"|"snorkeling"|"dance"|"jump_rope"|"pool_apnea"|"mobility"|"geocaching"|"canoeing"=sportSchema
steps
steps: ({
duration: {seconds:number;type:"time"; } | {meters:number;type:"distance"; } | {bpm:number;type:"heart_rate_less_than"; } | {bpm:number;repeatFrom:number;type:"repeat_until_heart_rate_greater_than"; } | {calories:number;type:"calories"; } | {type:"power_less_than";watts:number; } | {type:"power_greater_than";watts:number; } | {repeatFrom:number;seconds:number;type:"repeat_until_time"; } | {meters:number;repeatFrom:number;type:"repeat_until_distance"; } | {calories:number;repeatFrom:number;type:"repeat_until_calories"; } | {bpm:number;repeatFrom:number;type:"repeat_until_heart_rate_less_than"; } | {repeatFrom:number;type:"repeat_until_power_less_than";watts:number; } | {repeatFrom:number;type:"repeat_until_power_greater_than";watts:number; } | {type:"open"; };durationType:"time"|"distance"|"heart_rate_less_than"|"repeat_until_heart_rate_greater_than"|"calories"|"power_less_than"|"power_greater_than"|"repeat_until_time"|"repeat_until_distance"|"repeat_until_calories"|"repeat_until_heart_rate_less_than"|"repeat_until_power_less_than"|"repeat_until_power_greater_than"|"open";equipment?:"none"|"swim_fins"|"swim_kickboard"|"swim_paddles"|"swim_pull_buoy"|"swim_snorkel";extensions?:Record<string,unknown>;intensity?:"warmup"|"active"|"cooldown"|"rest"|"recovery"|"interval"|"other";name?:string;notes?:string;stepIndex:number;target: {type:"power";value: {max:number;min:number;unit:"range"; } | {unit:"watts";value:number; } | {unit:"percent_ftp";value:number; } | {unit:"zone";value:number; }; } | {type:"heart_rate";value: {max:number;min:number;unit:"range"; } | {unit:"bpm";value:number; } | {unit:"zone";value:number; } | {unit:"percent_max";value:number; }; } | {type:"cadence";value: {max:number;min:number;unit:"range"; } | {unit:"rpm";value:number; }; } | {type:"pace";value: {max:number;min:number;unit:"range"; } | {unit:"mps";value:number; } | {unit:"zone";value:number; }; } | {type:"stroke_type";value: {unit:"swim_stroke";value:number; }; } | {type:"open"; };targetType:"cadence"|"power"|"open"|"heart_rate"|"pace"|"stroke_type"; } | {id?:string;repeatCount:number;steps:object[]; })[]
subSport?
optionalsubSport?:"match"|"map"|"generic"|"treadmill"|"street"|"trail"|"track"|"spin"|"indoor_cycling"|"road"|"mountain"|"downhill"|"recumbent"|"cyclocross"|"hand_cycling"|"track_cycling"|"indoor_rowing"|"elliptical"|"stair_climbing"|"lap_swimming"|"open_water"|"flexibility_training"|"strength_training"|"warm_up"|"exercise"|"challenge"|"indoor_skiing"|"cardio_training"|"indoor_walking"|"e_bike_fitness"|"bmx"|"casual_walking"|"speed_walking"|"bike_to_run_transition"|"run_to_bike_transition"|"swim_to_bike_transition"|"atv"|"motocross"|"backcountry"|"resort"|"rc_drone"|"wingsuit"|"whitewater"|"skate_skiing"|"yoga"|"pilates"|"indoor_running"|"gravel_cycling"|"e_bike_mountain"|"commuting"|"mixed_surface"|"navigate"|"track_me"|"single_gas_diving"|"multi_gas_diving"|"gauge_diving"|"apnea_diving"|"apnea_hunting"|"virtual_activity"|"obstacle"|"all"
Throws
If KRD is not a structured workout or workout is invalid