@pwf-dev/core 0.1.1

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.
@@ -0,0 +1,439 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://pwf.dev/schema/v1/plan.json",
4
+ "title": "PWF Plan v1",
5
+ "description": "Portable Workout Format v1 schema for validating plan documents",
6
+ "type": "object",
7
+ "required": ["plan_version", "cycle"],
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "plan_version": {
11
+ "type": "integer",
12
+ "const": 1,
13
+ "description": "Specification version (must be 1)"
14
+ },
15
+ "meta": {
16
+ "$ref": "#/$defs/Meta"
17
+ },
18
+ "glossary": {
19
+ "type": "object",
20
+ "description": "Term definitions for exercises and concepts used in this plan",
21
+ "maxProperties": 100,
22
+ "additionalProperties": {
23
+ "type": "string",
24
+ "minLength": 1,
25
+ "maxLength": 500
26
+ },
27
+ "propertyNames": {
28
+ "pattern": "^[a-zA-Z0-9\\s\\-']+$",
29
+ "minLength": 1,
30
+ "maxLength": 50
31
+ }
32
+ },
33
+ "cycle": {
34
+ "$ref": "#/$defs/Cycle"
35
+ }
36
+ },
37
+ "$defs": {
38
+ "Meta": {
39
+ "type": "object",
40
+ "description": "Plan metadata for display and organization",
41
+ "additionalProperties": false,
42
+ "required": ["title"],
43
+ "properties": {
44
+ "id": {
45
+ "type": "string",
46
+ "description": "Unique plan identifier"
47
+ },
48
+ "title": {
49
+ "type": "string",
50
+ "maxLength": 80,
51
+ "minLength": 1,
52
+ "description": "Plan display name"
53
+ },
54
+ "description": {
55
+ "type": "string",
56
+ "description": "Brief plan description"
57
+ },
58
+ "author": {
59
+ "type": "string",
60
+ "description": "Coach or creator name"
61
+ },
62
+ "status": {
63
+ "type": "string",
64
+ "enum": ["draft", "active", "completed", "archived"],
65
+ "default": "draft",
66
+ "description": "Plan status"
67
+ },
68
+ "activated_at": {
69
+ "type": "string",
70
+ "format": "date-time",
71
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(Z|[+-]\\d{2}:\\d{2})$",
72
+ "description": "ISO 8601 timestamp when plan was activated"
73
+ },
74
+ "completed_at": {
75
+ "type": "string",
76
+ "format": "date-time",
77
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(Z|[+-]\\d{2}:\\d{2})$",
78
+ "description": "ISO 8601 timestamp when plan was completed"
79
+ },
80
+ "equipment": {
81
+ "type": "array",
82
+ "items": {
83
+ "type": "string"
84
+ },
85
+ "uniqueItems": true,
86
+ "description": "Required equipment tags"
87
+ },
88
+ "daysPerWeek": {
89
+ "type": "integer",
90
+ "minimum": 1,
91
+ "maximum": 7,
92
+ "description": "Intended training frequency"
93
+ },
94
+ "recommendedFirst": {
95
+ "type": "boolean",
96
+ "default": false,
97
+ "description": "Suggest as starter plan"
98
+ },
99
+ "tags": {
100
+ "type": "array",
101
+ "items": {
102
+ "type": "string"
103
+ },
104
+ "description": "Searchable tags"
105
+ },
106
+ "athlete_profile": {
107
+ "$ref": "#/$defs/AthleteProfile"
108
+ }
109
+ }
110
+ },
111
+ "AthleteProfile": {
112
+ "type": "object",
113
+ "description": "Athlete metrics for endurance training",
114
+ "additionalProperties": false,
115
+ "properties": {
116
+ "ftp_watts": {
117
+ "type": "integer",
118
+ "minimum": 0,
119
+ "description": "Functional Threshold Power in watts"
120
+ },
121
+ "threshold_hr_bpm": {
122
+ "type": "integer",
123
+ "minimum": 0,
124
+ "maximum": 250,
125
+ "description": "Threshold heart rate in BPM"
126
+ },
127
+ "max_hr_bpm": {
128
+ "type": "integer",
129
+ "minimum": 0,
130
+ "maximum": 250,
131
+ "description": "Maximum heart rate in BPM"
132
+ },
133
+ "threshold_pace_sec_per_km": {
134
+ "type": "integer",
135
+ "minimum": 0,
136
+ "description": "Threshold pace in seconds per kilometer"
137
+ },
138
+ "weight_kg": {
139
+ "type": "number",
140
+ "minimum": 0,
141
+ "description": "Athlete weight in kilograms"
142
+ }
143
+ }
144
+ },
145
+ "Cycle": {
146
+ "type": "object",
147
+ "description": "Training cycle containing workout days",
148
+ "additionalProperties": false,
149
+ "required": ["days"],
150
+ "properties": {
151
+ "start_date": {
152
+ "type": "string",
153
+ "format": "date",
154
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
155
+ "description": "ISO 8601 date (YYYY-MM-DD)"
156
+ },
157
+ "notes": {
158
+ "type": "string",
159
+ "description": "Cycle-level coaching notes"
160
+ },
161
+ "days": {
162
+ "type": "array",
163
+ "minItems": 1,
164
+ "items": {
165
+ "$ref": "#/$defs/Day"
166
+ },
167
+ "description": "Training days in this cycle"
168
+ }
169
+ }
170
+ },
171
+ "Day": {
172
+ "type": "object",
173
+ "description": "Single training day",
174
+ "additionalProperties": false,
175
+ "required": ["exercises"],
176
+ "properties": {
177
+ "id": {
178
+ "type": "string",
179
+ "description": "Unique day identifier"
180
+ },
181
+ "order": {
182
+ "type": "integer",
183
+ "minimum": 0,
184
+ "description": "Day sequence (0-indexed)"
185
+ },
186
+ "focus": {
187
+ "type": "string",
188
+ "description": "Training focus or theme"
189
+ },
190
+ "notes": {
191
+ "type": "string",
192
+ "description": "Day-level coaching notes"
193
+ },
194
+ "scheduled_date": {
195
+ "type": "string",
196
+ "format": "date",
197
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
198
+ "description": "Planned workout date"
199
+ },
200
+ "target_session_length_min": {
201
+ "type": "integer",
202
+ "minimum": 1,
203
+ "description": "Expected duration in minutes"
204
+ },
205
+ "exercises": {
206
+ "type": "array",
207
+ "minItems": 1,
208
+ "items": {
209
+ "$ref": "#/$defs/Exercise"
210
+ },
211
+ "description": "Exercises in this day"
212
+ }
213
+ }
214
+ },
215
+ "Exercise": {
216
+ "type": "object",
217
+ "description": "Single exercise definition",
218
+ "additionalProperties": false,
219
+ "required": ["modality"],
220
+ "properties": {
221
+ "id": {
222
+ "type": "string",
223
+ "description": "Unique exercise identifier"
224
+ },
225
+ "name": {
226
+ "type": "string",
227
+ "description": "Exercise name"
228
+ },
229
+ "modality": {
230
+ "type": "string",
231
+ "enum": ["strength", "countdown", "stopwatch", "interval", "cycling", "running", "rowing", "swimming"],
232
+ "description": "Exercise type"
233
+ },
234
+ "target_sets": {
235
+ "type": "integer",
236
+ "minimum": 1,
237
+ "description": "Target number of sets"
238
+ },
239
+ "target_reps": {
240
+ "type": "integer",
241
+ "minimum": 1,
242
+ "description": "Target reps per set"
243
+ },
244
+ "target_duration_sec": {
245
+ "type": "integer",
246
+ "minimum": 1,
247
+ "description": "Target duration in seconds"
248
+ },
249
+ "target_distance_meters": {
250
+ "type": "number",
251
+ "minimum": 0,
252
+ "description": "Target distance in meters"
253
+ },
254
+ "target_load": {
255
+ "type": "string",
256
+ "description": "Loading guidance (weight, RPE, %1RM)"
257
+ },
258
+ "target_weight_percent": {
259
+ "type": "number",
260
+ "minimum": 0,
261
+ "maximum": 200,
262
+ "description": "Target weight as percentage of reference max (requires percent_of)"
263
+ },
264
+ "percent_of": {
265
+ "type": "string",
266
+ "enum": ["1rm", "3rm", "5rm", "10rm"],
267
+ "description": "Reference max for percentage calculation (requires target_weight_percent)"
268
+ },
269
+ "reference_exercise": {
270
+ "type": "string",
271
+ "description": "Reference another exercise's max for percentage calculation"
272
+ },
273
+ "cues": {
274
+ "type": "string",
275
+ "description": "Form cues (alias for target_notes)"
276
+ },
277
+ "target_notes": {
278
+ "type": "string",
279
+ "description": "Coaching notes for this exercise"
280
+ },
281
+ "link": {
282
+ "type": "string",
283
+ "format": "uri",
284
+ "pattern": "^https://",
285
+ "description": "Tutorial URL (HTTPS only)"
286
+ },
287
+ "image": {
288
+ "type": "string",
289
+ "format": "uri",
290
+ "pattern": "^https://",
291
+ "description": "Demo image URL (HTTPS only)"
292
+ },
293
+ "group": {
294
+ "type": "string",
295
+ "minLength": 1,
296
+ "maxLength": 50,
297
+ "pattern": "^[a-zA-Z0-9_-]+$",
298
+ "description": "Group identifier for supersets/circuits (alphanumeric, hyphens, underscores only)"
299
+ },
300
+ "group_type": {
301
+ "type": "string",
302
+ "enum": ["superset", "circuit"],
303
+ "description": "Type of exercise grouping"
304
+ },
305
+ "rest_between_sets_sec": {
306
+ "type": "integer",
307
+ "minimum": 0,
308
+ "description": "Rest period in seconds between sets"
309
+ },
310
+ "rest_after_sec": {
311
+ "type": "integer",
312
+ "minimum": 0,
313
+ "description": "Rest period in seconds after completing all sets"
314
+ },
315
+ "zones": {
316
+ "type": "array",
317
+ "minItems": 1,
318
+ "items": {
319
+ "$ref": "#/$defs/TrainingZone"
320
+ },
321
+ "description": "Training zones for endurance workouts"
322
+ },
323
+ "ramp": {
324
+ "$ref": "#/$defs/RampConfig"
325
+ },
326
+ "interval_phases": {
327
+ "type": "array",
328
+ "minItems": 1,
329
+ "items": {
330
+ "$ref": "#/$defs/IntervalPhase"
331
+ },
332
+ "description": "Structured interval phases for complex endurance workouts"
333
+ }
334
+ }
335
+ },
336
+ "TrainingZone": {
337
+ "type": "object",
338
+ "description": "Training zone specification",
339
+ "additionalProperties": false,
340
+ "required": ["zone"],
341
+ "properties": {
342
+ "zone": {
343
+ "type": "integer",
344
+ "minimum": 1,
345
+ "maximum": 7,
346
+ "description": "Training zone number (1-7)"
347
+ },
348
+ "duration_sec": {
349
+ "type": "integer",
350
+ "minimum": 0,
351
+ "description": "Duration in this zone (seconds)"
352
+ },
353
+ "target_power_watts": {
354
+ "type": "integer",
355
+ "minimum": 0,
356
+ "description": "Target power in watts"
357
+ },
358
+ "target_hr_bpm": {
359
+ "type": "integer",
360
+ "minimum": 0,
361
+ "maximum": 250,
362
+ "description": "Target heart rate in BPM"
363
+ },
364
+ "target_pace_sec_per_km": {
365
+ "type": "integer",
366
+ "minimum": 0,
367
+ "description": "Target pace in seconds per kilometer"
368
+ }
369
+ }
370
+ },
371
+ "RampConfig": {
372
+ "type": "object",
373
+ "description": "Ramp configuration for gradual intensity changes",
374
+ "additionalProperties": false,
375
+ "required": ["start_power_watts", "end_power_watts", "duration_sec"],
376
+ "properties": {
377
+ "start_power_watts": {
378
+ "type": "integer",
379
+ "minimum": 0,
380
+ "description": "Starting power in watts"
381
+ },
382
+ "end_power_watts": {
383
+ "type": "integer",
384
+ "minimum": 0,
385
+ "description": "Ending power in watts"
386
+ },
387
+ "duration_sec": {
388
+ "type": "integer",
389
+ "minimum": 1,
390
+ "description": "Ramp duration in seconds"
391
+ },
392
+ "step_duration_sec": {
393
+ "type": "integer",
394
+ "minimum": 1,
395
+ "description": "Duration of each step in seconds"
396
+ }
397
+ }
398
+ },
399
+ "IntervalPhase": {
400
+ "type": "object",
401
+ "description": "Phase in a structured interval workout",
402
+ "additionalProperties": false,
403
+ "required": ["name", "duration_sec"],
404
+ "properties": {
405
+ "name": {
406
+ "type": "string",
407
+ "minLength": 1,
408
+ "description": "Phase name (e.g., 'warmup', 'work', 'recovery')"
409
+ },
410
+ "duration_sec": {
411
+ "type": "integer",
412
+ "minimum": 1,
413
+ "description": "Phase duration in seconds"
414
+ },
415
+ "target_power_watts": {
416
+ "type": "integer",
417
+ "minimum": 0,
418
+ "description": "Target power in watts"
419
+ },
420
+ "target_hr_bpm": {
421
+ "type": "integer",
422
+ "minimum": 0,
423
+ "maximum": 250,
424
+ "description": "Target heart rate in BPM"
425
+ },
426
+ "target_pace_sec_per_km": {
427
+ "type": "integer",
428
+ "minimum": 0,
429
+ "description": "Target pace in seconds per kilometer"
430
+ },
431
+ "cadence_rpm": {
432
+ "type": "integer",
433
+ "minimum": 0,
434
+ "description": "Target cadence in RPM"
435
+ }
436
+ }
437
+ }
438
+ }
439
+ }