@fayz-ai/shop 0.9.0 → 0.10.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 (144) hide show
  1. package/README.md +18 -1
  2. package/dist/admin.d.ts +2 -0
  3. package/dist/admin.d.ts.map +1 -0
  4. package/dist/catalog.d.ts +1 -3
  5. package/dist/catalog.d.ts.map +1 -1
  6. package/dist/catalog.js +2 -2
  7. package/dist/chunk-3GERSY5F.js +526 -0
  8. package/dist/chunk-3GERSY5F.js.map +1 -0
  9. package/dist/chunk-FQPGE3YN.js +3 -0
  10. package/dist/{chunk-JR25TISO.js.map → chunk-FQPGE3YN.js.map} +1 -1
  11. package/dist/{chunk-CTKZ6O2J.js → chunk-IKNGCRUM.js} +41 -15
  12. package/dist/chunk-IKNGCRUM.js.map +1 -0
  13. package/dist/{chunk-IYQHKMLB.js → chunk-IL6GA4ER.js} +4 -4
  14. package/dist/chunk-IL6GA4ER.js.map +1 -0
  15. package/dist/chunk-KLDS7IPU.js +17 -0
  16. package/dist/chunk-KLDS7IPU.js.map +1 -0
  17. package/dist/chunk-RGTIJQJL.js +93 -0
  18. package/dist/chunk-RGTIJQJL.js.map +1 -0
  19. package/dist/{chunk-SVTCJ6HP.js → chunk-U3O2I3RM.js} +395 -27
  20. package/dist/chunk-U3O2I3RM.js.map +1 -0
  21. package/dist/chunk-XERLK5LD.js +1415 -0
  22. package/dist/chunk-XERLK5LD.js.map +1 -0
  23. package/dist/chunk-XOP6SHPB.js +142 -0
  24. package/dist/chunk-XOP6SHPB.js.map +1 -0
  25. package/dist/client.d.ts +2 -0
  26. package/dist/client.d.ts.map +1 -0
  27. package/dist/{mock-catalog.d.ts → data/mock-catalog.d.ts} +21 -1
  28. package/dist/data/mock-catalog.d.ts.map +1 -0
  29. package/dist/data/mock-provider.d.ts +133 -0
  30. package/dist/data/mock-provider.d.ts.map +1 -0
  31. package/dist/data/mock.d.ts +3 -0
  32. package/dist/data/mock.d.ts.map +1 -0
  33. package/dist/data/provider.d.ts +142 -0
  34. package/dist/data/provider.d.ts.map +1 -0
  35. package/dist/data/supabase-provider.d.ts +100 -0
  36. package/dist/data/supabase-provider.d.ts.map +1 -0
  37. package/dist/data/tables.d.ts +35 -0
  38. package/dist/data/tables.d.ts.map +1 -0
  39. package/dist/data/tenant.d.ts.map +1 -0
  40. package/dist/entities/entity-reconcile.d.ts +32 -0
  41. package/dist/entities/entity-reconcile.d.ts.map +1 -0
  42. package/dist/entities/entity-sql.d.ts +50 -0
  43. package/dist/entities/entity-sql.d.ts.map +1 -0
  44. package/dist/entities/entity-store.d.ts +58 -0
  45. package/dist/entities/entity-store.d.ts.map +1 -0
  46. package/dist/entities/index.d.ts +11 -0
  47. package/dist/entities/index.d.ts.map +1 -0
  48. package/dist/entities/mock-entity-store.d.ts +17 -0
  49. package/dist/entities/mock-entity-store.d.ts.map +1 -0
  50. package/dist/entities/sidecar.d.ts +9 -0
  51. package/dist/entities/sidecar.d.ts.map +1 -0
  52. package/dist/entities/supabase-entity-store.d.ts +8 -0
  53. package/dist/entities/supabase-entity-store.d.ts.map +1 -0
  54. package/dist/entities.js +159 -0
  55. package/dist/entities.js.map +1 -0
  56. package/dist/index.d.ts +17 -10
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +728 -41
  59. package/dist/index.js.map +1 -1
  60. package/dist/inventory/allocation.d.ts +98 -0
  61. package/dist/inventory/allocation.d.ts.map +1 -0
  62. package/dist/inventory/index.d.ts +13 -0
  63. package/dist/inventory/index.d.ts.map +1 -0
  64. package/dist/inventory/mock-stock-store.d.ts +89 -0
  65. package/dist/inventory/mock-stock-store.d.ts.map +1 -0
  66. package/dist/inventory/postgrest-double.d.ts +65 -0
  67. package/dist/inventory/postgrest-double.d.ts.map +1 -0
  68. package/dist/inventory/shopify-mapping.d.ts +67 -0
  69. package/dist/inventory/shopify-mapping.d.ts.map +1 -0
  70. package/dist/inventory/stock-contract.d.ts +46 -0
  71. package/dist/inventory/stock-contract.d.ts.map +1 -0
  72. package/dist/inventory/stock-store.d.ts +96 -0
  73. package/dist/inventory/stock-store.d.ts.map +1 -0
  74. package/dist/inventory/stock-types.d.ts +292 -0
  75. package/dist/inventory/stock-types.d.ts.map +1 -0
  76. package/dist/inventory/supabase-stock-store.d.ts +59 -0
  77. package/dist/inventory/supabase-stock-store.d.ts.map +1 -0
  78. package/dist/inventory/vocabulary.d.ts +86 -0
  79. package/dist/inventory/vocabulary.d.ts.map +1 -0
  80. package/dist/inventory.js +4 -0
  81. package/dist/inventory.js.map +1 -0
  82. package/dist/lib/catalog.d.ts +4 -0
  83. package/dist/lib/catalog.d.ts.map +1 -0
  84. package/dist/{commerce.d.ts → lib/commerce.d.ts} +2 -2
  85. package/dist/lib/commerce.d.ts.map +1 -0
  86. package/dist/lib/emails.d.ts +50 -0
  87. package/dist/lib/emails.d.ts.map +1 -0
  88. package/dist/lib/message-events.d.ts +4 -0
  89. package/dist/lib/message-events.d.ts.map +1 -0
  90. package/dist/lib/runtime.d.ts +5 -0
  91. package/dist/lib/runtime.d.ts.map +1 -0
  92. package/dist/mock.d.ts +1 -2
  93. package/dist/mock.d.ts.map +1 -1
  94. package/dist/mock.js +4 -2
  95. package/dist/provider.d.ts +1 -88
  96. package/dist/provider.d.ts.map +1 -1
  97. package/dist/rules/adapter.d.ts +36 -0
  98. package/dist/rules/adapter.d.ts.map +1 -0
  99. package/dist/rules/attributes.d.ts +64 -0
  100. package/dist/rules/attributes.d.ts.map +1 -0
  101. package/dist/rules/context.d.ts +43 -0
  102. package/dist/rules/context.d.ts.map +1 -0
  103. package/dist/rules/evaluator.d.ts +37 -0
  104. package/dist/rules/evaluator.d.ts.map +1 -0
  105. package/dist/rules/index.d.ts +13 -0
  106. package/dist/rules/index.d.ts.map +1 -0
  107. package/dist/rules/prices.d.ts +25 -0
  108. package/dist/rules/prices.d.ts.map +1 -0
  109. package/dist/rules/rule-types.d.ts +160 -0
  110. package/dist/rules/rule-types.d.ts.map +1 -0
  111. package/dist/rules.js +22 -0
  112. package/dist/rules.js.map +1 -0
  113. package/dist/runtime.d.ts +1 -4
  114. package/dist/runtime.d.ts.map +1 -1
  115. package/dist/runtime.js +5 -3
  116. package/dist/types.d.ts +231 -0
  117. package/dist/types.d.ts.map +1 -1
  118. package/package.json +26 -20
  119. package/dist/chunk-CTKZ6O2J.js.map +0 -1
  120. package/dist/chunk-IYQHKMLB.js.map +0 -1
  121. package/dist/chunk-JR25TISO.js +0 -3
  122. package/dist/chunk-SVTCJ6HP.js.map +0 -1
  123. package/dist/commerce.d.ts.map +0 -1
  124. package/dist/mock-catalog.d.ts.map +0 -1
  125. package/dist/mock-provider.d.ts +0 -60
  126. package/dist/mock-provider.d.ts.map +0 -1
  127. package/dist/supabase-provider.d.ts +0 -56
  128. package/dist/supabase-provider.d.ts.map +0 -1
  129. package/dist/tables.d.ts +0 -12
  130. package/dist/tables.d.ts.map +0 -1
  131. package/dist/tenant.d.ts.map +0 -1
  132. package/src/catalog.ts +0 -3
  133. package/src/commerce.ts +0 -120
  134. package/src/index.ts +0 -40
  135. package/src/mock-catalog.ts +0 -266
  136. package/src/mock-provider.ts +0 -480
  137. package/src/mock.ts +0 -2
  138. package/src/provider.ts +0 -117
  139. package/src/runtime.ts +0 -20
  140. package/src/supabase-provider.ts +0 -754
  141. package/src/tables.ts +0 -17
  142. package/src/tenant.ts +0 -21
  143. package/src/types.ts +0 -623
  144. /package/dist/{tenant.d.ts → data/tenant.d.ts} +0 -0
