@sunsteel/contracts 0.86.0 → 0.88.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 +311 -4
- package/dist/index.d.cts +373 -10
- package/dist/index.d.ts +373 -10
- package/dist/index.js +280 -4
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -96,6 +96,19 @@ __export(index_exports, {
|
|
|
96
96
|
INCHES_PER_FOOT: () => INCHES_PER_FOOT,
|
|
97
97
|
LENGTH_UNITS: () => LENGTH_UNITS,
|
|
98
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,
|
|
99
112
|
MAX_SETS_PER_EXERCISE: () => MAX_SETS_PER_EXERCISE,
|
|
100
113
|
MEASURABLE_GOALS_MAX: () => MEASURABLE_GOALS_MAX,
|
|
101
114
|
MEASURABLE_GOAL_DIRECTIONS: () => MEASURABLE_GOAL_DIRECTIONS,
|
|
@@ -104,6 +117,7 @@ __export(index_exports, {
|
|
|
104
117
|
MESSAGES_PER_MINUTE_MAX: () => MESSAGES_PER_MINUTE_MAX,
|
|
105
118
|
MESSAGE_BODY_MAX: () => MESSAGE_BODY_MAX,
|
|
106
119
|
MESSAGE_PERMISSIONS: () => MESSAGE_PERMISSIONS,
|
|
120
|
+
MESSAGE_REPORT_CONTEXT_BEFORE: () => MESSAGE_REPORT_CONTEXT_BEFORE,
|
|
107
121
|
MINUTES_IN_DAY: () => MINUTES_IN_DAY,
|
|
108
122
|
MODERATION_ACTION_KINDS: () => MODERATION_ACTION_KINDS,
|
|
109
123
|
MODERATION_HISTORY_PAGE_SIZE: () => MODERATION_HISTORY_PAGE_SIZE,
|
|
@@ -237,6 +251,7 @@ __export(index_exports, {
|
|
|
237
251
|
WEIGHT_UNITS: () => WEIGHT_UNITS,
|
|
238
252
|
WORKOUT_SESSION_STATUSES: () => WORKOUT_SESSION_STATUSES,
|
|
239
253
|
accountExportFileName: () => accountExportFileName,
|
|
254
|
+
advanceLinearPeriodization: () => advanceLinearPeriodization,
|
|
240
255
|
apiError: () => apiError,
|
|
241
256
|
apiErrorMessage: () => apiErrorMessage,
|
|
242
257
|
applyDeload: () => applyDeload,
|
|
@@ -247,6 +262,7 @@ __export(index_exports, {
|
|
|
247
262
|
canLinkToNext: () => canLinkToNext,
|
|
248
263
|
cmToFeetInches: () => cmToFeetInches,
|
|
249
264
|
cmToInches: () => cmToInches,
|
|
265
|
+
continueLinearPeriodization: () => continueLinearPeriodization,
|
|
250
266
|
countsAsWork: () => countsAsWork,
|
|
251
267
|
countsForProgression: () => countsForProgression,
|
|
252
268
|
customExerciseProblem: () => customExerciseProblem,
|
|
@@ -276,6 +292,17 @@ __export(index_exports, {
|
|
|
276
292
|
isWithinQuietHours: () => isWithinQuietHours,
|
|
277
293
|
latestBodyWeight: () => latestBodyWeight,
|
|
278
294
|
leadWorkingWeight: () => leadWorkingWeight,
|
|
295
|
+
linearPeriodizationProblem: () => linearPeriodizationProblem,
|
|
296
|
+
lpEstimate: () => lpEstimate,
|
|
297
|
+
lpLoadKg: () => lpLoadKg,
|
|
298
|
+
lpPrescription: () => lpPrescription,
|
|
299
|
+
lpRecommendation: () => lpRecommendation,
|
|
300
|
+
lpRirMidpoint: () => lpRirMidpoint,
|
|
301
|
+
lpSampleRir: () => lpSampleRir,
|
|
302
|
+
lpSetEstimateKg: () => lpSetEstimateKg,
|
|
303
|
+
lpStep: () => lpStep,
|
|
304
|
+
lpStoredSets: () => lpStoredSets,
|
|
305
|
+
lpWorkingSets: () => lpWorkingSets,
|
|
279
306
|
matchesDeletionConfirmation: () => matchesDeletionConfirmation,
|
|
280
307
|
messageBodyLength: () => messageBodyLength,
|
|
281
308
|
normalizeDashboardLayout: () => normalizeDashboardLayout,
|
|
@@ -288,10 +315,14 @@ __export(index_exports, {
|
|
|
288
315
|
requiredToFinish: () => requiredToFinish,
|
|
289
316
|
resolveActiveTrainingBlock: () => resolveActiveTrainingBlock,
|
|
290
317
|
resolveRoutinePlan: () => resolveRoutinePlan,
|
|
318
|
+
roundDownToIncrement: () => roundDownToIncrement,
|
|
319
|
+
roundToIncrement: () => roundToIncrement,
|
|
291
320
|
routineDayLabel: () => routineDayLabel,
|
|
321
|
+
sameLpPosition: () => sameLpPosition,
|
|
292
322
|
sessionCorrectionWindow: () => sessionCorrectionWindow,
|
|
293
323
|
setKindOf: () => setKindOf,
|
|
294
324
|
setLogValuesDiffer: () => setLogValuesDiffer,
|
|
325
|
+
startLinearPeriodization: () => startLinearPeriodization,
|
|
295
326
|
summarizeBodyProgress: () => summarizeBodyProgress,
|
|
296
327
|
trainingBlockCovers: () => trainingBlockCovers,
|
|
297
328
|
warmUpLoad: () => warmUpLoad,
|
|
@@ -311,7 +342,9 @@ var REP_TYPES = ["FIXED", "RANGE"];
|
|
|
311
342
|
var PROGRESSION_SCHEMES = [
|
|
312
343
|
"NONE",
|
|
313
344
|
"DOUBLE_PROGRESSION",
|
|
314
|
-
"DYNAMIC_DOUBLE_PROGRESSION"
|
|
345
|
+
"DYNAMIC_DOUBLE_PROGRESSION",
|
|
346
|
+
// ROUT-17: an 8-step block by percentage of a reference max and target RIR.
|
|
347
|
+
"LINEAR_PERIODIZATION"
|
|
315
348
|
];
|
|
316
349
|
var MUSCLE_GROUPS = [
|
|
317
350
|
"PECTORAL",
|
|
@@ -533,7 +566,11 @@ var REPORT_SUBJECT_KINDS = [
|
|
|
533
566
|
// first one the TRUST-04 queue is likely to see in volume: everything above
|
|
534
567
|
// is content its owner created for themselves and shared, where a comment is
|
|
535
568
|
// aimed at somebody else.
|
|
536
|
-
"COMMENT"
|
|
569
|
+
"COMMENT",
|
|
570
|
+
// MSG-09. A message has no page of its own and is not shared with anyone but
|
|
571
|
+
// the other participant, so its report captures what that participant could
|
|
572
|
+
// see -- the message and up to five before it -- for the moderator to read.
|
|
573
|
+
"MESSAGE"
|
|
537
574
|
];
|
|
538
575
|
var REPORT_REASONS = [
|
|
539
576
|
"SPAM",
|
|
@@ -550,7 +587,12 @@ var MODERATION_ACTION_KINDS = [
|
|
|
550
587
|
"VIEW_SUBJECT",
|
|
551
588
|
"DISMISS_REPORT",
|
|
552
589
|
"HIDE_SUBJECT",
|
|
553
|
-
"RESTORE_SUBJECT"
|
|
590
|
+
"RESTORE_SUBJECT",
|
|
591
|
+
// MSG-09. A power over an account rather than over one thing it made: it
|
|
592
|
+
// stops the account sending and starting conversations until it is lifted.
|
|
593
|
+
// Recorded against the member, from the report it was taken on.
|
|
594
|
+
"RESTRICT_MESSAGING",
|
|
595
|
+
"LIFT_MESSAGING_RESTRICTION"
|
|
554
596
|
];
|
|
555
597
|
var MODERATION_NOTE_MAX_LENGTH = 500;
|
|
556
598
|
var MODERATION_QUEUE_PAGE_SIZE = 20;
|
|
@@ -894,6 +936,224 @@ function requiredToFinish(kind) {
|
|
|
894
936
|
return k === "WORKING" || k === "DROP";
|
|
895
937
|
}
|
|
896
938
|
|
|
939
|
+
// src/linear-periodization.ts
|
|
940
|
+
var LP_BLOCK_STEPS = 8;
|
|
941
|
+
var LP_WORKING_SETS = 3;
|
|
942
|
+
var LP_REST_SECONDS = 180;
|
|
943
|
+
var LP_REFERENCE_MAX_KG = 1e3;
|
|
944
|
+
var EASY = { rirMin: 2, rirMax: 3, amrap: false };
|
|
945
|
+
var AMRAP = { rirMin: 0, rirMax: 0, amrap: true };
|
|
946
|
+
var HARD = { rirMin: 1, rirMax: 2, amrap: false };
|
|
947
|
+
var LP_STEPS = [
|
|
948
|
+
{ step: 1, percentage: 0.63, sets: [EASY, EASY, AMRAP] },
|
|
949
|
+
{ step: 2, percentage: 0.66, sets: [EASY, EASY, AMRAP] },
|
|
950
|
+
{ step: 3, percentage: 0.69, sets: [EASY, EASY, AMRAP] },
|
|
951
|
+
{ step: 4, percentage: 0.72, sets: [EASY, EASY, AMRAP] },
|
|
952
|
+
{ step: 5, percentage: 0.75, sets: [HARD, HARD, HARD] },
|
|
953
|
+
{ step: 6, percentage: 0.78, sets: [HARD, HARD, HARD] },
|
|
954
|
+
{ step: 7, percentage: 0.81, sets: [HARD, HARD, HARD] },
|
|
955
|
+
{ step: 8, percentage: 0.85, sets: [HARD, HARD, HARD] }
|
|
956
|
+
];
|
|
957
|
+
function lpStep(step) {
|
|
958
|
+
const index = Math.min(Math.max(Math.trunc(step) || 1, 1), LP_BLOCK_STEPS);
|
|
959
|
+
return LP_STEPS[index - 1];
|
|
960
|
+
}
|
|
961
|
+
var LP_RECOVERY = { percentage: 0.7, sets: 2, reps: 5 };
|
|
962
|
+
var tidy = (value) => Math.round(value * 1e6) / 1e6;
|
|
963
|
+
function roundToIncrement(value, increment) {
|
|
964
|
+
if (!(increment > 0)) return tidy(value);
|
|
965
|
+
const steps = value / increment;
|
|
966
|
+
const floor = Math.floor(steps + 1e-9);
|
|
967
|
+
const fraction = steps - floor;
|
|
968
|
+
return tidy((fraction > 0.5 + 1e-9 ? floor + 1 : floor) * increment);
|
|
969
|
+
}
|
|
970
|
+
function roundDownToIncrement(value, increment) {
|
|
971
|
+
if (!(increment > 0)) return tidy(value);
|
|
972
|
+
return tidy(Math.floor(value / increment + 1e-9) * increment);
|
|
973
|
+
}
|
|
974
|
+
function lpLoadKg(referenceMaxKg, percentage, incrementKg) {
|
|
975
|
+
return roundToIncrement(referenceMaxKg * percentage, incrementKg);
|
|
976
|
+
}
|
|
977
|
+
var LP_PHASES = ["BLOCK", "RECOVERY", "FINISHED"];
|
|
978
|
+
function startLinearPeriodization(referenceMaxKg, cycle = 1) {
|
|
979
|
+
return {
|
|
980
|
+
referenceMaxKg,
|
|
981
|
+
phase: "BLOCK",
|
|
982
|
+
step: 1,
|
|
983
|
+
cycle,
|
|
984
|
+
samples: [],
|
|
985
|
+
finishedAt: null,
|
|
986
|
+
estimatedMaxKg: null,
|
|
987
|
+
nextReferenceMaxKg: null
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
function linearPeriodizationProblem(state) {
|
|
991
|
+
if (!Number.isFinite(state.referenceMaxKg) || state.referenceMaxKg <= 0 || state.referenceMaxKg > LP_REFERENCE_MAX_KG) {
|
|
992
|
+
return "The reference max must be above 0";
|
|
993
|
+
}
|
|
994
|
+
if (!LP_PHASES.includes(state.phase)) {
|
|
995
|
+
return "Unknown block phase";
|
|
996
|
+
}
|
|
997
|
+
if (!Number.isInteger(state.step) || state.step < 1 || state.step > LP_BLOCK_STEPS) {
|
|
998
|
+
return "The block step must be between 1 and 8";
|
|
999
|
+
}
|
|
1000
|
+
if (!Number.isInteger(state.cycle) || state.cycle < 1) {
|
|
1001
|
+
return "The block number must be 1 or more";
|
|
1002
|
+
}
|
|
1003
|
+
if (state.phase === "RECOVERY" && !((state.nextReferenceMaxKg ?? 0) > 0)) {
|
|
1004
|
+
return "A recovery step needs the next block's reference max";
|
|
1005
|
+
}
|
|
1006
|
+
return null;
|
|
1007
|
+
}
|
|
1008
|
+
function lpPrescription(state, incrementKg) {
|
|
1009
|
+
if (state.phase === "FINISHED") return [];
|
|
1010
|
+
if (state.phase === "RECOVERY") {
|
|
1011
|
+
const loadKg2 = lpLoadKg(
|
|
1012
|
+
state.referenceMaxKg,
|
|
1013
|
+
LP_RECOVERY.percentage,
|
|
1014
|
+
incrementKg
|
|
1015
|
+
);
|
|
1016
|
+
return Array.from({ length: LP_RECOVERY.sets }, () => ({
|
|
1017
|
+
loadKg: loadKg2,
|
|
1018
|
+
target: null,
|
|
1019
|
+
reps: LP_RECOVERY.reps
|
|
1020
|
+
}));
|
|
1021
|
+
}
|
|
1022
|
+
const step = lpStep(state.step);
|
|
1023
|
+
const loadKg = lpLoadKg(state.referenceMaxKg, step.percentage, incrementKg);
|
|
1024
|
+
return step.sets.map((target) => ({ loadKg, target, reps: null }));
|
|
1025
|
+
}
|
|
1026
|
+
function lpWorkingSets(sets) {
|
|
1027
|
+
return sets.filter((set) => setKindOf(set) !== "WARMUP");
|
|
1028
|
+
}
|
|
1029
|
+
var LP_ESTIMATE_STEPS = [7, 8];
|
|
1030
|
+
var LP_ESTIMATE_MAX_REPS_TO_FAILURE = 10;
|
|
1031
|
+
var LP_ESTIMATE_MIN_SETS = 3;
|
|
1032
|
+
function lpRirMidpoint(target) {
|
|
1033
|
+
return (target.rirMin + target.rirMax) / 2;
|
|
1034
|
+
}
|
|
1035
|
+
function lpSampleRir(sample) {
|
|
1036
|
+
const target = lpStep(sample.step).sets[sample.set - 1] ?? HARD;
|
|
1037
|
+
return lpRirMidpoint(target);
|
|
1038
|
+
}
|
|
1039
|
+
function lpSetEstimateKg(loadKg, reps, rir) {
|
|
1040
|
+
return loadKg * (1 + (reps + rir) / 30);
|
|
1041
|
+
}
|
|
1042
|
+
function lpEstimate(samples, incrementKg) {
|
|
1043
|
+
const sets = samples.filter((sample) => LP_ESTIMATE_STEPS.includes(sample.step)).map((sample) => ({ ...sample, rir: lpSampleRir(sample) })).filter(
|
|
1044
|
+
(sample) => sample.reps >= 1 && sample.loadKg > 0 && sample.reps + sample.rir <= LP_ESTIMATE_MAX_REPS_TO_FAILURE
|
|
1045
|
+
).map((sample) => ({
|
|
1046
|
+
...sample,
|
|
1047
|
+
estimateKg: tidy(lpSetEstimateKg(sample.loadKg, sample.reps, sample.rir))
|
|
1048
|
+
})).sort((a, b) => a.step - b.step || a.set - b.set);
|
|
1049
|
+
if (sets.length < LP_ESTIMATE_MIN_SETS) {
|
|
1050
|
+
return { estimatedMaxKg: null, sets };
|
|
1051
|
+
}
|
|
1052
|
+
const values = sets.map((set) => set.estimateKg).sort((a, b) => a - b);
|
|
1053
|
+
const middle = Math.floor(values.length / 2);
|
|
1054
|
+
const median = values.length % 2 ? values[middle] : (values[middle - 1] + values[middle]) / 2;
|
|
1055
|
+
return { estimatedMaxKg: roundDownToIncrement(median, incrementKg), sets };
|
|
1056
|
+
}
|
|
1057
|
+
var LP_PROGRESS_THRESHOLD = 0.025;
|
|
1058
|
+
var LP_PROGRESS_CAP = 0.05;
|
|
1059
|
+
var LP_NEXT_BLOCK_KINDS = ["PROGRESS", "REPEAT", "RESET"];
|
|
1060
|
+
function lpRecommendation(referenceMaxKg, estimatedMaxKg, incrementKg) {
|
|
1061
|
+
if (!(estimatedMaxKg && estimatedMaxKg > 0) || !(referenceMaxKg > 0)) {
|
|
1062
|
+
return { kind: "REPEAT", nextReferenceMaxKg: referenceMaxKg, change: null };
|
|
1063
|
+
}
|
|
1064
|
+
const change = estimatedMaxKg / referenceMaxKg - 1;
|
|
1065
|
+
if (change >= LP_PROGRESS_THRESHOLD - 1e-9) {
|
|
1066
|
+
const capped = Math.min(
|
|
1067
|
+
estimatedMaxKg,
|
|
1068
|
+
referenceMaxKg * (1 + LP_PROGRESS_CAP)
|
|
1069
|
+
);
|
|
1070
|
+
const next = roundDownToIncrement(capped, incrementKg);
|
|
1071
|
+
return {
|
|
1072
|
+
kind: "PROGRESS",
|
|
1073
|
+
nextReferenceMaxKg: next > referenceMaxKg ? next : referenceMaxKg,
|
|
1074
|
+
change
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
if (change < -LP_PROGRESS_THRESHOLD - 1e-9) {
|
|
1078
|
+
return {
|
|
1079
|
+
kind: "RESET",
|
|
1080
|
+
nextReferenceMaxKg: roundDownToIncrement(estimatedMaxKg, incrementKg),
|
|
1081
|
+
change
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
return { kind: "REPEAT", nextReferenceMaxKg: referenceMaxKg, change };
|
|
1085
|
+
}
|
|
1086
|
+
function sameLpPosition(a, b) {
|
|
1087
|
+
if (!a || !b) return !a && !b;
|
|
1088
|
+
return a.phase === b.phase && a.step === b.step && a.cycle === b.cycle && Math.abs(a.referenceMaxKg - b.referenceMaxKg) < 1e-6;
|
|
1089
|
+
}
|
|
1090
|
+
function advanceLinearPeriodization(state, logged, context) {
|
|
1091
|
+
if (state.phase === "FINISHED") return null;
|
|
1092
|
+
const prescribed = lpPrescription(state, context.incrementKg).length;
|
|
1093
|
+
for (let set = 1; set <= prescribed; set += 1) {
|
|
1094
|
+
if (!logged.some((entry) => entry.set === set && entry.completed)) {
|
|
1095
|
+
return null;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
if (state.phase === "RECOVERY") {
|
|
1099
|
+
return startLinearPeriodization(
|
|
1100
|
+
state.nextReferenceMaxKg ?? state.referenceMaxKg,
|
|
1101
|
+
state.cycle + 1
|
|
1102
|
+
);
|
|
1103
|
+
}
|
|
1104
|
+
const samples = LP_ESTIMATE_STEPS.includes(state.step) ? [
|
|
1105
|
+
...state.samples.filter((sample) => sample.step !== state.step),
|
|
1106
|
+
...logged.filter((entry) => entry.completed && entry.set <= prescribed).map((entry) => ({
|
|
1107
|
+
sessionId: context.sessionId,
|
|
1108
|
+
step: state.step,
|
|
1109
|
+
set: entry.set,
|
|
1110
|
+
loadKg: entry.loadKg,
|
|
1111
|
+
reps: entry.reps
|
|
1112
|
+
}))
|
|
1113
|
+
] : state.samples;
|
|
1114
|
+
if (state.step < LP_BLOCK_STEPS) {
|
|
1115
|
+
return { ...state, step: state.step + 1, samples };
|
|
1116
|
+
}
|
|
1117
|
+
return {
|
|
1118
|
+
...state,
|
|
1119
|
+
phase: "FINISHED",
|
|
1120
|
+
samples,
|
|
1121
|
+
finishedAt: context.finishedAt,
|
|
1122
|
+
estimatedMaxKg: lpEstimate(samples, context.incrementKg).estimatedMaxKg,
|
|
1123
|
+
nextReferenceMaxKg: null
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
function continueLinearPeriodization(state, choice) {
|
|
1127
|
+
if (state.phase !== "FINISHED") return null;
|
|
1128
|
+
if (choice.recovery) {
|
|
1129
|
+
return {
|
|
1130
|
+
...state,
|
|
1131
|
+
phase: "RECOVERY",
|
|
1132
|
+
nextReferenceMaxKg: choice.referenceMaxKg
|
|
1133
|
+
};
|
|
1134
|
+
}
|
|
1135
|
+
return startLinearPeriodization(choice.referenceMaxKg, state.cycle + 1);
|
|
1136
|
+
}
|
|
1137
|
+
function lpStoredSets(state, incrementKg) {
|
|
1138
|
+
if (!state) {
|
|
1139
|
+
return Array.from({ length: LP_WORKING_SETS }, () => ({
|
|
1140
|
+
repType: "FIXED",
|
|
1141
|
+
reps: null,
|
|
1142
|
+
weight: null,
|
|
1143
|
+
rir: null,
|
|
1144
|
+
kind: "WORKING"
|
|
1145
|
+
}));
|
|
1146
|
+
}
|
|
1147
|
+
const prescription = state.phase === "FINISHED" ? lpPrescription({ ...state, phase: "BLOCK", step: LP_BLOCK_STEPS }, incrementKg) : lpPrescription(state, incrementKg);
|
|
1148
|
+
return prescription.map((set) => ({
|
|
1149
|
+
repType: "FIXED",
|
|
1150
|
+
reps: set.reps,
|
|
1151
|
+
weight: set.loadKg,
|
|
1152
|
+
rir: set.target ? set.target.rirMin : null,
|
|
1153
|
+
kind: "WORKING"
|
|
1154
|
+
}));
|
|
1155
|
+
}
|
|
1156
|
+
|
|
897
1157
|
// src/plate-loading.ts
|
|
898
1158
|
var CANONICAL_SCALE = 1e4;
|
|
899
1159
|
var DENSE_CAPACITY_LIMIT = 2e5;
|
|
@@ -1526,7 +1786,9 @@ var NOTIFICATION_KINDS = [
|
|
|
1526
1786
|
"TRAINING_PARTNER_ENCOURAGEMENT",
|
|
1527
1787
|
// NOTIF-07: selected activity from a partner who currently shares it.
|
|
1528
1788
|
"TRAINING_PARTNER_SESSION",
|
|
1529
|
-
"TRAINING_PARTNER_ACHIEVEMENT"
|
|
1789
|
+
"TRAINING_PARTNER_ACHIEVEMENT",
|
|
1790
|
+
// ROUT-18: an LP exercise finished its 8-step block.
|
|
1791
|
+
"LINEAR_BLOCK_FINISHED"
|
|
1530
1792
|
];
|
|
1531
1793
|
var NOTIFICATIONS_LOOKBACK_DAYS = 30;
|
|
1532
1794
|
var NOTIFICATIONS_RETENTION_DAYS = 90;
|
|
@@ -1713,6 +1975,14 @@ var API_ERROR_MESSAGES = {
|
|
|
1713
1975
|
SWAP_SESSION_NOT_ACTIVE: "Exercises can only be swapped in an active session",
|
|
1714
1976
|
SWAP_SETS_COMPLETED: "This exercise already has completed sets. Swap it before completing any.",
|
|
1715
1977
|
NOTES_DISCARDED_SESSION: "A discarded workout cannot take notes",
|
|
1978
|
+
// ── Linear periodization (ROUT-17 to ROUT-19) ──────────────────────────
|
|
1979
|
+
LINEAR_BLOCK_INVALID: "This exercise's block is not valid: {reason}",
|
|
1980
|
+
LINEAR_BLOCK_EXTRA_SET: "An exercise on an 8-week block takes no extra sets",
|
|
1981
|
+
LINEAR_BLOCK_KIND_FIXED: "The working sets of an 8-week block cannot change kind",
|
|
1982
|
+
LINEAR_BLOCK_LOAD_FIXED: "The load of an 8-week block is prescribed and cannot be changed",
|
|
1983
|
+
LINEAR_BLOCK_SWAP_ROUTINE: "An exercise on an 8-week block can be swapped for this workout only",
|
|
1984
|
+
LINEAR_BLOCK_NOT_LINEAR: "This exercise is not on an 8-week block",
|
|
1985
|
+
LINEAR_BLOCK_NOT_FINISHED: "This exercise's block has not finished, so there is nothing to choose yet",
|
|
1716
1986
|
// ── Corrections (LIVE-17) ──────────────────────────────────────────────
|
|
1717
1987
|
CORRECTION_NOT_COMPLETED: "Only a completed workout can be corrected",
|
|
1718
1988
|
CORRECTION_NOT_LATEST: "Only your most recent workout can be corrected",
|
|
@@ -1766,6 +2036,11 @@ var API_ERROR_MESSAGES = {
|
|
|
1766
2036
|
MESSAGE_NOT_ADMITTED: "This member is not taking messages from you.",
|
|
1767
2037
|
CONVERSATION_CLOSED: "This conversation can no longer receive messages.",
|
|
1768
2038
|
MESSAGING_UNAVAILABLE: "Messaging is not available for this account.",
|
|
2039
|
+
// ── Message moderation (MSG-09) ────────────────────────────────────────
|
|
2040
|
+
MESSAGING_RESTRICTED: "Moderation has restricted your messaging. You can still read and delete messages.",
|
|
2041
|
+
REPORT_OWN_MESSAGE: "You cannot report your own message",
|
|
2042
|
+
MESSAGING_ALREADY_RESTRICTED: "This member's messaging is already restricted",
|
|
2043
|
+
MESSAGING_NOT_RESTRICTED: "This member's messaging is not restricted",
|
|
1769
2044
|
// ── Account and settings ───────────────────────────────────────────────
|
|
1770
2045
|
ACCOUNT_CONFLICT: "Account conflict for this email",
|
|
1771
2046
|
ACCOUNT_DELETE_CONFIRMATION: "Type your username exactly as it appears to confirm.",
|
|
@@ -1862,6 +2137,7 @@ function normalizeMessageBody(body) {
|
|
|
1862
2137
|
}
|
|
1863
2138
|
return text;
|
|
1864
2139
|
}
|
|
2140
|
+
var MESSAGE_REPORT_CONTEXT_BEFORE = 5;
|
|
1865
2141
|
function isMessagePermission(value) {
|
|
1866
2142
|
return MESSAGE_PERMISSIONS.includes(value);
|
|
1867
2143
|
}
|
|
@@ -1943,6 +2219,19 @@ function isMessagePermission(value) {
|
|
|
1943
2219
|
INCHES_PER_FOOT,
|
|
1944
2220
|
LENGTH_UNITS,
|
|
1945
2221
|
LIMITED_SHORTFALL_KG,
|
|
2222
|
+
LP_BLOCK_STEPS,
|
|
2223
|
+
LP_ESTIMATE_MAX_REPS_TO_FAILURE,
|
|
2224
|
+
LP_ESTIMATE_MIN_SETS,
|
|
2225
|
+
LP_ESTIMATE_STEPS,
|
|
2226
|
+
LP_NEXT_BLOCK_KINDS,
|
|
2227
|
+
LP_PHASES,
|
|
2228
|
+
LP_PROGRESS_CAP,
|
|
2229
|
+
LP_PROGRESS_THRESHOLD,
|
|
2230
|
+
LP_RECOVERY,
|
|
2231
|
+
LP_REFERENCE_MAX_KG,
|
|
2232
|
+
LP_REST_SECONDS,
|
|
2233
|
+
LP_STEPS,
|
|
2234
|
+
LP_WORKING_SETS,
|
|
1946
2235
|
MAX_SETS_PER_EXERCISE,
|
|
1947
2236
|
MEASURABLE_GOALS_MAX,
|
|
1948
2237
|
MEASURABLE_GOAL_DIRECTIONS,
|
|
@@ -1951,6 +2240,7 @@ function isMessagePermission(value) {
|
|
|
1951
2240
|
MESSAGES_PER_MINUTE_MAX,
|
|
1952
2241
|
MESSAGE_BODY_MAX,
|
|
1953
2242
|
MESSAGE_PERMISSIONS,
|
|
2243
|
+
MESSAGE_REPORT_CONTEXT_BEFORE,
|
|
1954
2244
|
MINUTES_IN_DAY,
|
|
1955
2245
|
MODERATION_ACTION_KINDS,
|
|
1956
2246
|
MODERATION_HISTORY_PAGE_SIZE,
|
|
@@ -2084,6 +2374,7 @@ function isMessagePermission(value) {
|
|
|
2084
2374
|
WEIGHT_UNITS,
|
|
2085
2375
|
WORKOUT_SESSION_STATUSES,
|
|
2086
2376
|
accountExportFileName,
|
|
2377
|
+
advanceLinearPeriodization,
|
|
2087
2378
|
apiError,
|
|
2088
2379
|
apiErrorMessage,
|
|
2089
2380
|
applyDeload,
|
|
@@ -2094,6 +2385,7 @@ function isMessagePermission(value) {
|
|
|
2094
2385
|
canLinkToNext,
|
|
2095
2386
|
cmToFeetInches,
|
|
2096
2387
|
cmToInches,
|
|
2388
|
+
continueLinearPeriodization,
|
|
2097
2389
|
countsAsWork,
|
|
2098
2390
|
countsForProgression,
|
|
2099
2391
|
customExerciseProblem,
|
|
@@ -2123,6 +2415,17 @@ function isMessagePermission(value) {
|
|
|
2123
2415
|
isWithinQuietHours,
|
|
2124
2416
|
latestBodyWeight,
|
|
2125
2417
|
leadWorkingWeight,
|
|
2418
|
+
linearPeriodizationProblem,
|
|
2419
|
+
lpEstimate,
|
|
2420
|
+
lpLoadKg,
|
|
2421
|
+
lpPrescription,
|
|
2422
|
+
lpRecommendation,
|
|
2423
|
+
lpRirMidpoint,
|
|
2424
|
+
lpSampleRir,
|
|
2425
|
+
lpSetEstimateKg,
|
|
2426
|
+
lpStep,
|
|
2427
|
+
lpStoredSets,
|
|
2428
|
+
lpWorkingSets,
|
|
2126
2429
|
matchesDeletionConfirmation,
|
|
2127
2430
|
messageBodyLength,
|
|
2128
2431
|
normalizeDashboardLayout,
|
|
@@ -2135,10 +2438,14 @@ function isMessagePermission(value) {
|
|
|
2135
2438
|
requiredToFinish,
|
|
2136
2439
|
resolveActiveTrainingBlock,
|
|
2137
2440
|
resolveRoutinePlan,
|
|
2441
|
+
roundDownToIncrement,
|
|
2442
|
+
roundToIncrement,
|
|
2138
2443
|
routineDayLabel,
|
|
2444
|
+
sameLpPosition,
|
|
2139
2445
|
sessionCorrectionWindow,
|
|
2140
2446
|
setKindOf,
|
|
2141
2447
|
setLogValuesDiffer,
|
|
2448
|
+
startLinearPeriodization,
|
|
2142
2449
|
summarizeBodyProgress,
|
|
2143
2450
|
trainingBlockCovers,
|
|
2144
2451
|
warmUpLoad,
|