@diby/openset-codegen
A fluent TypeScript builder for constructing valid OpenSet documents with full type safety, autocompletion, and optional runtime validation.
Installation
npm install @diby/openset-codegen
For validated builds, also install the validator:
npm install @diby/openset-codegen @diby/openset-validator
Quick Start
import { workout, set, fixed, range, amrap } from '@diby/openset-codegen';
const doc = workout('Upper Body Push')
.date('2024-01-15')
.sports('strength')
.block('Warm-Up', b => b
.series('SEQUENTIAL', s => s
.exercise('push_up', e => e
.set(set({ reps: fixed(15) }))
)
)
)
.block('Main Work', b => b
.series('SEQUENTIAL', s => s
.exercise('bench_press', e => e
.set(set({ reps: fixed(5), load: fixed(100, 'kg') }))
.set(set({ reps: fixed(5), load: fixed(100, 'kg') }))
.set(set({ reps: amrap(), load: fixed(100, 'kg'), rpe: fixed(9) }))
)
.rest(180, 's')
)
)
.build();
Value Helpers
Shorthand functions for creating ValueObject instances:
| Function | Output | Example |
|---|---|---|
fixed(value, unit?) | { type: 'fixed', value, unit } | fixed(100, 'kg') |
range(min, max, unit?) | { type: 'range', min, max, unit } | range(8, 12) |
min(value, unit?) | { type: 'min', value, unit } | min(5) |
amrap() | { type: 'amrap' } | amrap() |
max() | { type: 'max' } | max() |
any() | { type: 'any' } | any() |
The range() function validates that min < max at construction time.
Set Builder
The set() function is the single builder for all sets. It accepts an object of dimension key-value pairs and automatically infers the dimensions array from the keys you provide.
set({ reps: fixed(5), load: fixed(100, 'kg') })
// produces: { dimensions: ["reps", "load"], reps: { ... }, load: { ... } }
Any valid dimension name can be passed as a key. The dimensions array on the resulting set object is derived from the keys at build time — you never need to declare it manually.
Common dimension names include: reps, load, duration, distance, power, calories, rounds, height, sides, tempo, velocity, pace, speed, heart_rate, heart_rate_zone, rpe, incline, rest_between_sides, cadence, resistance.
The set() function also accepts rest_after and note as optional fields (these are not dimensions and are not included in the inferred dimensions array).
Document Builders
workout(name?)
Creates a WorkoutBuilder for building standalone workout documents.
workout('Morning Workout')
.id('sess-001')
.date('2024-01-15')
.sports('strength')
.note('Focus on form')
.extensions(['x_band'])
.block('Block Name', b => { /* ... */ })
.build()
program(name)
Creates a ProgramBuilder for building multi-phase program documents.
program('4-Week Strength')
.description('Progressive overload program')
.sports('strength')
.duration(4, 'week') // or .durationWeeks(4)
.author('Coach')
.createdAt('2024-01-01')
.phase('Base Building', p => p
.weeks(1, 2)
.goal('Build work capacity')
.workout('Day 1', s => { /* ... */ })
)
.build()
Hierarchy Builders
The builders use callback-based nesting that mirrors the document hierarchy:
workout → block → series → exercise → set
program → phase → workout → block → series → exercise → set
Block
.block('Name', b => b
.id('block-1')
.note('Heavy work')
.series('SEQUENTIAL', s => { /* ... */ })
)
Series
.series('CIRCUIT', s => s
.id('series-1')
.rounds(fixed(3))
.rest(120, 's') // shorthand for fixed rest
.restAfter(range(60, 90, 's')) // or full ValueObject
.note('3 rounds, minimal rest')
.exercise('push_up', e => { /* ... */ })
.namedExercise('Custom Drill', e => { /* ... */ })
)
Exercise
.exercise('bench_press', e => e
.name('Bench Press') // optional display name
.group('pair_a') // for CLUSTER mode
.note('Control the eccentric')
.set(set({ reps: fixed(5), load: fixed(100, 'kg') }))
.sets(3, set({ reps: fixed(8), load: fixed(80, 'kg') })) // 3 identical sets
)
The .sets(count, set) method creates N shallow copies — useful for "3×8 at 80kg" patterns.
Validated Builds
The .buildValidated() method runs @diby/openset-validator on the built document and throws a ValidationError if there are errors:
import { workout, set, fixed, ValidationError } from '@diby/openset-codegen';
try {
const doc = await workout('Test')
.block('A', b => b
.series('SEQUENTIAL', s => s
.exercise('bench_press', e => e
.set(set({ reps: fixed(5), load: fixed(100, 'kg') }))
)
)
)
.buildValidated();
console.log('Valid!', doc);
} catch (err) {
if (err instanceof ValidationError) {
console.log(err.result.errors); // ValidationMessage[]
}
}
buildValidated() is async and requires @diby/openset-validator to be installed. If the validator is not installed, it throws an error with installation instructions.
Full Program Example
import { program, set, fixed, range } from '@diby/openset-codegen';
const plan = program('4-Week Strength Foundation')
.sports('strength')
.duration(4, 'week')
.author('Coach')
.phase('Base Building', p => p
.weeks(1, 2)
.goal('Build work capacity with moderate loads')
.workout('Upper Body', s => s
.block('Main Work', b => b
.series('SEQUENTIAL', ser => ser
.exercise('bench_press', e => e
.sets(3, set({
reps: fixed(8),
load: fixed(60, 'kg'),
rpe: fixed(7),
rest_after: fixed(120, 's'),
}))
)
)
)
)
)
.phase('Intensification', p => p
.weeks(3, 4)
.goal('Increase intensity with heavier loads')
.workout('Upper Body', s => s
.block('Main Work', b => b
.series('SEQUENTIAL', ser => ser
.exercise('bench_press', e => e
.sets(5, set({
reps: fixed(5),
load: fixed(70, 'kg'),
rpe: fixed(8),
rest_after: fixed(180, 's'),
}))
)
)
)
)
)
.build();