@@ -1,60 +0,0 @@
1
- import type { ShopProvider } from './provider';
2
- import type { Product, ProductImage, Category, Order, ShopCustomer, Discount, ProductEnquiry, CreateProductInput, UpdateProductInput, ListProductsOptions, CreateProductEnquiryInput, ListProductEnquiriesOptions, CreateCategoryInput, UpdateCategoryInput, CreateOrderInput, UpdateOrderInput, ListOrdersOptions, PlaceOrderInput, CreateCustomerInput, UpdateCustomerInput, ListCustomersOptions, ResolveCustomerInput, CreateDiscountInput, UpdateDiscountInput, ListDiscountsOptions, SalesChannel, CreateSalesChannelInput, UpdateSalesChannelInput } from './types';
3
- export interface MockShopSeed {
4
- products?: Product[];
5
- categories?: Category[];
6
- discounts?: Discount[];
7
- }
8
- export declare class MockShopProvider implements ShopProvider {
9
- private products;
10
- private categories;
11
- private discounts;
12
- private orders;
13
- private customers;
14
- private enquiries;
15
- private channels;
16
- constructor(seed?: MockShopSeed);
17
- private syncOrders;
18
- private syncCustomers;
19
- private syncEnquiries;
20
- private syncChannels;
21
- private persistOrders;
22
- private persistCustomers;
23
- private persistEnquiries;
24
- private persistChannels;
25
- listProducts(opts?: ListProductsOptions): Promise<Product[]>;
26
- getProduct(id: string): Promise<Product | null>;
27
- createProduct(input: CreateProductInput): Promise<Product>;
28
- updateProduct(id: string, input: UpdateProductInput): Promise<Product>;
29
- deleteProduct(id: string): Promise<void>;
30
- uploadProductImage(_productId: string, _file: File): Promise<ProductImage>;
31
- deleteProductImage(_imageId: string): Promise<void>;
32
- createProductEnquiry(input: CreateProductEnquiryInput): Promise<ProductEnquiry>;
33
- listProductEnquiries(opts?: ListProductEnquiriesOptions): Promise<ProductEnquiry[]>;
34
- listCategories(): Promise<Category[]>;
35
- createCategory(input: CreateCategoryInput): Promise<Category>;
36
- updateCategory(id: string, input: UpdateCategoryInput): Promise<Category>;
37
- deleteCategory(id: string): Promise<void>;
38
- listOrders(opts?: ListOrdersOptions): Promise<Order[]>;
39
- getOrder(id: string): Promise<Order | null>;
40
- createOrder(input: CreateOrderInput): Promise<Order>;
41
- updateOrder(id: string, input: UpdateOrderInput): Promise<Order>;
42
- placeOrder(input: PlaceOrderInput): Promise<Order>;
43
- listCustomers(opts?: ListCustomersOptions): Promise<ShopCustomer[]>;
44
- getCustomer(id: string): Promise<ShopCustomer | null>;
45
- createCustomer(input: CreateCustomerInput): Promise<ShopCustomer>;
46
- updateCustomer(id: string, input: UpdateCustomerInput): Promise<ShopCustomer>;
47
- deleteCustomer(id: string): Promise<void>;
48
- resolveCustomer(input: ResolveCustomerInput): Promise<ShopCustomer>;
49
- listDiscounts(opts?: ListDiscountsOptions): Promise<Discount[]>;
50
- getDiscount(id: string): Promise<Discount | null>;
51
- createDiscount(input: CreateDiscountInput): Promise<Discount>;
52
- updateDiscount(id: string, input: UpdateDiscountInput): Promise<Discount>;
53
- deleteDiscount(id: string): Promise<void>;
54
- listSalesChannels(): Promise<SalesChannel[]>;
55
- createSalesChannel(input: CreateSalesChannelInput): Promise<SalesChannel>;
56
- updateSalesChannel(id: string, input: UpdateSalesChannelInput): Promise<SalesChannel>;
57
- deleteSalesChannel(id: string): Promise<void>;
58
- }
59
- export declare function createMockShopProvider(seed?: MockShopSeed): ShopProvider;
60
- //# sourceMappingURL=mock-provider.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mock-provider.d.ts","sourceRoot":"","sources":["../src/mock-provider.ts"],"names":[],"mappings":"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"}
@@ -1,56 +0,0 @@
1
- import type { ShopProvider } from './provider';
2
- import type { Product, ProductImage, Category, Order, ShopCustomer, Discount, CreateProductInput, UpdateProductInput, ListProductsOptions, CreateCategoryInput, UpdateCategoryInput, CreateOrderInput, UpdateOrderInput, ListOrdersOptions, PlaceOrderInput, CreateCustomerInput, UpdateCustomerInput, ListCustomersOptions, ResolveCustomerInput, CreateDiscountInput, UpdateDiscountInput, ListDiscountsOptions, SalesChannel, CreateSalesChannelInput, UpdateSalesChannelInput, ShippingZone, CreateShippingZoneInput, UpdateShippingZoneInput } from './types';
3
- export declare class SupabaseShopProvider implements ShopProvider {
4
- listProducts(options?: ListProductsOptions): Promise<Product[]>;
5
- getProduct(id: string): Promise<Product | null>;
6
- createProduct(input: CreateProductInput): Promise<Product>;
7
- updateProduct(id: string, input: UpdateProductInput): Promise<Product>;
8
- deleteProduct(id: string): Promise<void>;
9
- uploadProductImage(productId: string, file: File): Promise<ProductImage>;
10
- updateProductImage(imageId: string, input: {
11
- isPrimary?: boolean;
12
- sortOrder?: number;
13
- altText?: string | null;
14
- }): Promise<ProductImage>;
15
- deleteProductImage(imageId: string): Promise<void>;
16
- listCategories(): Promise<Category[]>;
17
- createCategory(input: CreateCategoryInput): Promise<Category>;
18
- updateCategory(id: string, input: UpdateCategoryInput): Promise<Category>;
19
- deleteCategory(id: string): Promise<void>;
20
- listOrders(options?: ListOrdersOptions): Promise<Order[]>;
21
- getOrder(id: string): Promise<Order | null>;
22
- createOrder(input: CreateOrderInput): Promise<Order>;
23
- updateOrder(id: string, input: UpdateOrderInput): Promise<Order>;
24
- listShippingZones(): Promise<ShippingZone[]>;
25
- createShippingZone(input: CreateShippingZoneInput): Promise<ShippingZone>;
26
- updateShippingZone(id: string, input: UpdateShippingZoneInput): Promise<ShippingZone>;
27
- deleteShippingZone(id: string): Promise<void>;
28
- /**
29
- * Settle a pending order: the merchant confirming that the money arrived.
30
- *
31
- * Goes through the RPC rather than an UPDATE so the payment ledger row in
32
- * public.transactions closes in the same transaction as the order — set
33
- * financial_status by hand and the order says paid while the ledger still
34
- * says pending. Since 0019 the RPC also enforces who may call it: a member of
35
- * the owning tenant, or a PSP webhook with the service role.
36
- */
37
- confirmPayment(id: string, reference?: string): Promise<Order | null>;
38
- placeOrder(input: PlaceOrderInput): Promise<Order>;
39
- listCustomers(options?: ListCustomersOptions): Promise<ShopCustomer[]>;
40
- getCustomer(id: string): Promise<ShopCustomer | null>;
41
- createCustomer(input: CreateCustomerInput): Promise<ShopCustomer>;
42
- updateCustomer(id: string, input: UpdateCustomerInput): Promise<ShopCustomer>;
43
- deleteCustomer(id: string): Promise<void>;
44
- resolveCustomer(input: ResolveCustomerInput): Promise<ShopCustomer>;
45
- listDiscounts(options?: ListDiscountsOptions): Promise<Discount[]>;
46
- getDiscount(id: string): Promise<Discount | null>;
47
- createDiscount(input: CreateDiscountInput): Promise<Discount>;
48
- updateDiscount(id: string, input: UpdateDiscountInput): Promise<Discount>;
49
- deleteDiscount(id: string): Promise<void>;
50
- listSalesChannels(): Promise<SalesChannel[]>;
51
- createSalesChannel(input: CreateSalesChannelInput): Promise<SalesChannel>;
52
- updateSalesChannel(id: string, input: UpdateSalesChannelInput): Promise<SalesChannel>;
53
- deleteSalesChannel(id: string): Promise<void>;
54
- }
55
- export declare function createSupabaseShopProvider(): ShopProvider;
56
- //# sourceMappingURL=supabase-provider.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"supabase-provider.d.ts","sourceRoot":"","sources":["../src/supabase-provider.ts"],"names":[],"mappings":"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"}
package/dist/tables.d.ts DELETED
@@ -1,12 +0,0 @@
1
- export declare const T: {
2
- readonly products: "plg_shop_products";
3
- readonly productImages: "plg_shop_product_images";
4
- readonly categories: "plg_shop_categories";
5
- readonly orders: "plg_shop_orders";
6
- readonly orderItems: "plg_shop_order_items";
7
- readonly customers: "plg_shop_customers";
8
- readonly discounts: "plg_shop_discounts";
9
- readonly salesChannels: "plg_shop_channels";
10
- readonly shippingZones: "shipping_zones";
11
- };
12
- //# sourceMappingURL=tables.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../src/tables.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,CAAC;;;;;;;;;;CAaJ,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"tenant.d.ts","sourceRoot":"","sources":["../src/tenant.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,cAAc,GAAG,MAAM,MAAM,GAAG,SAAS,CAAA;AAIrD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAEpE;AAED,wBAAgB,eAAe,IAAI,MAAM,GAAG,SAAS,CAOpD"}
package/src/catalog.ts DELETED
@@ -1,3 +0,0 @@
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'
package/src/commerce.ts DELETED
@@ -1,120 +0,0 @@
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 DELETED
@@ -1,40 +0,0 @@
1
- export type {
2
- Product, ProductImage, Category, ProductEnquiry, ProductEnquiryStatus,
3
- Order, OrderItem, OrderStatus, FinancialStatus, FulfillmentStatus,
4
- ShopCustomer, Discount, DiscountType, DiscountMethod, DiscountStatus,
5
- ProductStatus,
6
- CreateProductInput, UpdateProductInput, ListProductsOptions,
7
- CreateProductEnquiryInput, ListProductEnquiriesOptions,
8
- CreateCategoryInput, UpdateCategoryInput,
9
- CreateOrderInput, UpdateOrderInput, ListOrdersOptions,
10
- CreateCustomerInput, UpdateCustomerInput, ListCustomersOptions, ResolveCustomerInput,
11
- CreateDiscountInput, UpdateDiscountInput, ListDiscountsOptions,
12
- SalesChannel, SalesChannelKind, CreateSalesChannelInput, UpdateSalesChannelInput,
13
- PlaceOrderInput, PlaceOrderLine,
14
- DiscountValidation, InventoryLine, InventoryIssue, InventoryCheck, CartTotalLine, CartTotals,
15
- } from './types'
16
-
17
- export type {
18
- ShippingZone, CreateShippingZoneInput, UpdateShippingZoneInput,
19
- ShippingQuoteOption, CustomerAddress,
20
- } from './types'
21
- export type { ShopProvider } from './provider'
22
- export {
23
- validateDiscount, applyDiscount, checkInventory, computeCartTotals, getCustomerOrders,
24
- } from './commerce'
25
- export { SupabaseShopProvider, createSupabaseShopProvider } from './supabase-provider'
26
- export { MockShopProvider, createMockShopProvider } from './mock-provider'
27
- export type { MockShopSeed } from './mock-provider'
28
- export { setShopTenantResolver, getShopTenantId } from './tenant'
29
- export type { TenantResolver } from './tenant'
30
- export { getShopProvider, setShopProvider, resetShopProvider } from './runtime'
31
-
32
- // Deterministic mock catalog (also useful for seeding real databases and tests)
33
- export {
34
- MOCK_PRODUCTS,
35
- MOCK_CATEGORIES,
36
- MOCK_DISCOUNTS,
37
- svgPlaceholder,
38
- buildMockCatalog,
39
- } from './catalog'
40
- export type { CatalogInput, CatalogProductInput, CatalogCategoryInput } from './catalog'
@@ -1,266 +0,0 @@
1
- import type { Product, ProductImage, Category, Discount } from './types'
2
-
3
- // ---------------------------------------------------------------------------
4
- // Deterministic, offline product catalog for the mock provider.
5
- // Product names sourced from the Fayz client repo references
6
- // (apps/web ShopSection mockups + original shop seeds), expanded to a
7
- // 16-product / 4-category Shopify-style catalog.
8
- // ---------------------------------------------------------------------------
9
-
10
- const T0 = '2026-01-01T12:00:00.000Z'
11
-
12
- /** 600x600 gradient SVG with product initials — no network, e2e-stable. */
13
- export function svgPlaceholder(label: string, hueA: number, hueB: number): string {
14
- const initials = label
15
- .split(/\s+/)
16
- .map((w) => w[0])
17
- .slice(0, 2)
18
- .join('')
19
- .toUpperCase()
20
- const svg =
21
- `<svg xmlns="http://www.w3.org/2000/svg" width="600" height="600">` +
22
- `<defs><linearGradient id="g" x1="0" y1="0" x2="1" y2="1">` +
23
- `<stop offset="0" stop-color="hsl(${hueA},45%,82%)"/>` +
24
- `<stop offset="1" stop-color="hsl(${hueB},50%,62%)"/>` +
25
- `</linearGradient></defs>` +
26
- `<rect width="600" height="600" fill="url(#g)"/>` +
27
- `<text x="300" y="330" font-family="sans-serif" font-size="120" font-weight="700" ` +
28
- `fill="rgba(255,255,255,0.85)" text-anchor="middle">${initials}</text>` +
29
- `</svg>`
30
- return `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`
31
- }
32
-
33
- function image(productId: string, name: string, hueA: number, hueB: number, idx = 0): ProductImage {
34
- return {
35
- id: `img-${productId}-${idx}`,
36
- productId,
37
- url: svgPlaceholder(name, hueA, hueB),
38
- altText: name,
39
- sortOrder: idx,
40
- isPrimary: idx === 0,
41
- createdAt: T0,
42
- }
43
- }
44
-
45
- export const MOCK_CATEGORIES: Category[] = [
46
- { id: 'cat-apparel', tenantId: 'mock', name: 'Apparel', slug: 'apparel', description: 'Clothing and wearables', parentId: null, sortOrder: 0, createdAt: T0 },
47
- { id: 'cat-bags', tenantId: 'mock', name: 'Bags & Accessories', slug: 'bags-accessories', description: 'Bags, wallets and carry gear', parentId: null, sortOrder: 1, createdAt: T0 },
48
- { id: 'cat-home', tenantId: 'mock', name: 'Home & Kitchen', slug: 'home-kitchen', description: 'Everyday home goods', parentId: null, sortOrder: 2, createdAt: T0 },
49
- { id: 'cat-tech', tenantId: 'mock', name: 'Stationery & Tech', slug: 'stationery-tech', description: 'Desk, audio and paper goods', parentId: null, sortOrder: 3, createdAt: T0 },
50
- ]
51
-
52
- interface SeedProduct {
53
- id: string
54
- name: string
55
- slug: string
56
- description: string
57
- price: number
58
- compareAtPrice?: number
59
- inventoryCount: number
60
- sku: string
61
- categoryId: string
62
- hues: [number, number]
63
- weight: number
64
- }
65
-
66
- const SEEDS: SeedProduct[] = [
67
- // --- Apparel ---
68
- { id: 'p-01', name: 'Classic T-Shirt', slug: 'classic-t-shirt', description: 'A timeless crew-neck tee in heavyweight combed cotton. Pre-shrunk, breathable, and built to outlast trends.', price: 59.9, compareAtPrice: 79.9, inventoryCount: 50, sku: 'TSH-001', categoryId: 'cat-apparel', hues: [210, 240], weight: 0.2 },
69
- { id: 'p-02', name: 'Linen Shirt', slug: 'linen-shirt', description: 'A relaxed-fit shirt in 100% European linen. Naturally breathable and softer with every wash.', price: 189.9, inventoryCount: 24, sku: 'LNS-002', categoryId: 'cat-apparel', hues: [35, 20], weight: 0.3 },
70
- { id: 'p-03', name: 'Denim Jacket', slug: 'denim-jacket', description: 'A structured trucker jacket in 12oz raw denim. Ages beautifully with hidden interior pockets.', price: 299.9, inventoryCount: 12, sku: 'DNJ-003', categoryId: 'cat-apparel', hues: [215, 200], weight: 0.8 },
71
- { id: 'p-04', name: 'Wool Beanie', slug: 'wool-beanie', description: 'A double-layer merino wool beanie. Warm without itch, with a low-profile cuffed fit.', price: 79.9, inventoryCount: 0, sku: 'WBN-004', categoryId: 'cat-apparel', hues: [0, 350], weight: 0.1 },
72
-
73
- // --- Bags & Accessories ---
74
- { id: 'p-05', name: 'Canvas Tote Bag', slug: 'canvas-tote-bag', description: 'An everyday carryall in 16oz waxed canvas with reinforced straps and an interior zip pocket.', price: 39.9, inventoryCount: 30, sku: 'BAG-001', categoryId: 'cat-bags', hues: [80, 110], weight: 0.3 },
75
- { id: 'p-06', name: 'Leather Backpack', slug: 'leather-backpack', description: 'Full-grain leather backpack with a padded 15" laptop sleeve, magnetic closures and brass hardware.', price: 549.9, inventoryCount: 8, sku: 'LBP-006', categoryId: 'cat-bags', hues: [25, 15], weight: 1.4 },
76
- { id: 'p-07', name: 'Leather Card Wallet', slug: 'leather-card-wallet', description: 'A slim six-card wallet in vegetable-tanned leather. RFID-shielded with a center cash slot.', price: 89.9, compareAtPrice: 119.9, inventoryCount: 45, sku: 'LCW-007', categoryId: 'cat-bags', hues: [30, 45], weight: 0.08 },
77
- { id: 'p-08', name: 'Travel Duffel', slug: 'travel-duffel', description: 'A 40L weekender in ballistic nylon with a trolley sleeve, shoe compartment and detachable strap.', price: 329.9, inventoryCount: 15, sku: 'TDF-008', categoryId: 'cat-bags', hues: [150, 170], weight: 1.1 },
78
-
79
- // --- Home & Kitchen ---
80
- { id: 'p-09', name: 'Ceramic Coffee Mug', slug: 'ceramic-coffee-mug', description: 'A 350ml stoneware mug with a matte glaze and comfortable wide handle. Dishwasher and microwave safe.', price: 34.9, inventoryCount: 0, sku: 'MUG-001', categoryId: 'cat-home', hues: [190, 210], weight: 0.4 },
81
- { id: 'p-10', name: 'Scented Soy Candle', slug: 'scented-soy-candle', description: 'Hand-poured soy wax candle with cedar and bergamot notes. 45-hour burn time in a reusable glass jar.', price: 69.9, inventoryCount: 60, sku: 'SSC-010', categoryId: 'cat-home', hues: [45, 30], weight: 0.5 },
82
- { id: 'p-11', name: 'Bamboo Cutting Board', slug: 'bamboo-cutting-board', description: 'An end-grain bamboo board that is gentle on knives, with a juice groove and inset handles.', price: 119.9, inventoryCount: 22, sku: 'BCB-011', categoryId: 'cat-home', hues: [90, 70], weight: 1.2 },
83
- { id: 'p-12', name: 'Stoneware Plate Set', slug: 'stoneware-plate-set', description: 'A set of four reactive-glaze dinner plates. Each piece is unique, oven-safe to 220°C.', price: 249.9, inventoryCount: 10, sku: 'SPS-012', categoryId: 'cat-home', hues: [200, 180], weight: 2.4 },
84
-
85
- // --- Stationery & Tech ---
86
- { id: 'p-13', name: 'Wireless Headphones', slug: 'wireless-headphones', description: 'Over-ear headphones with active noise cancelling, 40-hour battery and multipoint Bluetooth 5.3.', price: 899.9, inventoryCount: 18, sku: 'WHP-013', categoryId: 'cat-tech', hues: [260, 280], weight: 0.35 },
87
- { id: 'p-14', name: 'Notebook Set', slug: 'notebook-set', description: 'Three A5 dot-grid notebooks with 120gsm paper, lay-flat binding and numbered pages.', price: 29.9, inventoryCount: 80, sku: 'NBS-014', categoryId: 'cat-tech', hues: [330, 310], weight: 0.45 },
88
- { id: 'p-15', name: 'Bluetooth Speaker', slug: 'bluetooth-speaker', description: 'A pocket-size speaker with surprising low end, IPX7 waterproofing and 14-hour playtime.', price: 199.9, compareAtPrice: 259.9, inventoryCount: 26, sku: 'BTS-015', categoryId: 'cat-tech', hues: [170, 190], weight: 0.3 },
89
- { id: 'p-16', name: 'Felt Desk Mat', slug: 'felt-desk-mat', description: 'A 80x30cm merino felt desk mat with a cork base. Protects the desk and quiets the keyboard.', price: 99.9, inventoryCount: 34, sku: 'FDM-016', categoryId: 'cat-tech', hues: [220, 230], weight: 0.6 },
90
- ]
91
-
92
- const CATEGORY_NAME: Record<string, string> = Object.fromEntries(
93
- MOCK_CATEGORIES.map((c) => [c.id, c.name]),
94
- )
95
-
96
- export const MOCK_PRODUCTS: Product[] = SEEDS.map((s, i) => ({
97
- id: s.id,
98
- tenantId: 'mock',
99
- name: s.name,
100
- slug: s.slug,
101
- description: s.description,
102
- price: s.price,
103
- compareAtPrice: s.compareAtPrice ?? null,
104
- currency: 'BRL',
105
- status: 'active',
106
- inventoryCount: s.inventoryCount,
107
- sku: s.sku,
108
- sortOrder: i,
109
- metadata: {},
110
- images: [image(s.id, s.name, s.hues[0], s.hues[1])],
111
- categoryId: s.categoryId,
112
- categoryName: CATEGORY_NAME[s.categoryId] ?? null,
113
- isPhysical: true,
114
- weight: s.weight,
115
- weightUnit: 'kg',
116
- // staggered timestamps → deterministic 'newest' sort (p-16 newest)
117
- createdAt: `2026-01-${String(i + 1).padStart(2, '0')}T12:00:00.000Z`,
118
- updatedAt: `2026-01-${String(i + 1).padStart(2, '0')}T12:00:00.000Z`,
119
- }))
120
-
121
- export const MOCK_DISCOUNTS: Discount[] = [
122
- {
123
- id: 'disc-welcome',
124
- tenantId: 'mock',
125
- title: 'Welcome 10%',
126
- code: 'BEMVINDO10',
127
- type: 'percentage',
128
- method: 'code',
129
- value: 10,
130
- usageLimit: null,
131
- oncePerCustomer: false,
132
- minSubtotal: null,
133
- appliesTo: 'all',
134
- productIds: [],
135
- categoryIds: [],
136
- startsAt: T0,
137
- endsAt: null,
138
- status: 'active',
139
- timesUsed: 0,
140
- createdAt: T0,
141
- updatedAt: T0,
142
- },
143
- ]
144
-
145
- // ---------------------------------------------------------------------------
146
- // buildMockCatalog — terse per-store catalog definition. Generates ids,
147
- // slugs, SVG imagery and staggered timestamps so every store gets a
148
- // deterministic, e2e-stable catalog from a few lines of data.
149
- // ---------------------------------------------------------------------------
150
-
151
- export interface CatalogCategoryInput {
152
- name: string
153
- description?: string
154
- image?: string
155
- }
156
-
157
- export interface CatalogProductInput {
158
- name: string
159
- description: string
160
- price: number
161
- compareAtPrice?: number
162
- inventory: number
163
- sku: string
164
- /** Category name — must match one of the categories */
165
- category: string
166
- hues?: [number, number]
167
- weight?: number
168
- image?: string
169
- metadata?: Record<string, unknown>
170
- }
171
-
172
- export interface CatalogInput {
173
- categories: CatalogCategoryInput[]
174
- products: CatalogProductInput[]
175
- /** Percentage discount codes, e.g. [{ code: 'BEMVINDO10', percent: 10 }] */
176
- discounts?: Array<{ code: string; percent: number; title?: string }>
177
- currency?: string
178
- }
179
-
180
- function slugifyName(name: string): string {
181
- return name
182
- .toLowerCase()
183
- .normalize('NFD')
184
- .replace(/[̀-ͯ]/g, '')
185
- .replace(/[^a-z0-9]+/g, '-')
186
- .replace(/^-|-$/g, '')
187
- }
188
-
189
- export function buildMockCatalog(input: CatalogInput): { products: Product[]; categories: Category[]; discounts: Discount[] } {
190
- const categories: Category[] = input.categories.map((c, i) => ({
191
- id: `cat-${slugifyName(c.name)}`,
192
- tenantId: 'mock',
193
- name: c.name,
194
- slug: slugifyName(c.name),
195
- description: c.description ?? null,
196
- parentId: null,
197
- sortOrder: i,
198
- imageUrl: c.image ?? null,
199
- createdAt: T0,
200
- }))
201
- const byName = Object.fromEntries(categories.map((c) => [c.name, c]))
202
-
203
- const products: Product[] = input.products.map((p, i) => {
204
- const cat = byName[p.category]
205
- const id = `p-${String(i + 1).padStart(2, '0')}`
206
- const hues = p.hues ?? [(i * 47) % 360, (i * 47 + 50) % 360]
207
- const stamp = `2026-01-${String((i % 27) + 1).padStart(2, '0')}T12:00:00.000Z`
208
- return {
209
- id,
210
- tenantId: 'mock',
211
- name: p.name,
212
- slug: slugifyName(p.name),
213
- description: p.description,
214
- price: p.price,
215
- compareAtPrice: p.compareAtPrice ?? null,
216
- currency: input.currency ?? 'BRL',
217
- status: 'active',
218
- inventoryCount: p.inventory,
219
- sku: p.sku,
220
- sortOrder: i,
221
- metadata: p.metadata ?? {},
222
- images: [
223
- {
224
- id: `img-${id}-0`,
225
- productId: id,
226
- url: p.image ?? svgPlaceholder(p.name, hues[0], hues[1]),
227
- altText: p.name,
228
- sortOrder: 0,
229
- isPrimary: true,
230
- createdAt: T0,
231
- },
232
- ],
233
- categoryId: cat?.id ?? null,
234
- categoryName: cat?.name ?? null,
235
- isPhysical: true,
236
- weight: p.weight ?? 0.5,
237
- weightUnit: 'kg',
238
- createdAt: stamp,
239
- updatedAt: stamp,
240
- }
241
- })
242
-
243
- const discounts: Discount[] = (input.discounts ?? []).map((d, i) => ({
244
- id: `disc-${slugifyName(d.code)}`,
245
- tenantId: 'mock',
246
- title: d.title ?? `${d.percent}% off`,
247
- code: d.code,
248
- type: 'percentage',
249
- method: 'code',
250
- value: d.percent,
251
- usageLimit: null,
252
- oncePerCustomer: false,
253
- minSubtotal: null,
254
- appliesTo: 'all',
255
- productIds: [],
256
- categoryIds: [],
257
- startsAt: T0,
258
- endsAt: null,
259
- status: 'active',
260
- timesUsed: 0,
261
- createdAt: T0,
262
- updatedAt: T0,
263
- }))
264
-
265
- return { products, categories, discounts }
266
- }