@sunsteel/contracts 0.85.0 → 0.87.0
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.
- package/dist/index.cjs +345 -3
- package/dist/index.d.cts +421 -6
- package/dist/index.d.ts +421 -6
- package/dist/index.js +305 -3
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -52,6 +52,7 @@ __export(index_exports, {
|
|
|
52
52
|
COMEBACK_REQUIRED_ACTIVE_DAYS: () => COMEBACK_REQUIRED_ACTIVE_DAYS,
|
|
53
53
|
COMEBACK_SESSION_LOOKBACK: () => COMEBACK_SESSION_LOOKBACK,
|
|
54
54
|
COMEBACK_WINDOW_DAYS: () => COMEBACK_WINDOW_DAYS,
|
|
55
|
+
CONVERSATIONS_PAGE_SIZE: () => CONVERSATIONS_PAGE_SIZE,
|
|
55
56
|
CUSTOM_EXERCISES_MAX: () => CUSTOM_EXERCISES_MAX,
|
|
56
57
|
CUSTOM_EXERCISE_NAME_MAX: () => CUSTOM_EXERCISE_NAME_MAX,
|
|
57
58
|
CUSTOM_EXERCISE_NOTE_MAX: () => CUSTOM_EXERCISE_NOTE_MAX,
|
|
@@ -60,6 +61,7 @@ __export(index_exports, {
|
|
|
60
61
|
DASHBOARD_SECTION_IDS: () => DASHBOARD_SECTION_IDS,
|
|
61
62
|
DEFAULT_DASHBOARD_LAYOUT: () => DEFAULT_DASHBOARD_LAYOUT,
|
|
62
63
|
DEFAULT_LOCALE: () => DEFAULT_LOCALE,
|
|
64
|
+
DEFAULT_MESSAGE_PERMISSION: () => DEFAULT_MESSAGE_PERMISSION,
|
|
63
65
|
DEFAULT_WEEK_STARTS_ON: () => DEFAULT_WEEK_STARTS_ON,
|
|
64
66
|
DELOAD_DEFAULT_DAYS: () => DELOAD_DEFAULT_DAYS,
|
|
65
67
|
DELOAD_DEFAULT_LOAD_REDUCTION: () => DELOAD_DEFAULT_LOAD_REDUCTION,
|
|
@@ -94,10 +96,27 @@ __export(index_exports, {
|
|
|
94
96
|
INCHES_PER_FOOT: () => INCHES_PER_FOOT,
|
|
95
97
|
LENGTH_UNITS: () => LENGTH_UNITS,
|
|
96
98
|
LIMITED_SHORTFALL_KG: () => LIMITED_SHORTFALL_KG,
|
|
99
|
+
LP_BLOCK_STEPS: () => LP_BLOCK_STEPS,
|
|
100
|
+
LP_ESTIMATE_MAX_REPS_TO_FAILURE: () => LP_ESTIMATE_MAX_REPS_TO_FAILURE,
|
|
101
|
+
LP_ESTIMATE_MIN_SETS: () => LP_ESTIMATE_MIN_SETS,
|
|
102
|
+
LP_ESTIMATE_STEPS: () => LP_ESTIMATE_STEPS,
|
|
103
|
+
LP_NEXT_BLOCK_KINDS: () => LP_NEXT_BLOCK_KINDS,
|
|
104
|
+
LP_PHASES: () => LP_PHASES,
|
|
105
|
+
LP_PROGRESS_CAP: () => LP_PROGRESS_CAP,
|
|
106
|
+
LP_PROGRESS_THRESHOLD: () => LP_PROGRESS_THRESHOLD,
|
|
107
|
+
LP_RECOVERY: () => LP_RECOVERY,
|
|
108
|
+
LP_REFERENCE_MAX_KG: () => LP_REFERENCE_MAX_KG,
|
|
109
|
+
LP_REST_SECONDS: () => LP_REST_SECONDS,
|
|
110
|
+
LP_STEPS: () => LP_STEPS,
|
|
111
|
+
LP_WORKING_SETS: () => LP_WORKING_SETS,
|
|
97
112
|
MAX_SETS_PER_EXERCISE: () => MAX_SETS_PER_EXERCISE,
|
|
98
113
|
MEASURABLE_GOALS_MAX: () => MEASURABLE_GOALS_MAX,
|
|
99
114
|
MEASURABLE_GOAL_DIRECTIONS: () => MEASURABLE_GOAL_DIRECTIONS,
|
|
100
115
|
MEASURABLE_GOAL_TYPES: () => MEASURABLE_GOAL_TYPES,
|
|
116
|
+
MESSAGES_PAGE_SIZE: () => MESSAGES_PAGE_SIZE,
|
|
117
|
+
MESSAGES_PER_MINUTE_MAX: () => MESSAGES_PER_MINUTE_MAX,
|
|
118
|
+
MESSAGE_BODY_MAX: () => MESSAGE_BODY_MAX,
|
|
119
|
+
MESSAGE_PERMISSIONS: () => MESSAGE_PERMISSIONS,
|
|
101
120
|
MINUTES_IN_DAY: () => MINUTES_IN_DAY,
|
|
102
121
|
MODERATION_ACTION_KINDS: () => MODERATION_ACTION_KINDS,
|
|
103
122
|
MODERATION_HISTORY_PAGE_SIZE: () => MODERATION_HISTORY_PAGE_SIZE,
|
|
@@ -105,6 +124,7 @@ __export(index_exports, {
|
|
|
105
124
|
MODERATION_QUEUE_PAGE_SIZE: () => MODERATION_QUEUE_PAGE_SIZE,
|
|
106
125
|
MOVEMENT_PATTERNS: () => MOVEMENT_PATTERNS,
|
|
107
126
|
MUSCLE_GROUPS: () => MUSCLE_GROUPS,
|
|
127
|
+
NEW_CONVERSATIONS_PER_DAY_MAX: () => NEW_CONVERSATIONS_PER_DAY_MAX,
|
|
108
128
|
NOTIFICATIONS_LIST_LIMIT: () => NOTIFICATIONS_LIST_LIMIT,
|
|
109
129
|
NOTIFICATIONS_LOOKBACK_DAYS: () => NOTIFICATIONS_LOOKBACK_DAYS,
|
|
110
130
|
NOTIFICATIONS_PAGE_SIZE: () => NOTIFICATIONS_PAGE_SIZE,
|
|
@@ -230,6 +250,7 @@ __export(index_exports, {
|
|
|
230
250
|
WEIGHT_UNITS: () => WEIGHT_UNITS,
|
|
231
251
|
WORKOUT_SESSION_STATUSES: () => WORKOUT_SESSION_STATUSES,
|
|
232
252
|
accountExportFileName: () => accountExportFileName,
|
|
253
|
+
advanceLinearPeriodization: () => advanceLinearPeriodization,
|
|
233
254
|
apiError: () => apiError,
|
|
234
255
|
apiErrorMessage: () => apiErrorMessage,
|
|
235
256
|
applyDeload: () => applyDeload,
|
|
@@ -240,6 +261,7 @@ __export(index_exports, {
|
|
|
240
261
|
canLinkToNext: () => canLinkToNext,
|
|
241
262
|
cmToFeetInches: () => cmToFeetInches,
|
|
242
263
|
cmToInches: () => cmToInches,
|
|
264
|
+
continueLinearPeriodization: () => continueLinearPeriodization,
|
|
243
265
|
countsAsWork: () => countsAsWork,
|
|
244
266
|
countsForProgression: () => countsForProgression,
|
|
245
267
|
customExerciseProblem: () => customExerciseProblem,
|
|
@@ -261,6 +283,7 @@ __export(index_exports, {
|
|
|
261
283
|
isDashboardSectionId: () => isDashboardSectionId,
|
|
262
284
|
isDefaultDashboardLayout: () => isDefaultDashboardLayout,
|
|
263
285
|
isGoalSuggestionKey: () => isGoalSuggestionKey,
|
|
286
|
+
isMessagePermission: () => isMessagePermission,
|
|
264
287
|
isOnboardingStepId: () => isOnboardingStepId,
|
|
265
288
|
isRealtimeTopic: () => isRealtimeTopic,
|
|
266
289
|
isSetKind: () => isSetKind,
|
|
@@ -268,20 +291,37 @@ __export(index_exports, {
|
|
|
268
291
|
isWithinQuietHours: () => isWithinQuietHours,
|
|
269
292
|
latestBodyWeight: () => latestBodyWeight,
|
|
270
293
|
leadWorkingWeight: () => leadWorkingWeight,
|
|
294
|
+
linearPeriodizationProblem: () => linearPeriodizationProblem,
|
|
295
|
+
lpEstimate: () => lpEstimate,
|
|
296
|
+
lpLoadKg: () => lpLoadKg,
|
|
297
|
+
lpPrescription: () => lpPrescription,
|
|
298
|
+
lpRecommendation: () => lpRecommendation,
|
|
299
|
+
lpRirMidpoint: () => lpRirMidpoint,
|
|
300
|
+
lpSampleRir: () => lpSampleRir,
|
|
301
|
+
lpSetEstimateKg: () => lpSetEstimateKg,
|
|
302
|
+
lpStep: () => lpStep,
|
|
303
|
+
lpStoredSets: () => lpStoredSets,
|
|
304
|
+
lpWorkingSets: () => lpWorkingSets,
|
|
271
305
|
matchesDeletionConfirmation: () => matchesDeletionConfirmation,
|
|
306
|
+
messageBodyLength: () => messageBodyLength,
|
|
272
307
|
normalizeDashboardLayout: () => normalizeDashboardLayout,
|
|
273
308
|
normalizeExerciseLinks: () => normalizeExerciseLinks,
|
|
274
309
|
normalizeExerciseName: () => normalizeExerciseName,
|
|
310
|
+
normalizeMessageBody: () => normalizeMessageBody,
|
|
275
311
|
normalizeSearchQuery: () => normalizeSearchQuery,
|
|
276
312
|
parseRealtimeEvent: () => parseRealtimeEvent,
|
|
277
313
|
primaryEquipment: () => primaryEquipment,
|
|
278
314
|
requiredToFinish: () => requiredToFinish,
|
|
279
315
|
resolveActiveTrainingBlock: () => resolveActiveTrainingBlock,
|
|
280
316
|
resolveRoutinePlan: () => resolveRoutinePlan,
|
|
317
|
+
roundDownToIncrement: () => roundDownToIncrement,
|
|
318
|
+
roundToIncrement: () => roundToIncrement,
|
|
281
319
|
routineDayLabel: () => routineDayLabel,
|
|
320
|
+
sameLpPosition: () => sameLpPosition,
|
|
282
321
|
sessionCorrectionWindow: () => sessionCorrectionWindow,
|
|
283
322
|
setKindOf: () => setKindOf,
|
|
284
323
|
setLogValuesDiffer: () => setLogValuesDiffer,
|
|
324
|
+
startLinearPeriodization: () => startLinearPeriodization,
|
|
285
325
|
summarizeBodyProgress: () => summarizeBodyProgress,
|
|
286
326
|
trainingBlockCovers: () => trainingBlockCovers,
|
|
287
327
|
warmUpLoad: () => warmUpLoad,
|
|
@@ -301,7 +341,9 @@ var REP_TYPES = ["FIXED", "RANGE"];
|
|
|
301
341
|
var PROGRESSION_SCHEMES = [
|
|
302
342
|
"NONE",
|
|
303
343
|
"DOUBLE_PROGRESSION",
|
|
304
|
-
"DYNAMIC_DOUBLE_PROGRESSION"
|
|
344
|
+
"DYNAMIC_DOUBLE_PROGRESSION",
|
|
345
|
+
// ROUT-17: an 8-step block by percentage of a reference max and target RIR.
|
|
346
|
+
"LINEAR_PERIODIZATION"
|
|
305
347
|
];
|
|
306
348
|
var MUSCLE_GROUPS = [
|
|
307
349
|
"PECTORAL",
|
|
@@ -884,6 +926,224 @@ function requiredToFinish(kind) {
|
|
|
884
926
|
return k === "WORKING" || k === "DROP";
|
|
885
927
|
}
|
|
886
928
|
|
|
929
|
+
// src/linear-periodization.ts
|
|
930
|
+
var LP_BLOCK_STEPS = 8;
|
|
931
|
+
var LP_WORKING_SETS = 3;
|
|
932
|
+
var LP_REST_SECONDS = 180;
|
|
933
|
+
var LP_REFERENCE_MAX_KG = 1e3;
|
|
934
|
+
var EASY = { rirMin: 2, rirMax: 3, amrap: false };
|
|
935
|
+
var AMRAP = { rirMin: 0, rirMax: 0, amrap: true };
|
|
936
|
+
var HARD = { rirMin: 1, rirMax: 2, amrap: false };
|
|
937
|
+
var LP_STEPS = [
|
|
938
|
+
{ step: 1, percentage: 0.63, sets: [EASY, EASY, AMRAP] },
|
|
939
|
+
{ step: 2, percentage: 0.66, sets: [EASY, EASY, AMRAP] },
|
|
940
|
+
{ step: 3, percentage: 0.69, sets: [EASY, EASY, AMRAP] },
|
|
941
|
+
{ step: 4, percentage: 0.72, sets: [EASY, EASY, AMRAP] },
|
|
942
|
+
{ step: 5, percentage: 0.75, sets: [HARD, HARD, HARD] },
|
|
943
|
+
{ step: 6, percentage: 0.78, sets: [HARD, HARD, HARD] },
|
|
944
|
+
{ step: 7, percentage: 0.81, sets: [HARD, HARD, HARD] },
|
|
945
|
+
{ step: 8, percentage: 0.85, sets: [HARD, HARD, HARD] }
|
|
946
|
+
];
|
|
947
|
+
function lpStep(step) {
|
|
948
|
+
const index = Math.min(Math.max(Math.trunc(step) || 1, 1), LP_BLOCK_STEPS);
|
|
949
|
+
return LP_STEPS[index - 1];
|
|
950
|
+
}
|
|
951
|
+
var LP_RECOVERY = { percentage: 0.7, sets: 2, reps: 5 };
|
|
952
|
+
var tidy = (value) => Math.round(value * 1e6) / 1e6;
|
|
953
|
+
function roundToIncrement(value, increment) {
|
|
954
|
+
if (!(increment > 0)) return tidy(value);
|
|
955
|
+
const steps = value / increment;
|
|
956
|
+
const floor = Math.floor(steps + 1e-9);
|
|
957
|
+
const fraction = steps - floor;
|
|
958
|
+
return tidy((fraction > 0.5 + 1e-9 ? floor + 1 : floor) * increment);
|
|
959
|
+
}
|
|
960
|
+
function roundDownToIncrement(value, increment) {
|
|
961
|
+
if (!(increment > 0)) return tidy(value);
|
|
962
|
+
return tidy(Math.floor(value / increment + 1e-9) * increment);
|
|
963
|
+
}
|
|
964
|
+
function lpLoadKg(referenceMaxKg, percentage, incrementKg) {
|
|
965
|
+
return roundToIncrement(referenceMaxKg * percentage, incrementKg);
|
|
966
|
+
}
|
|
967
|
+
var LP_PHASES = ["BLOCK", "RECOVERY", "FINISHED"];
|
|
968
|
+
function startLinearPeriodization(referenceMaxKg, cycle = 1) {
|
|
969
|
+
return {
|
|
970
|
+
referenceMaxKg,
|
|
971
|
+
phase: "BLOCK",
|
|
972
|
+
step: 1,
|
|
973
|
+
cycle,
|
|
974
|
+
samples: [],
|
|
975
|
+
finishedAt: null,
|
|
976
|
+
estimatedMaxKg: null,
|
|
977
|
+
nextReferenceMaxKg: null
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
function linearPeriodizationProblem(state) {
|
|
981
|
+
if (!Number.isFinite(state.referenceMaxKg) || state.referenceMaxKg <= 0 || state.referenceMaxKg > LP_REFERENCE_MAX_KG) {
|
|
982
|
+
return "The reference max must be above 0";
|
|
983
|
+
}
|
|
984
|
+
if (!LP_PHASES.includes(state.phase)) {
|
|
985
|
+
return "Unknown block phase";
|
|
986
|
+
}
|
|
987
|
+
if (!Number.isInteger(state.step) || state.step < 1 || state.step > LP_BLOCK_STEPS) {
|
|
988
|
+
return "The block step must be between 1 and 8";
|
|
989
|
+
}
|
|
990
|
+
if (!Number.isInteger(state.cycle) || state.cycle < 1) {
|
|
991
|
+
return "The block number must be 1 or more";
|
|
992
|
+
}
|
|
993
|
+
if (state.phase === "RECOVERY" && !((state.nextReferenceMaxKg ?? 0) > 0)) {
|
|
994
|
+
return "A recovery step needs the next block's reference max";
|
|
995
|
+
}
|
|
996
|
+
return null;
|
|
997
|
+
}
|
|
998
|
+
function lpPrescription(state, incrementKg) {
|
|
999
|
+
if (state.phase === "FINISHED") return [];
|
|
1000
|
+
if (state.phase === "RECOVERY") {
|
|
1001
|
+
const loadKg2 = lpLoadKg(
|
|
1002
|
+
state.referenceMaxKg,
|
|
1003
|
+
LP_RECOVERY.percentage,
|
|
1004
|
+
incrementKg
|
|
1005
|
+
);
|
|
1006
|
+
return Array.from({ length: LP_RECOVERY.sets }, () => ({
|
|
1007
|
+
loadKg: loadKg2,
|
|
1008
|
+
target: null,
|
|
1009
|
+
reps: LP_RECOVERY.reps
|
|
1010
|
+
}));
|
|
1011
|
+
}
|
|
1012
|
+
const step = lpStep(state.step);
|
|
1013
|
+
const loadKg = lpLoadKg(state.referenceMaxKg, step.percentage, incrementKg);
|
|
1014
|
+
return step.sets.map((target) => ({ loadKg, target, reps: null }));
|
|
1015
|
+
}
|
|
1016
|
+
function lpWorkingSets(sets) {
|
|
1017
|
+
return sets.filter((set) => setKindOf(set) !== "WARMUP");
|
|
1018
|
+
}
|
|
1019
|
+
var LP_ESTIMATE_STEPS = [7, 8];
|
|
1020
|
+
var LP_ESTIMATE_MAX_REPS_TO_FAILURE = 10;
|
|
1021
|
+
var LP_ESTIMATE_MIN_SETS = 3;
|
|
1022
|
+
function lpRirMidpoint(target) {
|
|
1023
|
+
return (target.rirMin + target.rirMax) / 2;
|
|
1024
|
+
}
|
|
1025
|
+
function lpSampleRir(sample) {
|
|
1026
|
+
const target = lpStep(sample.step).sets[sample.set - 1] ?? HARD;
|
|
1027
|
+
return lpRirMidpoint(target);
|
|
1028
|
+
}
|
|
1029
|
+
function lpSetEstimateKg(loadKg, reps, rir) {
|
|
1030
|
+
return loadKg * (1 + (reps + rir) / 30);
|
|
1031
|
+
}
|
|
1032
|
+
function lpEstimate(samples, incrementKg) {
|
|
1033
|
+
const sets = samples.filter((sample) => LP_ESTIMATE_STEPS.includes(sample.step)).map((sample) => ({ ...sample, rir: lpSampleRir(sample) })).filter(
|
|
1034
|
+
(sample) => sample.reps >= 1 && sample.loadKg > 0 && sample.reps + sample.rir <= LP_ESTIMATE_MAX_REPS_TO_FAILURE
|
|
1035
|
+
).map((sample) => ({
|
|
1036
|
+
...sample,
|
|
1037
|
+
estimateKg: tidy(lpSetEstimateKg(sample.loadKg, sample.reps, sample.rir))
|
|
1038
|
+
})).sort((a, b) => a.step - b.step || a.set - b.set);
|
|
1039
|
+
if (sets.length < LP_ESTIMATE_MIN_SETS) {
|
|
1040
|
+
return { estimatedMaxKg: null, sets };
|
|
1041
|
+
}
|
|
1042
|
+
const values = sets.map((set) => set.estimateKg).sort((a, b) => a - b);
|
|
1043
|
+
const middle = Math.floor(values.length / 2);
|
|
1044
|
+
const median = values.length % 2 ? values[middle] : (values[middle - 1] + values[middle]) / 2;
|
|
1045
|
+
return { estimatedMaxKg: roundDownToIncrement(median, incrementKg), sets };
|
|
1046
|
+
}
|
|
1047
|
+
var LP_PROGRESS_THRESHOLD = 0.025;
|
|
1048
|
+
var LP_PROGRESS_CAP = 0.05;
|
|
1049
|
+
var LP_NEXT_BLOCK_KINDS = ["PROGRESS", "REPEAT", "RESET"];
|
|
1050
|
+
function lpRecommendation(referenceMaxKg, estimatedMaxKg, incrementKg) {
|
|
1051
|
+
if (!(estimatedMaxKg && estimatedMaxKg > 0) || !(referenceMaxKg > 0)) {
|
|
1052
|
+
return { kind: "REPEAT", nextReferenceMaxKg: referenceMaxKg, change: null };
|
|
1053
|
+
}
|
|
1054
|
+
const change = estimatedMaxKg / referenceMaxKg - 1;
|
|
1055
|
+
if (change >= LP_PROGRESS_THRESHOLD - 1e-9) {
|
|
1056
|
+
const capped = Math.min(
|
|
1057
|
+
estimatedMaxKg,
|
|
1058
|
+
referenceMaxKg * (1 + LP_PROGRESS_CAP)
|
|
1059
|
+
);
|
|
1060
|
+
const next = roundDownToIncrement(capped, incrementKg);
|
|
1061
|
+
return {
|
|
1062
|
+
kind: "PROGRESS",
|
|
1063
|
+
nextReferenceMaxKg: next > referenceMaxKg ? next : referenceMaxKg,
|
|
1064
|
+
change
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
if (change < -LP_PROGRESS_THRESHOLD - 1e-9) {
|
|
1068
|
+
return {
|
|
1069
|
+
kind: "RESET",
|
|
1070
|
+
nextReferenceMaxKg: roundDownToIncrement(estimatedMaxKg, incrementKg),
|
|
1071
|
+
change
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
return { kind: "REPEAT", nextReferenceMaxKg: referenceMaxKg, change };
|
|
1075
|
+
}
|
|
1076
|
+
function sameLpPosition(a, b) {
|
|
1077
|
+
if (!a || !b) return !a && !b;
|
|
1078
|
+
return a.phase === b.phase && a.step === b.step && a.cycle === b.cycle && Math.abs(a.referenceMaxKg - b.referenceMaxKg) < 1e-6;
|
|
1079
|
+
}
|
|
1080
|
+
function advanceLinearPeriodization(state, logged, context) {
|
|
1081
|
+
if (state.phase === "FINISHED") return null;
|
|
1082
|
+
const prescribed = lpPrescription(state, context.incrementKg).length;
|
|
1083
|
+
for (let set = 1; set <= prescribed; set += 1) {
|
|
1084
|
+
if (!logged.some((entry) => entry.set === set && entry.completed)) {
|
|
1085
|
+
return null;
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
if (state.phase === "RECOVERY") {
|
|
1089
|
+
return startLinearPeriodization(
|
|
1090
|
+
state.nextReferenceMaxKg ?? state.referenceMaxKg,
|
|
1091
|
+
state.cycle + 1
|
|
1092
|
+
);
|
|
1093
|
+
}
|
|
1094
|
+
const samples = LP_ESTIMATE_STEPS.includes(state.step) ? [
|
|
1095
|
+
...state.samples.filter((sample) => sample.step !== state.step),
|
|
1096
|
+
...logged.filter((entry) => entry.completed && entry.set <= prescribed).map((entry) => ({
|
|
1097
|
+
sessionId: context.sessionId,
|
|
1098
|
+
step: state.step,
|
|
1099
|
+
set: entry.set,
|
|
1100
|
+
loadKg: entry.loadKg,
|
|
1101
|
+
reps: entry.reps
|
|
1102
|
+
}))
|
|
1103
|
+
] : state.samples;
|
|
1104
|
+
if (state.step < LP_BLOCK_STEPS) {
|
|
1105
|
+
return { ...state, step: state.step + 1, samples };
|
|
1106
|
+
}
|
|
1107
|
+
return {
|
|
1108
|
+
...state,
|
|
1109
|
+
phase: "FINISHED",
|
|
1110
|
+
samples,
|
|
1111
|
+
finishedAt: context.finishedAt,
|
|
1112
|
+
estimatedMaxKg: lpEstimate(samples, context.incrementKg).estimatedMaxKg,
|
|
1113
|
+
nextReferenceMaxKg: null
|
|
1114
|
+
};
|
|
1115
|
+
}
|
|
1116
|
+
function continueLinearPeriodization(state, choice) {
|
|
1117
|
+
if (state.phase !== "FINISHED") return null;
|
|
1118
|
+
if (choice.recovery) {
|
|
1119
|
+
return {
|
|
1120
|
+
...state,
|
|
1121
|
+
phase: "RECOVERY",
|
|
1122
|
+
nextReferenceMaxKg: choice.referenceMaxKg
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
return startLinearPeriodization(choice.referenceMaxKg, state.cycle + 1);
|
|
1126
|
+
}
|
|
1127
|
+
function lpStoredSets(state, incrementKg) {
|
|
1128
|
+
if (!state) {
|
|
1129
|
+
return Array.from({ length: LP_WORKING_SETS }, () => ({
|
|
1130
|
+
repType: "FIXED",
|
|
1131
|
+
reps: null,
|
|
1132
|
+
weight: null,
|
|
1133
|
+
rir: null,
|
|
1134
|
+
kind: "WORKING"
|
|
1135
|
+
}));
|
|
1136
|
+
}
|
|
1137
|
+
const prescription = state.phase === "FINISHED" ? lpPrescription({ ...state, phase: "BLOCK", step: LP_BLOCK_STEPS }, incrementKg) : lpPrescription(state, incrementKg);
|
|
1138
|
+
return prescription.map((set) => ({
|
|
1139
|
+
repType: "FIXED",
|
|
1140
|
+
reps: set.reps,
|
|
1141
|
+
weight: set.loadKg,
|
|
1142
|
+
rir: set.target ? set.target.rirMin : null,
|
|
1143
|
+
kind: "WORKING"
|
|
1144
|
+
}));
|
|
1145
|
+
}
|
|
1146
|
+
|
|
887
1147
|
// src/plate-loading.ts
|
|
888
1148
|
var CANONICAL_SCALE = 1e4;
|
|
889
1149
|
var DENSE_CAPACITY_LIMIT = 2e5;
|
|
@@ -1516,7 +1776,9 @@ var NOTIFICATION_KINDS = [
|
|
|
1516
1776
|
"TRAINING_PARTNER_ENCOURAGEMENT",
|
|
1517
1777
|
// NOTIF-07: selected activity from a partner who currently shares it.
|
|
1518
1778
|
"TRAINING_PARTNER_SESSION",
|
|
1519
|
-
"TRAINING_PARTNER_ACHIEVEMENT"
|
|
1779
|
+
"TRAINING_PARTNER_ACHIEVEMENT",
|
|
1780
|
+
// ROUT-18: an LP exercise finished its 8-step block.
|
|
1781
|
+
"LINEAR_BLOCK_FINISHED"
|
|
1520
1782
|
];
|
|
1521
1783
|
var NOTIFICATIONS_LOOKBACK_DAYS = 30;
|
|
1522
1784
|
var NOTIFICATIONS_RETENTION_DAYS = 90;
|
|
@@ -1621,6 +1883,8 @@ var API_ERROR_MESSAGES = {
|
|
|
1621
1883
|
REPORT_NOT_FOUND: "Report not found",
|
|
1622
1884
|
REPORT_SUBJECT_NOT_FOUND: "Subject not found",
|
|
1623
1885
|
REPORTED_CONTENT_GONE: "The reported content no longer exists",
|
|
1886
|
+
CONVERSATION_NOT_FOUND: "Conversation not found",
|
|
1887
|
+
MESSAGE_NOT_FOUND: "Message not found",
|
|
1624
1888
|
SHARE_LINK_NOT_FOUND: "Share link not found",
|
|
1625
1889
|
LINK_NOT_FOUND: "Link not found",
|
|
1626
1890
|
SHARED_SESSION_NOT_FOUND: "Shared session not found",
|
|
@@ -1701,6 +1965,14 @@ var API_ERROR_MESSAGES = {
|
|
|
1701
1965
|
SWAP_SESSION_NOT_ACTIVE: "Exercises can only be swapped in an active session",
|
|
1702
1966
|
SWAP_SETS_COMPLETED: "This exercise already has completed sets. Swap it before completing any.",
|
|
1703
1967
|
NOTES_DISCARDED_SESSION: "A discarded workout cannot take notes",
|
|
1968
|
+
// ── Linear periodization (ROUT-17 to ROUT-19) ──────────────────────────
|
|
1969
|
+
LINEAR_BLOCK_INVALID: "This exercise's block is not valid: {reason}",
|
|
1970
|
+
LINEAR_BLOCK_EXTRA_SET: "An exercise on an 8-week block takes no extra sets",
|
|
1971
|
+
LINEAR_BLOCK_KIND_FIXED: "The working sets of an 8-week block cannot change kind",
|
|
1972
|
+
LINEAR_BLOCK_LOAD_FIXED: "The load of an 8-week block is prescribed and cannot be changed",
|
|
1973
|
+
LINEAR_BLOCK_SWAP_ROUTINE: "An exercise on an 8-week block can be swapped for this workout only",
|
|
1974
|
+
LINEAR_BLOCK_NOT_LINEAR: "This exercise is not on an 8-week block",
|
|
1975
|
+
LINEAR_BLOCK_NOT_FINISHED: "This exercise's block has not finished, so there is nothing to choose yet",
|
|
1704
1976
|
// ── Corrections (LIVE-17) ──────────────────────────────────────────────
|
|
1705
1977
|
CORRECTION_NOT_COMPLETED: "Only a completed workout can be corrected",
|
|
1706
1978
|
CORRECTION_NOT_LATEST: "Only your most recent workout can be corrected",
|
|
@@ -1746,6 +2018,14 @@ var API_ERROR_MESSAGES = {
|
|
|
1746
2018
|
PARTNERS_MAX: "An account can have at most {max} training partners.",
|
|
1747
2019
|
ENCOURAGEMENTS_MAX: "You can send this partner at most {max} encouragements in 24 hours.",
|
|
1748
2020
|
ENCOURAGEMENT_RETRY: "Please retry the encouragement.",
|
|
2021
|
+
// ── Direct messages (MSG-01) ───────────────────────────────────────────
|
|
2022
|
+
MESSAGE_SELF: "You cannot message yourself",
|
|
2023
|
+
MESSAGE_LENGTH: "A message must not be empty and may be at most {max} characters.",
|
|
2024
|
+
MESSAGES_PER_MINUTE: "You can send at most {max} messages a minute.",
|
|
2025
|
+
CONVERSATIONS_PER_DAY: "You can start at most {max} new conversations a day.",
|
|
2026
|
+
MESSAGE_NOT_ADMITTED: "This member is not taking messages from you.",
|
|
2027
|
+
CONVERSATION_CLOSED: "This conversation can no longer receive messages.",
|
|
2028
|
+
MESSAGING_UNAVAILABLE: "Messaging is not available for this account.",
|
|
1749
2029
|
// ── Account and settings ───────────────────────────────────────────────
|
|
1750
2030
|
ACCOUNT_CONFLICT: "Account conflict for this email",
|
|
1751
2031
|
ACCOUNT_DELETE_CONFIRMATION: "Type your username exactly as it appears to confirm.",
|
|
@@ -1798,7 +2078,7 @@ var RECENT_SEARCH_KINDS = ["MEMBER", "EXERCISE", "ROUTINE", "WORKOUT"];
|
|
|
1798
2078
|
|
|
1799
2079
|
// src/realtime.ts
|
|
1800
2080
|
var REALTIME_STREAM_PATH = "/realtime/stream";
|
|
1801
|
-
var REALTIME_TOPICS = ["notifications"];
|
|
2081
|
+
var REALTIME_TOPICS = ["notifications", "conversations"];
|
|
1802
2082
|
var REALTIME_HEARTBEAT_MS = 25e3;
|
|
1803
2083
|
var REALTIME_MAX_STREAM_MS = 14 * 6e4;
|
|
1804
2084
|
var REALTIME_MAX_STREAMS_PER_MEMBER = 5;
|
|
@@ -1823,6 +2103,28 @@ function parseRealtimeEvent(data) {
|
|
|
1823
2103
|
}
|
|
1824
2104
|
return null;
|
|
1825
2105
|
}
|
|
2106
|
+
|
|
2107
|
+
// src/messages.ts
|
|
2108
|
+
var MESSAGE_PERMISSIONS = ["FOLLOWED", "NOBODY"];
|
|
2109
|
+
var DEFAULT_MESSAGE_PERMISSION = "FOLLOWED";
|
|
2110
|
+
var MESSAGE_BODY_MAX = 2e3;
|
|
2111
|
+
var MESSAGES_PER_MINUTE_MAX = 30;
|
|
2112
|
+
var NEW_CONVERSATIONS_PER_DAY_MAX = 20;
|
|
2113
|
+
var CONVERSATIONS_PAGE_SIZE = 20;
|
|
2114
|
+
var MESSAGES_PAGE_SIZE = 30;
|
|
2115
|
+
function messageBodyLength(body) {
|
|
2116
|
+
return Array.from(body).length;
|
|
2117
|
+
}
|
|
2118
|
+
function normalizeMessageBody(body) {
|
|
2119
|
+
const text = body.replace(/\r\n?/g, "\n").trim();
|
|
2120
|
+
if (text.length === 0 || messageBodyLength(text) > MESSAGE_BODY_MAX) {
|
|
2121
|
+
return null;
|
|
2122
|
+
}
|
|
2123
|
+
return text;
|
|
2124
|
+
}
|
|
2125
|
+
function isMessagePermission(value) {
|
|
2126
|
+
return MESSAGE_PERMISSIONS.includes(value);
|
|
2127
|
+
}
|
|
1826
2128
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1827
2129
|
0 && (module.exports = {
|
|
1828
2130
|
ACCOUNT_EXPORT_FORMAT,
|
|
@@ -1857,6 +2159,7 @@ function parseRealtimeEvent(data) {
|
|
|
1857
2159
|
COMEBACK_REQUIRED_ACTIVE_DAYS,
|
|
1858
2160
|
COMEBACK_SESSION_LOOKBACK,
|
|
1859
2161
|
COMEBACK_WINDOW_DAYS,
|
|
2162
|
+
CONVERSATIONS_PAGE_SIZE,
|
|
1860
2163
|
CUSTOM_EXERCISES_MAX,
|
|
1861
2164
|
CUSTOM_EXERCISE_NAME_MAX,
|
|
1862
2165
|
CUSTOM_EXERCISE_NOTE_MAX,
|
|
@@ -1865,6 +2168,7 @@ function parseRealtimeEvent(data) {
|
|
|
1865
2168
|
DASHBOARD_SECTION_IDS,
|
|
1866
2169
|
DEFAULT_DASHBOARD_LAYOUT,
|
|
1867
2170
|
DEFAULT_LOCALE,
|
|
2171
|
+
DEFAULT_MESSAGE_PERMISSION,
|
|
1868
2172
|
DEFAULT_WEEK_STARTS_ON,
|
|
1869
2173
|
DELOAD_DEFAULT_DAYS,
|
|
1870
2174
|
DELOAD_DEFAULT_LOAD_REDUCTION,
|
|
@@ -1899,10 +2203,27 @@ function parseRealtimeEvent(data) {
|
|
|
1899
2203
|
INCHES_PER_FOOT,
|
|
1900
2204
|
LENGTH_UNITS,
|
|
1901
2205
|
LIMITED_SHORTFALL_KG,
|
|
2206
|
+
LP_BLOCK_STEPS,
|
|
2207
|
+
LP_ESTIMATE_MAX_REPS_TO_FAILURE,
|
|
2208
|
+
LP_ESTIMATE_MIN_SETS,
|
|
2209
|
+
LP_ESTIMATE_STEPS,
|
|
2210
|
+
LP_NEXT_BLOCK_KINDS,
|
|
2211
|
+
LP_PHASES,
|
|
2212
|
+
LP_PROGRESS_CAP,
|
|
2213
|
+
LP_PROGRESS_THRESHOLD,
|
|
2214
|
+
LP_RECOVERY,
|
|
2215
|
+
LP_REFERENCE_MAX_KG,
|
|
2216
|
+
LP_REST_SECONDS,
|
|
2217
|
+
LP_STEPS,
|
|
2218
|
+
LP_WORKING_SETS,
|
|
1902
2219
|
MAX_SETS_PER_EXERCISE,
|
|
1903
2220
|
MEASURABLE_GOALS_MAX,
|
|
1904
2221
|
MEASURABLE_GOAL_DIRECTIONS,
|
|
1905
2222
|
MEASURABLE_GOAL_TYPES,
|
|
2223
|
+
MESSAGES_PAGE_SIZE,
|
|
2224
|
+
MESSAGES_PER_MINUTE_MAX,
|
|
2225
|
+
MESSAGE_BODY_MAX,
|
|
2226
|
+
MESSAGE_PERMISSIONS,
|
|
1906
2227
|
MINUTES_IN_DAY,
|
|
1907
2228
|
MODERATION_ACTION_KINDS,
|
|
1908
2229
|
MODERATION_HISTORY_PAGE_SIZE,
|
|
@@ -1910,6 +2231,7 @@ function parseRealtimeEvent(data) {
|
|
|
1910
2231
|
MODERATION_QUEUE_PAGE_SIZE,
|
|
1911
2232
|
MOVEMENT_PATTERNS,
|
|
1912
2233
|
MUSCLE_GROUPS,
|
|
2234
|
+
NEW_CONVERSATIONS_PER_DAY_MAX,
|
|
1913
2235
|
NOTIFICATIONS_LIST_LIMIT,
|
|
1914
2236
|
NOTIFICATIONS_LOOKBACK_DAYS,
|
|
1915
2237
|
NOTIFICATIONS_PAGE_SIZE,
|
|
@@ -2035,6 +2357,7 @@ function parseRealtimeEvent(data) {
|
|
|
2035
2357
|
WEIGHT_UNITS,
|
|
2036
2358
|
WORKOUT_SESSION_STATUSES,
|
|
2037
2359
|
accountExportFileName,
|
|
2360
|
+
advanceLinearPeriodization,
|
|
2038
2361
|
apiError,
|
|
2039
2362
|
apiErrorMessage,
|
|
2040
2363
|
applyDeload,
|
|
@@ -2045,6 +2368,7 @@ function parseRealtimeEvent(data) {
|
|
|
2045
2368
|
canLinkToNext,
|
|
2046
2369
|
cmToFeetInches,
|
|
2047
2370
|
cmToInches,
|
|
2371
|
+
continueLinearPeriodization,
|
|
2048
2372
|
countsAsWork,
|
|
2049
2373
|
countsForProgression,
|
|
2050
2374
|
customExerciseProblem,
|
|
@@ -2066,6 +2390,7 @@ function parseRealtimeEvent(data) {
|
|
|
2066
2390
|
isDashboardSectionId,
|
|
2067
2391
|
isDefaultDashboardLayout,
|
|
2068
2392
|
isGoalSuggestionKey,
|
|
2393
|
+
isMessagePermission,
|
|
2069
2394
|
isOnboardingStepId,
|
|
2070
2395
|
isRealtimeTopic,
|
|
2071
2396
|
isSetKind,
|
|
@@ -2073,20 +2398,37 @@ function parseRealtimeEvent(data) {
|
|
|
2073
2398
|
isWithinQuietHours,
|
|
2074
2399
|
latestBodyWeight,
|
|
2075
2400
|
leadWorkingWeight,
|
|
2401
|
+
linearPeriodizationProblem,
|
|
2402
|
+
lpEstimate,
|
|
2403
|
+
lpLoadKg,
|
|
2404
|
+
lpPrescription,
|
|
2405
|
+
lpRecommendation,
|
|
2406
|
+
lpRirMidpoint,
|
|
2407
|
+
lpSampleRir,
|
|
2408
|
+
lpSetEstimateKg,
|
|
2409
|
+
lpStep,
|
|
2410
|
+
lpStoredSets,
|
|
2411
|
+
lpWorkingSets,
|
|
2076
2412
|
matchesDeletionConfirmation,
|
|
2413
|
+
messageBodyLength,
|
|
2077
2414
|
normalizeDashboardLayout,
|
|
2078
2415
|
normalizeExerciseLinks,
|
|
2079
2416
|
normalizeExerciseName,
|
|
2417
|
+
normalizeMessageBody,
|
|
2080
2418
|
normalizeSearchQuery,
|
|
2081
2419
|
parseRealtimeEvent,
|
|
2082
2420
|
primaryEquipment,
|
|
2083
2421
|
requiredToFinish,
|
|
2084
2422
|
resolveActiveTrainingBlock,
|
|
2085
2423
|
resolveRoutinePlan,
|
|
2424
|
+
roundDownToIncrement,
|
|
2425
|
+
roundToIncrement,
|
|
2086
2426
|
routineDayLabel,
|
|
2427
|
+
sameLpPosition,
|
|
2087
2428
|
sessionCorrectionWindow,
|
|
2088
2429
|
setKindOf,
|
|
2089
2430
|
setLogValuesDiffer,
|
|
2431
|
+
startLinearPeriodization,
|
|
2090
2432
|
summarizeBodyProgress,
|
|
2091
2433
|
trainingBlockCovers,
|
|
2092
2434
|
warmUpLoad,
|