@thorprovider/adapters 4.2.1 → 5.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/PROVIDER_IMPLEMENTATION_ANALYSIS.md +659 -0
- package/dist/index.d.mts +225 -387
- package/dist/index.d.ts +225 -387
- package/dist/index.js +613 -3062
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +609 -3052
- 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 +4 -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/services-overview.md +4 -2
- package/package.json +1 -1
- package/quick-reference.md +33 -0
- package/src/backend/factory.ts +1 -11
- package/src/factory/index.ts +74 -52
- package/src/index.ts +5 -15
- package/src/providers/index.ts +1 -16
- 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/resilience/README.md +70 -48
- package/src/types/capabilities.types.ts +0 -250
- 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/bigcommerce/bigcommerce.ts +0 -6
- package/src/providers/bigcommerce/index.ts +0 -315
- package/src/providers/bigcommerce/transforms.ts +0 -227
- package/src/providers/magento/index.ts +0 -387
- package/src/providers/magento/magento.ts +0 -5
- package/src/providers/magento/queries.ts +0 -267
- package/src/providers/magento/transforms.ts +0 -249
- 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/providers/shopify/auth.ts +0 -402
- package/src/providers/shopify/index.ts +0 -444
- package/src/providers/shopify/mutations.ts +0 -247
- package/src/providers/shopify/queries.ts +0 -324
- package/src/providers/shopify/shopify.ts +0 -7
- package/src/providers/shopify/transforms.ts +0 -275
- package/src/providers/spree/index.ts +0 -344
- package/src/providers/spree/spree.ts +0 -5
- package/src/providers/spree/transforms.ts +0 -254
- package/src/providers/woocommerce/index.ts +0 -310
- package/src/providers/woocommerce/transforms.ts +0 -254
- package/src/providers/woocommerce/woocommerce.ts +0 -5
- package/src/types/capabilities.types.js +0 -141
- package/src/types/provider.js +0 -9
- package/src/utils/payment-utils.js +0 -152
|
@@ -1,13 +1,24 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
title: Retry Solution Summary
|
|
3
|
+
purpose: Executive summary of the fetch retry and circuit breaker resilience solution
|
|
4
|
+
type: reference
|
|
5
|
+
tags: [resilience, retry, fetch, summary]
|
|
6
|
+
related:
|
|
7
|
+
- fetch-reliability-solution.md
|
|
8
|
+
updated: 2026-05-03
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Resumen Ejecutivo — Solución a Errores de Fetch
|
|
2
12
|
|
|
3
13
|
## ❌ Problema
|
|
4
14
|
Errores frecuentes `Failed to fetch products: fetch failed` en el V0 starter al cargar productos desde el backend de Medusa.
|
|
5
15
|
|
|
6
16
|
## ✅ Solución Implementada
|
|
7
17
|
|
|
8
|
-
### 1.
|
|
18
|
+
### 1. Módulo de Retry Logic (`fetch-utils.ts`)
|
|
9
19
|
- **Ubicación**: `packages/adapters/src/core/fetch-utils.ts`
|
|
10
|
-
- **
|
|
20
|
+
- **Importación**: `import { fetchWithRetry } from '@thorprovider/adapters'` (no subpath exports)
|
|
21
|
+
- **Funcionalidad**:
|
|
11
22
|
- Reintentos automáticos con exponential backoff
|
|
12
23
|
- Timeout configurable (10s por defecto)
|
|
13
24
|
- Detección inteligente de errores retryables
|
|
@@ -17,10 +28,14 @@ Errores frecuentes `Failed to fetch products: fetch failed` en el V0 starter al
|
|
|
17
28
|
- **Ubicación**: `packages/adapters/src/providers/medusa/index.ts`
|
|
18
29
|
- **Cambios**:
|
|
19
30
|
- Nueva propiedad `retry?: RetryConfig` en `MedusaProviderConfig`
|
|
20
|
-
-
|
|
21
|
-
|
|
31
|
+
- Custom fetch inyectado en el constructor del SDK
|
|
32
|
+
|
|
33
|
+
### 3. Circuit Breaker (implementado)
|
|
34
|
+
- **Ubicación**: `packages/adapters/src/resilience/`
|
|
35
|
+
- **Estado**: ✅ Implementado y exportado desde `@thorprovider/adapters`
|
|
36
|
+
- **Uso**: `import { CircuitBreaker, backendCircuitBreaker } from '@thorprovider/adapters'`
|
|
22
37
|
|
|
23
|
-
###
|
|
38
|
+
### 4. Configuración por Defecto en V0 Starter
|
|
24
39
|
- **Ubicación**: `packages/core/lib/commerce.ts`
|
|
25
40
|
- **Configuración**:
|
|
26
41
|
```typescript
|
|
@@ -33,7 +48,7 @@ Errores frecuentes `Failed to fetch products: fetch failed` en el V0 starter al
|
|
|
33
48
|
}
|
|
34
49
|
```
|
|
35
50
|
|
|
36
|
-
## 📊 Impacto
|
|
51
|
+
## 📊 Impacto
|
|
37
52
|
- **90-98% reducción** en errores visibles al usuario
|
|
38
53
|
- Resiliencia ante problemas de red transitorios
|
|
39
54
|
- Mejor experiencia de usuario sin interrupciones
|
|
@@ -41,15 +56,15 @@ Errores frecuentes `Failed to fetch products: fetch failed` en el V0 starter al
|
|
|
41
56
|
## 🧪 Estado
|
|
42
57
|
- ✅ Código compila sin errores TypeScript
|
|
43
58
|
- ✅ Build de adapters exitoso
|
|
44
|
-
- ✅
|
|
45
|
-
- ✅
|
|
46
|
-
- 🔄 Pendiente:
|
|
59
|
+
- ✅ Tests unitarios con vitest (`yarn workspace @thorprovider/adapters test`)
|
|
60
|
+
- ✅ Circuit Breaker implementado
|
|
61
|
+
- 🔄 Pendiente: Validación en staging con métricas reales
|
|
47
62
|
|
|
48
63
|
## 📝 Documentación Completa
|
|
49
64
|
Ver [fetch-reliability-solution.md](./fetch-reliability-solution.md) para análisis detallado de causas, soluciones, y configuraciones avanzadas.
|
|
50
65
|
|
|
51
66
|
## 🚀 Next Steps
|
|
52
|
-
1. Probar en desarrollo: `yarn
|
|
67
|
+
1. Probar en desarrollo: `yarn workspace @thorprovider/core dev`
|
|
53
68
|
2. Verificar logs en consola mostrando reintentos
|
|
54
69
|
3. Confirmar reducción de errores en producción
|
|
55
70
|
4. (Opcional) Ajustar configuración de retry según métricas reales
|
package/docs/authentication.md
CHANGED
|
@@ -1,54 +1,61 @@
|
|
|
1
|
-
# Authentication
|
|
2
|
-
|
|
3
1
|
---
|
|
2
|
+
title: Authentication
|
|
3
|
+
purpose: Guide for setting up and using customer authentication with Thor adapters
|
|
4
4
|
type: how-to
|
|
5
5
|
tags: [auth, security, login, jwt, session, registration]
|
|
6
6
|
related:
|
|
7
7
|
- profile-management.md
|
|
8
8
|
- services-overview.md
|
|
9
9
|
- getting-started.md
|
|
10
|
-
updated: 2026-
|
|
10
|
+
updated: 2026-05-03
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
+
# Authentication
|
|
14
|
+
|
|
13
15
|
Complete guide to setting up and using authentication with Thor adapters.
|
|
14
16
|
|
|
17
|
+
> ⚠️ **Backend Disparity:** Authentication is only available on providers that expose an `auth` object. Check `commerce.auth` before calling any auth method. Stel Order and Mock providers do not support customer authentication.
|
|
18
|
+
|
|
15
19
|
## Prerequisites
|
|
16
20
|
|
|
17
|
-
- [Thor Adapters installed](getting-started.md
|
|
21
|
+
- [Thor Adapters installed](getting-started.md)
|
|
18
22
|
- A Medusa.js v2 backend configured with authentication providers
|
|
19
23
|
|
|
20
24
|
## Quick Example
|
|
21
25
|
|
|
22
26
|
```typescript
|
|
23
|
-
import {
|
|
27
|
+
import { createProvider } from '@thorprovider/adapters';
|
|
28
|
+
|
|
29
|
+
const commerce = createProvider({
|
|
30
|
+
provider: 'medusa',
|
|
31
|
+
config: {
|
|
32
|
+
baseUrl: process.env.NEXT_PUBLIC_COMMERCE_API_URL!,
|
|
33
|
+
publishableKey: process.env.NEXT_PUBLIC_COMMERCE_API_KEY!,
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
if (!commerce.auth) throw new Error('Auth not available for this provider');
|
|
24
38
|
|
|
25
|
-
//
|
|
39
|
+
// Login
|
|
26
40
|
const { customer } = await commerce.auth.login({
|
|
27
41
|
email: 'user@example.com',
|
|
28
42
|
password: 'SecurePassword123!',
|
|
29
43
|
});
|
|
30
44
|
|
|
31
|
-
console.log(`Welcome back, ${customer
|
|
45
|
+
console.log(`Welcome back, ${customer?.first_name}!`);
|
|
32
46
|
```
|
|
33
47
|
|
|
34
48
|
## Step-by-Step Instructions
|
|
35
49
|
|
|
36
|
-
### Step 1:
|
|
50
|
+
### Step 1: Check Auth Availability
|
|
37
51
|
|
|
38
|
-
|
|
52
|
+
Not all providers support authentication. Always guard before calling auth methods.
|
|
39
53
|
|
|
40
54
|
```typescript
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
// ... basic config
|
|
46
|
-
auth: {
|
|
47
|
-
method: 'jwt', // Recommended for SPAs
|
|
48
|
-
storage: 'localStorage', // Where to persist tokens
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
});
|
|
55
|
+
if (!commerce.auth) {
|
|
56
|
+
// Provider does not support customer auth (e.g. StelOrder, Mock)
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
52
59
|
```
|
|
53
60
|
|
|
54
61
|
### Step 2: Handle Registration
|
|
@@ -66,69 +73,89 @@ const { customer } = await commerce.auth.register({
|
|
|
66
73
|
|
|
67
74
|
### Step 3: Implement Login Flow
|
|
68
75
|
|
|
69
|
-
Create a login handler in your UI components.
|
|
70
|
-
|
|
71
76
|
```typescript
|
|
72
|
-
async function handleLogin(credentials) {
|
|
77
|
+
async function handleLogin(credentials: { email: string; password: string }) {
|
|
78
|
+
if (!commerce.auth) throw new Error('Auth not supported');
|
|
79
|
+
|
|
73
80
|
try {
|
|
74
|
-
await commerce.auth.login(credentials);
|
|
75
|
-
|
|
81
|
+
const { customer } = await commerce.auth.login(credentials);
|
|
82
|
+
return customer;
|
|
76
83
|
} catch (error) {
|
|
77
|
-
// Handle
|
|
84
|
+
// Handle CommerceError / invalid credentials
|
|
85
|
+
throw error;
|
|
78
86
|
}
|
|
79
87
|
}
|
|
80
88
|
```
|
|
81
89
|
|
|
82
90
|
### Step 4: Check Auth State
|
|
83
91
|
|
|
84
|
-
Protect routes or update UI based on authentication status.
|
|
85
|
-
|
|
86
92
|
```typescript
|
|
87
93
|
const isAuth = await commerce.auth.isAuthenticated();
|
|
88
94
|
const customer = await commerce.auth.getCurrentCustomer();
|
|
89
95
|
```
|
|
90
96
|
|
|
91
|
-
|
|
97
|
+
### Step 5: Logout
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
await commerce.auth.logout();
|
|
101
|
+
```
|
|
92
102
|
|
|
93
|
-
|
|
94
|
-
2. **Use Session for SSR:** If you use Next.js Server Components, Session cookies are more secure.
|
|
95
|
-
3. **Handle AuthErrors specifically:** Use `instanceof AuthError` to show user-friendly messages.
|
|
96
|
-
4. **Enable Auto-refresh:** Keep `autoRefresh: true` (default) to ensure seamless user sessions.
|
|
97
|
-
5. **Clear local state on logout:** Always call `commerce.auth.logout()` to clear stored tokens.
|
|
103
|
+
## Password Management
|
|
98
104
|
|
|
99
|
-
|
|
105
|
+
### Request Password Reset
|
|
100
106
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
5. **Don't hardcode login URLs:** The adapter knows where to send requests based on the provider.
|
|
107
|
+
```typescript
|
|
108
|
+
await commerce.auth.requestPasswordReset('user@example.com');
|
|
109
|
+
// Backend sends reset email
|
|
110
|
+
```
|
|
106
111
|
|
|
107
|
-
|
|
112
|
+
### Confirm Password Reset
|
|
108
113
|
|
|
109
|
-
### Protected Route Hook (React)
|
|
110
114
|
```typescript
|
|
111
|
-
|
|
112
|
-
const { customer, loading } = useAuth(); // custom hook
|
|
113
|
-
const router = useRouter();
|
|
114
|
-
|
|
115
|
-
useEffect(() => {
|
|
116
|
-
if (!loading && !customer) router.push('/login');
|
|
117
|
-
}, [customer, loading]);
|
|
118
|
-
|
|
119
|
-
return { customer, loading };
|
|
120
|
-
}
|
|
115
|
+
await commerce.auth.resetPassword(tokenFromEmail, 'NewPassword123!');
|
|
121
116
|
```
|
|
122
117
|
|
|
123
|
-
|
|
118
|
+
## Address Management
|
|
119
|
+
|
|
120
|
+
Auth providers also expose address operations:
|
|
121
|
+
|
|
124
122
|
```typescript
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
123
|
+
// List addresses
|
|
124
|
+
const addresses = await commerce.auth.getAddresses();
|
|
125
|
+
|
|
126
|
+
// Create address
|
|
127
|
+
const address = await commerce.auth.createAddress({
|
|
128
|
+
firstName: 'Jane',
|
|
129
|
+
lastName: 'Doe',
|
|
130
|
+
address1: '123 Main St',
|
|
131
|
+
city: 'Madrid',
|
|
132
|
+
postalCode: '28001',
|
|
133
|
+
countryCode: 'ES',
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Update address
|
|
137
|
+
await commerce.auth.updateAddress(address.id, { city: 'Barcelona' });
|
|
138
|
+
|
|
139
|
+
// Delete address
|
|
140
|
+
await commerce.auth.deleteAddress(address.id);
|
|
130
141
|
```
|
|
131
142
|
|
|
143
|
+
## ✅ DO's
|
|
144
|
+
|
|
145
|
+
1. **Guard with `commerce.auth`:** Always check the property exists before calling auth methods.
|
|
146
|
+
2. **Use JWT for headless SPAs:** More flexible and works better across different origins.
|
|
147
|
+
3. **Use Session for SSR:** Cookie-based sessions are more secure in server-rendered contexts.
|
|
148
|
+
4. **Handle errors specifically:** Catch and surface meaningful messages to the user.
|
|
149
|
+
5. **Clear state on logout:** Always call `commerce.auth.logout()` to clear stored tokens.
|
|
150
|
+
|
|
151
|
+
## ❌ DONT's
|
|
152
|
+
|
|
153
|
+
1. **Don't store sensitive data in the customer object:** Only use public profile fields.
|
|
154
|
+
2. **Don't handle JWT tokens manually:** Let the adapter manage storage and refresh headers.
|
|
155
|
+
3. **Don't use Secret Keys in the browser:** Authentication should only use Publishable Keys.
|
|
156
|
+
4. **Don't ignore the `isAuthenticated` check:** Verify auth status before protected operations.
|
|
157
|
+
5. **Don't import React or Next.js in adapter code:** Keep adapters framework-agnostic.
|
|
158
|
+
|
|
132
159
|
## Common Errors & Solutions
|
|
133
160
|
|
|
134
161
|
### Error: `AUTH_FAILED`
|
|
@@ -148,18 +175,44 @@ export async function getServerSideProps() {
|
|
|
148
175
|
> ⚠️ **Pitfall 1: CORS issues with Session cookies**
|
|
149
176
|
> Session-based auth requires matching domains or properly configured `cross-site` cookies in the backend.
|
|
150
177
|
|
|
151
|
-
> ⚠️ **Pitfall 2:
|
|
152
|
-
>
|
|
178
|
+
> ⚠️ **Pitfall 2: `commerce.auth` is undefined**
|
|
179
|
+
> Not all providers implement auth. Always check `if (commerce.auth)` before calling any method.
|
|
153
180
|
|
|
154
181
|
## Troubleshooting
|
|
155
182
|
|
|
156
183
|
| Issue | Solution |
|
|
157
184
|
|-------|----------|
|
|
158
|
-
| Token not sent in headers |
|
|
159
|
-
| Login works but profile fails |
|
|
185
|
+
| Token not sent in headers | Verify the provider config and that `publishableKey` is set |
|
|
186
|
+
| Login works but profile fails | Ensure `commerce.auth.getCurrentCustomer()` is called after login |
|
|
160
187
|
| Infinite login loop | Check cookie path and domain settings on the backend |
|
|
161
188
|
|
|
189
|
+
## Multi-Tenant: Linking Customer to Sales Channel
|
|
190
|
+
|
|
191
|
+
After login or registration, call `linkCustomerToChannel()` to associate the customer with the storefront's sales channel. This is **required** for the customer to appear in the admin panel of the correct tenant and is idempotent.
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
if (!commerce.auth) throw new Error('Auth not available');
|
|
195
|
+
|
|
196
|
+
const { customer } = await commerce.auth.login({
|
|
197
|
+
email: 'user@example.com',
|
|
198
|
+
password: 'SecurePassword123!',
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// Link the customer to the sales channel resolved from the publishable key
|
|
202
|
+
const { linked, reason } = await commerce.auth.linkCustomerToChannel();
|
|
203
|
+
// linked === true → new link created
|
|
204
|
+
// linked === true, reason === 'already_linked' → idempotent — no action needed
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Throws:**
|
|
208
|
+
- `ProviderAPIError('NOT_AUTHENTICATED', 401)` — login must succeed first
|
|
209
|
+
- `ProviderAPIError('CHANNEL_RESOLUTION_FAILED', 400)` — publishable key doesn't map to a channel
|
|
210
|
+
- `ProviderAPIError('CONFIGURATION_ERROR')` — provider instantiated without `baseUrl`/`publishableKey`
|
|
211
|
+
|
|
212
|
+
**Provider support:** Medusa only. Shopify and other stubs return `{ linked: false, reason: 'not_applicable' }`.
|
|
213
|
+
|
|
162
214
|
## Next Steps
|
|
163
215
|
|
|
164
216
|
- [Profile Management](profile-management.md)
|
|
165
217
|
- [Services Overview](services-overview.md)
|
|
218
|
+
- [Multi-Tenant Channel Scoping](multi-tenant-channel-scoping.md)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# Capabilities System
|
|
2
|
-
|
|
3
1
|
---
|
|
2
|
+
title: Capabilities System
|
|
3
|
+
purpose: Reference for backend capability detection and matrices
|
|
4
4
|
type: reference
|
|
5
5
|
tags: [architecture, capabilities, backend, detection]
|
|
6
6
|
related:
|
|
@@ -9,6 +9,8 @@ related:
|
|
|
9
9
|
updated: 2026-02-28
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
+
# Capabilities System
|
|
13
|
+
|
|
12
14
|
The capabilities system exposes which operations each backend provider supports, enabling adaptive UIs across platforms.
|
|
13
15
|
|
|
14
16
|
## Quick Reference
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Category Customization & Inheritance
|
|
3
|
+
purpose: Explains the 5-phase system for hiding, renaming, and adding custom categories on top of backend-provided ones
|
|
4
|
+
type: guide
|
|
5
|
+
tags: [categories, customization, metadata, multi-tenant, architecture]
|
|
6
|
+
related:
|
|
7
|
+
- providers-overview.md
|
|
8
|
+
- services-overview.md
|
|
9
|
+
- multi-tenant-channel-scoping.md
|
|
10
|
+
updated: 2026-05-03
|
|
11
|
+
---
|
|
12
|
+
|
|
1
13
|
# Category Customization & Inheritance
|
|
2
14
|
|
|
3
15
|
## The Problem (Simple Version)
|
|
@@ -58,7 +70,7 @@ Your store sells **only shoes**. But the backend (Medusa) gives you 10 categorie
|
|
|
58
70
|
"hidden": ["sandalias"],
|
|
59
71
|
"renamed": { "accesorios": "Extras" },
|
|
60
72
|
"custom": [
|
|
61
|
-
{ "id": "bestsellers", "name": "Bestsellers"
|
|
73
|
+
{ "id": "bestsellers", "name": "Bestsellers" }
|
|
62
74
|
]
|
|
63
75
|
}
|
|
64
76
|
}
|
|
@@ -74,7 +86,9 @@ What is a customization?
|
|
|
74
86
|
→ { hidden: ["id1", "id2"], renamed: {id: "newname"}, custom: [{...}] }
|
|
75
87
|
```
|
|
76
88
|
|
|
77
|
-
|
|
89
|
+
Defined once in `@thorprovider/types` so every layer speaks the same language.
|
|
90
|
+
|
|
91
|
+
> ⚠️ **Implementation status**: Category customization types are planned but not yet merged into `@thorprovider/types`. This document describes the intended architecture.
|
|
78
92
|
|
|
79
93
|
---
|
|
80
94
|
|
|
@@ -87,12 +101,12 @@ BackendMetadataAdapter
|
|
|
87
101
|
Your customizations
|
|
88
102
|
```
|
|
89
103
|
|
|
90
|
-
**Key principle**:
|
|
104
|
+
**Key principle**: Uses the backend's native **metadata storage**, not a separate database.
|
|
91
105
|
|
|
92
106
|
Why?
|
|
93
107
|
- **Simple**: No external SQLite/Supabase
|
|
94
108
|
- **Multi-tenant ready**: Each sales channel has its own metadata
|
|
95
|
-
- **
|
|
109
|
+
- **Agnostic**: Medusa, Shopify, WordPress — same interface
|
|
96
110
|
|
|
97
111
|
---
|
|
98
112
|
|
|
@@ -114,6 +128,8 @@ Output:
|
|
|
114
128
|
|
|
115
129
|
**Why L2?** This is business logic (not UI, not type definition). It belongs with the adapter.
|
|
116
130
|
|
|
131
|
+
> ⚠️ **Implementation status**: `mergeCategories()` service is planned. Not yet implemented in `packages/adapters/src/`.
|
|
132
|
+
|
|
117
133
|
---
|
|
118
134
|
|
|
119
135
|
### Phase 4: Expose via API (L5 — Route Handler)
|
|
@@ -123,13 +139,12 @@ GET /api/admin/categories
|
|
|
123
139
|
|
|
124
140
|
POST /api/admin/categories
|
|
125
141
|
→ Dashboard sends new customizations, backend saves them
|
|
126
|
-
|
|
127
|
-
PUT /api/admin/categories
|
|
128
|
-
→ (Optional) Update a single category (hide/rename)
|
|
129
142
|
```
|
|
130
143
|
|
|
131
144
|
**Why L5?** Only the app knows HTTP. Only the app can validate requests.
|
|
132
145
|
|
|
146
|
+
> ⚠️ **Implementation status**: API route is planned. Not yet implemented in `packages/core/`.
|
|
147
|
+
|
|
133
148
|
---
|
|
134
149
|
|
|
135
150
|
### Phase 5: Dashboard UI (L4 — Component)
|
|
@@ -149,18 +164,17 @@ PUT /api/admin/categories
|
|
|
149
164
|
│ ✨ Bestsellers │
|
|
150
165
|
│ [Edit] [Delete] │
|
|
151
166
|
├─────────────────────────────────┤
|
|
152
|
-
│ │
|
|
153
167
|
│ [+ Add Custom] [Save] │
|
|
154
168
|
└─────────────────────────────────┘
|
|
155
169
|
```
|
|
156
170
|
|
|
157
|
-
**
|
|
171
|
+
> ⚠️ **Implementation status**: Dashboard component is planned. Not yet implemented in `packages/components/`.
|
|
158
172
|
|
|
159
173
|
---
|
|
160
174
|
|
|
161
|
-
## Why This Architecture Is "
|
|
175
|
+
## Why This Architecture Is "Agnostic"
|
|
162
176
|
|
|
163
|
-
**
|
|
177
|
+
**Agnostic** = "Works with any backend, no vendor lock-in."
|
|
164
178
|
|
|
165
179
|
### Data Flow
|
|
166
180
|
|
|
@@ -176,7 +190,6 @@ PUT /api/admin/categories
|
|
|
176
190
|
│ BackendMetadataAdapter (Interface)│
|
|
177
191
|
│ - getMetadata() │
|
|
178
192
|
│ - setMetadata() │
|
|
179
|
-
│ (Same methods for all backends) │
|
|
180
193
|
└──────────┬───────────────────────────┘
|
|
181
194
|
│ (Phase 3: mergeCategories)
|
|
182
195
|
↓
|
|
@@ -210,8 +223,6 @@ PUT /api/admin/categories
|
|
|
210
223
|
- Phase 4 (API route)
|
|
211
224
|
- Phase 5 (UI component)
|
|
212
225
|
|
|
213
|
-
**Why?** All non-Phase-2 code depends on the **interface**, not the **implementation**.
|
|
214
|
-
|
|
215
226
|
---
|
|
216
227
|
|
|
217
228
|
## Key Concepts
|
|
@@ -243,21 +254,17 @@ Medusa: sales_channel.metadata = { category_customizations: {...} }
|
|
|
243
254
|
Shopify: custom field or app data store (same interface)
|
|
244
255
|
```
|
|
245
256
|
|
|
246
|
-
Result: One less database to maintain.
|
|
247
|
-
|
|
248
257
|
---
|
|
249
258
|
|
|
250
259
|
## Implementation Order (For Next Agent)
|
|
251
260
|
|
|
252
|
-
See `.development-guides/category-customization-implementation.md` for detailed step-by-step.
|
|
253
|
-
|
|
254
261
|
**TL;DR:**
|
|
255
|
-
1. Add types in L1
|
|
256
|
-
2. Extend metadata adapter in L2
|
|
262
|
+
1. Add types in L1 (`@thorprovider/types`)
|
|
263
|
+
2. Extend metadata adapter in L2 (`BackendMetadataAdapter`)
|
|
257
264
|
3. Create `mergeCategories()` service in L2
|
|
258
265
|
4. Integrate into `CommerceProvider.getCategories()` in L2
|
|
259
|
-
5. Create API route in L5
|
|
260
|
-
6. Create UI component in L4
|
|
266
|
+
5. Create API route in L5 (`packages/core/app/api/admin/categories/route.ts`)
|
|
267
|
+
6. Create UI component in L4 (`packages/components/`)
|
|
261
268
|
7. Integrate into dashboard in L5
|
|
262
269
|
|
|
263
270
|
---
|
|
@@ -299,11 +306,12 @@ Storefront should render it like base categories.
|
|
|
299
306
|
|
|
300
307
|
## Related Files
|
|
301
308
|
|
|
302
|
-
|
|
303
|
-
- **Types**: `packages/types/src/admin/category-customizations.ts`
|
|
304
|
-
- **Metadata adapter**: `packages/adapters/src/backend/BackendMetadataAdapter.ts`
|
|
305
|
-
- **Service**: `packages/adapters/src/services/category-service.ts`
|
|
306
|
-
- **Provider integration**: `packages/adapters/src/providers/medusa/index.ts` (getCategories)
|
|
307
|
-
- **API route**: `packages/core/app/api/admin/categories/route.ts`
|
|
308
|
-
- **Component**: `packages/components/src/patterns/modules/dashboard/CategoryCustomization.tsx`
|
|
309
|
+
> ⚠️ The following files are **planned** and do not yet exist in the codebase:
|
|
309
310
|
|
|
311
|
+
| File | Status |
|
|
312
|
+
|------|--------|
|
|
313
|
+
| `packages/types/src/admin/category-customizations.ts` | 🗓️ Planned |
|
|
314
|
+
| `packages/adapters/src/backend/BackendMetadataAdapter.ts` | ✅ Exists (extend for categories) |
|
|
315
|
+
| `packages/adapters/src/services/category-service.ts` | 🗓️ Planned |
|
|
316
|
+
| `packages/core/app/api/admin/categories/route.ts` | 🗓️ Planned |
|
|
317
|
+
| `packages/components/src/patterns/modules/dashboard/CategoryCustomization.tsx` | 🗓️ Planned |
|