@thorprovider/types 2.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/src/payment.ts ADDED
@@ -0,0 +1,259 @@
1
+ /**
2
+ * @thorprovider/adapters v2.0
3
+ * Payment Types - Unified payment method interface
4
+ *
5
+ * Provides backend-agnostic types for payment methods and providers.
6
+ * Each backend transforms its payment providers to this common format.
7
+ *
8
+ * @see packages/adapters/PAYMENT_METHODS_ARCHITECTURE.md
9
+ */
10
+
11
+ /**
12
+ * Payment method type identifier
13
+ * Normalized across all backends
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * // Medusa transforms:
18
+ * 'pp_stripe_stripe' → type: 'card'
19
+ * 'pp_paypal_paypal' → type: 'wallet'
20
+ * 'pp_system_default' → type: 'cash_on_delivery'
21
+ * ```
22
+ */
23
+ export type PaymentMethodType =
24
+ | 'card' // Credit/debit cards (Visa, Mastercard, Amex, etc.)
25
+ | 'wallet' // Digital wallets (Apple Pay, Google Pay, PayPal)
26
+ | 'bank_transfer' // Bank transfers (ACH, SEPA, wire transfer)
27
+ | 'buy_now_pay_later' // BNPL services (Klarna, Affirm, Afterpay)
28
+ | 'cash_on_delivery' // Cash on delivery / Manual payment
29
+ | 'crypto' // Cryptocurrency payments
30
+ | 'other'; // Custom or unclassified methods
31
+
32
+ /**
33
+ * Payment method features and capabilities
34
+ * Describes what a payment method supports
35
+ */
36
+ export interface PaymentMethodFeatures {
37
+ /**
38
+ * Supports saving payment method for future use
39
+ *
40
+ * Example: Stripe allows saving cards, PayPal allows vaulting
41
+ */
42
+ saveForLater?: boolean;
43
+
44
+ /**
45
+ * Requires 3D Secure authentication
46
+ *
47
+ * Important for SCA compliance in EU
48
+ */
49
+ requires3DS?: boolean;
50
+
51
+ /**
52
+ * Supports refunds
53
+ *
54
+ * Some methods like COD may not support automatic refunds
55
+ */
56
+ supportsRefunds?: boolean;
57
+
58
+ /**
59
+ * Minimum transaction amount (in smallest currency unit, e.g., cents)
60
+ *
61
+ * Example: Stripe has minimum amounts per currency
62
+ */
63
+ minAmount?: number;
64
+
65
+ /**
66
+ * Maximum transaction amount (in smallest currency unit, e.g., cents)
67
+ *
68
+ * Example: Some BNPL providers have maximum limits
69
+ */
70
+ maxAmount?: number;
71
+
72
+ /**
73
+ * Supported currencies (ISO 4217 codes)
74
+ *
75
+ * If undefined, assumes all currencies supported
76
+ */
77
+ supportedCurrencies?: string[];
78
+
79
+ /**
80
+ * Requires additional customer verification
81
+ *
82
+ * Example: Bank transfers may require identity verification
83
+ */
84
+ requiresVerification?: boolean;
85
+ }
86
+
87
+ /**
88
+ * Payment method information
89
+ * Backend-agnostic representation of a payment provider
90
+ *
91
+ * This is the normalized format that all backends transform their
92
+ * payment providers into, allowing the storefront to work with
93
+ * any backend without knowing provider-specific details.
94
+ *
95
+ * @example
96
+ * ```typescript
97
+ * // Medusa Stripe provider:
98
+ * {
99
+ * id: 'pp_stripe_stripe',
100
+ * name: 'Stripe',
101
+ * type: 'card',
102
+ * isEnabled: true,
103
+ * requiresSetup: true,
104
+ * features: {
105
+ * saveForLater: true,
106
+ * requires3DS: true,
107
+ * supportsRefunds: true
108
+ * }
109
+ * }
110
+ * ```
111
+ */
112
+ export interface PaymentMethod {
113
+ /**
114
+ * Unique identifier from backend
115
+ *
116
+ * Format varies by backend:
117
+ * - Medusa: "pp_stripe_stripe", "pp_paypal_paypal", "pp_system_default"
118
+ * - Shopify: "shopify_payments", "paypal_express"
119
+ * - WooCommerce: "stripe", "paypal", "cod"
120
+ */
121
+ id: string;
122
+
123
+ /**
124
+ * Display name for UI
125
+ *
126
+ * Human-readable name shown to customers
127
+ * Example: "Credit Card", "PayPal", "Cash on Delivery"
128
+ */
129
+ name: string;
130
+
131
+ /**
132
+ * Payment method type (normalized)
133
+ *
134
+ * Used for categorization and UI rendering
135
+ */
136
+ type: PaymentMethodType;
137
+
138
+ /**
139
+ * Whether this provider is currently enabled
140
+ *
141
+ * Backend administrators control this setting
142
+ */
143
+ isEnabled: boolean;
144
+
145
+ /**
146
+ * Whether this provider requires additional setup
147
+ *
148
+ * Examples:
149
+ * - Card payments: requires entering card details
150
+ * - Wallets: requires authorizing wallet connection
151
+ * - COD: no setup required (false)
152
+ */
153
+ requiresSetup: boolean;
154
+
155
+ /**
156
+ * Optional description for display
157
+ *
158
+ * Additional information shown to customer
159
+ */
160
+ description?: string;
161
+
162
+ /**
163
+ * Metadata from backend (provider-specific)
164
+ *
165
+ * Contains original backend data for advanced use cases
166
+ * Structure varies by backend
167
+ */
168
+ metadata?: Record<string, unknown>;
169
+
170
+ /**
171
+ * Supported features and capabilities
172
+ */
173
+ features?: PaymentMethodFeatures;
174
+
175
+ /**
176
+ * Logo URL or icon identifier
177
+ *
178
+ * Optional visual representation
179
+ */
180
+ logo?: string;
181
+ }
182
+
183
+ /**
184
+ * Payment methods query options
185
+ *
186
+ * Used to filter/query available payment methods
187
+ * Different backends support different filters
188
+ */
189
+ export interface PaymentMethodsOptions {
190
+ /**
191
+ * Region ID (for backends that support regional payments)
192
+ *
193
+ * Example: Medusa requires region_id
194
+ * Required for: Medusa
195
+ * Optional for: Shopify (global), WooCommerce (may use)
196
+ */
197
+ regionId?: string;
198
+
199
+ /**
200
+ * Cart ID (for cart-specific payment methods)
201
+ *
202
+ * Some backends determine available methods based on cart contents
203
+ * Example: Certain methods only for high-value carts
204
+ */
205
+ cartId?: string;
206
+
207
+ /**
208
+ * Currency code filter (ISO 4217)
209
+ *
210
+ * Filter methods that support this currency
211
+ * Example: 'USD', 'EUR', 'GBP'
212
+ */
213
+ currencyCode?: string;
214
+
215
+ /**
216
+ * Filter by enabled status
217
+ *
218
+ * If true, only return enabled payment methods
219
+ * Default: true (recommended for checkout)
220
+ */
221
+ enabledOnly?: boolean;
222
+
223
+ /**
224
+ * Force refresh from backend (ignore cache)
225
+ *
226
+ * Set to true to bypass client-side cache
227
+ * Default: false
228
+ */
229
+ forceRefresh?: boolean;
230
+ }
231
+
232
+ /**
233
+ * Cached payment methods data
234
+ *
235
+ * Used for client-side caching with TTL
236
+ */
237
+ export interface CachedPaymentMethods {
238
+ /**
239
+ * Cache key (stringified options)
240
+ */
241
+ key: string;
242
+
243
+ /**
244
+ * Cached payment methods
245
+ */
246
+ methods: PaymentMethod[];
247
+
248
+ /**
249
+ * Timestamp when cached (milliseconds since epoch)
250
+ */
251
+ cachedAt: number;
252
+
253
+ /**
254
+ * Time-to-live in milliseconds
255
+ *
256
+ * Default: 5 minutes (300000ms)
257
+ */
258
+ ttl: number;
259
+ }
package/src/product.ts ADDED
@@ -0,0 +1,364 @@
1
+ /**
2
+ * @thorprovider/adapters v2.0
3
+ * Product types - unified interface for all commerce providers
4
+ */
5
+
6
+ import type { Image, Money, SEO } from './common';
7
+ import type { InventoryLevel, StockStatus } from './stock-location';
8
+
9
+ /**
10
+ * Product variant option (e.g., Size: Large, Color: Red)
11
+ */
12
+ export interface ProductOption {
13
+ id: string;
14
+ name: string;
15
+ values: string[];
16
+ }
17
+
18
+ /**
19
+ * Selected option for a variant
20
+ */
21
+ export interface SelectedOption {
22
+ name: string;
23
+ value: string;
24
+ }
25
+
26
+ /**
27
+ * Product variant (SKU)
28
+ *
29
+ * Universal format compatible with:
30
+ * - Shopify: ProductVariant with price/compareAtPrice as MoneyV2
31
+ * - Medusa: StoreProductVariant with calculated_price
32
+ * - WooCommerce: Variation with price/regular_price
33
+ * - Magento: ConfigurableProductOptions
34
+ */
35
+ export interface ProductVariant {
36
+ id: string;
37
+ title: string;
38
+ sku?: string;
39
+ availableForSale: boolean;
40
+ selectedOptions: SelectedOption[];
41
+
42
+ // Pricing
43
+ price: Money;
44
+ compareAtPrice?: Money; // Original price before discount (Shopify: compareAtPrice, WooCommerce: regular_price)
45
+
46
+ // Inventory
47
+ quantityAvailable?: number; // Available stock quantity
48
+ stockStatus?: StockStatus;
49
+ inventoryLevels?: InventoryLevel[];
50
+
51
+ // Media
52
+ image?: Image; // Variant-specific image
53
+ }
54
+
55
+ /**
56
+ * Price range for product
57
+ */
58
+ export interface PriceRange {
59
+ minVariantPrice: Money;
60
+ maxVariantPrice: Money;
61
+ }
62
+
63
+ /**
64
+ * Main product type - normalized across all providers
65
+ *
66
+ * Universal format compatible with:
67
+ * - Shopify: Product (Storefront API 2024-01)
68
+ * - Medusa.js: StoreProduct (v2)
69
+ * - WooCommerce: Product (REST API v3)
70
+ * - Magento/Adobe Commerce: ProductInterface (GraphQL)
71
+ *
72
+ * Platform-specific fields are stored in metadata.
73
+ */
74
+ export interface Product {
75
+ id: string;
76
+ handle: string; // Shopify/Medusa: handle, WooCommerce: slug, Magento: url_key
77
+ title: string; // Shopify/Medusa: title, WooCommerce/Magento: name
78
+ description: string;
79
+ descriptionHtml?: string;
80
+ availableForSale: boolean; // Computed from platform-specific status/stock fields
81
+
82
+ // Options and variants
83
+ options: ProductOption[];
84
+ variants: ProductVariant[];
85
+
86
+ // Media
87
+ featuredImage?: Image; // Shopify: featuredImage, Medusa: thumbnail, WooCommerce: images[0]
88
+ images: Image[];
89
+
90
+ // Pricing
91
+ priceRange: PriceRange;
92
+
93
+ // Organization (optional, platform-dependent)
94
+ collections?: Array<{ id: string; title: string; handle: string }>; // Shopify/Medusa: collections, WooCommerce/Magento: categories
95
+ productType?: string; // Shopify: productType, Medusa: type, WooCommerce: type (simple/variable)
96
+ vendor?: string; // Shopify-specific, stored in metadata for others
97
+ tags?: string[];
98
+
99
+ // SEO
100
+ seo?: SEO;
101
+
102
+ // Dates
103
+ createdAt: string; // ISO 8601 timestamp
104
+ updatedAt: string;
105
+
106
+ // Platform-specific extensions
107
+ metadata?: Record<string, unknown>; // Store platform-specific fields here
108
+ }
109
+
110
+ /**
111
+ * Options for fetching products
112
+ */
113
+ export interface GetProductsOptions {
114
+ query?: string;
115
+ sortKey?: string;
116
+ reverse?: boolean;
117
+ first?: number;
118
+ skip?: number;
119
+ /** Cursor (string) or offset (number) for pagination */
120
+ after?: string | number;
121
+ regionId?: string;
122
+ /** Basic category filtering — backend IDs resolved from URL slugs */
123
+ categoryIds?: string[];
124
+ /** Basic collection filtering — backend IDs resolved from URL slugs */
125
+ collectionIds?: string[];
126
+ }
127
+
128
+ // ─────────────────────────────────────────────────────────────────────
129
+ // UI/UX Types for Product Filtering and Display
130
+ // ─────────────────────────────────────────────────────────────────────
131
+
132
+ /**
133
+ * Represents a single active filter displayed as a chip
134
+ * Used in ActiveFiltersChips component
135
+ */
136
+ export interface ActiveFilter {
137
+ /** Unique identifier (e.g., "material-wood") */
138
+ id: string;
139
+ /** Display category (e.g., "Material") */
140
+ category: string;
141
+ /** Display value (e.g., "Wood") */
142
+ label: string;
143
+ /** Raw value for tracking */
144
+ value: string | string[];
145
+ }
146
+
147
+ /**
148
+ * Sort options available for product listing
149
+ */
150
+ export type SortOption =
151
+ | 'featured'
152
+ | 'price-asc'
153
+ | 'price-desc'
154
+ | 'name'
155
+ | 'newest'
156
+ | 'rating';
157
+
158
+ /**
159
+ * A single option within a filter section (e.g., "Wood" under "Material")
160
+ */
161
+ export interface FilterOption {
162
+ /** URL-friendly slug (human-readable). This is what goes in the URL. */
163
+ value: string;
164
+ /** Display label shown in the UI. */
165
+ label: string;
166
+ /**
167
+ * Backend identifier (e.g., database ID).
168
+ * Present when `value` is a human-readable slug that differs from the
169
+ * backend ID. Used by L5 page.tsx to resolve slug → ID before querying
170
+ * the adapter. Optional: when absent, `value` is used as the backend ID.
171
+ */
172
+ id?: string;
173
+ /** Optional facet count */
174
+ count?: number;
175
+ }
176
+
177
+ /**
178
+ * Represents a collapsible filter section in the sidebar
179
+ */
180
+ export interface FilterSection {
181
+ id: string;
182
+ label: string;
183
+ type: 'checkbox' | 'range' | 'radio' | 'select';
184
+ options?: FilterOption[];
185
+ min?: number;
186
+ max?: number;
187
+ defaultExpanded?: boolean;
188
+ }
189
+
190
+ /**
191
+ * Simplified product preview for e-commerce patterns
192
+ * Used in ProductCard, QuickView, etc.
193
+ */
194
+ export interface ProductPreview {
195
+ id: string;
196
+ name: string;
197
+ price: number;
198
+ originalPrice?: number;
199
+ image: string;
200
+ rating: number;
201
+ reviewCount: number;
202
+ isNew?: boolean;
203
+ isOnSale?: boolean;
204
+ isOutOfStock?: boolean;
205
+ discountPercent?: number;
206
+ stock?: number;
207
+ category?: string;
208
+ description?: string;
209
+ }
210
+
211
+ /**
212
+ * Product review type
213
+ */
214
+ export interface Review {
215
+ id: string;
216
+ author: string;
217
+ rating: number;
218
+ date: string;
219
+ title: string;
220
+ body: string;
221
+ verified?: boolean;
222
+ }
223
+
224
+ /**
225
+ * Product compare item structure
226
+ */
227
+ export interface CompareProduct {
228
+ id: string;
229
+ name: string;
230
+ image: string;
231
+ price: number;
232
+ attributes: Record<string, string>;
233
+ }
234
+
235
+ // ─────────────────────────────────────────────────────────────────────
236
+ // Admin Types (Admin API — backend-agnostic)
237
+ // ─────────────────────────────────────────────────────────────────────
238
+
239
+ /**
240
+ * Product variant as returned by the admin API.
241
+ *
242
+ * Distinct from `ProductVariant` (storefront API) because admin variants
243
+ * carry backend-specific fields like inventory counts and raw prices.
244
+ */
245
+ export interface AdminProductVariant {
246
+ id: string;
247
+ title: string;
248
+ sku?: string;
249
+ /** Total inventory quantity across all stock locations */
250
+ quantityAvailable?: number;
251
+ /** Prices available for this variant */
252
+ prices?: Array<{ amount: number; currencyCode: string }>;
253
+ }
254
+
255
+ /**
256
+ * Product as returned by the admin API.
257
+ *
258
+ * Intentionally separate from `Product` (storefront API) because:
259
+ * - The storefront API only returns published products and derives
260
+ * `availableForSale` from stock/sales-channel rules — no explicit status.
261
+ * - The admin API returns ALL products (including draft, proposed, rejected)
262
+ * with the backend-native `status` field.
263
+ *
264
+ * Use this type exclusively in admin dashboard contexts.
265
+ */
266
+ export interface AdminProduct {
267
+ id: string;
268
+ handle: string;
269
+ title: string;
270
+ /** Optional product description */
271
+ description?: string;
272
+ /**
273
+ * Backend-native status string.
274
+ * Values are provider-specific — e.g. 'published' | 'draft' | 'proposed' | 'rejected'.
275
+ */
276
+ status: string;
277
+ variants: AdminProductVariant[];
278
+ /** URL of the product thumbnail image */
279
+ thumbnail?: string;
280
+ /** URLs of all product images */
281
+ images?: string[];
282
+ }
283
+
284
+ // ============================================
285
+ // Advanced Search & Filter Types
286
+ // ============================================
287
+
288
+ /**
289
+ * Options for advanced product search (requires backend search engine support).
290
+ * Extends GetProductsOptions so it can be passed as a fallback to getProducts().
291
+ * Used with CommerceProvider.searchProductsAdvanced().
292
+ */
293
+ export interface AdvancedSearchProductsOptions extends GetProductsOptions {
294
+ /** Filter by category IDs */
295
+ categoryIds?: string[];
296
+ /** Filter by collection IDs */
297
+ collectionIds?: string[];
298
+ /** Filter by tag IDs */
299
+ tagIds?: string[];
300
+ /** Minimum price (in major currency units) */
301
+ priceMin?: number;
302
+ /** Maximum price (in major currency units) */
303
+ priceMax?: number;
304
+ /** Currency code for price filtering */
305
+ currencyCode?: string;
306
+ /** Only include products with stock available */
307
+ inStockOnly?: boolean;
308
+ /** Attribute filters (e.g. RAM, brand) */
309
+ attributes?: Array<{ id: string; values: string[] }>;
310
+ /** Include variant-level data in results */
311
+ includeVariants?: boolean;
312
+ }
313
+
314
+ /**
315
+ * Metadata returned alongside advanced search results.
316
+ */
317
+ export interface SearchResultMeta {
318
+ /** Total number of matching products */
319
+ total?: number;
320
+ /** Max results requested */
321
+ limit?: number;
322
+ /** Pagination offset */
323
+ offset?: number;
324
+ /** Backend search processing time in milliseconds */
325
+ processingTimeMs?: number;
326
+ /** Whether hybrid (vector + keyword) search was used */
327
+ hybridSearchUsed?: boolean;
328
+ /** Filters that were actually applied by the backend search engine */
329
+ appliedFilters?: Record<string, unknown>;
330
+ /** Price context used for price filtering */
331
+ priceContext?: {
332
+ currencyCode: string;
333
+ regionId?: string;
334
+ };
335
+ }
336
+
337
+ /**
338
+ * Filter configuration returned by CommerceProvider.getAvailableFilters().
339
+ * Represents one filterable dimension (e.g. Color, RAM, Price range).
340
+ *
341
+ * Types:
342
+ * - `checkbox` / `multi` → multiple selectable options
343
+ * - `select` / `single` → single selectable option
344
+ * - `range` → numeric min/max range
345
+ * - `boolean` → on/off toggle (e.g. "In Stock")
346
+ */
347
+ export interface FilterConfig {
348
+ /** Unique filter identifier */
349
+ id: string;
350
+ /** Display label for the filter group */
351
+ label: string;
352
+ /** Filter UI type */
353
+ type: 'checkbox' | 'multi' | 'select' | 'single' | 'range' | 'boolean';
354
+ /** Selectable options (for checkbox/multi/select/single types) */
355
+ options?: FilterOption[];
356
+ /** Min value (for range type) */
357
+ min?: number;
358
+ /** Max value (for range type) */
359
+ max?: number;
360
+ /** Whether this filter is currently applied */
361
+ isActive?: boolean;
362
+ /** Sort priority — lower number = displayed first */
363
+ ranking?: number;
364
+ }