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