@dgpholdings/greatoak-shared 1.2.92 → 1.2.94

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.
@@ -5,7 +5,6 @@ const testIds_1 = require("./testIds");
5
5
  exports.mockUser = {
6
6
  // TUserPreferences fields (flat in TUserMetric)
7
7
  preferredDurationMinutes: 45,
8
- fitnessGoals: ["hypertrophy"],
9
8
  preferredEquipment: ["dumbbell", "barbell"],
10
9
  preferCompoundMovements: true,
11
10
  includeWarmup: "ai_decide",
@@ -17,6 +16,7 @@ exports.mockUser = {
17
16
  bodyFatPercentage: 15,
18
17
  metricSystem: "EU",
19
18
  appLanguage: "en",
19
+ goalProfile: undefined,
20
20
  preferencesUpdatedAt: new Date("2023-01-01").toISOString(),
21
21
  femaleHealth: null, // Male user
22
22
  constellationLevel: 1,
@@ -1,3 +1,2 @@
1
1
  export * from "./BodyCategories";
2
2
  export * from "./AiExerciseVocabulary";
3
- export * from "./goalJourney";
@@ -16,4 +16,3 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./BodyCategories"), exports);
18
18
  __exportStar(require("./AiExerciseVocabulary"), exports);
19
- __exportStar(require("./goalJourney"), exports);
@@ -1,4 +1,5 @@
1
1
  import { TAiEnergyLevel, TAiUserInjury, TQuickStartEquipmentGroupExplicit } from "../constants/AiExerciseVocabulary";
2
+ import { TTrainingPhase, TUserFitnessGoal } from "./goalFeature";
2
3
  import { TTemplate } from "./TApiTemplateData";
3
4
  export type TApiAiQuickStartWorkoutReq = {
4
5
  /**
@@ -62,6 +63,12 @@ export type TApiAiQuickStartWorkoutReq = {
62
63
  * backend treats it as "no equipment filter" — i.e. all candidates eligible.
63
64
  */
64
65
  equipmentSelectionOverride: TQuickStartEquipmentSelection;
66
+ goalContext?: {
67
+ activeGoal: TUserFitnessGoal;
68
+ currentPhase: TTrainingPhase;
69
+ currentWeek: number;
70
+ phaseName: string;
71
+ };
65
72
  };
66
73
  export type TQuickStartEquipmentSelection = {
67
74
  type: "preferences";
@@ -1,5 +1,4 @@
1
- import type { TAiFitnessGoal } from "../constants/AiExerciseVocabulary";
2
- import { TAuthType, TGender, TProfessionalCategory, TUserMetric, TUserType, TInjuryInfo } from "./TApiUser";
1
+ import { TAuthType, TGender, TInjuryInfo, TProfessionalCategory, TUserMetric, TUserType } from "./TApiUser";
3
2
  import { TActivityLevel } from "./TUserPreferences";
4
3
  /**
5
4
  * Anonymous signup request.
@@ -25,7 +24,6 @@ export type TApiSignupAnonymousReq = {
25
24
  gender: TGender;
26
25
  bodyFatPercentage: number;
27
26
  metricSystem: "US" | "EU";
28
- fitnessGoals: TAiFitnessGoal[];
29
27
  fitnessLevel: TActivityLevel;
30
28
  injuryInfo?: TInjuryInfo;
31
29
  gdprAnalytics: boolean;
@@ -1,6 +1,7 @@
1
1
  import type { TAiFitnessGoal, TAiUserInjury } from "../constants/AiExerciseVocabulary";
2
2
  import { TConstellationUserLevel } from "../utils";
3
3
  import { TGdprData } from "./commonTypes";
4
+ import { TGoalProfile } from "./goalFeature";
4
5
  import { TUserPreferences } from "./TUserPreferences";
5
6
  export type TAuthType = "email" | "apple" | "anonymous" | "token";
6
7
  export type TGender = "male" | "female" | "unmentioned";
@@ -25,8 +26,9 @@ export type TUserMetric = TUserPreferences & {
25
26
  userType: TUserType;
26
27
  professionType?: TProfessionalCategory;
27
28
  gender: TGender;
28
- weightKg?: number;
29
- heightCm?: number;
29
+ weightKg: number;
30
+ heightCm: number;
31
+ goalProfile: TGoalProfile | undefined;
30
32
  emailAddress?: string;
31
33
  injuryInfo?: TInjuryInfo;
32
34
  subscriptionStatus: TSubscriptionStatus;
@@ -1,11 +1,10 @@
1
- import { TAiEquipmentType, TAiFitnessGoal } from "../constants";
1
+ import { TAiEquipmentType } from "../constants";
2
2
  import { TBodyPart } from "./TApiExercise";
3
3
  export type TPreferredDuration = 20 | 30 | 45 | 60 | 90 | 120;
4
4
  export type TAIDecision = "yes" | "no" | "ai_decide";
5
5
  export type TWarmupStyle = "dynamic_stretching" | "light_cardio";
6
6
  export type TStablePreferences = {
7
7
  preferredDurationMinutes: TPreferredDuration;
8
- fitnessGoals: TAiFitnessGoal[];
9
8
  preferredEquipment: TAiEquipmentType[];
10
9
  preferCompoundMovements: boolean;
11
10
  includeWarmup: TAIDecision;
@@ -13,7 +12,7 @@ export type TStablePreferences = {
13
12
  warmupStyle: TWarmupStyle | null;
14
13
  favouriteMuscleGroups: TBodyPart[];
15
14
  blacklistedExerciseIds: string[];
16
- fitnessLevel?: TActivityLevel;
15
+ fitnessLevel: TActivityLevel;
17
16
  bodyFatPercentage?: number;
18
17
  metricSystem: "US" | "EU";
19
18
  appLanguage: string;
@@ -0,0 +1,64 @@
1
+ import { TWorkoutIntent } from "../../constants";
2
+ import { TDaysPerWeek, TSessionIntent, TTrainingPhase, TUserFitnessGoal } from "./goalJourney";
3
+ export type TGoalProfile = {
4
+ activeGoal: TUserFitnessGoal;
5
+ goalStartDate: string;
6
+ daysPerWeek: TDaysPerWeek;
7
+ phaseCompletions: {
8
+ phase: TTrainingPhase;
9
+ completedAt: string;
10
+ celebrationShown: boolean;
11
+ }[];
12
+ previousGoals: {
13
+ goal: TUserFitnessGoal;
14
+ startDate: string;
15
+ endDate: string;
16
+ weeksCompleted: number;
17
+ reasonEnded: "completed" | "changed";
18
+ }[];
19
+ };
20
+ export type TGoalContext = {
21
+ goal: TUserFitnessGoal;
22
+ daysPerWeek: TDaysPerWeek;
23
+ currentPhase: TTrainingPhase;
24
+ currentWeek: number;
25
+ weeklyStructure: TSessionIntent[];
26
+ lastSessionDate: string;
27
+ lastSessionIntent: TSessionIntent;
28
+ sessionsCompletedThisWeek: number;
29
+ totalSessionsSinceGoalStart: number;
30
+ sessionsInLast4Weeks: number;
31
+ };
32
+ export type TIntentRecommendation = {
33
+ intent: TWorkoutIntent;
34
+ confidence: number;
35
+ rationale: string;
36
+ decisionPath: "global_recovery" | "returning_user" | "gap_bridge" | "structure_follow" | "muscle_override";
37
+ intentScores: Partial<Record<TWorkoutIntent, number>>;
38
+ };
39
+ export type TGoalSessionSummary = {
40
+ sessionId: string;
41
+ createdAt: string;
42
+ intentId: TWorkoutIntent | undefined;
43
+ exercises: {
44
+ exerciseId: string;
45
+ thumbnailUrl: string;
46
+ name: string;
47
+ }[];
48
+ };
49
+ export type TApiGoalContextRes = {
50
+ status: 200;
51
+ goalProfile: TGoalProfile;
52
+ goalContext: TGoalContext;
53
+ recommendation: TIntentRecommendation;
54
+ recentSessions: TGoalSessionSummary[];
55
+ weeklySessionCounts: {
56
+ weekNumber: number;
57
+ weekStartDate: string;
58
+ count: number;
59
+ }[];
60
+ newPhaseUnlocked?: TTrainingPhase;
61
+ } | {
62
+ status: 400 | 401 | 404 | 500;
63
+ message: string;
64
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,111 @@
1
+ import { TActivityLevel } from "..";
2
+ import { TAiFitnessGoal, TWorkoutIntent } from "../../constants/AiExerciseVocabulary";
3
+ export declare const USER_FITNESS_GOALS: readonly ["lose_weight", "build_muscle", "lean_and_toned", "glutes_and_legs", "get_stronger", "boost_endurance", "athletic_performance", "general_fitness", "posture_and_back", "stress_relief", "rehabilitation", "core_strength", "mobility_flexibility"];
4
+ export type TUserFitnessGoal = (typeof USER_FITNESS_GOALS)[number];
5
+ export type TSessionIntent = TWorkoutIntent | "rest";
6
+ export declare const GOAL_SESSION_INTENTS: Record<TUserFitnessGoal, TWeeklyIntentTemplate>;
7
+ export declare const GOAL_TO_AI_FITNESS_GOAL: Record<TUserFitnessGoal, TAiFitnessGoal>;
8
+ export declare const TRAINING_PHASES: readonly ["foundation", "momentum", "push", "peak"];
9
+ export type TTrainingPhase = (typeof TRAINING_PHASES)[number];
10
+ export type TPhaseDisplay = {
11
+ /** i18n key for the phase name — e.g. "goalPhase.foundation.name" */
12
+ nameKey: string;
13
+ /** English fallback for nameKey */
14
+ nameEn: string;
15
+ /** i18n key for the short subtitle — e.g. "goalPhase.foundation.subtitle" */
16
+ subtitleKey: string;
17
+ /** English fallback for subtitleKey */
18
+ subtitleEn: string;
19
+ /** i18n key for the longer purpose description shown in phase detail */
20
+ purposeKey: string;
21
+ /** English fallback for purposeKey */
22
+ purposeEn: string;
23
+ /** i18n key for the week range label — e.g. "goalPhase.foundation.duration" */
24
+ durationKey: string;
25
+ /** English fallback for durationKey */
26
+ durationEn: string;
27
+ /** Inclusive week range [startWeek, endWeek]. Phase 4 end is open (99). */
28
+ weekRange: [number, number];
29
+ /**
30
+ * Reference intensity as a percentage (0–100).
31
+ * For UI progress bars / intensity indicators only — not used in algorithm.
32
+ */
33
+ intensityPercent: number;
34
+ };
35
+ export declare const PHASE_DISPLAY: Record<TTrainingPhase, TPhaseDisplay>;
36
+ export type TIntentDisplay = {
37
+ /** i18n key for the full label — e.g. "sessionIntent.push.label" */
38
+ labelKey: string;
39
+ /** English fallback for labelKey */
40
+ labelEn: string;
41
+ /**
42
+ * i18n key for the short label used in compact journey nodes.
43
+ * e.g. "sessionIntent.push.shortLabel"
44
+ */
45
+ shortLabelKey: string;
46
+ /** English fallback for shortLabelKey */
47
+ shortLabelEn: string;
48
+ /**
49
+ * Emoji for the session node icon.
50
+ * Not translated — emoji is universal.
51
+ */
52
+ emoji: string;
53
+ };
54
+ export declare const INTENT_DISPLAY: Record<Exclude<TSessionIntent, "arms" | "back" | "unstructured">, TIntentDisplay>;
55
+ export type TGoalDisplay = {
56
+ /** i18n key for the goal name — e.g. "userGoal.loseWeight.name" */
57
+ nameKey: string;
58
+ /** English fallback for nameKey */
59
+ nameEn: string;
60
+ /** i18n key for the short description shown on goal selection screen */
61
+ descriptionKey: string;
62
+ /** English fallback for descriptionKey */
63
+ descriptionEn: string;
64
+ /**
65
+ * i18n key for the motivational tagline shown on the goal journey screen.
66
+ * e.g. "userGoal.loseWeight.tagline"
67
+ */
68
+ taglineKey: string;
69
+ /** English fallback for taglineKey */
70
+ taglineEn: string;
71
+ /** Emoji representing the goal — universal, not translated */
72
+ emoji: string;
73
+ /**
74
+ * Goal category — used to group goals on the selection screen.
75
+ * Category label itself is also translated via categoryKey.
76
+ */
77
+ category: "appearance" | "performance" | "health" | "focused";
78
+ };
79
+ export declare const GOAL_DISPLAY: Record<TUserFitnessGoal, TGoalDisplay>;
80
+ export type TGoalCategory = TGoalDisplay["category"];
81
+ export type TGoalCategoryDisplay = {
82
+ labelKey: string;
83
+ labelEn: string;
84
+ };
85
+ export declare const GOAL_CATEGORY_DISPLAY: Record<TGoalCategory, TGoalCategoryDisplay>;
86
+ export declare const PHASE_ENTRY_CREDIT: Record<TActivityLevel, number>;
87
+ export declare const RECENCY_CREDIT: Record<TActivityLevel, number>;
88
+ /**
89
+ * Derive training phase from time + volume + recency.
90
+ * All three must pass — week count alone is NOT sufficient.
91
+ * A user doing 12 sessions over one year has not built a training base.
92
+ *
93
+ * On goal start day (no sessions yet), pass:
94
+ * totalSessions = PHASE_ENTRY_CREDIT[user.fitnessLevel]
95
+ * sessionsInLast4Weeks = RECENCY_CREDIT[user.fitnessLevel]
96
+ */
97
+ export declare function derivePhase(weekNumber: number, totalSessions: number, sessionsInLast4Weeks: number): TTrainingPhase;
98
+ export declare const DAYS_PER_WEEK_OPTIONS: readonly [2, 3, 4, 5];
99
+ export type TDaysPerWeek = (typeof DAYS_PER_WEEK_OPTIONS)[number];
100
+ export type TDurationHint = "short" | "medium" | "long";
101
+ export type TWeeklyIntentTemplate = [
102
+ TSessionIntent,
103
+ TSessionIntent,
104
+ TSessionIntent,
105
+ TSessionIntent,
106
+ TSessionIntent,
107
+ TSessionIntent,
108
+ TSessionIntent
109
+ ];
110
+ export type TGoalPhaseHints = Record<TTrainingPhase, string>;
111
+ export declare const GOAL_PHASE_HINTS: Record<TUserFitnessGoal, TGoalPhaseHints>;