@thorprovider/adapters 4.2.0 → 4.3.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/CHANGELOG.md +6 -0
- package/PROVIDER_IMPLEMENTATION_ANALYSIS.md +659 -0
- package/README.md +19 -0
- package/dist/index.d.mts +249 -2
- package/dist/index.d.ts +249 -2
- package/dist/index.js +1450 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1448 -2
- package/dist/index.mjs.map +1 -1
- package/docs/RETRY_SOLUTION_SUMMARY.md +26 -11
- package/docs/authentication.md +115 -62
- package/docs/capabilities-system.md +34 -2
- package/docs/category-customization.md +37 -29
- package/docs/fetch-reliability-solution.md +127 -278
- package/docs/getting-started.md +43 -29
- package/docs/profile-management.md +68 -38
- package/docs/providers-overview.md +14 -0
- package/docs/services-overview.md +20 -2
- package/package.json +10 -6
- package/quick-reference.md +34 -0
- package/src/factory/index.ts +4 -0
- package/src/index.ts +3 -0
- package/src/providers/index.ts +3 -0
- package/src/providers/medusa/admin.test.ts +404 -0
- package/src/providers/medusa/admin.ts +442 -0
- package/src/providers/medusa/auth.test.ts +137 -0
- package/src/providers/medusa/auth.ts +96 -1
- package/src/providers/medusa/index.ts +21 -1
- package/src/providers/shopify/auth.ts +6 -0
- package/src/providers/stelorder/client.test-helpers.ts +3 -0
- package/src/providers/stelorder/client.test.ts +58 -0
- package/src/providers/stelorder/client.ts +326 -0
- package/src/providers/stelorder/index.test.ts +48 -0
- package/src/providers/stelorder/index.ts +685 -0
- package/src/providers/stelorder/live-smoke.test.ts +54 -0
- package/src/providers/stelorder/transforms.test.ts +103 -0
- package/src/providers/stelorder/transforms.ts +631 -0
- package/src/resilience/README.md +70 -48
- package/src/types/capabilities.types.ts +51 -0
- package/vitest.config.ts +13 -0
- package/vitest.setup.ts +6 -0
- package/.turbo/turbo-build.log +0 -18
- package/dist/.tsbuildinfo +0 -1
- package/src/core/errors.js +0 -161
- package/src/core/fetch-utils.js +0 -240
- package/src/core/logger.js +0 -63
- package/src/metadata/channel-scope-validation.js +0 -136
- package/src/metadata/channel-scope.js +0 -174
- package/src/metadata/index.js +0 -31
- package/src/providers/medusa/auth.js +0 -601
- package/src/providers/medusa/index.js +0 -2485
- package/src/providers/medusa/payment.js +0 -265
- package/src/providers/medusa/transforms.js +0 -505
- package/src/types/capabilities.types.js +0 -141
- package/src/types/provider.js +0 -9
- package/src/utils/payment-utils.js +0 -152
|
@@ -1,14 +1,26 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
title: Fetch Reliability Solution
|
|
3
|
+
purpose: Documents the retry and circuit breaker patterns implemented for robust backend communication
|
|
4
|
+
type: reference
|
|
5
|
+
tags: [resilience, retry, circuit-breaker, fetch, network, medusa]
|
|
6
|
+
related:
|
|
7
|
+
- RETRY_SOLUTION_SUMMARY.md
|
|
8
|
+
- providers-overview.md
|
|
9
|
+
- ../src/resilience/README.md
|
|
10
|
+
updated: 2026-05-03
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Fetch Reliability Solution
|
|
2
14
|
|
|
3
|
-
**
|
|
4
|
-
**
|
|
5
|
-
**
|
|
15
|
+
**Problem identified**: February 10, 2026
|
|
16
|
+
**Status**: ✅ Resolved + Extended with Circuit Breaker
|
|
17
|
+
**Severity**: High (intermittent user-facing failures)
|
|
6
18
|
|
|
7
19
|
---
|
|
8
20
|
|
|
9
|
-
## 🔴
|
|
21
|
+
## 🔴 Original Problem
|
|
10
22
|
|
|
11
|
-
|
|
23
|
+
The error `ProviderAPIError: Failed to fetch products: fetch failed` occurred intermittently in the V0 starter when loading products from the Medusa backend.
|
|
12
24
|
|
|
13
25
|
```
|
|
14
26
|
⨯ Error [ProviderAPIError]: Failed to fetch products: fetch failed
|
|
@@ -18,80 +30,55 @@ El error `ProviderAPIError: Failed to fetch products: fetch failed` ocurría con
|
|
|
18
30
|
provider: 'medusa',
|
|
19
31
|
```
|
|
20
32
|
|
|
21
|
-
**
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
- No
|
|
33
|
+
**Characteristics:**
|
|
34
|
+
- Occurred without a predictable pattern
|
|
35
|
+
- Backend was healthy when tested directly
|
|
36
|
+
- Error was `fetch failed` with no further detail
|
|
37
|
+
- No information about the root cause
|
|
26
38
|
|
|
27
39
|
---
|
|
28
40
|
|
|
29
|
-
## 🔍
|
|
41
|
+
## 🔍 Root Causes
|
|
30
42
|
|
|
31
|
-
### 1.
|
|
32
|
-
|
|
33
|
-
- **Impacto**: Fallos transitorios (picos de latencia, pérdida momentánea de paquetes) causaban errores visibles al usuario
|
|
34
|
-
- **Solución**: Implementar reintentos automáticos con exponential backoff
|
|
43
|
+
### 1. No Retry Logic
|
|
44
|
+
Any transient network failure caused an immediate error. Spikes in latency or momentary packet loss were visible to the user.
|
|
35
45
|
|
|
36
|
-
### 2.
|
|
37
|
-
|
|
38
|
-
- **Impacto**: Requests lentos bloqueaban la aplicación sin dar feedback
|
|
39
|
-
- **Solución**: Timeout de 10 segundos por request
|
|
46
|
+
### 2. No Timeout
|
|
47
|
+
Requests had no time limit and could hang indefinitely.
|
|
40
48
|
|
|
41
|
-
### 3.
|
|
42
|
-
|
|
43
|
-
- **Impacto**: Mayor latencia, mayor probabilidad de fallos de conexión
|
|
44
|
-
- **Solución**: Implementada implícitamente en el SDK de Medusa (reutiliza conexiones)
|
|
49
|
+
### 3. No Keep-Alive
|
|
50
|
+
Each request created a new TCP connection (full handshake every time), increasing latency and failure probability.
|
|
45
51
|
|
|
46
|
-
### 4.
|
|
47
|
-
|
|
48
|
-
- **Impacto**: Difícil diagnosticar la causa raíz
|
|
49
|
-
- **Solución**: Logging detallado con contexto de reintentos
|
|
50
|
-
|
|
51
|
-
### 5. **Configuración de URL del Backend**
|
|
52
|
-
- **Observación**: URL con trailing slash `https://erpthor.com/`
|
|
53
|
-
- **Riesgo potencial**: Redirecciones innecesarias dependiendo de la configuración del servidor
|
|
54
|
-
- **Recomendación**: Verificar que el backend esté bien configurado para manejar trailing slashes
|
|
52
|
+
### 4. Generic Error Messages
|
|
53
|
+
`fetch failed` with no context made root-cause diagnosis difficult.
|
|
55
54
|
|
|
56
55
|
---
|
|
57
56
|
|
|
58
|
-
## ✅
|
|
59
|
-
|
|
60
|
-
### 1. Nuevo Módulo `fetch-utils.ts` (`@thorprovider/adapters/core`)
|
|
57
|
+
## ✅ Solutions Implemented
|
|
61
58
|
|
|
62
|
-
|
|
59
|
+
### 1. Retry Logic with Exponential Backoff (`fetch-utils.ts`)
|
|
63
60
|
|
|
64
|
-
|
|
65
|
-
- ✅ **Retry automático con exponential backoff**
|
|
66
|
-
- 3 reintentos por defecto (4 intentos totales)
|
|
67
|
-
- Delay inicial: 300ms
|
|
68
|
-
- Backoff multiplicador: 2x (300ms → 600ms → 1200ms)
|
|
69
|
-
- Delay máximo: 5 segundos
|
|
61
|
+
Located at `packages/adapters/src/core/fetch-utils.ts`. Exported from the main package entry point:
|
|
70
62
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
- Timeout independiente por cada intento
|
|
63
|
+
```typescript
|
|
64
|
+
import { fetchWithRetry, createFetchWithRetry } from '@thorprovider/adapters';
|
|
65
|
+
```
|
|
75
66
|
|
|
76
|
-
|
|
77
|
-
- Network errors (fetch failed, connection refused)
|
|
78
|
-
- Timeout errors
|
|
79
|
-
- HTTP status codes específicos: 408, 429, 500, 502, 503, 504
|
|
67
|
+
> ⚠️ **Note:** `@thorprovider/adapters` does not expose subpath exports (e.g. `@thorprovider/adapters/core/fetch-utils`). Always import from the main entry point.
|
|
80
68
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
69
|
+
**Features:**
|
|
70
|
+
- ✅ Automatic retry with exponential backoff (3 retries by default, 4 total attempts)
|
|
71
|
+
- ✅ Configurable timeout via `AbortController` (10s default)
|
|
72
|
+
- ✅ Smart retryable error detection: network errors, timeouts, HTTP 408/429/500/502/503/504
|
|
73
|
+
- ✅ Detailed logging per attempt
|
|
85
74
|
|
|
86
|
-
**API
|
|
75
|
+
**API:**
|
|
87
76
|
|
|
88
77
|
```typescript
|
|
89
|
-
//
|
|
90
|
-
import { fetchWithRetry } from '@thorprovider/adapters/core/fetch-utils';
|
|
91
|
-
|
|
78
|
+
// Basic usage
|
|
92
79
|
const response = await fetchWithRetry('https://api.example.com/products');
|
|
93
80
|
|
|
94
|
-
//
|
|
81
|
+
// Custom configuration
|
|
95
82
|
const response = await fetchWithRetry(
|
|
96
83
|
'https://api.example.com/products',
|
|
97
84
|
{ method: 'POST', body: JSON.stringify(data) },
|
|
@@ -102,298 +89,160 @@ const response = await fetchWithRetry(
|
|
|
102
89
|
}
|
|
103
90
|
);
|
|
104
91
|
|
|
105
|
-
// Factory
|
|
106
|
-
import { createFetchWithRetry } from '@thorprovider/adapters/core/fetch-utils';
|
|
107
|
-
|
|
92
|
+
// Factory for a reusable configured fetch
|
|
108
93
|
const customFetch = createFetchWithRetry({ maxRetries: 5 });
|
|
109
94
|
const response = await customFetch(url);
|
|
110
95
|
```
|
|
111
96
|
|
|
112
|
-
### 2.
|
|
97
|
+
### 2. MedusaProvider Integration
|
|
113
98
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
1. **Import de fetch utilities**:
|
|
117
|
-
```typescript
|
|
118
|
-
import { createFetchWithRetry, type RetryConfig } from '../../core/fetch-utils';
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
2. **Nueva propiedad en `MedusaProviderConfig`**:
|
|
122
|
-
```typescript
|
|
123
|
-
export interface MedusaProviderConfig extends ProviderConfig {
|
|
124
|
-
// ... propiedades existentes
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Retry configuration for fetch requests (optional)
|
|
128
|
-
*/
|
|
129
|
-
retry?: RetryConfig;
|
|
130
|
-
}
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
3. **Inyección de fetch personalizado en el constructor**:
|
|
134
|
-
```typescript
|
|
135
|
-
constructor(config: MedusaProviderConfig) {
|
|
136
|
-
// ... código existente
|
|
137
|
-
|
|
138
|
-
// Create fetch with retry logic
|
|
139
|
-
const fetchWithRetry = createFetchWithRetry(config.retry);
|
|
140
|
-
|
|
141
|
-
this.sdk = new Medusa({
|
|
142
|
-
baseUrl: config.baseUrl,
|
|
143
|
-
publishableKey: config.publishableKey,
|
|
144
|
-
fetch: fetchWithRetry as any, // ← Custom fetch with retries!
|
|
145
|
-
// ... resto de configuración
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
### 3. Configuración por Defecto en V0 Starter
|
|
151
|
-
|
|
152
|
-
Actualizamos `/packages/core/lib/commerce.ts`:
|
|
99
|
+
The `MedusaProviderConfig` accepts an optional `retry` field:
|
|
153
100
|
|
|
154
101
|
```typescript
|
|
155
102
|
export const commerce = createProvider({
|
|
156
|
-
provider:
|
|
103
|
+
provider: 'medusa',
|
|
157
104
|
config: {
|
|
158
|
-
baseUrl: NEXT_PUBLIC_COMMERCE_API_URL
|
|
159
|
-
publishableKey: NEXT_PUBLIC_COMMERCE_API_KEY
|
|
160
|
-
|
|
161
|
-
// ✨ Nueva configuración de retry
|
|
105
|
+
baseUrl: process.env.NEXT_PUBLIC_COMMERCE_API_URL!,
|
|
106
|
+
publishableKey: process.env.NEXT_PUBLIC_COMMERCE_API_KEY!,
|
|
162
107
|
retry: {
|
|
163
|
-
maxRetries: 3,
|
|
164
|
-
timeoutMs: 10000,
|
|
165
|
-
initialDelayMs: 300,
|
|
166
|
-
backoffMultiplier: 2,
|
|
167
|
-
maxDelayMs: 5000,
|
|
108
|
+
maxRetries: 3, // 4 total attempts
|
|
109
|
+
timeoutMs: 10000, // 10s per request
|
|
110
|
+
initialDelayMs: 300, // Initial delay
|
|
111
|
+
backoffMultiplier: 2, // Exponential backoff (2x each retry)
|
|
112
|
+
maxDelayMs: 5000, // Cap at 5s
|
|
168
113
|
},
|
|
169
|
-
|
|
170
|
-
// ... resto de configuración
|
|
171
114
|
},
|
|
172
115
|
});
|
|
173
116
|
```
|
|
174
117
|
|
|
175
|
-
|
|
118
|
+
### 3. Circuit Breaker Pattern
|
|
176
119
|
|
|
177
|
-
|
|
120
|
+
The Circuit Breaker is **fully implemented** in `packages/adapters/src/resilience/`. It prevents cascading failures when the backend is consistently unavailable.
|
|
178
121
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
Request 1 → Network glitch → ❌ FAIL → Error al usuario
|
|
182
|
-
Probabilidad de fallo visible: ~5-10% en condiciones normales
|
|
183
|
-
```
|
|
122
|
+
```typescript
|
|
123
|
+
import { CircuitBreaker, backendCircuitBreaker } from '@thorprovider/adapters';
|
|
184
124
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
125
|
+
// Use the pre-configured global instance
|
|
126
|
+
const products = await backendCircuitBreaker.execute(
|
|
127
|
+
() => commerce.getProducts(),
|
|
128
|
+
[] // Fallback: return empty array if circuit is open
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
// Or create a custom instance
|
|
132
|
+
const breaker = new CircuitBreaker({
|
|
133
|
+
failureThreshold: 5,
|
|
134
|
+
resetTimeout: 60000,
|
|
135
|
+
name: 'medusa-api',
|
|
136
|
+
});
|
|
189
137
|
```
|
|
190
138
|
|
|
191
|
-
|
|
192
|
-
- ✅ **90-98% reducción en errores visibles** al usuario
|
|
193
|
-
- ✅ **Mejor experiencia de usuario** sin interrupciones
|
|
194
|
-
- ✅ **Resiliencia ante problemas de red transitorios**
|
|
195
|
-
- ✅ **Mejor observabilidad** con logs detallados
|
|
196
|
-
- ✅ **Configuración flexible** por entorno (dev/staging/prod)
|
|
139
|
+
See [resilience/README.md](../src/resilience/README.md) for full Circuit Breaker documentation.
|
|
197
140
|
|
|
198
141
|
---
|
|
199
142
|
|
|
200
|
-
##
|
|
201
|
-
|
|
202
|
-
### Escenarios de Prueba
|
|
203
|
-
|
|
204
|
-
1. **Backend funcionando normalmente**
|
|
205
|
-
- ✅ Debe funcionar igual que antes
|
|
206
|
-
- ✅ Sin delays innecesarios
|
|
207
|
-
- ✅ Log: "Fetch successful" en primer intento
|
|
208
|
-
|
|
209
|
-
2. **Backend lento (respuesta > 10s)**
|
|
210
|
-
- ✅ Timeout después de 10 segundos
|
|
211
|
-
- ✅ Retry automático
|
|
212
|
-
- ✅ Log: "Request timeout", luego reintento
|
|
213
|
-
|
|
214
|
-
3. **Backend temporalmente caído**
|
|
215
|
-
- ✅ 4 intentos totales (1 original + 3 retries)
|
|
216
|
-
- ✅ Delays: 300ms, 600ms, 1200ms
|
|
217
|
-
- ✅ Log: "Fetch failed after all retries"
|
|
218
|
-
|
|
219
|
-
4. **Red intermitente**
|
|
220
|
-
- ✅ Recovery automático en siguiente intento exitoso
|
|
221
|
-
- ✅ Sin error visible al usuario si se recupera antes del 4º intento
|
|
143
|
+
## 📊 Expected Impact
|
|
222
144
|
|
|
223
|
-
###
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
# Verificar tipos
|
|
230
|
-
yarn type-check
|
|
231
|
-
|
|
232
|
-
# Correr tests (cuando existan)
|
|
233
|
-
yarn test
|
|
145
|
+
### Before (No Retry Logic)
|
|
146
|
+
```
|
|
147
|
+
Request → Network glitch → ❌ FAIL → Error shown to user
|
|
148
|
+
Visible failure rate: ~5-10% under normal conditions
|
|
149
|
+
```
|
|
234
150
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
151
|
+
### After (With Retry Logic)
|
|
152
|
+
```
|
|
153
|
+
Request → Network glitch → ⏳ Retry 1 (300ms) → ⏳ Retry 2 (600ms) → ✅ SUCCESS
|
|
154
|
+
Visible failure rate: ~0.1-0.5% (90-98% reduction)
|
|
238
155
|
```
|
|
239
156
|
|
|
240
157
|
---
|
|
241
158
|
|
|
242
|
-
## 🎛️
|
|
159
|
+
## 🎛️ Configuration by Environment
|
|
243
160
|
|
|
244
|
-
### Development (
|
|
161
|
+
### Development (fast feedback)
|
|
245
162
|
```typescript
|
|
246
163
|
retry: {
|
|
247
|
-
maxRetries: 2,
|
|
248
|
-
timeoutMs: 5000,
|
|
164
|
+
maxRetries: 2,
|
|
165
|
+
timeoutMs: 5000,
|
|
249
166
|
initialDelayMs: 200,
|
|
250
167
|
}
|
|
251
168
|
```
|
|
252
169
|
|
|
253
|
-
### Production (
|
|
170
|
+
### Production (maximum availability)
|
|
254
171
|
```typescript
|
|
255
172
|
retry: {
|
|
256
|
-
maxRetries: 5,
|
|
257
|
-
timeoutMs: 15000,
|
|
173
|
+
maxRetries: 5,
|
|
174
|
+
timeoutMs: 15000,
|
|
258
175
|
initialDelayMs: 500,
|
|
259
176
|
maxDelayMs: 10000,
|
|
260
177
|
}
|
|
261
178
|
```
|
|
262
179
|
|
|
263
|
-
### Testing/CI (
|
|
180
|
+
### Testing / CI (fail fast)
|
|
264
181
|
```typescript
|
|
265
182
|
retry: {
|
|
266
|
-
maxRetries: 0,
|
|
267
|
-
timeoutMs: 3000,
|
|
183
|
+
maxRetries: 0,
|
|
184
|
+
timeoutMs: 3000,
|
|
268
185
|
}
|
|
269
186
|
```
|
|
270
187
|
|
|
271
188
|
---
|
|
272
189
|
|
|
273
|
-
## 📝 Logging
|
|
274
|
-
|
|
275
|
-
### Logs Generados
|
|
190
|
+
## 📝 Logging
|
|
276
191
|
|
|
277
|
-
**
|
|
192
|
+
**DEBUG** (development only):
|
|
278
193
|
```
|
|
279
194
|
[FetchUtils] Fetch attempt { url, attempt: 1, maxAttempts: 4, timeout: 10000 }
|
|
280
195
|
[FetchUtils] Fetch successful { url, attempt: 1 }
|
|
281
196
|
```
|
|
282
197
|
|
|
283
|
-
**
|
|
198
|
+
**WARN** (retry attempts):
|
|
284
199
|
```
|
|
285
|
-
[FetchUtils] Fetch failed, retrying... {
|
|
286
|
-
url,
|
|
287
|
-
attempt: 2,
|
|
288
|
-
maxAttempts: 4,
|
|
289
|
-
nextRetryIn: '600ms',
|
|
290
|
-
error: 'fetch failed'
|
|
291
|
-
}
|
|
200
|
+
[FetchUtils] Fetch failed, retrying... { url, attempt: 2, maxAttempts: 4, nextRetryIn: '600ms', error: 'fetch failed' }
|
|
292
201
|
```
|
|
293
202
|
|
|
294
|
-
**
|
|
203
|
+
**ERROR** (all retries exhausted):
|
|
295
204
|
```
|
|
296
|
-
[FetchUtils] Fetch failed after all retries {
|
|
297
|
-
url,
|
|
298
|
-
totalAttempts: 4,
|
|
299
|
-
error: 'fetch failed'
|
|
300
|
-
}
|
|
205
|
+
[FetchUtils] Fetch failed after all retries { url, totalAttempts: 4, error: 'fetch failed' }
|
|
301
206
|
[MedusaProvider] Failed to fetch products { error: 'fetch failed' }
|
|
302
207
|
```
|
|
303
208
|
|
|
304
|
-
### Métricas Recomendadas (Future Work)
|
|
305
|
-
|
|
306
|
-
Para producción, considerar agregar:
|
|
307
|
-
- ✅ Contador de reintentos exitosos vs fallidos
|
|
308
|
-
- ✅ Latencia promedio de requests
|
|
309
|
-
- ✅ Rate de timeout por endpoint
|
|
310
|
-
- ✅ Alertas cuando retry rate > 10%
|
|
311
|
-
|
|
312
209
|
---
|
|
313
210
|
|
|
314
|
-
##
|
|
315
|
-
|
|
316
|
-
### 1. Circuit Breaker Pattern
|
|
317
|
-
Si el backend está completamente caído, evitar reintentos innecesarios:
|
|
318
|
-
|
|
319
|
-
```typescript
|
|
320
|
-
// Después de N fallos consecutivos, "abrir el circuito"
|
|
321
|
-
// y fallar rápido por X segundos antes de intentar de nuevo
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
### 2. Cache Layer
|
|
325
|
-
Para reducir dependencia en el backend:
|
|
326
|
-
|
|
327
|
-
```typescript
|
|
328
|
-
// Cache en memoria/Redis de productos frecuentes
|
|
329
|
-
// Fallback a cache si backend falla
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
### 3. Health Check Endpoint
|
|
333
|
-
Endpoint ligero para verificar salud del backend:
|
|
211
|
+
## 🧪 Testing
|
|
334
212
|
|
|
335
|
-
```
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
```
|
|
213
|
+
```bash
|
|
214
|
+
# Run adapter unit tests (includes fetch-utils and circuit breaker)
|
|
215
|
+
yarn workspace @thorprovider/adapters test
|
|
339
216
|
|
|
340
|
-
|
|
341
|
-
|
|
217
|
+
# Type check
|
|
218
|
+
yarn workspace @thorprovider/adapters type-check
|
|
342
219
|
|
|
343
|
-
|
|
344
|
-
|
|
220
|
+
# Build
|
|
221
|
+
yarn workspace @thorprovider/adapters build
|
|
345
222
|
```
|
|
346
223
|
|
|
347
224
|
---
|
|
348
225
|
|
|
349
|
-
##
|
|
350
|
-
|
|
351
|
-
### Archivos Modificados
|
|
352
|
-
|
|
353
|
-
1. **`packages/adapters/src/core/fetch-utils.ts`** (NUEVO)
|
|
354
|
-
- Utilidades de retry y timeout
|
|
226
|
+
## ✅ Validation Checklist
|
|
355
227
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
- Configuración por defecto de retry
|
|
366
|
-
|
|
367
|
-
### Documentación Relacionada
|
|
368
|
-
|
|
369
|
-
- `packages/adapters/README.md` - Arquitectura de adapters
|
|
370
|
-
- `packages/adapters/docs/providers-overview.md` - Providers overview
|
|
371
|
-
- `.github/copilot-instructions.md` - Copilot patterns
|
|
372
|
-
|
|
373
|
-
### Patrones Implementados
|
|
374
|
-
|
|
375
|
-
- ✅ **Retry Pattern** con Exponential Backoff
|
|
376
|
-
- ✅ **Timeout Pattern** con AbortController
|
|
377
|
-
- ✅ **Strategy Pattern** (configurable retry strategies)
|
|
378
|
-
- ✅ **Dependency Injection** (custom fetch en SDK)
|
|
228
|
+
- [x] Code compiles without TypeScript errors
|
|
229
|
+
- [x] Exports updated in `core/index.ts`
|
|
230
|
+
- [x] Default retry config added in V0 starter
|
|
231
|
+
- [x] Logging implemented at appropriate levels
|
|
232
|
+
- [x] Backward compatible (`retry` is optional)
|
|
233
|
+
- [x] Unit tests added (vitest)
|
|
234
|
+
- [x] Circuit Breaker implemented and exported
|
|
235
|
+
- [ ] Validation in staging environment (TODO)
|
|
236
|
+
- [ ] Production metrics baseline (TODO)
|
|
379
237
|
|
|
380
238
|
---
|
|
381
239
|
|
|
382
|
-
##
|
|
383
|
-
|
|
384
|
-
- [x] Código compila sin errores TypeScript
|
|
385
|
-
- [x] Exports actualizados en `core/index.ts`
|
|
386
|
-
- [x] Configuración por defecto agregada en V0 starter
|
|
387
|
-
- [x] Logging implementado en niveles apropiados
|
|
388
|
-
- [x] Documentación completa creada
|
|
389
|
-
- [x] Backward compatible (retry es opcional)
|
|
390
|
-
- [ ] Tests unitarios agregados (TODO)
|
|
391
|
-
- [ ] Tests de integración con backend real (TODO)
|
|
392
|
-
- [ ] Validación en entorno de staging (TODO)
|
|
393
|
-
- [ ] Deploy a producción (TODO)
|
|
394
|
-
|
|
395
|
-
---
|
|
240
|
+
## 🔗 Related Files
|
|
396
241
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
242
|
+
| File | Purpose |
|
|
243
|
+
|------|---------|
|
|
244
|
+
| `packages/adapters/src/core/fetch-utils.ts` | Retry + timeout utilities |
|
|
245
|
+
| `packages/adapters/src/core/index.ts` | Core exports |
|
|
246
|
+
| `packages/adapters/src/resilience/` | Circuit Breaker implementation |
|
|
247
|
+
| `packages/adapters/src/providers/medusa/index.ts` | MedusaProvider with retry injection |
|
|
248
|
+
| `packages/core/lib/commerce.ts` | Default retry config in V0 starter |
|
package/docs/getting-started.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Getting started"
|
|
3
3
|
purpose: "Provide a quick setup guide for integrating Thor adapters in mock or live backend mode"
|
|
4
|
-
lastReviewed: "2026-05-
|
|
4
|
+
lastReviewed: "2026-05-03"
|
|
5
5
|
type: how-to
|
|
6
6
|
tags: [setup, installation, adapters, medusa, configuration]
|
|
7
7
|
related:
|
|
@@ -12,7 +12,7 @@ related:
|
|
|
12
12
|
|
|
13
13
|
# Getting Started
|
|
14
14
|
|
|
15
|
-
Quick setup guide for integrating Thor adapters into
|
|
15
|
+
Quick setup guide for integrating Thor adapters into your project. Start with `mock` for backend-optional development, or configure a real backend immediately.
|
|
16
16
|
|
|
17
17
|
## Prerequisites
|
|
18
18
|
|
|
@@ -42,7 +42,7 @@ const liveCommerce = createProvider({
|
|
|
42
42
|
},
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
-
// Fetch products
|
|
45
|
+
// Fetch products (framework-agnostic)
|
|
46
46
|
const products = await commerce.getProducts({ first: 10 });
|
|
47
47
|
```
|
|
48
48
|
|
|
@@ -56,7 +56,7 @@ yarn add @thorprovider/adapters
|
|
|
56
56
|
|
|
57
57
|
### Step 2: Initialize the Provider
|
|
58
58
|
|
|
59
|
-
Create a shared instance for
|
|
59
|
+
Create a shared singleton instance for your application.
|
|
60
60
|
|
|
61
61
|
```typescript
|
|
62
62
|
// lib/commerce.ts
|
|
@@ -95,30 +95,60 @@ NEXT_PUBLIC_COMMERCE_API_URL=http://localhost:9000
|
|
|
95
95
|
NEXT_PUBLIC_COMMERCE_API_KEY=your_publishable_key_here
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
-
### Step 4: Use in
|
|
98
|
+
### Step 4: Use in Your Application
|
|
99
|
+
|
|
100
|
+
The adapters are framework-agnostic. Use them in any server or client context:
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
// Pure TypeScript / Node.js
|
|
104
|
+
import { commerce } from './lib/commerce';
|
|
105
|
+
|
|
106
|
+
const products = await commerce.getProducts();
|
|
107
|
+
console.log(products.map(p => p.title));
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
#### Framework Integration Example (Next.js App Router)
|
|
99
111
|
|
|
100
112
|
```typescript
|
|
101
|
-
// app/products/page.tsx
|
|
113
|
+
// app/products/page.tsx ← This is framework code, NOT adapter code
|
|
102
114
|
import { commerce } from '@/lib/commerce';
|
|
103
115
|
|
|
104
116
|
export default async function ProductsPage() {
|
|
105
117
|
const products = await commerce.getProducts();
|
|
106
|
-
|
|
118
|
+
|
|
107
119
|
return (
|
|
108
|
-
<
|
|
120
|
+
<ul>
|
|
109
121
|
{products.map(product => (
|
|
110
|
-
<
|
|
122
|
+
<li key={product.id}>{product.title}</li>
|
|
111
123
|
))}
|
|
112
|
-
</
|
|
124
|
+
</ul>
|
|
113
125
|
);
|
|
114
126
|
}
|
|
115
127
|
```
|
|
116
128
|
|
|
129
|
+
> ℹ️ The adapter (`commerce`) is framework-agnostic. Only the page component is Next.js-specific.
|
|
130
|
+
|
|
131
|
+
## Capability Checks
|
|
132
|
+
|
|
133
|
+
Before calling optional features, always check the provider's capabilities:
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
// ✅ Correct: check capability before calling
|
|
137
|
+
if (commerce.capabilities.advancedSearch && commerce.searchProductsAdvanced) {
|
|
138
|
+
const result = await commerce.searchProductsAdvanced({ query: 'laptop' });
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ✅ Correct: check auth availability before calling auth methods
|
|
142
|
+
if (commerce.auth) {
|
|
143
|
+
const customer = await commerce.auth.getCurrentCustomer();
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
117
147
|
## ✅ DO's
|
|
118
148
|
|
|
119
149
|
1. **Use Singleton Pattern:** Create one instance of the provider and export it.
|
|
120
150
|
2. **Handle Environment Variables:** Require `baseUrl` only for real backends.
|
|
121
|
-
3. **
|
|
151
|
+
3. **Check Capabilities:** Use `commerce.capabilities.featureName` before calling optional methods.
|
|
122
152
|
4. **Use TypeScript Types:** Leverage the exported types from `@thorprovider/adapters` for better DX.
|
|
123
153
|
5. **Configure Debug Mode:** Enable `debug: true` during development to trace API issues.
|
|
124
154
|
|
|
@@ -127,16 +157,8 @@ export default async function ProductsPage() {
|
|
|
127
157
|
1. **Don't hardcode credentials:** Never put URLs or API keys directly in the code.
|
|
128
158
|
2. **Don't create multiple instances:** Creating providers inside components causes memory leaks and inconsistent state.
|
|
129
159
|
3. **Don't expose Secret Keys:** Only use Publishable Keys in frontend code.
|
|
130
|
-
4. **Don't
|
|
131
|
-
5. **Don't
|
|
132
|
-
|
|
133
|
-
## Common Patterns
|
|
134
|
-
|
|
135
|
-
### Shared Client and Server Instance
|
|
136
|
-
Using a single file to export the provider ensure both SSR and Client-side transitions use the same configuration.
|
|
137
|
-
|
|
138
|
-
### Dynamic Backend Switching
|
|
139
|
-
You can switch providers (e.g., from 'medusa' to 'mock') for testing purposes by changing the configuration.
|
|
160
|
+
4. **Don't import React or Next.js in adapter code:** Keep adapters framework-agnostic.
|
|
161
|
+
5. **Don't call optional methods without checking:** `commerce.auth`, `commerce.searchProductsAdvanced`, etc. may be undefined.
|
|
140
162
|
|
|
141
163
|
## Common Errors & Solutions
|
|
142
164
|
|
|
@@ -152,14 +174,6 @@ You can switch providers (e.g., from 'medusa' to 'mock') for testing purposes by
|
|
|
152
174
|
**Cause:** Backend doesn't allow the frontend origin.
|
|
153
175
|
**Solution:** Add your frontend URL to Medusa server configuration.
|
|
154
176
|
|
|
155
|
-
## Common Pitfalls
|
|
156
|
-
|
|
157
|
-
> ⚠️ **Pitfall 1: Mixing Session and JWT**
|
|
158
|
-
> Ensure you consistently use one authentication method. JWT is preferred for headless setups.
|
|
159
|
-
|
|
160
|
-
> ⚠️ **Pitfall 2: Forgetting to regenerate types**
|
|
161
|
-
> If the backend data structure changes, ensure your adapter matches the expected interface.
|
|
162
|
-
|
|
163
177
|
## Troubleshooting
|
|
164
178
|
|
|
165
179
|
| Issue | Solution |
|