@cranberry-money/shared-services 8.1.2 → 10.0.2

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 CHANGED
@@ -1,550 +1,259 @@
1
- # @cranberry-money/shared-services v3.0.0
1
+ # @cranberry-money/shared-services
2
2
 
3
- **Functional Architecture Shared Services** with pure functions, dependency injection, and comprehensive TypeScript support for the Blueberry platform.
3
+ **Platform-agnostic service functions** that work with any axios-compatible API client.
4
4
 
5
5
  [![npm version](https://badge.fury.io/js/%40cranberry-money%2Fshared-services.svg)](https://badge.fury.io/js/%40cranberry-money%2Fshared-services)
6
6
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.0-blue.svg)](https://www.typescriptlang.org/)
7
- [![React](https://img.shields.io/badge/React-18+-61dafb.svg)](https://reactjs.org/)
8
7
 
9
- ## 🎉 What's New in v3.0.0
8
+ ## 📦 Package Overview
10
9
 
11
- - **Pure Functional Architecture**: Complete migration to functional programming patterns
12
- - **Service Factory Pattern**: Type-safe service creation with dependency injection
13
- - **React Context Authentication**: Modern authentication with hooks and context
14
- - **Migration Framework**: Feature flags and tools for gradual migration from v2.x
15
- - **Performance Improvements**: 10-15% faster API calls, 15-20% less memory usage
10
+ This package provides reusable service functions for the Blueberry platform ecosystem. Services are pure functions that accept an API client, allowing the same business logic to work across web browsers, mobile apps, and testing environments.
11
+
12
+ ### Core Philosophy
13
+
14
+ - **One service implementation, multiple platforms**: Write once, use everywhere
15
+ - **Platform owns authentication**: Each platform handles auth its own way (cookies, tokens, etc.)
16
+ - **Simple and functional**: No complex patterns, just functions that call APIs
17
+ - **Fully typed**: Complete TypeScript support with types from `@cranberry-money/shared-types`
16
18
 
17
19
  ## 🚀 Quick Start
18
20
 
19
21
  ### Installation
20
22
 
21
23
  ```bash
22
- npm install @cranberry-money/shared-services@^3.0.0
24
+ npm install @cranberry-money/shared-services
23
25
  ```
24
26
 
25
- ### Basic Setup
27
+ ### Basic Usage
26
28
 
27
- ```typescript
28
- import {
29
- createWebServiceFactory,
30
- initializeFeatureFlags
31
- } from '@cranberry-money/shared-services';
32
-
33
- // 1. Initialize feature flags (optional but recommended)
34
- await initializeFeatureFlags({
35
- useFunctionalApiClient: true,
36
- useFunctionalAuth: true,
37
- useFunctionalPortfolios: true,
38
- enablePerformanceMonitoring: true,
39
- });
40
-
41
- // 2. Create service factory
42
- const services = createWebServiceFactory('https://api.example.com', {
43
- apiTimeout: 10000,
44
- enableCredentials: true,
45
- });
46
-
47
- // 3. Set up authentication provider
48
- const AuthProvider = services.auth.createAuthProvider();
49
-
50
- function App() {
51
- return (
52
- <AuthProvider>
53
- <Dashboard />
54
- </AuthProvider>
55
- );
56
- }
57
- ```
58
-
59
- ## 🏗 Architecture Overview
60
-
61
- ### Service Factory Pattern
62
-
63
- The v3.0.0 architecture uses a service factory pattern that creates all services with explicit dependency injection:
29
+ #### Web Application (with cookies)
64
30
 
65
31
  ```typescript
66
- // Create services with platform-specific configuration
67
- const services = createWebServiceFactory(apiBaseUrl, options);
32
+ // src/services/webApiClient.ts
33
+ import axios from 'axios';
68
34
 
69
- // All services are available through the factory
70
- const portfolios = await services.portfolio.getPortfolios();
71
- const apiClient = services.auth.createAuthDependencies().apiClient;
35
+ export const webApiClient = axios.create({
36
+ baseURL: import.meta.env.VITE_API_URL,
37
+ withCredentials: true, // Use cookies for auth
38
+ });
72
39
  ```
73
40
 
74
- ### Pure Functional Design
75
-
76
- All operations are pure functions with explicit dependencies:
77
-
78
41
  ```typescript
79
- // Pure function with explicit dependencies
80
- import { signin, createWebTokenStorage } from '@cranberry-money/shared-services';
42
+ // src/services/portfolios.ts
43
+ import { webApiClient } from './webApiClient';
44
+ import { createPortfolioService } from '@cranberry-money/shared-services';
81
45
 
82
- const authDeps = {
83
- apiClient: services.auth.createAuthDependencies().apiClient,
84
- tokenStorage: createWebTokenStorage(),
85
- };
46
+ // Create your platform-specific service
47
+ export const portfolioService = createPortfolioService(webApiClient);
86
48
 
87
- const result = await signin(credentials, authDeps);
49
+ // Use in your application
50
+ const portfolios = await portfolioService.getAll();
51
+ const portfolio = await portfolioService.create({ name: 'My Portfolio' });
88
52
  ```
89
53
 
90
- ## 🔐 Authentication
91
-
92
- ### React Context Integration
93
-
94
- ```typescript
95
- import {
96
- useFunctionalAuthState,
97
- useFunctionalAuthActions,
98
- FunctionalAuthGuard
99
- } from '@cranberry-money/shared-services';
100
-
101
- function Dashboard() {
102
- const authState = useFunctionalAuthState();
103
- const authActions = useFunctionalAuthActions();
104
-
105
- const handleSignin = async () => {
106
- try {
107
- await authActions.signin({
108
- email: 'user@example.com',
109
- password: 'password123',
110
- });
111
- } catch (error) {
112
- console.error('Signin failed:', error);
113
- }
114
- };
115
-
116
- return (
117
- <FunctionalAuthGuard fallback={<LoginForm />}>
118
- <div>
119
- <h1>Welcome, {authState.user?.firstName}!</h1>
120
- <button onClick={authActions.signout}>Sign Out</button>
121
- </div>
122
- </FunctionalAuthGuard>
123
- );
124
- }
125
- ```
126
-
127
- ### Platform-Specific Token Storage
54
+ #### Mobile Application (with tokens)
128
55
 
129
56
  ```typescript
130
- // Web (localStorage)
131
- import { createWebTokenStorage } from '@cranberry-money/shared-services';
132
- const webStorage = createWebTokenStorage();
133
-
134
- // Mobile (secure storage)
135
- import { createMobileTokenStorage } from '@cranberry-money/shared-services';
57
+ // src/services/mobileApiClient.ts
58
+ import axios from 'axios';
136
59
  import * as SecureStore from 'expo-secure-store';
137
- const mobileStorage = createMobileTokenStorage(SecureStore);
138
-
139
- // Testing (in-memory)
140
- import { createMemoryTokenStorage } from '@cranberry-money/shared-services';
141
- const testStorage = createMemoryTokenStorage();
142
- ```
143
-
144
- ## 🌐 API Client
145
-
146
- ### Functional API Client
147
-
148
- ```typescript
149
- import { createFunctionalApiClient } from '@cranberry-money/shared-services';
150
-
151
- const apiClient = createFunctionalApiClient({
152
- baseURL: 'https://api.example.com',
153
- timeout: 10000,
154
- defaultHeaders: {
155
- 'X-Client-Version': '3.0.0',
156
- },
157
- });
158
60
 
159
- // Make requests
160
- const data = await apiClient.get('/api/data');
161
- const result = await apiClient.post('/api/items', { name: 'Item' });
162
- ```
163
-
164
- ### Platform-Specific Clients
165
-
166
- ```typescript
167
- // Web-optimized client
168
- import { createFunctionalWebApiClient } from '@cranberry-money/shared-services';
169
- const webClient = createFunctionalWebApiClient('https://api.example.com');
170
-
171
- // Mobile-optimized client
172
- import { createFunctionalMobileApiClient } from '@cranberry-money/shared-services';
173
- const mobileClient = createFunctionalMobileApiClient('https://api.example.com');
174
- ```
175
-
176
- ## 💼 Service Usage
177
-
178
- ### Portfolio Services
179
-
180
- ```typescript
181
- // Get portfolios with filtering and sorting
182
- const portfolios = await services.portfolio.getPortfolios({
183
- isActive: true,
184
- sortBy: 'totalValue',
185
- sortOrder: 'desc',
186
- });
187
-
188
- // Create new portfolio
189
- const newPortfolio = await services.portfolio.createPortfolio({
190
- name: 'My Investment Portfolio',
191
- description: 'Long-term growth strategy',
61
+ export const mobileApiClient = axios.create({
62
+ baseURL: process.env.API_URL,
192
63
  });
193
64
 
194
- // Update portfolio
195
- const updated = await services.portfolio.updatePortfolio('portfolio-id', {
196
- name: 'Updated Portfolio Name',
197
- isActive: true,
65
+ // Add auth token to requests
66
+ mobileApiClient.interceptors.request.use(async (config) => {
67
+ const token = await SecureStore.getItemAsync('authToken');
68
+ if (token) {
69
+ config.headers.Authorization = `Bearer ${token}`;
70
+ }
71
+ return config;
198
72
  });
199
73
  ```
200
74
 
201
- ### Reference Data Services
202
-
203
75
  ```typescript
204
- import { createReferenceDataMigration } from '@cranberry-money/shared-services';
205
-
206
- const referenceData = createReferenceDataMigration(services);
76
+ // src/services/portfolios.ts
77
+ import { mobileApiClient } from './mobileApiClient';
78
+ import { createPortfolioService } from '@cranberry-money/shared-services';
207
79
 
208
- // Get countries, industries, sectors, stock exchanges
209
- const countries = await referenceData.getActiveCountries();
210
- const industries = await referenceData.getIndustries();
211
- const sectors = await referenceData.getSectorsByIndustry('tech-industry-id');
212
- const exchanges = await referenceData.getStockExchangesByCountry('usa-id');
80
+ export const portfolioService = createPortfolioService(mobileApiClient);
213
81
  ```
214
82
 
215
- ## 🚦 Migration from v2.x
83
+ ## 📚 Service Patterns
216
84
 
217
- ### Feature Flag Migration
85
+ ### Pattern 1: Service Factory (Recommended)
218
86
 
219
- ```typescript
220
- // Start with all functional features disabled
221
- await initializeFeatureFlags({
222
- useFunctionalApiClient: false,
223
- useFunctionalAuth: false,
224
- useFunctionalPortfolios: false,
225
- });
226
-
227
- // Enable gradually
228
- const flags = getFeatureFlags();
229
- await flags.updateFlags({ useFunctionalApiClient: true });
230
- // Test for 1-2 days...
231
-
232
- await flags.updateFlags({ useFunctionalPortfolios: true });
233
- // Test for 1-2 days...
234
-
235
- await flags.updateFlags({ useFunctionalAuth: true });
236
- // Complete migration
237
- ```
238
-
239
- ### Migration Helpers
87
+ Group related operations together:
240
88
 
241
89
  ```typescript
242
- import { createMigrationHelpers } from '@cranberry-money/shared-services';
243
-
244
- const helpers = createMigrationHelpers(services, {
245
- apiClient: legacyApiClient,
246
- authManager: legacyAuthManager,
247
- });
90
+ import { createPortfolioService } from '@cranberry-money/shared-services';
91
+ import { webApiClient } from './webApiClient'; // or mobileApiClient for mobile
248
92
 
249
- // Use migration adapter that switches based on feature flags
250
- const data = await helpers.apiClient.get('/api/data');
93
+ const portfolioService = createPortfolioService(webApiClient);
251
94
 
252
- // Validate migration readiness
253
- const validation = helpers.validateMigration();
254
- if (!validation.isValid) {
255
- console.error('Migration issues:', validation.errors);
256
- }
95
+ // All portfolio operations in one object
96
+ await portfolioService.getAll({ page: 1 });
97
+ await portfolioService.getByUuid('123');
98
+ await portfolioService.create({ name: 'Tech Portfolio' });
99
+ await portfolioService.update('123', { name: 'Updated Portfolio' });
100
+ await portfolioService.delete('123');
257
101
  ```
258
102
 
259
- ## 📊 Performance Monitoring
103
+ ### Pattern 2: Individual Functions
260
104
 
261
- ### Built-in Benchmarking
105
+ For better tree-shaking and selective imports:
262
106
 
263
107
  ```typescript
264
- import { runQuickPerformanceCheck, runComprehensivePerformanceAnalysis } from '@cranberry-money/shared-services';
108
+ import { getPortfolios, createPortfolio } from '@cranberry-money/shared-services';
109
+ import { webApiClient } from './webApiClient'; // or mobileApiClient for mobile
265
110
 
266
- // Quick performance check
267
- const quickReport = await runQuickPerformanceCheck();
268
- console.log(quickReport);
111
+ // Create platform-specific functions
112
+ const getMyPortfolios = getPortfolios(webApiClient);
113
+ const createMyPortfolio = createPortfolio(webApiClient);
269
114
 
270
- // Comprehensive analysis
271
- const detailedReport = await runComprehensivePerformanceAnalysis();
272
- console.log(detailedReport);
273
- ```
274
-
275
- ### Performance Tracking
276
-
277
- ```typescript
278
- import { monitorMigrationPerformance } from '@cranberry-money/shared-services';
279
-
280
- // Wrap functions for automatic performance tracking
281
- const monitored = monitorMigrationPerformance('api.getPortfolios', 'functional', () =>
282
- services.portfolio.getPortfolios()
283
- );
284
-
285
- const result = await monitored();
286
- // Automatically logs performance metrics
115
+ // Use them
116
+ await getMyPortfolios({ page: 1 });
117
+ await createMyPortfolio({ name: 'New Portfolio' });
287
118
  ```
288
119
 
289
120
  ## 🧪 Testing
290
121
 
291
- ### Pure Function Testing
292
-
293
- ```typescript
294
- import { signin, createMemoryTokenStorage } from '@cranberry-money/shared-services';
295
-
296
- describe('Authentication', () => {
297
- it('should signin successfully', async () => {
298
- const mockApiClient = {
299
- post: jest.fn().mockResolvedValue({
300
- access: 'access-token',
301
- refresh: 'refresh-token',
302
- user: { id: '1', email: 'test@example.com' },
303
- }),
304
- };
305
-
306
- const deps = {
307
- apiClient: mockApiClient,
308
- tokenStorage: createMemoryTokenStorage(),
309
- };
310
-
311
- const result = await signin({ email: 'test@example.com', password: 'password' }, deps);
312
-
313
- expect(result.user.email).toBe('test@example.com');
314
- expect(mockApiClient.post).toHaveBeenCalledWith('/api/signin/', {
315
- email: 'test@example.com',
316
- password: 'password',
317
- });
318
- });
319
- });
320
- ```
321
-
322
- ### React Testing
122
+ Services are easy to test with mock API clients:
323
123
 
324
124
  ```typescript
325
- import { render, screen } from '@testing-library/react';
326
- import { createMemoryTokenStorage } from '@cranberry-money/shared-services';
327
-
328
- const TestAuthProvider = ({ children }) => {
329
- const services = createWebServiceFactory('http://test-api.com');
330
- const AuthProvider = services.auth.createAuthProvider();
331
-
332
- return <AuthProvider>{children}</AuthProvider>;
333
- };
334
-
335
- test('should display user name when authenticated', () => {
336
- render(
337
- <TestAuthProvider>
338
- <Dashboard />
339
- </TestAuthProvider>
340
- );
341
-
342
- // Test auth-dependent component
343
- });
344
- ```
345
-
346
- ## 🔧 Configuration
347
-
348
- ### Web Application Setup
349
-
350
- ```typescript
351
- import { createWebServiceFactory } from '@cranberry-money/shared-services';
352
-
353
- const services = createWebServiceFactory(process.env.REACT_APP_API_URL, {
354
- apiTimeout: 15000,
355
- enableCredentials: true,
356
- });
357
-
358
- export { services };
359
- ```
360
-
361
- ### Mobile Application Setup
362
-
363
- ```typescript
364
- import { createMobileServiceFactory } from '@cranberry-money/shared-services';
365
- import * as SecureStore from 'expo-secure-store';
366
-
367
- const services = createMobileServiceFactory(process.env.EXPO_PUBLIC_API_URL, SecureStore, {
368
- apiTimeout: 20000,
369
- });
370
-
371
- export { services };
372
- ```
373
-
374
- ### Environment-Based Configuration
375
-
376
- ```typescript
377
- const getApiUrl = () => {
378
- switch (process.env.NODE_ENV) {
379
- case 'production':
380
- return 'https://api.myportfolio.com';
381
- case 'staging':
382
- return 'https://staging-api.myportfolio.com';
383
- default:
384
- return 'http://localhost:8000';
385
- }
386
- };
387
-
388
- const services = createWebServiceFactory(getApiUrl());
389
- ```
390
-
391
- ## 📚 Advanced Usage
125
+ import { createPortfolioService } from '@cranberry-money/shared-services';
126
+
127
+ describe('Portfolio Service', () => {
128
+ const mockApiClient = {
129
+ get: jest.fn(),
130
+ post: jest.fn(),
131
+ patch: jest.fn(),
132
+ delete: jest.fn()
133
+ };
392
134
 
393
- ### Custom Service Factory
135
+ const portfolioService = createPortfolioService(mockApiClient);
394
136
 
395
- ```typescript
396
- import { createServiceFactory } from '@cranberry-money/shared-services';
397
-
398
- const customServices = createServiceFactory({
399
- platform: {
400
- platform: 'web',
401
- apiBaseUrl: 'https://custom-api.com',
402
- apiTimeout: 5000,
403
- },
137
+ it('should fetch portfolios', async () => {
138
+ mockApiClient.get.mockResolvedValue({
139
+ data: { results: [], count: 0 }
140
+ });
141
+
142
+ const result = await portfolioService.getAll();
143
+
144
+ expect(mockApiClient.get).toHaveBeenCalledWith('/api/portfolios/', {
145
+ params: undefined
146
+ });
147
+ });
404
148
  });
405
149
  ```
406
150
 
407
- ### Error Handling
151
+ ## 🔄 React Query Integration
408
152
 
409
- ```typescript
410
- import { isFunctionalApiError } from '@cranberry-money/shared-services';
411
-
412
- try {
413
- const data = await services.portfolio.getPortfolios();
414
- } catch (error) {
415
- if (isFunctionalApiError(error)) {
416
- console.error(`API Error (${error.status}): ${error.message}`);
417
- if (error.details) {
418
- console.error('Details:', error.details);
419
- }
420
- } else {
421
- console.error('Unexpected error:', error);
422
- }
423
- }
424
- ```
425
-
426
- ### Integration with React Query
153
+ Works seamlessly with React Query:
427
154
 
428
155
  ```typescript
429
156
  import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
157
+ import { portfolioService } from '@services/portfolios';
430
158
 
431
- function usePortfolios() {
159
+ export const usePortfolios = (params?) => {
432
160
  return useQuery({
433
- queryKey: ['portfolios'],
434
- queryFn: () =>
435
- services.portfolio.getPortfolios({
436
- isActive: true,
437
- sortBy: 'totalValue',
438
- sortOrder: 'desc',
439
- }),
440
- staleTime: 5 * 60 * 1000, // 5 minutes
161
+ queryKey: ['portfolios', params],
162
+ queryFn: () => portfolioService.getAll(params),
163
+ staleTime: 5 * 60 * 1000,
441
164
  });
442
- }
165
+ };
443
166
 
444
- function useCreatePortfolio() {
167
+ export const useCreatePortfolio = () => {
445
168
  const queryClient = useQueryClient();
446
-
169
+
447
170
  return useMutation({
448
- mutationFn: data => services.portfolio.createPortfolio(data),
171
+ mutationFn: portfolioService.create,
449
172
  onSuccess: () => {
450
173
  queryClient.invalidateQueries({ queryKey: ['portfolios'] });
451
174
  },
452
175
  });
453
- }
176
+ };
454
177
  ```
455
178
 
456
- ## 📖 Documentation
457
-
458
- - **[Migration Guide](../../docs/MIGRATION-V3.md)**: Complete guide for migrating from v2.x
459
- - **[Architecture Overview](../../docs/ARCHITECTURE.md)**: Detailed architecture documentation
460
- - **[Best Practices](../../docs/BEST-PRACTICES.md)**: Code standards and patterns
461
- - **[Service Development Guide](../../docs/SERVICE-DEVELOPMENT-GUIDE.md)**: Creating new services
462
-
463
- ## 🛠 Development
464
-
465
- ```bash
466
- # Install dependencies
467
- npm install
179
+ ## 🌐 Available Services
468
180
 
469
- # Build the package
470
- npm run build
181
+ ### Financial Services
182
+ - `createPortfolioService` - Portfolio management
183
+ - `createAccountService` - Account operations
184
+ - `createTradeService` - Trading operations
185
+ - `createInstrumentService` - Financial instruments
186
+ - `createWithdrawalService` - Withdrawal requests
471
187
 
472
- # Run tests
473
- npm run test
188
+ ### Banking Services
189
+ - `createBankService` - Bank management
190
+ - `createCashAccountService` - Cash accounts
191
+ - `createTransactionService` - Transactions
474
192
 
475
- # Run tests with coverage
476
- npm run test:coverage
193
+ ### User & Compliance
194
+ - `createAuthService` - Authentication
195
+ - `createUserService` - User profiles
196
+ - `createDocumentService` - Documents
197
+ - `createTaxResidencyService` - Tax information
198
+ - `createInvestmentPreferenceService` - Investment preferences
477
199
 
478
- # Type check
479
- npm run typecheck
200
+ ### Reference Data
201
+ - `createCountryService` - Country data
202
+ - `createIndustryService` - Industries
203
+ - `createSectorService` - Sectors
204
+ - `createStockExchangeService` - Stock exchanges
480
205
 
481
- # Lint code
482
- npm run lint
483
- ```
206
+ ## 📦 API Client Requirements
484
207
 
485
- ## 📦 Package Structure
208
+ Your API client must implement this interface:
486
209
 
487
- ```
488
- src/
489
- ├── api/ # Functional API client
490
- │ ├── functional-client.ts
491
- │ └── __tests__/
492
- ├── auth/ # Authentication system
493
- │ ├── functional/ # Pure auth functions
494
- │ ├── react/ # React integration
495
- │ └── __tests__/
496
- ├── services/ # Service factories
497
- │ ├── factories/ # Factory pattern implementation
498
- │ └── __tests__/
499
- ├── migration/ # Migration utilities
500
- │ ├── domains/ # Domain-specific migrations
501
- │ ├── feature-flags.ts # Feature flag system
502
- │ ├── migration-helpers.ts
503
- │ └── __tests__/
504
- ├── integration/ # Integration examples
505
- │ ├── blueberry-integration.tsx
506
- │ ├── performance-benchmark.ts
507
- │ └── __tests__/
508
- └── index.ts # Main exports
210
+ ```typescript
211
+ interface ApiClient {
212
+ get<T>(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>;
213
+ post<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>;
214
+ put<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>;
215
+ patch<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>;
216
+ delete<T>(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>;
217
+ }
509
218
  ```
510
219
 
511
- ## 🔗 Dependencies
220
+ Any axios instance automatically satisfies this interface.
512
221
 
513
- ### Required
222
+ ## 🔗 Related Packages
514
223
 
515
- - `axios` - HTTP client library
516
- - `react` >=18.0.0 - React framework (peer dependency)
517
- - `typescript` >=4.5.0 - TypeScript support (peer dependency)
224
+ - `@cranberry-money/shared-types` - TypeScript type definitions
225
+ - `@cranberry-money/shared-constants` - API endpoints and business constants
226
+ - `@cranberry-money/shared-utils` - Utility functions for formatting and validation
518
227
 
519
- ### Optional
228
+ ## 💡 Migration from Local Services
520
229
 
521
- - `@tanstack/react-query` - For optimized data fetching
522
- - `expo-secure-store` - For mobile secure storage
230
+ If you're currently using local service implementations in your app:
523
231
 
524
- ## 🆙 Version Support
232
+ ### Before (Local Implementation)
233
+ ```typescript
234
+ // src/services/portfolios.ts
235
+ import webApiClient from './webApiClient';
525
236
 
526
- - **v3.x**: Current (Active development)
527
- - **v2.x**: Legacy (Security fixes only, 6 months)
528
- - **v1.x**: End of life
237
+ export const getPortfolios = (params?) => {
238
+ return webApiClient.get('/api/portfolios/', { params });
239
+ };
240
+ ```
529
241
 
530
- ## 🤝 Contributing
242
+ ### After (Shared Implementation)
243
+ ```typescript
244
+ // src/services/portfolios.ts
245
+ import webApiClient from './webApiClient';
246
+ import { createPortfolioService } from '@cranberry-money/shared-services';
531
247
 
532
- 1. Fork the repository
533
- 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
534
- 3. Commit your changes (`git commit -m 'Add amazing feature'`)
535
- 4. Push to the branch (`git push origin feature/amazing-feature`)
536
- 5. Open a Pull Request
248
+ export const portfolioService = createPortfolioService(webApiClient);
249
+ // or
250
+ export const { getAll: getPortfolios } = createPortfolioService(webApiClient);
251
+ ```
537
252
 
538
253
  ## 📄 License
539
254
 
540
255
  MIT License - see the [LICENSE](LICENSE) file for details.
541
256
 
542
- ## 🙏 Acknowledgments
543
-
544
- - Built for the Blueberry platform
545
- - Inspired by functional programming principles
546
- - Designed for maximum type safety and developer experience
547
-
548
257
  ---
549
258
 
550
- **Need help migrating from v2.x?** Check out our [comprehensive migration guide](../../docs/MIGRATION-V3.md) with step-by-step instructions and examples.
259
+ For complete documentation and architecture details, see the [Cranberry Development Guide](../../docs/CRANBERRY-DEVELOPMENT-GUIDE.md).
package/dist/auth.d.ts ADDED
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Platform-agnostic authentication service functions.
3
+ * Works with any axios-compatible API client.
4
+ */
5
+ import { AxiosInstance, AxiosResponse } from 'axios';
6
+ import type { SigninPayload, SignupPayload, EmailVerificationPayload, TokenRefreshData as TokenRefreshResponse, TokenRefreshPayload } from '@cranberry-money/shared-types';
7
+ /**
8
+ * Individual authentication functions with dependency injection
9
+ */
10
+ /**
11
+ * Sign in a user
12
+ * @param apiClient - Axios-compatible API client
13
+ * @returns Function that accepts signin payload and returns API response
14
+ */
15
+ export declare const signin: (apiClient: AxiosInstance) => (data: SigninPayload) => Promise<AxiosResponse>;
16
+ /**
17
+ * Sign out the current user
18
+ * @param apiClient - Axios-compatible API client
19
+ * @returns Function that performs signout
20
+ */
21
+ export declare const signout: (apiClient: AxiosInstance) => () => Promise<AxiosResponse>;
22
+ /**
23
+ * Sign up a new user
24
+ * @param apiClient - Axios-compatible API client
25
+ * @returns Function that accepts signup payload and returns API response
26
+ */
27
+ export declare const signup: (apiClient: AxiosInstance) => (data: SignupPayload) => Promise<AxiosResponse>;
28
+ /**
29
+ * Verify user's email with verification code
30
+ * @param apiClient - Axios-compatible API client
31
+ * @returns Function that accepts verification payload and returns API response
32
+ */
33
+ export declare const verifyEmail: (apiClient: AxiosInstance) => (data: EmailVerificationPayload) => Promise<AxiosResponse>;
34
+ /**
35
+ * Resend email verification code
36
+ * @param apiClient - Axios-compatible API client
37
+ * @returns Function that performs resend operation
38
+ */
39
+ export declare const resendVerificationCode: (apiClient: AxiosInstance) => () => Promise<AxiosResponse>;
40
+ /**
41
+ * Refresh authentication token
42
+ * @param apiClient - Axios-compatible API client
43
+ * @returns Function that accepts refresh payload and returns token response
44
+ */
45
+ export declare const refreshToken: (apiClient: AxiosInstance) => (data: TokenRefreshPayload) => Promise<AxiosResponse<TokenRefreshResponse>>;
46
+ /**
47
+ * Authentication service interface
48
+ */
49
+ export interface AuthService {
50
+ signin: (data: SigninPayload) => Promise<AxiosResponse>;
51
+ signout: () => Promise<AxiosResponse>;
52
+ signup: (data: SignupPayload) => Promise<AxiosResponse>;
53
+ verifyEmail: (data: EmailVerificationPayload) => Promise<AxiosResponse>;
54
+ resendVerificationCode: () => Promise<AxiosResponse>;
55
+ refreshToken: (data: TokenRefreshPayload) => Promise<AxiosResponse<TokenRefreshResponse>>;
56
+ }
57
+ /**
58
+ * Create a complete authentication service with all operations
59
+ * @param apiClient - Axios-compatible API client
60
+ * @returns Object containing all auth operations
61
+ *
62
+ * @example
63
+ * // Web application with cookies
64
+ * const webApiClient = axios.create({
65
+ * baseURL: 'https://api.example.com',
66
+ * withCredentials: true
67
+ * });
68
+ * const authService = createAuthService(webApiClient);
69
+ * await authService.signin({ email: 'user@example.com', password: 'pass' });
70
+ *
71
+ * @example
72
+ * // Mobile application with tokens
73
+ * const mobileApiClient = axios.create({
74
+ * baseURL: 'https://api.example.com'
75
+ * });
76
+ * // Add token interceptor for mobile
77
+ * mobileApiClient.interceptors.request.use(async (config) => {
78
+ * const token = await getStoredToken();
79
+ * if (token) config.headers.Authorization = `Bearer ${token}`;
80
+ * return config;
81
+ * });
82
+ * const authService = createAuthService(mobileApiClient);
83
+ */
84
+ export declare const createAuthService: (apiClient: AxiosInstance) => AuthService;
85
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAErD,OAAO,KAAK,EACV,aAAa,EACb,aAAa,EACb,wBAAwB,EACxB,gBAAgB,IAAI,oBAAoB,EACxC,mBAAmB,EACpB,MAAM,+BAA+B,CAAC;AAEvC;;GAEG;AAEH;;;;GAIG;AACH,eAAO,MAAM,MAAM,GAAI,WAAW,aAAa,MACrC,MAAM,aAAa,KAAG,OAAO,CAAC,aAAa,CAGpD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,OAAO,GAAI,WAAW,aAAa,WACnC,OAAO,CAAC,aAAa,CAGjC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,MAAM,GAAI,WAAW,aAAa,MACrC,MAAM,aAAa,KAAG,OAAO,CAAC,aAAa,CAGpD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,WAAW,GAAI,WAAW,aAAa,MAC1C,MAAM,wBAAwB,KAAG,OAAO,CAAC,aAAa,CAG/D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GAAI,WAAW,aAAa,WAClD,OAAO,CAAC,aAAa,CAGjC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAI,WAAW,aAAa,MAC3C,MAAM,mBAAmB,KAAG,OAAO,CAAC,aAAa,CAAC,oBAAoB,CAAC,CAGhF,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;IACxD,OAAO,EAAE,MAAM,OAAO,CAAC,aAAa,CAAC,CAAC;IACtC,MAAM,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;IACxD,WAAW,EAAE,CAAC,IAAI,EAAE,wBAAwB,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;IACxE,sBAAsB,EAAE,MAAM,OAAO,CAAC,aAAa,CAAC,CAAC;IACrD,YAAY,EAAE,CAAC,IAAI,EAAE,mBAAmB,KAAK,OAAO,CAAC,aAAa,CAAC,oBAAoB,CAAC,CAAC,CAAC;CAC3F;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,iBAAiB,GAAI,WAAW,aAAa,KAAG,WAS5D,CAAC"}
package/dist/auth.js ADDED
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Platform-agnostic authentication service functions.
3
+ * Works with any axios-compatible API client.
4
+ */
5
+ import { AUTH_ENDPOINTS } from '@cranberry-money/shared-constants';
6
+ /**
7
+ * Individual authentication functions with dependency injection
8
+ */
9
+ /**
10
+ * Sign in a user
11
+ * @param apiClient - Axios-compatible API client
12
+ * @returns Function that accepts signin payload and returns API response
13
+ */
14
+ export const signin = (apiClient) => {
15
+ return (data) => {
16
+ return apiClient.post(AUTH_ENDPOINTS.SIGNIN, data);
17
+ };
18
+ };
19
+ /**
20
+ * Sign out the current user
21
+ * @param apiClient - Axios-compatible API client
22
+ * @returns Function that performs signout
23
+ */
24
+ export const signout = (apiClient) => {
25
+ return () => {
26
+ return apiClient.post(AUTH_ENDPOINTS.SIGNOUT);
27
+ };
28
+ };
29
+ /**
30
+ * Sign up a new user
31
+ * @param apiClient - Axios-compatible API client
32
+ * @returns Function that accepts signup payload and returns API response
33
+ */
34
+ export const signup = (apiClient) => {
35
+ return (data) => {
36
+ return apiClient.post(AUTH_ENDPOINTS.SIGNUP, data);
37
+ };
38
+ };
39
+ /**
40
+ * Verify user's email with verification code
41
+ * @param apiClient - Axios-compatible API client
42
+ * @returns Function that accepts verification payload and returns API response
43
+ */
44
+ export const verifyEmail = (apiClient) => {
45
+ return (data) => {
46
+ return apiClient.post(AUTH_ENDPOINTS.EMAIL_VERIFICATION, data);
47
+ };
48
+ };
49
+ /**
50
+ * Resend email verification code
51
+ * @param apiClient - Axios-compatible API client
52
+ * @returns Function that performs resend operation
53
+ */
54
+ export const resendVerificationCode = (apiClient) => {
55
+ return () => {
56
+ return apiClient.post(AUTH_ENDPOINTS.RESEND_VERIFICATION);
57
+ };
58
+ };
59
+ /**
60
+ * Refresh authentication token
61
+ * @param apiClient - Axios-compatible API client
62
+ * @returns Function that accepts refresh payload and returns token response
63
+ */
64
+ export const refreshToken = (apiClient) => {
65
+ return (data) => {
66
+ return apiClient.post(AUTH_ENDPOINTS.TOKEN_REFRESH, data);
67
+ };
68
+ };
69
+ /**
70
+ * Create a complete authentication service with all operations
71
+ * @param apiClient - Axios-compatible API client
72
+ * @returns Object containing all auth operations
73
+ *
74
+ * @example
75
+ * // Web application with cookies
76
+ * const webApiClient = axios.create({
77
+ * baseURL: 'https://api.example.com',
78
+ * withCredentials: true
79
+ * });
80
+ * const authService = createAuthService(webApiClient);
81
+ * await authService.signin({ email: 'user@example.com', password: 'pass' });
82
+ *
83
+ * @example
84
+ * // Mobile application with tokens
85
+ * const mobileApiClient = axios.create({
86
+ * baseURL: 'https://api.example.com'
87
+ * });
88
+ * // Add token interceptor for mobile
89
+ * mobileApiClient.interceptors.request.use(async (config) => {
90
+ * const token = await getStoredToken();
91
+ * if (token) config.headers.Authorization = `Bearer ${token}`;
92
+ * return config;
93
+ * });
94
+ * const authService = createAuthService(mobileApiClient);
95
+ */
96
+ export const createAuthService = (apiClient) => {
97
+ return {
98
+ signin: signin(apiClient),
99
+ signout: signout(apiClient),
100
+ signup: signup(apiClient),
101
+ verifyEmail: verifyEmail(apiClient),
102
+ resendVerificationCode: resendVerificationCode(apiClient),
103
+ refreshToken: refreshToken(apiClient),
104
+ };
105
+ };
106
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AASnE;;GAEG;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,SAAwB,EAAE,EAAE;IACjD,OAAO,CAAC,IAAmB,EAA0B,EAAE;QACrD,OAAO,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACrD,CAAC,CAAC;AACJ,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,SAAwB,EAAE,EAAE;IAClD,OAAO,GAA2B,EAAE;QAClC,OAAO,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;IAChD,CAAC,CAAC;AACJ,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,SAAwB,EAAE,EAAE;IACjD,OAAO,CAAC,IAAmB,EAA0B,EAAE;QACrD,OAAO,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACrD,CAAC,CAAC;AACJ,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,SAAwB,EAAE,EAAE;IACtD,OAAO,CAAC,IAA8B,EAA0B,EAAE;QAChE,OAAO,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC;IACjE,CAAC,CAAC;AACJ,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,SAAwB,EAAE,EAAE;IACjE,OAAO,GAA2B,EAAE;QAClC,OAAO,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC;IAC5D,CAAC,CAAC;AACJ,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,SAAwB,EAAE,EAAE;IACvD,OAAO,CAAC,IAAyB,EAAgD,EAAE;QACjF,OAAO,SAAS,CAAC,IAAI,CAAuB,cAAc,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;IAClF,CAAC,CAAC;AACJ,CAAC,CAAC;AAcF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,SAAwB,EAAe,EAAE;IACzE,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,SAAS,CAAC;QACzB,OAAO,EAAE,OAAO,CAAC,SAAS,CAAC;QAC3B,MAAM,EAAE,MAAM,CAAC,SAAS,CAAC;QACzB,WAAW,EAAE,WAAW,CAAC,SAAS,CAAC;QACnC,sBAAsB,EAAE,sBAAsB,CAAC,SAAS,CAAC;QACzD,YAAY,EAAE,YAAY,CAAC,SAAS,CAAC;KACtC,CAAC;AACJ,CAAC,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1 +1,6 @@
1
+ /**
2
+ * @cranberry-money/shared-services
3
+ * Platform-agnostic service functions for the Blueberry platform ecosystem.
4
+ */
5
+ export { signin, signout, signup, verifyEmail, resendVerificationCode, refreshToken, createAuthService, type AuthService, } from './auth';
1
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACL,MAAM,EACN,OAAO,EACP,MAAM,EACN,WAAW,EACX,sBAAsB,EACtB,YAAY,EACZ,iBAAiB,EACjB,KAAK,WAAW,GACjB,MAAM,QAAQ,CAAC"}
package/dist/index.js CHANGED
@@ -1,3 +1,7 @@
1
- "use strict";
2
- // Placeholder for package
1
+ /**
2
+ * @cranberry-money/shared-services
3
+ * Platform-agnostic service functions for the Blueberry platform ecosystem.
4
+ */
5
+ // Authentication services
6
+ export { signin, signout, signup, verifyEmail, resendVerificationCode, refreshToken, createAuthService, } from './auth';
3
7
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,2BAA2B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,0BAA0B;AAC1B,OAAO,EACL,MAAM,EACN,OAAO,EACP,MAAM,EACN,WAAW,EACX,sBAAsB,EACtB,YAAY,EACZ,iBAAiB,GAElB,MAAM,QAAQ,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cranberry-money/shared-services",
3
- "version": "8.1.2",
4
- "description": "Functional architecture shared services with pure functions, dependency injection, and comprehensive TypeScript support for the Blueberry platform",
3
+ "version": "10.0.2",
4
+ "description": "Platform-agnostic API services with pure functions and dependency injection. Includes auth, portfolios, instruments, countries, sectors, and more.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -29,6 +29,8 @@
29
29
  "prepublishOnly": "npm run clean && npm run typecheck && npm run build"
30
30
  },
31
31
  "dependencies": {
32
+ "@cranberry-money/shared-constants": "^8.1.3",
33
+ "@cranberry-money/shared-types": "^8.1.3",
32
34
  "axios": "^1.9.0"
33
35
  },
34
36
  "peerDependencies": {