@fayz-ai/shop 0.2.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.
Files changed (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +36 -0
  3. package/dist/catalog.cjs +176 -0
  4. package/dist/catalog.cjs.map +1 -0
  5. package/dist/catalog.d.ts +4 -0
  6. package/dist/catalog.d.ts.map +1 -0
  7. package/dist/catalog.js +170 -0
  8. package/dist/catalog.js.map +1 -0
  9. package/dist/commerce.d.ts +28 -0
  10. package/dist/commerce.d.ts.map +1 -0
  11. package/dist/index.cjs +1047 -0
  12. package/dist/index.cjs.map +1 -0
  13. package/dist/index.d.ts +12 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +1027 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/mock-catalog.d.ts +42 -0
  18. package/dist/mock-catalog.d.ts.map +1 -0
  19. package/dist/mock-provider.d.ts +48 -0
  20. package/dist/mock-provider.d.ts.map +1 -0
  21. package/dist/mock.cjs +430 -0
  22. package/dist/mock.cjs.map +1 -0
  23. package/dist/mock.d.ts +3 -0
  24. package/dist/mock.d.ts.map +1 -0
  25. package/dist/mock.js +427 -0
  26. package/dist/mock.js.map +1 -0
  27. package/dist/provider.cjs +4 -0
  28. package/dist/provider.cjs.map +1 -0
  29. package/dist/provider.d.ts +42 -0
  30. package/dist/provider.d.ts.map +1 -0
  31. package/dist/provider.js +3 -0
  32. package/dist/provider.js.map +1 -0
  33. package/dist/runtime.cjs +447 -0
  34. package/dist/runtime.cjs.map +1 -0
  35. package/dist/runtime.d.ts +5 -0
  36. package/dist/runtime.d.ts.map +1 -0
  37. package/dist/runtime.js +443 -0
  38. package/dist/runtime.js.map +1 -0
  39. package/dist/supabase-provider.d.ts +33 -0
  40. package/dist/supabase-provider.d.ts.map +1 -0
  41. package/dist/tenant.d.ts +4 -0
  42. package/dist/tenant.d.ts.map +1 -0
  43. package/dist/types.cjs +4 -0
  44. package/dist/types.cjs.map +1 -0
  45. package/dist/types.d.ts +345 -0
  46. package/dist/types.d.ts.map +1 -0
  47. package/dist/types.js +3 -0
  48. package/dist/types.js.map +1 -0
  49. package/package.json +99 -0
  50. package/src/catalog.ts +3 -0
  51. package/src/commerce.ts +120 -0
  52. package/src/index.ts +34 -0
  53. package/src/mock-catalog.ts +258 -0
  54. package/src/mock-provider.ts +372 -0
  55. package/src/mock.ts +2 -0
  56. package/src/provider.ts +59 -0
  57. package/src/runtime.ts +20 -0
  58. package/src/supabase-provider.ts +499 -0
  59. package/src/tenant.ts +21 -0
  60. package/src/types.ts +401 -0
@@ -0,0 +1,345 @@
1
+ export type ProductStatus = 'draft' | 'active' | 'archived';
2
+ export interface Product {
3
+ id: string;
4
+ tenantId: string;
5
+ name: string;
6
+ slug: string;
7
+ description: string | null;
8
+ price: number;
9
+ compareAtPrice: number | null;
10
+ currency: string;
11
+ status: ProductStatus;
12
+ inventoryCount: number;
13
+ sku: string | null;
14
+ sortOrder: number;
15
+ metadata: Record<string, unknown>;
16
+ images: ProductImage[];
17
+ categoryId: string | null;
18
+ categoryName: string | null;
19
+ isPhysical: boolean;
20
+ weight: number | null;
21
+ weightUnit: string;
22
+ createdAt: string;
23
+ updatedAt: string;
24
+ }
25
+ export interface ProductImage {
26
+ id: string;
27
+ productId: string;
28
+ url: string;
29
+ altText: string | null;
30
+ sortOrder: number;
31
+ isPrimary: boolean;
32
+ createdAt: string;
33
+ }
34
+ export interface CreateProductInput {
35
+ name: string;
36
+ slug?: string;
37
+ description?: string;
38
+ price: number;
39
+ compareAtPrice?: number;
40
+ currency?: string;
41
+ status?: ProductStatus;
42
+ inventoryCount?: number;
43
+ sku?: string;
44
+ sortOrder?: number;
45
+ metadata?: Record<string, unknown>;
46
+ categoryId?: string;
47
+ isPhysical?: boolean;
48
+ weight?: number;
49
+ weightUnit?: string;
50
+ }
51
+ export interface UpdateProductInput {
52
+ name?: string;
53
+ slug?: string;
54
+ description?: string;
55
+ price?: number;
56
+ compareAtPrice?: number | null;
57
+ currency?: string;
58
+ status?: ProductStatus;
59
+ inventoryCount?: number;
60
+ sku?: string | null;
61
+ sortOrder?: number;
62
+ metadata?: Record<string, unknown>;
63
+ categoryId?: string | null;
64
+ isPhysical?: boolean;
65
+ weight?: number | null;
66
+ weightUnit?: string;
67
+ }
68
+ export interface ListProductsOptions {
69
+ status?: ProductStatus;
70
+ categoryId?: string;
71
+ search?: string;
72
+ /** Exact-match slug lookup (storefront product-detail routing) */
73
+ slug?: string;
74
+ limit?: number;
75
+ offset?: number;
76
+ orderBy?: 'name' | 'price' | 'created_at' | 'sort_order';
77
+ order?: 'asc' | 'desc';
78
+ }
79
+ export interface Category {
80
+ id: string;
81
+ tenantId: string;
82
+ name: string;
83
+ slug: string;
84
+ description: string | null;
85
+ parentId: string | null;
86
+ sortOrder: number;
87
+ /** Showcase image (storefront category tiles). Not yet a shop_categories column. */
88
+ imageUrl?: string | null;
89
+ children?: Category[];
90
+ createdAt: string;
91
+ }
92
+ export interface CreateCategoryInput {
93
+ name: string;
94
+ slug?: string;
95
+ description?: string;
96
+ parentId?: string;
97
+ sortOrder?: number;
98
+ }
99
+ export interface UpdateCategoryInput {
100
+ name?: string;
101
+ slug?: string;
102
+ description?: string | null;
103
+ parentId?: string | null;
104
+ sortOrder?: number;
105
+ }
106
+ export type OrderStatus = 'open' | 'archived' | 'cancelled';
107
+ export type FinancialStatus = 'pending' | 'paid' | 'partially_paid' | 'refunded' | 'partially_refunded' | 'voided';
108
+ export type FulfillmentStatus = 'unfulfilled' | 'partially_fulfilled' | 'fulfilled';
109
+ export interface Order {
110
+ id: string;
111
+ tenantId: string;
112
+ orderNumber: number;
113
+ status: OrderStatus;
114
+ financialStatus: FinancialStatus;
115
+ fulfillmentStatus: FulfillmentStatus;
116
+ currency: string;
117
+ subtotal: number;
118
+ taxTotal: number;
119
+ discountTotal: number;
120
+ shippingTotal: number;
121
+ total: number;
122
+ customerId: string | null;
123
+ customerName: string | null;
124
+ customerEmail: string | null;
125
+ discountCode: string | null;
126
+ notes: string | null;
127
+ items: OrderItem[];
128
+ createdAt: string;
129
+ updatedAt: string;
130
+ }
131
+ export interface OrderItem {
132
+ id: string;
133
+ orderId: string;
134
+ productId: string | null;
135
+ name: string;
136
+ sku: string | null;
137
+ quantity: number;
138
+ unitPrice: number;
139
+ total: number;
140
+ imageUrl: string | null;
141
+ }
142
+ export interface CreateOrderInput {
143
+ customerId?: string;
144
+ customerName?: string;
145
+ customerEmail?: string;
146
+ currency?: string;
147
+ notes?: string;
148
+ discountCode?: string;
149
+ discountTotal?: number;
150
+ shippingTotal?: number;
151
+ items: Array<{
152
+ productId?: string;
153
+ name: string;
154
+ sku?: string;
155
+ quantity: number;
156
+ unitPrice: number;
157
+ imageUrl?: string;
158
+ }>;
159
+ }
160
+ export interface UpdateOrderInput {
161
+ status?: OrderStatus;
162
+ financialStatus?: FinancialStatus;
163
+ fulfillmentStatus?: FulfillmentStatus;
164
+ notes?: string;
165
+ }
166
+ export interface ListOrdersOptions {
167
+ status?: OrderStatus;
168
+ financialStatus?: FinancialStatus;
169
+ fulfillmentStatus?: FulfillmentStatus;
170
+ customerId?: string;
171
+ customerEmail?: string;
172
+ search?: string;
173
+ limit?: number;
174
+ offset?: number;
175
+ }
176
+ /**
177
+ * A single line in a trusted order-placement request. Carries NO price — the
178
+ * server re-reads the product price from the catalog so the browser can never
179
+ * set what it pays.
180
+ */
181
+ export interface PlaceOrderLine {
182
+ productId: string;
183
+ quantity: number;
184
+ /** Variant/options label appended to the stored item name (display only). */
185
+ optionsLabel?: string;
186
+ }
187
+ /**
188
+ * Input for {@link ShopProvider.placeOrder} — the only trusted path to create a
189
+ * storefront order. Prices, discount math, and inventory are resolved
190
+ * server-side; the client supplies product ids, quantities, and intent only.
191
+ */
192
+ export interface PlaceOrderInput {
193
+ /** Tenant/store id. The Supabase provider falls back to the resolved shop tenant. */
194
+ tenantId?: string;
195
+ /** Existing customer id (preferred when the session already resolved one). */
196
+ customerId?: string;
197
+ /** Customer identity for find-or-create when {@link PlaceOrderInput.customerId} is absent. */
198
+ customer?: {
199
+ name?: string;
200
+ email?: string;
201
+ };
202
+ currency?: string;
203
+ notes?: string;
204
+ /** Discount code to validate + apply server-side (never trust a client total). */
205
+ discountCode?: string;
206
+ /** Shipping cost from the store's shipping policy. Clamped to >= 0 server-side. */
207
+ shippingTotal?: number;
208
+ items: PlaceOrderLine[];
209
+ }
210
+ export interface ShopCustomer {
211
+ id: string;
212
+ tenantId: string;
213
+ firstName: string;
214
+ lastName: string;
215
+ email: string | null;
216
+ phone: string | null;
217
+ notes: string | null;
218
+ ordersCount: number;
219
+ totalSpent: number;
220
+ createdAt: string;
221
+ updatedAt: string;
222
+ }
223
+ export interface CreateCustomerInput {
224
+ firstName: string;
225
+ lastName?: string;
226
+ email?: string;
227
+ phone?: string;
228
+ notes?: string;
229
+ }
230
+ export interface UpdateCustomerInput {
231
+ firstName?: string;
232
+ lastName?: string;
233
+ email?: string | null;
234
+ phone?: string | null;
235
+ notes?: string | null;
236
+ }
237
+ export interface ListCustomersOptions {
238
+ search?: string;
239
+ limit?: number;
240
+ offset?: number;
241
+ }
242
+ /**
243
+ * Input for {@link ShopProvider.resolveCustomer} — find-or-create a customer by
244
+ * email and (on Supabase) link it to the authenticated user (auth.uid) so
245
+ * "my orders" can be enforced by RLS. Runs server-side; the browser never
246
+ * supplies the auth id.
247
+ */
248
+ export interface ResolveCustomerInput {
249
+ email: string;
250
+ name?: string;
251
+ /** Tenant/store id. The Supabase provider falls back to the resolved shop tenant. */
252
+ tenantId?: string;
253
+ }
254
+ export type DiscountType = 'percentage' | 'fixed_amount' | 'free_shipping' | 'buy_x_get_y';
255
+ export type DiscountMethod = 'code' | 'automatic';
256
+ export type DiscountStatus = 'active' | 'expired' | 'scheduled' | 'draft';
257
+ export interface Discount {
258
+ id: string;
259
+ tenantId: string;
260
+ title: string;
261
+ code: string | null;
262
+ type: DiscountType;
263
+ method: DiscountMethod;
264
+ value: number;
265
+ usageLimit: number | null;
266
+ oncePerCustomer: boolean;
267
+ startsAt: string;
268
+ endsAt: string | null;
269
+ status: DiscountStatus;
270
+ timesUsed: number;
271
+ createdAt: string;
272
+ updatedAt: string;
273
+ }
274
+ export interface CreateDiscountInput {
275
+ title: string;
276
+ code?: string;
277
+ type: DiscountType;
278
+ method?: DiscountMethod;
279
+ value: number;
280
+ usageLimit?: number;
281
+ oncePerCustomer?: boolean;
282
+ startsAt?: string;
283
+ endsAt?: string;
284
+ status?: DiscountStatus;
285
+ }
286
+ export interface UpdateDiscountInput {
287
+ title?: string;
288
+ code?: string | null;
289
+ type?: DiscountType;
290
+ method?: DiscountMethod;
291
+ value?: number;
292
+ usageLimit?: number | null;
293
+ oncePerCustomer?: boolean;
294
+ startsAt?: string;
295
+ endsAt?: string | null;
296
+ status?: DiscountStatus;
297
+ }
298
+ export interface ListDiscountsOptions {
299
+ status?: DiscountStatus;
300
+ type?: DiscountType;
301
+ search?: string;
302
+ limit?: number;
303
+ offset?: number;
304
+ }
305
+ export interface DiscountValidation {
306
+ valid: boolean;
307
+ code: string | null;
308
+ type: DiscountType | null;
309
+ /** Percentage points for 'percentage'; absolute amount for 'fixed_amount'. */
310
+ value: number;
311
+ /** Machine-readable reason when invalid: not_found | expired | not_started | usage_limit | unsupported | empty */
312
+ reason?: string;
313
+ }
314
+ export interface InventoryLine {
315
+ productId: string;
316
+ quantity: number;
317
+ }
318
+ export interface InventoryIssue {
319
+ productId: string;
320
+ name: string;
321
+ requested: number;
322
+ available: number;
323
+ }
324
+ export interface InventoryCheck {
325
+ ok: boolean;
326
+ issues: InventoryIssue[];
327
+ }
328
+ export interface CartTotalLine {
329
+ productId: string;
330
+ name: string;
331
+ sku: string | null;
332
+ unitPrice: number;
333
+ quantity: number;
334
+ lineTotal: number;
335
+ }
336
+ export interface CartTotals {
337
+ lines: CartTotalLine[];
338
+ subtotal: number;
339
+ discountTotal: number;
340
+ discountCode: string | null;
341
+ taxTotal: number;
342
+ shippingTotal: number;
343
+ total: number;
344
+ }
345
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+
2
+ //# sourceMappingURL=types.js.map
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"types.js"}
package/package.json ADDED
@@ -0,0 +1,99 @@
1
+ {
2
+ "name": "@fayz-ai/shop",
3
+ "version": "0.2.0",
4
+ "description": "Fayz SDK ecommerce layer — products, orders, customers, discounts, shipping",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "source": "./src/index.ts",
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ },
16
+ "./client": {
17
+ "source": "./src/index.ts",
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js",
20
+ "require": "./dist/index.cjs"
21
+ },
22
+ "./admin": {
23
+ "source": "./src/index.ts",
24
+ "types": "./dist/index.d.ts",
25
+ "import": "./dist/index.js",
26
+ "require": "./dist/index.cjs"
27
+ },
28
+ "./runtime": {
29
+ "source": "./src/runtime.ts",
30
+ "types": "./dist/runtime.d.ts",
31
+ "import": "./dist/runtime.js",
32
+ "require": "./dist/runtime.cjs"
33
+ },
34
+ "./provider": {
35
+ "source": "./src/provider.ts",
36
+ "types": "./dist/provider.d.ts",
37
+ "import": "./dist/provider.js",
38
+ "require": "./dist/provider.cjs"
39
+ },
40
+ "./types": {
41
+ "source": "./src/types.ts",
42
+ "types": "./dist/types.d.ts",
43
+ "import": "./dist/types.js",
44
+ "require": "./dist/types.cjs"
45
+ },
46
+ "./catalog": {
47
+ "source": "./src/catalog.ts",
48
+ "types": "./dist/catalog.d.ts",
49
+ "import": "./dist/catalog.js",
50
+ "require": "./dist/catalog.cjs"
51
+ },
52
+ "./mock": {
53
+ "source": "./src/mock.ts",
54
+ "types": "./dist/mock.d.ts",
55
+ "import": "./dist/mock.js",
56
+ "require": "./dist/mock.cjs"
57
+ }
58
+ },
59
+ "files": [
60
+ "dist",
61
+ "src"
62
+ ],
63
+ "dependencies": {
64
+ "lucide-react": "^0.400.0",
65
+ "@supabase/supabase-js": "^2.45.0",
66
+ "zustand": "^4.5.0",
67
+ "@fayz-ai/ui": "^0.2.0",
68
+ "@fayz-ai/auth": "^0.2.0",
69
+ "@fayz-ai/core": "^0.2.0"
70
+ },
71
+ "peerDependencies": {
72
+ "react": "^18.0.0 || ^19.0.0",
73
+ "react-dom": "^18.0.0 || ^19.0.0"
74
+ },
75
+ "devDependencies": {
76
+ "@types/react": "^18.3.0",
77
+ "@types/react-dom": "^18.3.0",
78
+ "react": "^18.3.0",
79
+ "react-dom": "^18.3.0",
80
+ "tsup": "^8.2.0",
81
+ "typescript": "^5.5.0"
82
+ },
83
+ "keywords": [
84
+ "fayz",
85
+ "ecommerce",
86
+ "shop",
87
+ "supabase"
88
+ ],
89
+ "license": "MIT",
90
+ "publishConfig": {
91
+ "access": "public"
92
+ },
93
+ "scripts": {
94
+ "build": "tsup && tsc --emitDeclarationOnly --declaration --declarationMap --noEmit false",
95
+ "dev": "tsup --watch",
96
+ "typecheck": "tsc --noEmit",
97
+ "clean": "rm -rf dist"
98
+ }
99
+ }
package/src/catalog.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { MOCK_PRODUCTS, MOCK_CATEGORIES, MOCK_DISCOUNTS, svgPlaceholder } from './mock-catalog'
2
+ export { buildMockCatalog } from './mock-catalog'
3
+ export type { CatalogInput, CatalogProductInput, CatalogCategoryInput } from './mock-catalog'
@@ -0,0 +1,120 @@
1
+ import { getShopProvider } from './runtime'
2
+ import type { ShopProvider } from './provider'
3
+ import type {
4
+ CartTotals, DiscountValidation, InventoryCheck, InventoryIssue, InventoryLine, Order,
5
+ } from './types'
6
+
7
+ // ---------------------------------------------------------------------------
8
+ // Commerce primitive APIs (FAY-1193)
9
+ //
10
+ // Provider-agnostic helpers that custom app workflows call instead of re-deriving
11
+ // discount/inventory/total logic. They take an explicit ShopProvider (default:
12
+ // the configured runtime provider) so they work the same against mock or Supabase.
13
+ //
14
+ // NOTE: when run in the browser these compute the *advisory* answer (UI preview).
15
+ // The authoritative values are recomputed server-side in the shop_place_order RPC
16
+ // — the browser can never set what it pays.
17
+ // ---------------------------------------------------------------------------
18
+
19
+ function round2(n: number): number {
20
+ return Math.round(n * 100) / 100
21
+ }
22
+
23
+ /** Validate a discount code against the provider: existence, status, date window, usage limit, supported type. */
24
+ export async function validateDiscount(
25
+ input: { code: string; subtotal?: number; customerId?: string },
26
+ provider: ShopProvider = getShopProvider(),
27
+ ): Promise<DiscountValidation> {
28
+ const code = input.code.trim()
29
+ if (!code) return { valid: false, code: null, type: null, value: 0, reason: 'empty' }
30
+
31
+ const list = await provider.listDiscounts({ status: 'active' })
32
+ const discount = list.find((d) => (d.code ?? '').toUpperCase() === code.toUpperCase())
33
+ if (!discount) return { valid: false, code: null, type: null, value: 0, reason: 'not_found' }
34
+
35
+ const now = new Date().toISOString()
36
+ if (discount.startsAt > now) return { valid: false, code: discount.code, type: discount.type, value: 0, reason: 'not_started' }
37
+ if (discount.endsAt && discount.endsAt < now) return { valid: false, code: discount.code, type: discount.type, value: 0, reason: 'expired' }
38
+ if (discount.usageLimit != null && discount.timesUsed >= discount.usageLimit) {
39
+ return { valid: false, code: discount.code, type: discount.type, value: 0, reason: 'usage_limit' }
40
+ }
41
+ if (discount.type !== 'percentage' && discount.type !== 'fixed_amount') {
42
+ return { valid: false, code: discount.code, type: discount.type, value: 0, reason: 'unsupported' }
43
+ }
44
+ return { valid: true, code: discount.code, type: discount.type, value: discount.value }
45
+ }
46
+
47
+ /** Apply a validated discount to a subtotal, clamped to [0, subtotal]. Pure. */
48
+ export function applyDiscount(validation: DiscountValidation, subtotal: number): { discountTotal: number; code: string | null } {
49
+ if (!validation.valid) return { discountTotal: 0, code: null }
50
+ if (validation.type === 'percentage') {
51
+ return { discountTotal: round2(Math.min(subtotal, (subtotal * validation.value) / 100)), code: validation.code }
52
+ }
53
+ if (validation.type === 'fixed_amount') {
54
+ return { discountTotal: round2(Math.min(subtotal, validation.value)), code: validation.code }
55
+ }
56
+ return { discountTotal: 0, code: validation.code }
57
+ }
58
+
59
+ /** Report out-of-stock / unavailable lines against current catalog inventory. No mutation. */
60
+ export async function checkInventory(
61
+ lines: InventoryLine[],
62
+ provider: ShopProvider = getShopProvider(),
63
+ ): Promise<InventoryCheck> {
64
+ const issues: InventoryIssue[] = []
65
+ for (const line of lines) {
66
+ const product = await provider.getProduct(line.productId)
67
+ if (!product || product.status !== 'active') {
68
+ issues.push({ productId: line.productId, name: product?.name ?? line.productId, requested: line.quantity, available: 0 })
69
+ continue
70
+ }
71
+ if (product.inventoryCount < line.quantity) {
72
+ issues.push({ productId: line.productId, name: product.name, requested: line.quantity, available: product.inventoryCount })
73
+ }
74
+ }
75
+ return { ok: issues.length === 0, issues }
76
+ }
77
+
78
+ /**
79
+ * Recompute cart totals from authoritative catalog prices (ignores any client
80
+ * unitPrice), applying a validated discount and shipping. Tax is 0 in v1 (hook).
81
+ */
82
+ export async function computeCartTotals(
83
+ input: { lines: InventoryLine[]; discountCode?: string; customerId?: string; shipping?: number },
84
+ provider: ShopProvider = getShopProvider(),
85
+ ): Promise<CartTotals> {
86
+ const lines: CartTotals['lines'] = []
87
+ let subtotal = 0
88
+ for (const line of input.lines) {
89
+ const product = await provider.getProduct(line.productId)
90
+ if (!product) continue
91
+ const lineTotal = round2(product.price * line.quantity)
92
+ subtotal += lineTotal
93
+ lines.push({ productId: product.id, name: product.name, sku: product.sku, unitPrice: product.price, quantity: line.quantity, lineTotal })
94
+ }
95
+ subtotal = round2(subtotal)
96
+
97
+ let discountTotal = 0
98
+ let discountCode: string | null = null
99
+ if (input.discountCode) {
100
+ const applied = applyDiscount(
101
+ await validateDiscount({ code: input.discountCode, subtotal, customerId: input.customerId }, provider),
102
+ subtotal,
103
+ )
104
+ discountTotal = applied.discountTotal
105
+ discountCode = applied.code
106
+ }
107
+
108
+ const shippingTotal = round2(Math.max(input.shipping ?? 0, 0))
109
+ const taxTotal = 0
110
+ const total = round2(Math.max(subtotal - discountTotal + shippingTotal + taxTotal, 0))
111
+ return { lines, subtotal, discountTotal, discountCode, taxTotal, shippingTotal, total }
112
+ }
113
+
114
+ /** A customer's orders (scoped by id; RLS-enforced to the owner on Supabase). */
115
+ export async function getCustomerOrders(
116
+ customerId: string,
117
+ provider: ShopProvider = getShopProvider(),
118
+ ): Promise<Order[]> {
119
+ return provider.listOrders({ customerId, limit: 100 })
120
+ }
package/src/index.ts ADDED
@@ -0,0 +1,34 @@
1
+ export type {
2
+ Product, ProductImage, Category,
3
+ Order, OrderItem, OrderStatus, FinancialStatus, FulfillmentStatus,
4
+ ShopCustomer, Discount, DiscountType, DiscountMethod, DiscountStatus,
5
+ ProductStatus,
6
+ CreateProductInput, UpdateProductInput, ListProductsOptions,
7
+ CreateCategoryInput, UpdateCategoryInput,
8
+ CreateOrderInput, UpdateOrderInput, ListOrdersOptions,
9
+ CreateCustomerInput, UpdateCustomerInput, ListCustomersOptions, ResolveCustomerInput,
10
+ CreateDiscountInput, UpdateDiscountInput, ListDiscountsOptions,
11
+ PlaceOrderInput, PlaceOrderLine,
12
+ DiscountValidation, InventoryLine, InventoryIssue, InventoryCheck, CartTotalLine, CartTotals,
13
+ } from './types'
14
+
15
+ export type { ShopProvider } from './provider'
16
+ export {
17
+ validateDiscount, applyDiscount, checkInventory, computeCartTotals, getCustomerOrders,
18
+ } from './commerce'
19
+ export { SupabaseShopProvider, createSupabaseShopProvider } from './supabase-provider'
20
+ export { MockShopProvider, createMockShopProvider } from './mock-provider'
21
+ export type { MockShopSeed } from './mock-provider'
22
+ export { setShopTenantResolver, getShopTenantId } from './tenant'
23
+ export type { TenantResolver } from './tenant'
24
+ export { getShopProvider, setShopProvider, resetShopProvider } from './runtime'
25
+
26
+ // Deterministic mock catalog (also useful for seeding real databases and tests)
27
+ export {
28
+ MOCK_PRODUCTS,
29
+ MOCK_CATEGORIES,
30
+ MOCK_DISCOUNTS,
31
+ svgPlaceholder,
32
+ buildMockCatalog,
33
+ } from './catalog'
34
+ export type { CatalogInput, CatalogProductInput, CatalogCategoryInput } from './catalog'