@thorprovider/adapters 3.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/.turbo/turbo-build.log +18 -0
- package/.turbo/turbo-test.log +1 -0
- package/.turbo/turbo-type-check.log +0 -0
- package/AGENTS.md +1 -0
- package/CHANGELOG.md +271 -0
- package/README.md +634 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/index.d.mts +2524 -0
- package/dist/index.d.ts +2524 -0
- package/dist/index.js +8136 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +8045 -0
- package/dist/index.mjs.map +1 -0
- package/docs/RETRY_SOLUTION_SUMMARY.md +55 -0
- package/docs/authentication.md +165 -0
- package/docs/capabilities-system.md +143 -0
- package/docs/category-customization.md +309 -0
- package/docs/fetch-reliability-solution.md +399 -0
- package/docs/getting-started.md +154 -0
- package/docs/multi-tenant-channel-scoping.md +225 -0
- package/docs/profile-management.md +147 -0
- package/docs/providers-overview.md +85 -0
- package/docs/services-overview.md +135 -0
- package/docs/update-documentation.md +113 -0
- package/package.json +43 -0
- package/publish.log +93 -0
- package/quick-reference.md +169 -0
- package/src/admin/AdminAuthService.ts +88 -0
- package/src/admin/AdminSessionService.ts +91 -0
- package/src/admin/MetricsService.ts +145 -0
- package/src/admin/index.ts +13 -0
- package/src/backend/BackendMetadataAdapter.ts +88 -0
- package/src/backend/factory.ts +107 -0
- package/src/backend/index.ts +12 -0
- package/src/backend/medusa/MedusaMetadataAdapter.ts +139 -0
- package/src/core/errors.js +161 -0
- package/src/core/errors.ts +132 -0
- package/src/core/fetch-utils.js +240 -0
- package/src/core/fetch-utils.ts +227 -0
- package/src/core/index.ts +8 -0
- package/src/core/logger.js +63 -0
- package/src/core/logger.ts +37 -0
- package/src/factory/index.ts +102 -0
- package/src/index.ts +205 -0
- package/src/lib/money-transform.ts +80 -0
- package/src/metadata/channel-scope-validation.js +136 -0
- package/src/metadata/channel-scope-validation.ts +151 -0
- package/src/metadata/channel-scope.js +174 -0
- package/src/metadata/channel-scope.ts +215 -0
- package/src/metadata/index.js +31 -0
- package/src/metadata/index.ts +37 -0
- package/src/providers/bigcommerce/bigcommerce.ts +6 -0
- package/src/providers/bigcommerce/index.ts +315 -0
- package/src/providers/bigcommerce/transforms.ts +227 -0
- package/src/providers/index.ts +22 -0
- package/src/providers/magento/index.ts +387 -0
- package/src/providers/magento/magento.ts +5 -0
- package/src/providers/magento/queries.ts +267 -0
- package/src/providers/magento/transforms.ts +249 -0
- package/src/providers/medusa/auth.js +601 -0
- package/src/providers/medusa/auth.ts +496 -0
- package/src/providers/medusa/fulfillment-planner.ts +225 -0
- package/src/providers/medusa/index.js +2485 -0
- package/src/providers/medusa/index.ts +4239 -0
- package/src/providers/medusa/medusa.ts +8 -0
- package/src/providers/medusa/payment.js +265 -0
- package/src/providers/medusa/payment.ts +269 -0
- package/src/providers/medusa/transforms.js +505 -0
- package/src/providers/medusa/transforms.ts +625 -0
- package/src/providers/shopify/index.ts +444 -0
- package/src/providers/shopify/mutations.ts +102 -0
- package/src/providers/shopify/queries.ts +324 -0
- package/src/providers/shopify/shopify.ts +7 -0
- package/src/providers/shopify/transforms.ts +270 -0
- package/src/providers/spree/index.ts +344 -0
- package/src/providers/spree/spree.ts +5 -0
- package/src/providers/spree/transforms.ts +254 -0
- package/src/providers/woocommerce/index.ts +310 -0
- package/src/providers/woocommerce/transforms.ts +254 -0
- package/src/providers/woocommerce/woocommerce.ts +5 -0
- package/src/resilience/README.md +299 -0
- package/src/resilience/circuit-breaker.ts +222 -0
- package/src/resilience/index.ts +10 -0
- package/src/types/capabilities.types.js +141 -0
- package/src/types/capabilities.types.ts +366 -0
- package/src/types/index.ts +24 -0
- package/src/types/provider.js +9 -0
- package/src/types/provider.ts +9 -0
- package/src/types/ui-responses.ts +261 -0
- package/src/utils/city-coordinates.ts +40 -0
- package/src/utils/geolocation.ts +52 -0
- package/src/utils/payment-utils.js +152 -0
- package/src/utils/payment-utils.ts +203 -0
- package/tsconfig.json +15 -0
- package/tsup.config.ts +14 -0
package/README.md
ADDED
|
@@ -0,0 +1,634 @@
|
|
|
1
|
+
# @thorprovider/adapters v2.0
|
|
2
|
+
|
|
3
|
+
**Unified commerce adapters for Thor Commerce ecosystem**
|
|
4
|
+
|
|
5
|
+
Framework-agnostic TypeScript library providing a unified interface for multiple e-commerce platforms (Medusa, Shopify, WooCommerce, etc.).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🎯 Why This Package Exists
|
|
10
|
+
|
|
11
|
+
**Problem**: Direct backend integration leads to:
|
|
12
|
+
- ❌ Vendor lock-in (Medusa code in 50+ files → impossible to switch to Shopify)
|
|
13
|
+
- ❌ Inconsistent data structures (Medusa Product ≠ Shopify Product ≠ WooCommerce Product)
|
|
14
|
+
- ❌ Duplicate business logic (cart logic in 10 apps = 10x maintenance)
|
|
15
|
+
- ❌ Framework coupling (Medusa SDK tied to Next.js server actions)
|
|
16
|
+
|
|
17
|
+
**Solution**: @thorprovider/adapters provides:
|
|
18
|
+
- ✅ **Unified Interface**: One API for all backends (switch provider with 1 line change)
|
|
19
|
+
- ✅ **Bidirectional Compatibility Layer**: Full read **and** write support — Backend responses → `@thorprovider/types` (inbound) and `@thorprovider/types` → Backend API payloads (outbound). Mutations (cart, orders, customers, addresses) are fully abstracted.
|
|
20
|
+
- ✅ **Framework Agnostic**: Works with Next.js, Remix, Astro, or vanilla JS
|
|
21
|
+
- ✅ **Capability Detection**: 21 capabilities for adaptive UIs (does backend support X?)
|
|
22
|
+
|
|
23
|
+
**Inspired by**: Vercel Commerce (multi-provider), Stripe SDK (unified payments), Auth.js (multi-auth-provider)
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 💰 ROI / Value Proposition
|
|
28
|
+
|
|
29
|
+
### Backend Migration Cost Reduction
|
|
30
|
+
```typescript
|
|
31
|
+
// Without @thorprovider/adapters (direct Medusa integration)
|
|
32
|
+
Migration from Medusa → Shopify:
|
|
33
|
+
1. Find all Medusa SDK calls: 200+ files
|
|
34
|
+
2. Rewrite each call to Shopify API: 40 hours
|
|
35
|
+
3. Transform data structures: 20 hours
|
|
36
|
+
4. Update tests: 10 hours
|
|
37
|
+
5. Fix bugs from migration: 20 hours
|
|
38
|
+
= 90 hours per app × $100/hour = $9,000 per app
|
|
39
|
+
|
|
40
|
+
For 10 apps: $90,000 migration cost
|
|
41
|
+
|
|
42
|
+
// With @thorprovider/adapters
|
|
43
|
+
1. Change provider in config: 1 line
|
|
44
|
+
createProvider({ provider: 'shopify' }) // was 'medusa'
|
|
45
|
+
2. Verify transforms work: 2 hours
|
|
46
|
+
3. Update env variables: 1 hour
|
|
47
|
+
= 3 hours × $100/hour = $300 per app
|
|
48
|
+
|
|
49
|
+
For 10 apps: $3,000 migration cost
|
|
50
|
+
|
|
51
|
+
// ROI: $90,000 - $3,000 = $87,000 saved (97% cost reduction)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Development Speed
|
|
55
|
+
```typescript
|
|
56
|
+
// Traditional approach (per feature)
|
|
57
|
+
1. Learn Medusa SDK: 4 hours
|
|
58
|
+
2. Implement getProducts(): 2 hours
|
|
59
|
+
3. Handle errors: 1 hour
|
|
60
|
+
4. Add retry logic: 2 hours
|
|
61
|
+
5. Write tests: 2 hours
|
|
62
|
+
= 11 hours per feature
|
|
63
|
+
|
|
64
|
+
// With @thorprovider/adapters
|
|
65
|
+
1. const products = await commerce.getProducts();
|
|
66
|
+
2. Already has: types, errors, retries, tests
|
|
67
|
+
= 10 minutes per feature
|
|
68
|
+
|
|
69
|
+
// Speed improvement: 66x faster
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Multi-Tenant Platforms
|
|
73
|
+
```typescript
|
|
74
|
+
// Scenario: Platform with 100 clients
|
|
75
|
+
// 50 clients use Medusa, 30 use Shopify, 20 use WooCommerce
|
|
76
|
+
|
|
77
|
+
// Without adapters:
|
|
78
|
+
- Maintain 3 separate codebases
|
|
79
|
+
- 3x development cost
|
|
80
|
+
- 3x bug surface area
|
|
81
|
+
|
|
82
|
+
// With @thorprovider/adapters:
|
|
83
|
+
- Single codebase
|
|
84
|
+
- Provider selected per client: clientConfig.provider
|
|
85
|
+
- 1x development, 1x maintenance
|
|
86
|
+
|
|
87
|
+
// ROI: 67% cost reduction for multi-backend platforms
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 🤖 AI Agent Decision Framework
|
|
93
|
+
|
|
94
|
+
### When to Use @thorprovider/adapters
|
|
95
|
+
✅ Integrating with e-commerce backends (Medusa, Shopify, custom APIs)
|
|
96
|
+
✅ Building multi-tenant platforms (different backends per client)
|
|
97
|
+
✅ Need to switch backends in the future (reduce vendor lock-in)
|
|
98
|
+
✅ Framework-agnostic apps (Next.js, Remix, Astro, etc.)
|
|
99
|
+
✅ Want standardized data shapes across backends
|
|
100
|
+
|
|
101
|
+
### When NOT to Use
|
|
102
|
+
❌ Single backend forever + never migrating (direct SDK might be simpler)
|
|
103
|
+
❌ Need 100% of platform-specific features (use native SDK)
|
|
104
|
+
❌ Non-commerce application (no products/carts/orders)
|
|
105
|
+
❌ Backend has no JS SDK (need REST/GraphQL client first)
|
|
106
|
+
|
|
107
|
+
### Decision Tree
|
|
108
|
+
```
|
|
109
|
+
Need e-commerce backend integration?
|
|
110
|
+
├─ YES → Multiple backends OR might switch?
|
|
111
|
+
│ ├─ YES → Use @thorprovider/adapters (future-proof)
|
|
112
|
+
│ └─ NO → Single backend forever?
|
|
113
|
+
│ ├─ Need framework flexibility → Use @thorprovider/adapters
|
|
114
|
+
│ └─ Tight coupling OK → Use native SDK
|
|
115
|
+
└─ NO → Use generic HTTP client (fetch/axios)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Key Rules for AI Agents
|
|
119
|
+
1. **NEVER import React/Next.js** in adapters code (framework-agnostic layer)
|
|
120
|
+
2. **ALWAYS transform to @thorprovider/types** (don't expose backend-specific types)
|
|
121
|
+
3. **ALWAYS use try/catch** with proper error handling (CommerceError)
|
|
122
|
+
4. **DOCUMENT capabilities** when adding new backends (updateCapabilities())
|
|
123
|
+
5. **WRITE transform functions** for each data type (transformProduct, transformCart)
|
|
124
|
+
|
|
125
|
+
### Supported Providers
|
|
126
|
+
|
|
127
|
+
Currently implemented:
|
|
128
|
+
- ✅ **Medusa JS** (v2) — Full implementation with multi-tenant support
|
|
129
|
+
|
|
130
|
+
Planned (Phase 2–3):
|
|
131
|
+
- 🟡 **Shopify** — In progress
|
|
132
|
+
- 🟡 **BigCommerce** — Planned
|
|
133
|
+
- 🟡 **WooCommerce** — Planned
|
|
134
|
+
- 🟡 **Spree** — Planned
|
|
135
|
+
- 🟡 **Magento** — Planned
|
|
136
|
+
|
|
137
|
+
### Provider Selection
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
// Medusa backend (currently the only supported provider)
|
|
141
|
+
const commerce = createProvider({
|
|
142
|
+
provider: 'medusa',
|
|
143
|
+
config: {
|
|
144
|
+
baseUrl: process.env.NEXT_PUBLIC_COMMERCE_API_URL,
|
|
145
|
+
publishableKey: process.env.NEXT_PUBLIC_COMMERCE_API_KEY,
|
|
146
|
+
currencyCode: 'USD',
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const products = await commerce.getProducts();
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### 🔒 Multi-Tenant Data Isolation: Sales Channel Filtering
|
|
154
|
+
|
|
155
|
+
**Design Principle**: Every product query in `@thorprovider/adapters` includes explicit **sales channel filtering** to ensure one storefront instance never sees another tenant's products.
|
|
156
|
+
|
|
157
|
+
#### The 1:1 Relationship
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
V0 Starter Instance (Storefront)
|
|
161
|
+
↓
|
|
162
|
+
NEXT_PUBLIC_SALES_CHANNEL_ID = "sc_01KJJY1..."
|
|
163
|
+
↓
|
|
164
|
+
All product queries automatically filter:
|
|
165
|
+
{
|
|
166
|
+
sales_channel_id: [process.env.NEXT_PUBLIC_SALES_CHANNEL_ID],
|
|
167
|
+
...
|
|
168
|
+
}
|
|
169
|
+
↓
|
|
170
|
+
Backend returns products linked to THIS channel ONLY
|
|
171
|
+
↓
|
|
172
|
+
Result: Multi-tenant isolation at data layer
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
#### Implementation (Medusa Provider)
|
|
176
|
+
|
|
177
|
+
All product retrieval methods filter by sales channel (updated 2026-03-26):
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
// Example: getProducts() in src/providers/medusa/index.ts
|
|
181
|
+
async getProducts(options = {}) {
|
|
182
|
+
const salesChannelId = process.env.NEXT_PUBLIC_SALES_CHANNEL_ID;
|
|
183
|
+
|
|
184
|
+
const queryParams = {
|
|
185
|
+
q: options.query,
|
|
186
|
+
limit: options.first ?? 100,
|
|
187
|
+
// ... other params
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
// CRITICAL: Apply sales channel filter
|
|
191
|
+
if (salesChannelId) {
|
|
192
|
+
queryParams.sales_channel_id = [salesChannelId];
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const { products } = await this.sdk.store.product.list(queryParams);
|
|
196
|
+
return products.map(p => transformProduct(p));
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**Methods implementing this pattern**:
|
|
201
|
+
- ✅ `getProducts()` — Main product listing
|
|
202
|
+
- ✅ `getProduct()` — Single product by handle
|
|
203
|
+
- ✅ `getCollectionProducts()` — Products in collection
|
|
204
|
+
- ✅ `getCategoryProducts()` — Products in category
|
|
205
|
+
- ✅ `createLineItem()` — Pre-check variant validation
|
|
206
|
+
|
|
207
|
+
#### Why Explicit Filtering?
|
|
208
|
+
|
|
209
|
+
> **Question**: Medusa's publishable API key already restricts unauthorized endpoints. Why add `sales_channel_id` parameter?
|
|
210
|
+
>
|
|
211
|
+
> **Answer**: **Defense-in-depth + Future-proofing**
|
|
212
|
+
> 1. **API Key**: Prevents unauthorized endpoint access (✅)
|
|
213
|
+
> 2. **sales_channel_id parameter**: Prevents data leakage within store endpoints (✅)
|
|
214
|
+
> 3. **Together**: Best practice for multi-tenant SaaS platforms
|
|
215
|
+
|
|
216
|
+
#### Configuration (Per Platform)
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
// Medusa (REQUIRED for multi-tenant)
|
|
220
|
+
const commerce = createProvider({
|
|
221
|
+
provider: 'medusa',
|
|
222
|
+
config: {
|
|
223
|
+
baseUrl: process.env.NEXT_PUBLIC_COMMERCE_API_URL,
|
|
224
|
+
publishableKey: process.env.NEXT_PUBLIC_COMMERCE_API_KEY,
|
|
225
|
+
currencyCode: 'USD',
|
|
226
|
+
// CRITICAL: Derived from env, used in every product query
|
|
227
|
+
// salesChannelId: process.env.NEXT_PUBLIC_SALES_CHANNEL_ID,
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// Shopify (when implemented — optional parameter, uses default if not set)
|
|
232
|
+
// NEXT_PUBLIC_SHOPIFY_STOREFRONT_ID = '...'
|
|
233
|
+
|
|
234
|
+
// WooCommerce (optional — uses default site)
|
|
235
|
+
// NEXT_PUBLIC_WOOCOMMERCE_SITE_ID = '1'
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
**Learn more**: [packages/core/docs/storefront-sales-channel-mapping.md](../core/docs/storefront-sales-channel-mapping.md) for platform-specific implementation details and multi-brand deployment patterns.
|
|
239
|
+
|
|
240
|
+
### Adding a New Provider
|
|
241
|
+
|
|
242
|
+
To implement Shopify, BigCommerce, or another platform:
|
|
243
|
+
|
|
244
|
+
1. **Read the standardized guide**: [.development-guides/add-new-provider.md](../../.development-guides/add-new-provider.md)
|
|
245
|
+
2. **Follow 11-step checklist**: Registration in L1, implementation in L2, factory updates, tests, docs
|
|
246
|
+
3. **Use transform pattern**: Convert platform-specific types to `@thorprovider/types`
|
|
247
|
+
|
|
248
|
+
Example checklist item:
|
|
249
|
+
- [ ] Update `SupportedProviderType` enum in `packages/types/src/provider.ts`
|
|
250
|
+
- [ ] Create `packages/adapters/src/providers/[platform]/index.ts`
|
|
251
|
+
- [ ] Implement `CommerceProvider` interface
|
|
252
|
+
- [ ] Add discriminated union case to factory
|
|
253
|
+
- [ ] Write tests
|
|
254
|
+
- [ ] Update documentation
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
### Transform Function Pattern
|
|
258
|
+
```typescript
|
|
259
|
+
// ALWAYS follow this pattern when adding new providers
|
|
260
|
+
export function transformProduct(backendProduct: any): Product {
|
|
261
|
+
return {
|
|
262
|
+
id: backendProduct.id,
|
|
263
|
+
handle: backendProduct.slug,
|
|
264
|
+
title: backendProduct.name,
|
|
265
|
+
// ... transform all fields to @thorprovider/types shape
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Imbricación Principle: No Downstream Transformations
|
|
271
|
+
|
|
272
|
+
**@thorprovider/adapters is the ONLY transformation layer** in the Thor Stack architecture.
|
|
273
|
+
|
|
274
|
+
```
|
|
275
|
+
Backend (Medusa/Shopify) → Adapter.transform() → @thorprovider/types → Elements → Starters
|
|
276
|
+
↑ ONLY transformation point
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
**Key Rules**:
|
|
280
|
+
1. **Adapters transform ONCE**: Backend-specific format → `@thorprovider/types` unified format
|
|
281
|
+
2. **Elements consume directly**: Import types from `@thorprovider/types`, never create local copies
|
|
282
|
+
3. **Starters pass through**: Use `@thorprovider/types` directly, no UI-specific transformations
|
|
283
|
+
4. **Single source of truth**: `@thorprovider/types` defines the contract for entire system
|
|
284
|
+
|
|
285
|
+
**Why this matters**:
|
|
286
|
+
- Changes to `@thorprovider/types` propagate instantly (no intermediate layers to update)
|
|
287
|
+
- Zero duplicate type definitions (DRY principle)
|
|
288
|
+
- 78× faster development (1 change vs 78 files)
|
|
289
|
+
|
|
290
|
+
**Example: Order Management**
|
|
291
|
+
```typescript
|
|
292
|
+
// ✅ CORRECT: Adapter transforms backend → @thorprovider/types
|
|
293
|
+
// File: src/providers/medusa/transforms.ts
|
|
294
|
+
export function transformOrder(medusaOrder: any): Order {
|
|
295
|
+
return {
|
|
296
|
+
id: medusaOrder.id,
|
|
297
|
+
orderNumber: medusaOrder.display_id,
|
|
298
|
+
status: transformOrderStatus(medusaOrder.status),
|
|
299
|
+
createdAt: medusaOrder.created_at,
|
|
300
|
+
items: medusaOrder.items.map(transformOrderItem),
|
|
301
|
+
total: { amount: medusaOrder.total.toString(), currencyCode: 'USD' },
|
|
302
|
+
// ... all fields mapped to @thorprovider/types Order
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// ✅ CORRECT: Elements import from @thorprovider/types
|
|
307
|
+
// File: packages/components/src/patterns/modules/profile/ProfileModule.props.ts
|
|
308
|
+
import type { Order } from '@thorprovider/types'
|
|
309
|
+
export interface ProfileModuleProps {
|
|
310
|
+
orders?: Order[] // Uses @thorprovider/types directly
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// ✅ CORRECT: Starters pass through
|
|
314
|
+
// File: packages/core/app/[locale]/profile/page.tsx
|
|
315
|
+
const orders = await commerce.getOrders(customerId)
|
|
316
|
+
<ProfileModule orders={orders} /> // No transformation!
|
|
317
|
+
|
|
318
|
+
// ❌ WRONG: Creating intermediate types in L5
|
|
319
|
+
interface UIOrder { // ← Duplicates @thorprovider/types Order
|
|
320
|
+
id: string
|
|
321
|
+
date: string // ← Different field name (vs createdAt)
|
|
322
|
+
total: number // ← Different structure (vs Money object)
|
|
323
|
+
}
|
|
324
|
+
function transformOrderForUI(order: Order): UIOrder { ... } // ← Unnecessary layer
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
**Reference**: See commits c3d5efd2 and cbaa83ef for real-world refactoring that eliminated an unnecessary transformer by making ProfileModule use `@thorprovider/types` directly.
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## Quick Start
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
yarn add @thorprovider/adapters
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
import { createProvider } from '@thorprovider/adapters';
|
|
339
|
+
|
|
340
|
+
const commerce = createProvider({
|
|
341
|
+
provider: 'medusa',
|
|
342
|
+
config: {
|
|
343
|
+
baseUrl: process.env.NEXT_PUBLIC_COMMERCE_API_URL!,
|
|
344
|
+
publishableKey: process.env.NEXT_PUBLIC_COMMERCE_API_KEY!,
|
|
345
|
+
},
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
// Use unified API
|
|
349
|
+
const products = await commerce.getProducts();
|
|
350
|
+
const cart = await commerce.createCart();
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
## Features
|
|
356
|
+
|
|
357
|
+
✅ **Multi-platform** - Single API for Medusa, Shopify, WooCommerce, etc.
|
|
358
|
+
✅ **Type-safe** - Full TypeScript support with strict types
|
|
359
|
+
✅ **Framework agnostic** - Works with Next.js, Remix, Astro, or any JS framework
|
|
360
|
+
✅ **Easy swapping** - Change backends with minimal code changes
|
|
361
|
+
✅ **Capability Detection** - 21 backend capabilities for adaptive UIs (NEW in v2.0)
|
|
362
|
+
✅ **Authentication** - Built-in JWT and session-based auth
|
|
363
|
+
✅ **Auto-refresh** - Automatic token refresh for JWT
|
|
364
|
+
✅ **Profile Management** - Complete customer operations with capability awareness
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## Core Concepts
|
|
369
|
+
|
|
370
|
+
### Unified Interface
|
|
371
|
+
|
|
372
|
+
All providers implement the same interface:
|
|
373
|
+
|
|
374
|
+
```typescript
|
|
375
|
+
interface CommerceProvider {
|
|
376
|
+
readonly name: string;
|
|
377
|
+
readonly version: string;
|
|
378
|
+
readonly capabilities: BackendCapabilities; // NEW!
|
|
379
|
+
|
|
380
|
+
// Products
|
|
381
|
+
getProduct(handle: string): Promise<Product | undefined>;
|
|
382
|
+
getProducts(options?: GetProductsOptions): Promise<Product[]>;
|
|
383
|
+
|
|
384
|
+
// Collections
|
|
385
|
+
getCollection(handle: string): Promise<Collection | undefined>;
|
|
386
|
+
getCollections(): Promise<Collection[]>;
|
|
387
|
+
|
|
388
|
+
// Cart
|
|
389
|
+
createCart(): Promise<Cart>;
|
|
390
|
+
addToCart(cartId: string, lines: CartLineInput[]): Promise<Cart>;
|
|
391
|
+
|
|
392
|
+
// Auth (optional)
|
|
393
|
+
auth?: AuthService;
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### Capabilities System (v2.0)
|
|
398
|
+
|
|
399
|
+
Check backend support before attempting operations:
|
|
400
|
+
|
|
401
|
+
```typescript
|
|
402
|
+
// Check if backend supports email updates
|
|
403
|
+
if (commerce.capabilities.updateEmail) {
|
|
404
|
+
// Show email field as editable
|
|
405
|
+
} else {
|
|
406
|
+
// Show as disabled with info message
|
|
407
|
+
}
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
**21 Capabilities** across 5 categories:
|
|
411
|
+
- Profile: updateProfile, updateEmail, changePassword, deleteAccount, etc.
|
|
412
|
+
- Avatar: uploadAvatar, avatarViaMetadata
|
|
413
|
+
- Cart: promoCode, giftCards, notes, customAttributes
|
|
414
|
+
- Products: variants, categories, inventory, digital, reviews
|
|
415
|
+
- Auth: JWT, session, OAuth, passwordless
|
|
416
|
+
|
|
417
|
+
**See**: [Capabilities System](./docs/capabilities-system.md)
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## Supported Platforms
|
|
422
|
+
|
|
423
|
+
| Platform | Status | Documentation |
|
|
424
|
+
|----------|--------|---------------|
|
|
425
|
+
| **Medusa v2** | ✅ Production Ready | [Medusa Docs](https://docs.medusajs.com/) |
|
|
426
|
+
| **Shopify** | 🚧 Planned | [Shopify API](https://shopify.dev/api/storefront) |
|
|
427
|
+
| **WooCommerce** | 🗓️ Roadmap | [WooCommerce API](https://woocommerce.github.io/woocommerce-rest-api-docs/) |
|
|
428
|
+
| **BigCommerce** | 🗓️ Roadmap | [BigCommerce API](https://developer.bigcommerce.com/) |
|
|
429
|
+
| **Local (Mock)** | 🗓️ Roadmap | For testing |
|
|
430
|
+
|
|
431
|
+
**See**: [Providers Overview](./docs/providers-overview.md)
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
## Documentation
|
|
436
|
+
|
|
437
|
+
### Getting Started
|
|
438
|
+
- [Installation & Quick Start](./docs/getting-started.md)
|
|
439
|
+
- [Services Overview](./docs/services-overview.md) - Available operations
|
|
440
|
+
- [Providers Overview](./docs/providers-overview.md) - Supported platforms
|
|
441
|
+
|
|
442
|
+
### Key Features
|
|
443
|
+
- [Capabilities System](./docs/capabilities-system.md) - Feature detection (NEW!)
|
|
444
|
+
- [Authentication](./docs/authentication.md) - JWT and session auth
|
|
445
|
+
- [Profile Management](./docs/profile-management.md) - Customer operations
|
|
446
|
+
- [Category Customization & Inheritance](./docs/category-customization.md) - Personalize inherited categories (NEW!)
|
|
447
|
+
|
|
448
|
+
### Advanced
|
|
449
|
+
- [Add New Provider](../../docs/workflows/legacy/add-provider.md) - Step-by-step workflow
|
|
450
|
+
- [Add New Service](../../docs/workflows/legacy/add-adapter-service.md) - Service workflow
|
|
451
|
+
- [Type Definitions](./src/types/) - Full TypeScript types
|
|
452
|
+
|
|
453
|
+
---
|
|
454
|
+
|
|
455
|
+
## Examples
|
|
456
|
+
|
|
457
|
+
### Next.js Integration
|
|
458
|
+
|
|
459
|
+
```typescript
|
|
460
|
+
// lib/commerce.ts
|
|
461
|
+
import { createProvider } from '@thorprovider/adapters';
|
|
462
|
+
|
|
463
|
+
export const commerce = createProvider({
|
|
464
|
+
provider: 'medusa',
|
|
465
|
+
config: {
|
|
466
|
+
baseUrl: process.env.NEXT_PUBLIC_COMMERCE_API_URL!,
|
|
467
|
+
publishableKey: process.env.NEXT_PUBLIC_COMMERCE_API_KEY!,
|
|
468
|
+
auth: {
|
|
469
|
+
method: 'jwt',
|
|
470
|
+
storage: 'localStorage',
|
|
471
|
+
},
|
|
472
|
+
},
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
// app/products/page.tsx
|
|
476
|
+
import { commerce } from '@/lib/commerce';
|
|
477
|
+
|
|
478
|
+
export default async function ProductsPage() {
|
|
479
|
+
const products = await commerce.getProducts();
|
|
480
|
+
return <ProductList products={products} />;
|
|
481
|
+
}
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
### Authentication
|
|
485
|
+
|
|
486
|
+
```typescript
|
|
487
|
+
// Login
|
|
488
|
+
const { customer, token } = await commerce.auth.login({
|
|
489
|
+
email: 'user@example.com',
|
|
490
|
+
password: 'password123',
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
// Register
|
|
494
|
+
const { customer } = await commerce.auth.register({
|
|
495
|
+
email: 'user@example.com',
|
|
496
|
+
password: 'password123',
|
|
497
|
+
first_name: 'John',
|
|
498
|
+
last_name: 'Doe',
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
// Get current customer
|
|
502
|
+
const customer = await commerce.auth.getCurrentCustomer();
|
|
503
|
+
|
|
504
|
+
// Update profile (capability-aware)
|
|
505
|
+
if (commerce.capabilities.updateProfile) {
|
|
506
|
+
await commerce.auth.updateCustomer({
|
|
507
|
+
first_name: 'Jane',
|
|
508
|
+
phone: '+1234567890',
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
### Cart Management
|
|
514
|
+
|
|
515
|
+
```typescript
|
|
516
|
+
// Create cart
|
|
517
|
+
const cart = await commerce.createCart();
|
|
518
|
+
|
|
519
|
+
// Add items
|
|
520
|
+
const updated = await commerce.addToCart(cart.id!, [
|
|
521
|
+
{ merchandiseId: 'variant_123', quantity: 2 },
|
|
522
|
+
]);
|
|
523
|
+
|
|
524
|
+
// Update quantities
|
|
525
|
+
await commerce.updateCart(cart.id!, [
|
|
526
|
+
{ id: 'line_1', quantity: 3 },
|
|
527
|
+
]);
|
|
528
|
+
|
|
529
|
+
// Remove items
|
|
530
|
+
await commerce.removeFromCart(cart.id!, ['line_2']);
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
---
|
|
534
|
+
|
|
535
|
+
## Switching Providers
|
|
536
|
+
|
|
537
|
+
Change platforms with minimal code changes:
|
|
538
|
+
|
|
539
|
+
```typescript
|
|
540
|
+
// From Medusa
|
|
541
|
+
const commerce = createProvider({
|
|
542
|
+
provider: 'medusa',
|
|
543
|
+
config: { /* ... */ }
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
// To Shopify (when available)
|
|
547
|
+
const commerce = createProvider({
|
|
548
|
+
provider: 'shopify',
|
|
549
|
+
config: { /* ... */ }
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
// ✅ Rest of your code stays the same!
|
|
553
|
+
const products = await commerce.getProducts();
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
---
|
|
557
|
+
|
|
558
|
+
## Architecture
|
|
559
|
+
|
|
560
|
+
```
|
|
561
|
+
@thorprovider/adapters/
|
|
562
|
+
├── docs/ # Modular documentation
|
|
563
|
+
│ ├── getting-started.md
|
|
564
|
+
│ ├── services-overview.md
|
|
565
|
+
│ ├── providers-overview.md
|
|
566
|
+
│ ├── capabilities-system.md
|
|
567
|
+
│ ├── authentication.md
|
|
568
|
+
│ └── profile-management.md
|
|
569
|
+
├── types/ # Unified types (Product, Cart, etc.)
|
|
570
|
+
├── providers/ # Provider implementations
|
|
571
|
+
│ ├── medusa/ # Medusa v2 provider
|
|
572
|
+
│ └── shopify/ # (coming soon)
|
|
573
|
+
├── core/ # Core utilities (errors, logger)
|
|
574
|
+
└── factory/ # Provider factory
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
---
|
|
578
|
+
|
|
579
|
+
## Design Principles
|
|
580
|
+
|
|
581
|
+
1. **Provider Pattern** - One adapter per platform
|
|
582
|
+
2. **Interface First** - Unified API defined by shared types
|
|
583
|
+
3. **Framework Agnostic** - Zero dependencies on React/Next.js
|
|
584
|
+
4. **TypeScript Strict** - Strong types for all operations
|
|
585
|
+
5. **Composable** - Providers can be easily swapped
|
|
586
|
+
6. **Capability-Aware** - Build adaptive UIs that work across backends
|
|
587
|
+
|
|
588
|
+
---
|
|
589
|
+
|
|
590
|
+
## Contributing
|
|
591
|
+
|
|
592
|
+
Contributions welcome! See [CONTRIBUTING.md](../../CONTRIBUTING.md).
|
|
593
|
+
|
|
594
|
+
### Adding a Provider
|
|
595
|
+
|
|
596
|
+
1. Define capabilities in `capabilities.ts`
|
|
597
|
+
2. Implement API client
|
|
598
|
+
3. Implement core services (Products, Cart, Auth)
|
|
599
|
+
4. Map responses to unified types
|
|
600
|
+
5. Write integration tests
|
|
601
|
+
6. Update factory and documentation
|
|
602
|
+
|
|
603
|
+
**See**: [Add Provider Workflow](../../docs/workflows/legacy/add-provider.md)
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
---
|
|
607
|
+
|
|
608
|
+
## Version
|
|
609
|
+
|
|
610
|
+
**Current**: v2.0 - Production Ready ✅
|
|
611
|
+
|
|
612
|
+
**New in v2.0**:
|
|
613
|
+
- ✅ Capabilities system (21 backend capabilities)
|
|
614
|
+
- ✅ Profile management with capability awareness
|
|
615
|
+
- ✅ Auto token refresh
|
|
616
|
+
- ✅ Comprehensive TypeScript types
|
|
617
|
+
- ✅ Modular documentation
|
|
618
|
+
|
|
619
|
+
**See**: [CHANGELOG.md](./CHANGELOG.md)
|
|
620
|
+
|
|
621
|
+
---
|
|
622
|
+
|
|
623
|
+
## Resources
|
|
624
|
+
|
|
625
|
+
- [Medusa Documentation](https://docs.medusajs.com/)
|
|
626
|
+
- [TypeScript Handbook](https://www.typescriptlang.org/docs/)
|
|
627
|
+
- [Examples](./examples/) - Code samples
|
|
628
|
+
- [Thor Commerce](../../README.md) - Monorepo overview
|
|
629
|
+
|
|
630
|
+
---
|
|
631
|
+
|
|
632
|
+
## License
|
|
633
|
+
|
|
634
|
+
MIT
|