@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
package/src/types.ts ADDED
@@ -0,0 +1,401 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Product
3
+ // ---------------------------------------------------------------------------
4
+
5
+ export type ProductStatus = 'draft' | 'active' | 'archived'
6
+
7
+ export interface Product {
8
+ id: string
9
+ tenantId: string
10
+ name: string
11
+ slug: string
12
+ description: string | null
13
+ price: number
14
+ compareAtPrice: number | null
15
+ currency: string
16
+ status: ProductStatus
17
+ inventoryCount: number
18
+ sku: string | null
19
+ sortOrder: number
20
+ metadata: Record<string, unknown>
21
+ images: ProductImage[]
22
+ categoryId: string | null
23
+ categoryName: string | null
24
+ isPhysical: boolean
25
+ weight: number | null
26
+ weightUnit: string
27
+ createdAt: string
28
+ updatedAt: string
29
+ }
30
+
31
+ export interface ProductImage {
32
+ id: string
33
+ productId: string
34
+ url: string
35
+ altText: string | null
36
+ sortOrder: number
37
+ isPrimary: boolean
38
+ createdAt: string
39
+ }
40
+
41
+ export interface CreateProductInput {
42
+ name: string
43
+ slug?: string
44
+ description?: string
45
+ price: number
46
+ compareAtPrice?: number
47
+ currency?: string
48
+ status?: ProductStatus
49
+ inventoryCount?: number
50
+ sku?: string
51
+ sortOrder?: number
52
+ metadata?: Record<string, unknown>
53
+ categoryId?: string
54
+ isPhysical?: boolean
55
+ weight?: number
56
+ weightUnit?: string
57
+ }
58
+
59
+ export interface UpdateProductInput {
60
+ name?: string
61
+ slug?: string
62
+ description?: string
63
+ price?: number
64
+ compareAtPrice?: number | null
65
+ currency?: string
66
+ status?: ProductStatus
67
+ inventoryCount?: number
68
+ sku?: string | null
69
+ sortOrder?: number
70
+ metadata?: Record<string, unknown>
71
+ categoryId?: string | null
72
+ isPhysical?: boolean
73
+ weight?: number | null
74
+ weightUnit?: string
75
+ }
76
+
77
+ export interface ListProductsOptions {
78
+ status?: ProductStatus
79
+ categoryId?: string
80
+ search?: string
81
+ /** Exact-match slug lookup (storefront product-detail routing) */
82
+ slug?: string
83
+ limit?: number
84
+ offset?: number
85
+ orderBy?: 'name' | 'price' | 'created_at' | 'sort_order'
86
+ order?: 'asc' | 'desc'
87
+ }
88
+
89
+ // ---------------------------------------------------------------------------
90
+ // Category
91
+ // ---------------------------------------------------------------------------
92
+
93
+ export interface Category {
94
+ id: string
95
+ tenantId: string
96
+ name: string
97
+ slug: string
98
+ description: string | null
99
+ parentId: string | null
100
+ sortOrder: number
101
+ /** Showcase image (storefront category tiles). Not yet a shop_categories column. */
102
+ imageUrl?: string | null
103
+ children?: Category[]
104
+ createdAt: string
105
+ }
106
+
107
+ export interface CreateCategoryInput {
108
+ name: string
109
+ slug?: string
110
+ description?: string
111
+ parentId?: string
112
+ sortOrder?: number
113
+ }
114
+
115
+ export interface UpdateCategoryInput {
116
+ name?: string
117
+ slug?: string
118
+ description?: string | null
119
+ parentId?: string | null
120
+ sortOrder?: number
121
+ }
122
+
123
+ // ---------------------------------------------------------------------------
124
+ // Order
125
+ // ---------------------------------------------------------------------------
126
+
127
+ export type OrderStatus = 'open' | 'archived' | 'cancelled'
128
+ export type FinancialStatus = 'pending' | 'paid' | 'partially_paid' | 'refunded' | 'partially_refunded' | 'voided'
129
+ export type FulfillmentStatus = 'unfulfilled' | 'partially_fulfilled' | 'fulfilled'
130
+
131
+ export interface Order {
132
+ id: string
133
+ tenantId: string
134
+ orderNumber: number
135
+ status: OrderStatus
136
+ financialStatus: FinancialStatus
137
+ fulfillmentStatus: FulfillmentStatus
138
+ currency: string
139
+ subtotal: number
140
+ taxTotal: number
141
+ discountTotal: number
142
+ shippingTotal: number
143
+ total: number
144
+ customerId: string | null
145
+ customerName: string | null
146
+ customerEmail: string | null
147
+ discountCode: string | null
148
+ notes: string | null
149
+ items: OrderItem[]
150
+ createdAt: string
151
+ updatedAt: string
152
+ }
153
+
154
+ export interface OrderItem {
155
+ id: string
156
+ orderId: string
157
+ productId: string | null
158
+ name: string
159
+ sku: string | null
160
+ quantity: number
161
+ unitPrice: number
162
+ total: number
163
+ imageUrl: string | null
164
+ }
165
+
166
+ export interface CreateOrderInput {
167
+ customerId?: string
168
+ customerName?: string
169
+ customerEmail?: string
170
+ currency?: string
171
+ notes?: string
172
+ discountCode?: string
173
+ discountTotal?: number
174
+ shippingTotal?: number
175
+ items: Array<{
176
+ productId?: string
177
+ name: string
178
+ sku?: string
179
+ quantity: number
180
+ unitPrice: number
181
+ imageUrl?: string
182
+ }>
183
+ }
184
+
185
+ export interface UpdateOrderInput {
186
+ status?: OrderStatus
187
+ financialStatus?: FinancialStatus
188
+ fulfillmentStatus?: FulfillmentStatus
189
+ notes?: string
190
+ }
191
+
192
+ export interface ListOrdersOptions {
193
+ status?: OrderStatus
194
+ financialStatus?: FinancialStatus
195
+ fulfillmentStatus?: FulfillmentStatus
196
+ customerId?: string
197
+ customerEmail?: string
198
+ search?: string
199
+ limit?: number
200
+ offset?: number
201
+ }
202
+
203
+ // ---------------------------------------------------------------------------
204
+ // Order placement (server-trust primitive)
205
+ // ---------------------------------------------------------------------------
206
+
207
+ /**
208
+ * A single line in a trusted order-placement request. Carries NO price — the
209
+ * server re-reads the product price from the catalog so the browser can never
210
+ * set what it pays.
211
+ */
212
+ export interface PlaceOrderLine {
213
+ productId: string
214
+ quantity: number
215
+ /** Variant/options label appended to the stored item name (display only). */
216
+ optionsLabel?: string
217
+ }
218
+
219
+ /**
220
+ * Input for {@link ShopProvider.placeOrder} — the only trusted path to create a
221
+ * storefront order. Prices, discount math, and inventory are resolved
222
+ * server-side; the client supplies product ids, quantities, and intent only.
223
+ */
224
+ export interface PlaceOrderInput {
225
+ /** Tenant/store id. The Supabase provider falls back to the resolved shop tenant. */
226
+ tenantId?: string
227
+ /** Existing customer id (preferred when the session already resolved one). */
228
+ customerId?: string
229
+ /** Customer identity for find-or-create when {@link PlaceOrderInput.customerId} is absent. */
230
+ customer?: { name?: string; email?: string }
231
+ currency?: string
232
+ notes?: string
233
+ /** Discount code to validate + apply server-side (never trust a client total). */
234
+ discountCode?: string
235
+ /** Shipping cost from the store's shipping policy. Clamped to >= 0 server-side. */
236
+ shippingTotal?: number
237
+ items: PlaceOrderLine[]
238
+ }
239
+
240
+ // ---------------------------------------------------------------------------
241
+ // Customer
242
+ // ---------------------------------------------------------------------------
243
+
244
+ export interface ShopCustomer {
245
+ id: string
246
+ tenantId: string
247
+ firstName: string
248
+ lastName: string
249
+ email: string | null
250
+ phone: string | null
251
+ notes: string | null
252
+ ordersCount: number
253
+ totalSpent: number
254
+ createdAt: string
255
+ updatedAt: string
256
+ }
257
+
258
+ export interface CreateCustomerInput {
259
+ firstName: string
260
+ lastName?: string
261
+ email?: string
262
+ phone?: string
263
+ notes?: string
264
+ }
265
+
266
+ export interface UpdateCustomerInput {
267
+ firstName?: string
268
+ lastName?: string
269
+ email?: string | null
270
+ phone?: string | null
271
+ notes?: string | null
272
+ }
273
+
274
+ export interface ListCustomersOptions {
275
+ search?: string
276
+ limit?: number
277
+ offset?: number
278
+ }
279
+
280
+ /**
281
+ * Input for {@link ShopProvider.resolveCustomer} — find-or-create a customer by
282
+ * email and (on Supabase) link it to the authenticated user (auth.uid) so
283
+ * "my orders" can be enforced by RLS. Runs server-side; the browser never
284
+ * supplies the auth id.
285
+ */
286
+ export interface ResolveCustomerInput {
287
+ email: string
288
+ name?: string
289
+ /** Tenant/store id. The Supabase provider falls back to the resolved shop tenant. */
290
+ tenantId?: string
291
+ }
292
+
293
+ // ---------------------------------------------------------------------------
294
+ // Discount
295
+ // ---------------------------------------------------------------------------
296
+
297
+ export type DiscountType = 'percentage' | 'fixed_amount' | 'free_shipping' | 'buy_x_get_y'
298
+ export type DiscountMethod = 'code' | 'automatic'
299
+ export type DiscountStatus = 'active' | 'expired' | 'scheduled' | 'draft'
300
+
301
+ export interface Discount {
302
+ id: string
303
+ tenantId: string
304
+ title: string
305
+ code: string | null
306
+ type: DiscountType
307
+ method: DiscountMethod
308
+ value: number
309
+ usageLimit: number | null
310
+ oncePerCustomer: boolean
311
+ startsAt: string
312
+ endsAt: string | null
313
+ status: DiscountStatus
314
+ timesUsed: number
315
+ createdAt: string
316
+ updatedAt: string
317
+ }
318
+
319
+ export interface CreateDiscountInput {
320
+ title: string
321
+ code?: string
322
+ type: DiscountType
323
+ method?: DiscountMethod
324
+ value: number
325
+ usageLimit?: number
326
+ oncePerCustomer?: boolean
327
+ startsAt?: string
328
+ endsAt?: string
329
+ status?: DiscountStatus
330
+ }
331
+
332
+ export interface UpdateDiscountInput {
333
+ title?: string
334
+ code?: string | null
335
+ type?: DiscountType
336
+ method?: DiscountMethod
337
+ value?: number
338
+ usageLimit?: number | null
339
+ oncePerCustomer?: boolean
340
+ startsAt?: string
341
+ endsAt?: string | null
342
+ status?: DiscountStatus
343
+ }
344
+
345
+ export interface ListDiscountsOptions {
346
+ status?: DiscountStatus
347
+ type?: DiscountType
348
+ search?: string
349
+ limit?: number
350
+ offset?: number
351
+ }
352
+
353
+ // ---------------------------------------------------------------------------
354
+ // Commerce primitives (FAY-1193) — provider-agnostic helpers for custom workflows
355
+ // ---------------------------------------------------------------------------
356
+
357
+ export interface DiscountValidation {
358
+ valid: boolean
359
+ code: string | null
360
+ type: DiscountType | null
361
+ /** Percentage points for 'percentage'; absolute amount for 'fixed_amount'. */
362
+ value: number
363
+ /** Machine-readable reason when invalid: not_found | expired | not_started | usage_limit | unsupported | empty */
364
+ reason?: string
365
+ }
366
+
367
+ export interface InventoryLine {
368
+ productId: string
369
+ quantity: number
370
+ }
371
+
372
+ export interface InventoryIssue {
373
+ productId: string
374
+ name: string
375
+ requested: number
376
+ available: number
377
+ }
378
+
379
+ export interface InventoryCheck {
380
+ ok: boolean
381
+ issues: InventoryIssue[]
382
+ }
383
+
384
+ export interface CartTotalLine {
385
+ productId: string
386
+ name: string
387
+ sku: string | null
388
+ unitPrice: number
389
+ quantity: number
390
+ lineTotal: number
391
+ }
392
+
393
+ export interface CartTotals {
394
+ lines: CartTotalLine[]
395
+ subtotal: number
396
+ discountTotal: number
397
+ discountCode: string | null
398
+ taxTotal: number
399
+ shippingTotal: number
400
+ total: number
401
+ }