@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/README.md ADDED
@@ -0,0 +1,824 @@
1
+ # OpenLifeLog TypeScript SDK
2
+
3
+ > A comprehensive, type-safe TypeScript SDK for the OpenLifeLog API - the complete fitness and nutrition tracking platform.
4
+
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.3+-blue.svg)](https://www.typescriptlang.org/)
6
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](./LICENSE)
7
+
8
+ ## Features
9
+
10
+ - **🎯 Type-Safe** - Full TypeScript support with comprehensive type definitions
11
+ - **🔄 Auto-Pagination** - Automatic pagination for large data sets
12
+ - **📏 Unit Conversion** - Automatic conversion between metric and imperial units
13
+ - **🔁 Auto-Retry** - Automatic retries with exponential backoff for failed requests
14
+ - **❌ Error Handling** - Rich error classes with detailed error information
15
+ - **🚀 Modern API** - Intuitive, resource-based API design
16
+ - **📦 Tree-Shakeable** - Modular design supports tree-shaking
17
+ - **⚡ Fast** - Optimized for performance with HTTP/2 support
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npm install @openlifelog/sdk
23
+ ```
24
+
25
+ ```bash
26
+ yarn add @openlifelog/sdk
27
+ ```
28
+
29
+ ```bash
30
+ pnpm add @openlifelog/sdk
31
+ ```
32
+
33
+ ## Quick Start
34
+
35
+ ### Option 1: With JWT Token (Recommended for apps with existing auth)
36
+
37
+ If your app already handles authentication and you have a JWT token:
38
+
39
+ ```typescript
40
+ import { OpenLifeLog } from '@openlifelog/sdk';
41
+
42
+ // Initialize with JWT token
43
+ const client = new OpenLifeLog({
44
+ apiKey: 'your-jwt-token-here',
45
+ baseUrl: 'http://localhost:8080',
46
+ measurementSystem: 'metric', // or 'imperial'
47
+ });
48
+
49
+ // Ready to use immediately - no login needed!
50
+ const user = await client.users.me();
51
+ await client.logFood({ ... });
52
+ ```
53
+
54
+ ### Option 2: With SDK Authentication
55
+
56
+ If you want the SDK to handle authentication:
57
+
58
+ ```typescript
59
+ import { OpenLifeLog } from '@openlifelog/sdk';
60
+
61
+ // Initialize without token
62
+ const client = new OpenLifeLog({
63
+ baseUrl: 'http://localhost:8080',
64
+ measurementSystem: 'metric',
65
+ });
66
+
67
+ // Login to get token
68
+ const { token, user } = await client.auth.login({
69
+ email: 'john@example.com',
70
+ password: 'securePassword123',
71
+ });
72
+
73
+ // Token is automatically set, ready to use!
74
+ await client.logFood({ ... });
75
+ ```
76
+
77
+ ### Option 3: Set Token Later
78
+
79
+ ```typescript
80
+ import { OpenLifeLog } from '@openlifelog/sdk';
81
+
82
+ // Initialize without token
83
+ const client = new OpenLifeLog({
84
+ baseUrl: 'http://localhost:8080',
85
+ });
86
+
87
+ // Set token later (e.g., after your own auth flow)
88
+ client.setApiKey('jwt-token-from-your-auth-system');
89
+
90
+ // Now ready to use
91
+ const user = await client.users.me();
92
+ ```
93
+
94
+ ## Core Concepts
95
+
96
+ ### Authentication
97
+
98
+ #### Using External Authentication
99
+
100
+ If your application already has an authentication system (e.g., Auth0, Clerk, Firebase Auth, custom JWT), you can simply pass the JWT token when initializing the SDK:
101
+
102
+ ```typescript
103
+ // Example: Using with Auth0
104
+ import { useAuth0 } from '@auth0/auth0-react';
105
+ import { OpenLifeLog } from '@openlifelog/sdk';
106
+
107
+ function MyComponent() {
108
+ const { getAccessTokenSilently } = useAuth0();
109
+
110
+ const getClient = async () => {
111
+ const token = await getAccessTokenSilently();
112
+ return new OpenLifeLog({
113
+ apiKey: token,
114
+ baseUrl: process.env.NEXT_PUBLIC_API_URL,
115
+ });
116
+ };
117
+
118
+ // Use the client...
119
+ }
120
+ ```
121
+
122
+ ```typescript
123
+ // Example: Using with Next.js session
124
+ import { getSession } from 'next-auth/react';
125
+ import { OpenLifeLog } from '@openlifelog/sdk';
126
+
127
+ export async function getServerSideProps(context) {
128
+ const session = await getSession(context);
129
+
130
+ const client = new OpenLifeLog({
131
+ apiKey: session.accessToken,
132
+ baseUrl: process.env.API_URL,
133
+ });
134
+
135
+ const user = await client.users.me();
136
+ // ...
137
+ }
138
+ ```
139
+
140
+ #### Using SDK Authentication
141
+
142
+ If you want the SDK to handle authentication directly:
143
+
144
+ ```typescript
145
+ // Sign up a new user
146
+ const { token, user } = await client.auth.signup({
147
+ name: 'John Doe',
148
+ email: 'john@example.com',
149
+ password: 'securePassword123',
150
+ });
151
+
152
+ // Login
153
+ const { token, user } = await client.auth.login({
154
+ email: 'john@example.com',
155
+ password: 'securePassword123',
156
+ });
157
+
158
+ // The token is automatically set after login/signup
159
+
160
+ // Request password reset
161
+ await client.auth.requestPasswordReset({
162
+ email: 'john@example.com',
163
+ });
164
+
165
+ // Logout (clears token)
166
+ client.auth.logout();
167
+ ```
168
+
169
+ #### Dynamic Token Updates
170
+
171
+ You can update the token at any time, useful for token refresh scenarios:
172
+
173
+ ```typescript
174
+ // Initial setup
175
+ const client = new OpenLifeLog({
176
+ apiKey: 'initial-token',
177
+ baseUrl: 'http://localhost:8080',
178
+ });
179
+
180
+ // Later, when token refreshes
181
+ client.setApiKey('new-refreshed-token');
182
+
183
+ // Continue using the client with new token
184
+ await client.users.me();
185
+ ```
186
+
187
+ ### User Management
188
+
189
+ ```typescript
190
+ // Get current user
191
+ const user = await client.users.me();
192
+
193
+ // Update profile
194
+ await client.users.update({
195
+ name: 'Jane Doe',
196
+ isOnboarded: true,
197
+ });
198
+
199
+ // Get preferences
200
+ const prefs = await client.users.getPreferences();
201
+ console.log(prefs.measurementSystem); // 'metric' or 'imperial'
202
+
203
+ // Update preferences
204
+ await client.users.updatePreferences({
205
+ measurementSystem: 'imperial',
206
+ });
207
+ ```
208
+
209
+ ### Food & Nutrition Tracking
210
+
211
+ #### Search and Browse Foods
212
+
213
+ ```typescript
214
+ // Search for foods
215
+ const { data: foods } = await client.foods.search({
216
+ q: 'chicken breast',
217
+ limit: 10,
218
+ });
219
+
220
+ // List all foods with pagination
221
+ const { data, pageInfo } = await client.foods.list({
222
+ limit: 20,
223
+ search: 'protein',
224
+ });
225
+
226
+ // Get specific food details
227
+ const food = await client.foods.get('food-id');
228
+ console.log(food.nutrients.protein); // Protein in grams
229
+
230
+ // Add to favorites
231
+ await client.foods.addToFavorites(food.id);
232
+ ```
233
+
234
+ #### Log Food
235
+
236
+ ```typescript
237
+ // Simple way - using the convenience method
238
+ const log = await client.logFood({
239
+ foodId: 'food-uuid',
240
+ name: 'Chicken Breast',
241
+ servingSizeName: 'breast (200g)',
242
+ quantity: 1.5,
243
+ unitGrams: 200,
244
+ mealType: 'lunch',
245
+ notes: 'Grilled with olive oil',
246
+ });
247
+
248
+ // Or use the resource directly
249
+ const log = await client.foodLogs.create({
250
+ foodId: 'food-uuid',
251
+ name: 'Chicken Breast',
252
+ servingSizeName: 'breast (200g)',
253
+ quantity: 1.5,
254
+ unitGrams: 200,
255
+ mealType: 'lunch',
256
+ });
257
+ ```
258
+
259
+ #### Get Nutrition Summary
260
+
261
+ ```typescript
262
+ // Get today's nutrition
263
+ const summary = await client.getTodayNutrition();
264
+ console.log(`Calories: ${summary.totalCalories}`);
265
+ console.log(`Protein: ${summary.totalProtein}g`);
266
+
267
+ // Get specific date
268
+ const summary = await client.foodLogs.getDailySummary({
269
+ date: '2024-01-15',
270
+ });
271
+
272
+ // Filter by meal type
273
+ const lunchSummary = await client.foodLogs.getDailySummary({
274
+ date: '2024-01-15',
275
+ mealType: 'lunch',
276
+ });
277
+ ```
278
+
279
+ ### Exercise & Workout Management
280
+
281
+ #### Exercises
282
+
283
+ ```typescript
284
+ // List exercises
285
+ const { data: exercises } = await client.exercises.list({
286
+ category: 'strength',
287
+ muscleGroup: 'chest',
288
+ });
289
+
290
+ // Search exercises
291
+ const { data } = await client.exercises.search({
292
+ q: 'bench press',
293
+ });
294
+
295
+ // Create custom exercise
296
+ const exercise = await client.exercises.create({
297
+ name: 'Custom Push-up Variation',
298
+ category: 'strength',
299
+ primaryMuscles: ['chest', 'triceps'],
300
+ secondaryMuscles: ['front_delts'],
301
+ equipment: ['bodyweight'],
302
+ capabilities: {
303
+ supportsWeight: false,
304
+ supportsBodyweightOnly: true,
305
+ supportsAssistance: false,
306
+ supportsDistance: false,
307
+ supportsDuration: true,
308
+ supportsTempo: true,
309
+ },
310
+ });
311
+ ```
312
+
313
+ #### Workout Templates
314
+
315
+ ```typescript
316
+ // Create a workout template
317
+ const workout = await client.workouts.create({
318
+ name: 'Upper Body A',
319
+ description: 'Push-focused upper body workout',
320
+ type: 'strength',
321
+ });
322
+
323
+ // Add exercises to workout
324
+ await client.workouts.addExercises(workout.id, {
325
+ exercises: [
326
+ {
327
+ exerciseId: 'bench-press-id',
328
+ orderIndex: 1,
329
+ workoutFormat: 'straight_sets',
330
+ setsData: [
331
+ { order: 1, reps: 5, weight: 100, restSeconds: 180 },
332
+ { order: 2, reps: 5, weight: 100, restSeconds: 180 },
333
+ { order: 3, reps: 5, weight: 100, restSeconds: 180 },
334
+ ],
335
+ },
336
+ ],
337
+ });
338
+
339
+ // Clone a workout
340
+ const clonedWorkout = await client.workouts.clone(workout.id);
341
+ ```
342
+
343
+ #### Workout Sessions (Performance Tracking)
344
+
345
+ ```typescript
346
+ // Start a workout session
347
+ const session = await client.sessions.start({
348
+ workoutId: 'workout-id',
349
+ userBodyweight: 82.5, // Will be converted based on user preference
350
+ notes: 'Feeling strong today',
351
+ mood: 'energetic',
352
+ });
353
+
354
+ // Update session during workout
355
+ await client.sessions.update(session.id, {
356
+ notes: 'Added extra set to bench press',
357
+ });
358
+
359
+ // Complete the session
360
+ await client.sessions.complete(session.id, 'Great workout!');
361
+
362
+ // Get session details
363
+ const completedSession = await client.sessions.get(session.id);
364
+
365
+ // Get exercise history
366
+ const history = await client.sessions.getExerciseHistory('exercise-id');
367
+
368
+ // Get personal records
369
+ const prs = await client.sessions.getPersonalRecords();
370
+ ```
371
+
372
+ ### Training Programs
373
+
374
+ ```typescript
375
+ // List program templates
376
+ const { data: templates } = await client.programs.listTemplates();
377
+
378
+ // Create a program
379
+ const program = await client.programs.create({
380
+ name: '5/3/1 Strength Program',
381
+ description: "Wendler's 5/3/1 for main lifts",
382
+ schedule: [
383
+ {
384
+ week: 1,
385
+ day: 1,
386
+ name: 'Squat Day',
387
+ exercises: [
388
+ {
389
+ exerciseId: 'squat-id',
390
+ orderIndex: 1,
391
+ sets: [
392
+ { order: 1, reps: 5, weight: 100 },
393
+ { order: 2, reps: 5, weight: 110 },
394
+ { order: 3, reps: 5, weight: 120 },
395
+ ],
396
+ },
397
+ ],
398
+ },
399
+ ],
400
+ metadata: {
401
+ difficulty: 'intermediate',
402
+ type: 'strength',
403
+ },
404
+ });
405
+
406
+ // Enroll in program
407
+ const enrollment = await client.programs.enroll(program.id, {
408
+ startDate: '2024-01-15',
409
+ });
410
+
411
+ // Get current week schedule
412
+ const currentWeek = await client.programs.getCurrentWeek(enrollment.id);
413
+
414
+ // Get enrollment progress
415
+ const progress = await client.programs.getEnrollmentProgress(enrollment.id);
416
+ console.log(`${progress.completionPercentage}% complete`);
417
+ ```
418
+
419
+ ### Metrics & Goals
420
+
421
+ #### Track Metrics
422
+
423
+ ```typescript
424
+ // List available metrics
425
+ const { data: metrics } = await client.metrics.list();
426
+
427
+ // Log a metric (e.g., body weight)
428
+ await client.metrics.log({
429
+ metricKey: 'body_weight',
430
+ value: 82.5,
431
+ metadata: {
432
+ time: 'morning',
433
+ conditions: 'fasted',
434
+ },
435
+ loggedAt: new Date().toISOString(),
436
+ });
437
+
438
+ // Get daily metric aggregate
439
+ const dailyWeight = await client.metrics.getDailyMetric('body_weight', {
440
+ date: '2024-01-15',
441
+ });
442
+ ```
443
+
444
+ #### Set Goals
445
+
446
+ ```typescript
447
+ // Create a goal
448
+ await client.goals.create({
449
+ metricKey: 'body_weight',
450
+ targetValue: 75.0,
451
+ targetType: 'exact',
452
+ effectiveFrom: '2024-01-15',
453
+ });
454
+
455
+ // Get today's progress
456
+ const progress = await client.getTodayProgress();
457
+ console.log(progress.goals);
458
+
459
+ // Set nutrition goals
460
+ await client.goals.createNutritionGoals({
461
+ effectiveDate: '2024-01-15',
462
+ goals: {
463
+ calories: { type: 'exact', value: 2200 },
464
+ protein: { type: 'min', value: 180 },
465
+ carbs: { type: 'range', min: 200, max: 300 },
466
+ fat: { type: 'range', min: 60, max: 80 },
467
+ },
468
+ });
469
+
470
+ // Get nutrition progress
471
+ const nutritionProgress = await client.getTodayNutritionProgress();
472
+ ```
473
+
474
+ ### AI Insights
475
+
476
+ ```typescript
477
+ // Get AI-powered nutrition insights
478
+ const insights = await client.ai.getFoodInsights({
479
+ startDate: '2024-01-01',
480
+ endDate: '2024-01-15',
481
+ });
482
+
483
+ console.log('Insights:', insights.insights);
484
+ console.log('Recommendations:', insights.recommendations);
485
+ ```
486
+
487
+ ## Advanced Features
488
+
489
+ ### Auto-Pagination
490
+
491
+ ```typescript
492
+ // Iterate through all foods automatically
493
+ for await (const food of client.foods.listAutoPaginate({ limit: 100 })) {
494
+ console.log(food.name);
495
+ }
496
+
497
+ // Or get all pages at once (use with caution for large datasets)
498
+ const allFoods = await client.foods.listAutoPaginate().toArray();
499
+ ```
500
+
501
+ ### Unit Conversion
502
+
503
+ The SDK automatically handles unit conversion based on your measurement system preference:
504
+
505
+ ```typescript
506
+ // Set measurement system
507
+ client.setMeasurementSystem('imperial');
508
+
509
+ // Get unit converter
510
+ const converter = client.getUnitConverter();
511
+
512
+ // Manual conversion
513
+ const kgValue = converter.weightToMetric(150); // Convert 150 lbs to kg
514
+ const lbsValue = converter.weightFromMetric(68); // Convert 68 kg to lbs
515
+
516
+ // Get unit labels
517
+ console.log(converter.getWeightUnit()); // 'lbs' or 'kg'
518
+ console.log(converter.getDistanceUnit()); // 'mi' or 'm'
519
+ ```
520
+
521
+ ### Error Handling
522
+
523
+ ```typescript
524
+ import {
525
+ OpenLifeLogError,
526
+ AuthenticationError,
527
+ ValidationError,
528
+ NotFoundError,
529
+ } from '@openlifelog/sdk';
530
+
531
+ try {
532
+ await client.foods.get('invalid-id');
533
+ } catch (error) {
534
+ if (error instanceof NotFoundError) {
535
+ console.error('Food not found');
536
+ } else if (error instanceof AuthenticationError) {
537
+ console.error('Please login first');
538
+ } else if (error instanceof ValidationError) {
539
+ console.error('Validation errors:', error.validationErrors);
540
+ } else if (error instanceof OpenLifeLogError) {
541
+ console.error('API error:', error.message, error.statusCode);
542
+ }
543
+ }
544
+ ```
545
+
546
+ ### Configuration Options
547
+
548
+ ```typescript
549
+ const client = new OpenLifeLog({
550
+ // Required: API base URL
551
+ baseUrl: 'http://localhost:8080',
552
+
553
+ // Optional: JWT token - Use this if you already have a token!
554
+ // This is the recommended approach for apps with existing authentication
555
+ apiKey: 'your-jwt-token',
556
+
557
+ // Optional: Measurement system preference
558
+ measurementSystem: 'metric', // or 'imperial'
559
+
560
+ // Optional: Automatic unit conversion
561
+ autoConvertUnits: true,
562
+
563
+ // Optional: Request timeout (ms)
564
+ timeout: 30000,
565
+
566
+ // Optional: Max retry attempts
567
+ maxRetries: 3,
568
+
569
+ // Optional: Enable retries
570
+ enableRetries: true,
571
+
572
+ // Optional: Custom headers
573
+ headers: {
574
+ 'X-Custom-Header': 'value',
575
+ },
576
+
577
+ // Optional: Enable debug logging
578
+ debug: false,
579
+
580
+ // Optional: Custom fetch implementation
581
+ fetch: customFetch,
582
+ });
583
+ ```
584
+
585
+ ## Complete Example: Tracking a Full Day
586
+
587
+ ```typescript
588
+ import { OpenLifeLog } from '@openlifelog/sdk';
589
+
590
+ const client = new OpenLifeLog({
591
+ baseUrl: 'http://localhost:8080',
592
+ });
593
+
594
+ // Login
595
+ await client.auth.login({
596
+ email: 'athlete@example.com',
597
+ password: 'securePassword123',
598
+ });
599
+
600
+ // --- Morning: Log body weight ---
601
+ await client.metrics.log({
602
+ metricKey: 'body_weight',
603
+ value: 82.3,
604
+ metadata: { time: 'morning', conditions: 'fasted' },
605
+ });
606
+
607
+ // --- Breakfast: Log food ---
608
+ await client.logFood({
609
+ foodId: 'oatmeal-id',
610
+ name: 'Oatmeal with Berries',
611
+ quantity: 1,
612
+ unitGrams: 250,
613
+ mealType: 'breakfast',
614
+ });
615
+
616
+ // --- Workout: Track training session ---
617
+ const session = await client.sessions.start({
618
+ workoutId: 'upper-body-workout-id',
619
+ userBodyweight: 82.3,
620
+ mood: 'energetic',
621
+ });
622
+
623
+ // ... perform workout ...
624
+
625
+ await client.sessions.complete(session.id, 'Hit new PR on bench press!');
626
+
627
+ // --- Post-workout: Log protein shake ---
628
+ await client.logFood({
629
+ foodId: 'protein-shake-id',
630
+ name: 'Protein Shake',
631
+ quantity: 1,
632
+ unitGrams: 300,
633
+ mealType: 'snack',
634
+ notes: 'Post-workout',
635
+ });
636
+
637
+ // --- Evening: Check progress ---
638
+ const nutritionSummary = await client.getTodayNutrition();
639
+ console.log('Daily totals:', {
640
+ calories: nutritionSummary.totalCalories,
641
+ protein: nutritionSummary.totalProtein,
642
+ carbs: nutritionSummary.totalCarbs,
643
+ fat: nutritionSummary.totalFat,
644
+ });
645
+
646
+ const goalProgress = await client.getTodayNutritionProgress();
647
+ console.log('Goal progress:', goalProgress.progress);
648
+
649
+ // Get personal records
650
+ const prs = await client.sessions.getPersonalRecords();
651
+ console.log('Personal Records:', prs);
652
+ ```
653
+
654
+ ## TypeScript Support
655
+
656
+ The SDK is written in TypeScript and provides comprehensive type definitions:
657
+
658
+ ```typescript
659
+ import type {
660
+ User,
661
+ Food,
662
+ FoodLog,
663
+ Exercise,
664
+ Workout,
665
+ WorkoutSession,
666
+ NutritionGoal,
667
+ MetricEvent,
668
+ } from '@openlifelog/sdk';
669
+
670
+ // All API responses are fully typed
671
+ const user: User = await client.users.me();
672
+ const food: Food = await client.foods.get('food-id');
673
+ const log: FoodLog = await client.logFood({...});
674
+ ```
675
+
676
+ ## Best Practices
677
+
678
+ ### 1. **Error Handling**
679
+
680
+ Always wrap API calls in try-catch blocks:
681
+
682
+ ```typescript
683
+ try {
684
+ const user = await client.users.me();
685
+ } catch (error) {
686
+ if (error instanceof AuthenticationError) {
687
+ // Redirect to login
688
+ }
689
+ }
690
+ ```
691
+
692
+ ### 2. **Pagination**
693
+
694
+ Use auto-pagination for large datasets:
695
+
696
+ ```typescript
697
+ // Good: Memory efficient
698
+ for await (const food of client.foods.listAutoPaginate()) {
699
+ processFood(food);
700
+ }
701
+
702
+ // Avoid: Loads all data at once
703
+ const allFoods = await client.foods.listAutoPaginate().toArray();
704
+ ```
705
+
706
+ ### 3. **Unit Conversion**
707
+
708
+ Let the SDK handle unit conversion automatically:
709
+
710
+ ```typescript
711
+ // Set preference once
712
+ client.setMeasurementSystem('imperial');
713
+
714
+ // SDK handles conversion automatically
715
+ const session = await client.sessions.create({
716
+ userBodyweight: 180, // SDK knows this is lbs and converts to kg for API
717
+ });
718
+ ```
719
+
720
+ ### 4. **Reuse Client Instance**
721
+
722
+ Create one client instance and reuse it:
723
+
724
+ ```typescript
725
+ // Good: Single instance
726
+ const client = new OpenLifeLog(config);
727
+ export default client;
728
+
729
+ // Avoid: Creating multiple instances
730
+ function getUser() {
731
+ const client = new OpenLifeLog(config); // Don't do this
732
+ return client.users.me();
733
+ }
734
+ ```
735
+
736
+ ## Framework Integration
737
+
738
+ ### Next.js (App Router)
739
+
740
+ ```typescript
741
+ // lib/openlifelog.ts
742
+ import { OpenLifeLog } from '@openlifelog/sdk';
743
+
744
+ export const getClient = (token?: string) => {
745
+ return new OpenLifeLog({
746
+ baseUrl: process.env.NEXT_PUBLIC_API_URL!,
747
+ apiKey: token,
748
+ });
749
+ };
750
+
751
+ // app/actions.ts
752
+ 'use server';
753
+
754
+ import { cookies } from 'next/headers';
755
+ import { getClient } from '@/lib/openlifelog';
756
+
757
+ export async function getUserProfile() {
758
+ const token = cookies().get('token')?.value;
759
+ const client = getClient(token);
760
+ return await client.users.me();
761
+ }
762
+ ```
763
+
764
+ ### React Hook
765
+
766
+ ```typescript
767
+ // hooks/useOpenLifeLog.ts
768
+ import { useMemo } from 'react';
769
+ import { OpenLifeLog } from '@openlifelog/sdk';
770
+ import { useAuth } from './useAuth';
771
+
772
+ export function useOpenLifeLog() {
773
+ const { token } = useAuth();
774
+
775
+ return useMemo(
776
+ () =>
777
+ new OpenLifeLog({
778
+ baseUrl: process.env.NEXT_PUBLIC_API_URL,
779
+ apiKey: token,
780
+ }),
781
+ [token]
782
+ );
783
+ }
784
+
785
+ // In your component
786
+ function MyComponent() {
787
+ const client = useOpenLifeLog();
788
+
789
+ const fetchData = async () => {
790
+ const user = await client.users.me();
791
+ // ...
792
+ };
793
+
794
+ // ...
795
+ }
796
+ ```
797
+
798
+ ## API Reference
799
+
800
+ Full API documentation is available at:
801
+ - [API Documentation](https://docs.openlifelog.com)
802
+ - [TypeScript Types](./types)
803
+
804
+ ## Contributing
805
+
806
+ Contributions are welcome! Please see [CONTRIBUTING.md](../../CONTRIBUTING.md) for details.
807
+
808
+ ## License
809
+
810
+ MIT License - see [LICENSE](../../LICENSE) for details.
811
+
812
+ ## Support
813
+
814
+ - 📧 Email: support@openlifelog.com
815
+ - 🐛 Issues: [GitHub Issues](https://github.com/kukicado/openlifelog/issues)
816
+ - 📖 Documentation: [docs.openlifelog.com](https://docs.openlifelog.com)
817
+
818
+ ## Changelog
819
+
820
+ See [CHANGELOG.md](./CHANGELOG.md) for version history and updates.
821
+
822
+ ---
823
+
824
+ Made with ❤️ by the OpenLifeLog team