@openlifelog/sdk 1.0.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/client.ts ADDED
@@ -0,0 +1,190 @@
1
+ import { mergeConfig, type OpenLifeLogConfig, type ResolvedConfig } from './config';
2
+ import { HttpClient } from './utils/http';
3
+ import { createUnitConverter, type UnitConverter } from './utils/units';
4
+ import { AuthResource } from './resources/auth';
5
+ import { UsersResource } from './resources/users';
6
+ import { FoodsResource } from './resources/foods';
7
+ import { FoodLogsResource } from './resources/food-logs';
8
+ import { ExercisesResource } from './resources/exercises';
9
+ import { WorkoutsResource } from './resources/workouts';
10
+ import { SessionsResource } from './resources/sessions';
11
+ import { ProgramsResource } from './resources/programs';
12
+ import { MetricsResource } from './resources/metrics';
13
+ import { GoalsResource } from './resources/goals';
14
+ import { AIResource } from './resources/ai';
15
+ import type { CreateFoodLogRequest, FoodLog } from './types';
16
+
17
+ /**
18
+ * OpenLifeLog SDK Client
19
+ *
20
+ * Main entry point for interacting with the OpenLifeLog API
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * import { OpenLifeLog } from '@openlifelog/sdk';
25
+ *
26
+ * // Initialize the client
27
+ * const client = new OpenLifeLog({
28
+ * apiKey: 'your-jwt-token',
29
+ * baseUrl: 'http://localhost:8080',
30
+ * autoConvertUnits: true
31
+ * });
32
+ *
33
+ * // Or initialize without API key and login
34
+ * const client = new OpenLifeLog();
35
+ * await client.auth.login({ email: '...', password: '...' });
36
+ *
37
+ * // Use the client
38
+ * const user = await client.users.me();
39
+ * const foods = await client.foods.list();
40
+ * ```
41
+ */
42
+ export class OpenLifeLog {
43
+ private config: ResolvedConfig;
44
+ private httpClient: HttpClient;
45
+ private unitConverter: UnitConverter;
46
+
47
+ // Resource instances
48
+ public readonly auth: AuthResource;
49
+ public readonly users: UsersResource;
50
+ public readonly foods: FoodsResource;
51
+ public readonly foodLogs: FoodLogsResource;
52
+ public readonly exercises: ExercisesResource;
53
+ public readonly workouts: WorkoutsResource;
54
+ public readonly sessions: SessionsResource;
55
+ public readonly programs: ProgramsResource;
56
+ public readonly metrics: MetricsResource;
57
+ public readonly goals: GoalsResource;
58
+ public readonly ai: AIResource;
59
+
60
+ constructor(config: OpenLifeLogConfig = {}) {
61
+ this.config = mergeConfig(config);
62
+ this.httpClient = new HttpClient(this.config);
63
+ this.unitConverter = createUnitConverter(this.config.measurementSystem);
64
+
65
+ // Initialize all resources
66
+ this.auth = new AuthResource(this.httpClient);
67
+ this.users = new UsersResource(this.httpClient);
68
+ this.foods = new FoodsResource(this.httpClient);
69
+ this.foodLogs = new FoodLogsResource(this.httpClient);
70
+ this.exercises = new ExercisesResource(this.httpClient);
71
+ this.workouts = new WorkoutsResource(this.httpClient);
72
+ this.sessions = new SessionsResource(this.httpClient);
73
+ this.programs = new ProgramsResource(this.httpClient);
74
+ this.metrics = new MetricsResource(this.httpClient);
75
+ this.goals = new GoalsResource(this.httpClient);
76
+ this.ai = new AIResource(this.httpClient);
77
+ }
78
+
79
+ /**
80
+ * Update the API key (JWT token)
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * client.setApiKey('new-jwt-token');
85
+ * ```
86
+ */
87
+ setApiKey(apiKey: string): void {
88
+ this.httpClient.setApiKey(apiKey);
89
+ }
90
+
91
+ /**
92
+ * Get the current API key
93
+ */
94
+ getApiKey(): string | undefined {
95
+ return this.httpClient.getApiKey();
96
+ }
97
+
98
+ /**
99
+ * Get the unit converter instance
100
+ */
101
+ getUnitConverter(): UnitConverter {
102
+ return this.unitConverter;
103
+ }
104
+
105
+ /**
106
+ * Update the measurement system preference
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * client.setMeasurementSystem('imperial');
111
+ * ```
112
+ */
113
+ setMeasurementSystem(system: 'metric' | 'imperial'): void {
114
+ this.config.measurementSystem = system;
115
+ this.unitConverter.setMeasurementSystem(system);
116
+ }
117
+
118
+ /**
119
+ * Get the current measurement system
120
+ */
121
+ getMeasurementSystem(): 'metric' | 'imperial' {
122
+ return this.config.measurementSystem;
123
+ }
124
+
125
+ /**
126
+ * Convenience method: Log food (combines food lookup and logging)
127
+ *
128
+ * This is a high-level method that developers love - simple and intuitive.
129
+ *
130
+ * @example
131
+ * ```typescript
132
+ * const log = await client.logFood({
133
+ * foodId: 'food-uuid',
134
+ * name: 'Chicken Breast',
135
+ * servingSizeName: 'breast (200g)',
136
+ * quantity: 1.5,
137
+ * mealType: 'lunch',
138
+ * notes: 'Grilled with olive oil'
139
+ * });
140
+ * ```
141
+ */
142
+ async logFood(request: CreateFoodLogRequest): Promise<FoodLog> {
143
+ return this.foodLogs.create(request);
144
+ }
145
+
146
+ /**
147
+ * Convenience method: Get today's nutrition summary
148
+ *
149
+ * @example
150
+ * ```typescript
151
+ * const summary = await client.getTodayNutrition();
152
+ * console.log(`Calories: ${summary.totalCalories}`);
153
+ * ```
154
+ */
155
+ async getTodayNutrition() {
156
+ const today = new Date().toISOString().split('T')[0];
157
+ return this.foodLogs.getDailySummary({ date: today });
158
+ }
159
+
160
+ /**
161
+ * Convenience method: Get today's goal progress
162
+ *
163
+ * @example
164
+ * ```typescript
165
+ * const progress = await client.getTodayProgress();
166
+ * ```
167
+ */
168
+ async getTodayProgress() {
169
+ return this.goals.getProgress();
170
+ }
171
+
172
+ /**
173
+ * Convenience method: Get today's nutrition progress
174
+ *
175
+ * @example
176
+ * ```typescript
177
+ * const progress = await client.getTodayNutritionProgress();
178
+ * ```
179
+ */
180
+ async getTodayNutritionProgress() {
181
+ return this.goals.getNutritionProgress();
182
+ }
183
+
184
+ /**
185
+ * Check if user is authenticated
186
+ */
187
+ isAuthenticated(): boolean {
188
+ return !!this.httpClient.getApiKey();
189
+ }
190
+ }
package/config.ts ADDED
@@ -0,0 +1,96 @@
1
+ import type { MeasurementSystem } from './types/common';
2
+
3
+ /**
4
+ * Configuration options for the OpenLifeLog SDK
5
+ */
6
+ export interface OpenLifeLogConfig {
7
+ /**
8
+ * API key (JWT token) for authenticated requests.
9
+ * Can be omitted if using auth methods to obtain token.
10
+ */
11
+ apiKey?: string;
12
+
13
+ /**
14
+ * Base URL for the API
15
+ * @default "https://api.openlifelog.com"
16
+ */
17
+ baseUrl?: string;
18
+
19
+ /**
20
+ * Measurement system preference (metric or imperial)
21
+ * @default "metric"
22
+ */
23
+ measurementSystem?: MeasurementSystem;
24
+
25
+ /**
26
+ * Automatically convert units based on measurement system preference
27
+ * @default true
28
+ */
29
+ autoConvertUnits?: boolean;
30
+
31
+ /**
32
+ * Request timeout in milliseconds
33
+ * @default 30000 (30 seconds)
34
+ */
35
+ timeout?: number;
36
+
37
+ /**
38
+ * Maximum number of retry attempts for failed requests
39
+ * @default 3
40
+ */
41
+ maxRetries?: number;
42
+
43
+ /**
44
+ * Enable automatic retries for network errors and 5xx errors
45
+ * @default true
46
+ */
47
+ enableRetries?: boolean;
48
+
49
+ /**
50
+ * Custom headers to include in all requests
51
+ */
52
+ headers?: Record<string, string>;
53
+
54
+ /**
55
+ * Enable debug logging
56
+ * @default false
57
+ */
58
+ debug?: boolean;
59
+
60
+ /**
61
+ * Custom fetch implementation (useful for Node.js < 18 or testing)
62
+ */
63
+ fetch?: typeof fetch;
64
+ }
65
+
66
+ /**
67
+ * Default configuration values
68
+ */
69
+ export const DEFAULT_CONFIG: Required<Omit<OpenLifeLogConfig, 'apiKey' | 'fetch' | 'headers'>> = {
70
+ baseUrl: 'https://api.openlifelog.com',
71
+ measurementSystem: 'metric',
72
+ autoConvertUnits: true,
73
+ timeout: 30000,
74
+ maxRetries: 3,
75
+ enableRetries: true,
76
+ debug: false,
77
+ };
78
+
79
+ /**
80
+ * Resolved configuration with all defaults applied
81
+ */
82
+ export type ResolvedConfig = Required<Omit<OpenLifeLogConfig, 'apiKey'>> & { apiKey?: string };
83
+
84
+ /**
85
+ * Merge user config with defaults
86
+ */
87
+ export function mergeConfig(userConfig: OpenLifeLogConfig): ResolvedConfig {
88
+ return {
89
+ ...DEFAULT_CONFIG,
90
+ ...userConfig,
91
+ apiKey: userConfig.apiKey,
92
+ headers: userConfig.headers || {},
93
+ // Bind fetch to globalThis to preserve context
94
+ fetch: userConfig.fetch || globalThis.fetch.bind(globalThis),
95
+ };
96
+ }
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Metric definitions matching the database reference_data.sql
3
+ * These are the available metrics that users can track
4
+ */
5
+
6
+ export interface MetricDefinition {
7
+ key: string;
8
+ displayName: string;
9
+ category: 'body' | 'activity' | 'sleep' | 'vital' | 'wellness';
10
+ unit: string;
11
+ aggregation: 'last' | 'sum' | 'avg' | 'min' | 'max';
12
+ imperialUnit?: string;
13
+ imperialConversion?: number;
14
+ }
15
+
16
+ /**
17
+ * Body Metrics - measurements related to body composition and physical attributes
18
+ */
19
+ export const BODY_METRICS: MetricDefinition[] = [
20
+ { key: 'height', displayName: 'Height', category: 'body', unit: 'cm', aggregation: 'last', imperialUnit: 'in', imperialConversion: 0.393701 },
21
+ { key: 'weight', displayName: 'Body Weight', category: 'body', unit: 'kg', aggregation: 'last', imperialUnit: 'lbs', imperialConversion: 2.20462 },
22
+ { key: 'body_fat', displayName: 'Body Fat %', category: 'body', unit: '%', aggregation: 'last' },
23
+ { key: 'muscle_mass', displayName: 'Muscle Mass', category: 'body', unit: 'kg', aggregation: 'last', imperialUnit: 'lbs', imperialConversion: 2.20462 },
24
+ { key: 'bone_mass', displayName: 'Bone Mass', category: 'body', unit: 'kg', aggregation: 'last', imperialUnit: 'lbs', imperialConversion: 2.20462 },
25
+ { key: 'body_water', displayName: 'Body Water %', category: 'body', unit: '%', aggregation: 'last' },
26
+ { key: 'bmi', displayName: 'BMI', category: 'body', unit: 'kg/m²', aggregation: 'last' },
27
+ { key: 'waist', displayName: 'Waist Circumference', category: 'body', unit: 'cm', aggregation: 'last', imperialUnit: 'in', imperialConversion: 0.393701 },
28
+ { key: 'hip', displayName: 'Hip Circumference', category: 'body', unit: 'cm', aggregation: 'last', imperialUnit: 'in', imperialConversion: 0.393701 },
29
+ { key: 'chest', displayName: 'Chest Circumference', category: 'body', unit: 'cm', aggregation: 'last', imperialUnit: 'in', imperialConversion: 0.393701 },
30
+ { key: 'neck', displayName: 'Neck Circumference', category: 'body', unit: 'cm', aggregation: 'last', imperialUnit: 'in', imperialConversion: 0.393701 },
31
+ { key: 'left_bicep', displayName: 'Left Bicep', category: 'body', unit: 'cm', aggregation: 'last', imperialUnit: 'in', imperialConversion: 0.393701 },
32
+ { key: 'right_bicep', displayName: 'Right Bicep', category: 'body', unit: 'cm', aggregation: 'last', imperialUnit: 'in', imperialConversion: 0.393701 },
33
+ { key: 'left_forearm', displayName: 'Left Forearm', category: 'body', unit: 'cm', aggregation: 'last', imperialUnit: 'in', imperialConversion: 0.393701 },
34
+ { key: 'right_forearm', displayName: 'Right Forearm', category: 'body', unit: 'cm', aggregation: 'last', imperialUnit: 'in', imperialConversion: 0.393701 },
35
+ { key: 'left_thigh', displayName: 'Left Thigh', category: 'body', unit: 'cm', aggregation: 'last', imperialUnit: 'in', imperialConversion: 0.393701 },
36
+ { key: 'right_thigh', displayName: 'Right Thigh', category: 'body', unit: 'cm', aggregation: 'last', imperialUnit: 'in', imperialConversion: 0.393701 },
37
+ { key: 'left_calf', displayName: 'Left Calf', category: 'body', unit: 'cm', aggregation: 'last', imperialUnit: 'in', imperialConversion: 0.393701 },
38
+ { key: 'right_calf', displayName: 'Right Calf', category: 'body', unit: 'cm', aggregation: 'last', imperialUnit: 'in', imperialConversion: 0.393701 },
39
+ ];
40
+
41
+ /**
42
+ * Activity Metrics - measurements related to daily activity and exercise
43
+ */
44
+ export const ACTIVITY_METRICS: MetricDefinition[] = [
45
+ { key: 'steps', displayName: 'Steps', category: 'activity', unit: 'steps', aggregation: 'sum' },
46
+ { key: 'distance', displayName: 'Distance', category: 'activity', unit: 'km', aggregation: 'sum', imperialUnit: 'mi', imperialConversion: 0.621371 },
47
+ { key: 'active_minutes', displayName: 'Active Minutes', category: 'activity', unit: 'min', aggregation: 'sum' },
48
+ { key: 'exercise_minutes', displayName: 'Exercise Minutes', category: 'activity', unit: 'min', aggregation: 'sum' },
49
+ { key: 'exercise_calories', displayName: 'Exercise Calories', category: 'activity', unit: 'kcal', aggregation: 'sum' },
50
+ { key: 'floors_climbed', displayName: 'Floors Climbed', category: 'activity', unit: 'floors', aggregation: 'sum' },
51
+ { key: 'standing_hours', displayName: 'Standing Hours', category: 'activity', unit: 'hours', aggregation: 'sum' },
52
+ { key: 'vo2_max', displayName: 'VO2 Max', category: 'activity', unit: 'ml/kg/min', aggregation: 'last' },
53
+ ];
54
+
55
+ /**
56
+ * Sleep Metrics - measurements related to sleep quality and duration
57
+ */
58
+ export const SLEEP_METRICS: MetricDefinition[] = [
59
+ { key: 'sleep_duration', displayName: 'Sleep Duration', category: 'sleep', unit: 'hours', aggregation: 'last' },
60
+ { key: 'deep_sleep', displayName: 'Deep Sleep', category: 'sleep', unit: 'hours', aggregation: 'last' },
61
+ { key: 'rem_sleep', displayName: 'REM Sleep', category: 'sleep', unit: 'hours', aggregation: 'last' },
62
+ { key: 'light_sleep', displayName: 'Light Sleep', category: 'sleep', unit: 'hours', aggregation: 'last' },
63
+ { key: 'sleep_quality', displayName: 'Sleep Quality', category: 'sleep', unit: 'score', aggregation: 'avg' },
64
+ { key: 'sleep_efficiency', displayName: 'Sleep Efficiency', category: 'sleep', unit: '%', aggregation: 'avg' },
65
+ { key: 'time_to_fall_asleep', displayName: 'Time to Fall Asleep', category: 'sleep', unit: 'min', aggregation: 'avg' },
66
+ { key: 'times_awakened', displayName: 'Times Awakened', category: 'sleep', unit: 'count', aggregation: 'sum' },
67
+ ];
68
+
69
+ /**
70
+ * Vital Metrics - measurements related to vital signs and health indicators
71
+ */
72
+ export const VITAL_METRICS: MetricDefinition[] = [
73
+ { key: 'heart_rate', displayName: 'Heart Rate', category: 'vital', unit: 'bpm', aggregation: 'avg' },
74
+ { key: 'resting_heart_rate', displayName: 'Resting Heart Rate', category: 'vital', unit: 'bpm', aggregation: 'min' },
75
+ { key: 'heart_rate_variability', displayName: 'Heart Rate Variability', category: 'vital', unit: 'ms', aggregation: 'avg' },
76
+ { key: 'blood_pressure_systolic', displayName: 'Blood Pressure (Systolic)', category: 'vital', unit: 'mmHg', aggregation: 'last' },
77
+ { key: 'blood_pressure_diastolic', displayName: 'Blood Pressure (Diastolic)', category: 'vital', unit: 'mmHg', aggregation: 'last' },
78
+ { key: 'blood_glucose', displayName: 'Blood Glucose', category: 'vital', unit: 'mg/dL', aggregation: 'avg' },
79
+ { key: 'blood_oxygen', displayName: 'Blood Oxygen', category: 'vital', unit: '%', aggregation: 'avg' },
80
+ { key: 'respiratory_rate', displayName: 'Respiratory Rate', category: 'vital', unit: 'breaths/min', aggregation: 'avg' },
81
+ { key: 'body_temperature', displayName: 'Body Temperature', category: 'vital', unit: '°C', aggregation: 'avg', imperialUnit: '°F', imperialConversion: 1.8 }, // Note: Fahrenheit conversion requires adding 32, handle separately
82
+ ];
83
+
84
+ /**
85
+ * Wellness Metrics - measurements related to mental health and well-being
86
+ */
87
+ export const WELLNESS_METRICS: MetricDefinition[] = [
88
+ { key: 'mood', displayName: 'Mood', category: 'wellness', unit: 'score', aggregation: 'avg' },
89
+ { key: 'energy_level', displayName: 'Energy Level', category: 'wellness', unit: 'score', aggregation: 'avg' },
90
+ { key: 'stress_level', displayName: 'Stress Level', category: 'wellness', unit: 'score', aggregation: 'avg' },
91
+ ];
92
+
93
+ /**
94
+ * All available metrics
95
+ */
96
+ export const ALL_METRICS: MetricDefinition[] = [
97
+ ...BODY_METRICS,
98
+ ...ACTIVITY_METRICS,
99
+ ...SLEEP_METRICS,
100
+ ...VITAL_METRICS,
101
+ ...WELLNESS_METRICS,
102
+ ];
103
+
104
+ /**
105
+ * Helper function to get a metric definition by key
106
+ */
107
+ export function getMetricDefinition(key: string): MetricDefinition | undefined {
108
+ return ALL_METRICS.find(m => m.key === key);
109
+ }
110
+
111
+ /**
112
+ * Helper function to get metrics by category
113
+ */
114
+ export function getMetricsByCategory(category: MetricDefinition['category']): MetricDefinition[] {
115
+ return ALL_METRICS.filter(m => m.category === category);
116
+ }