@fayz-ai/shop 0.7.0 → 0.8.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.
package/README.md CHANGED
@@ -5,6 +5,8 @@
5
5
  [![npm](https://img.shields.io/npm/v/@fayz-ai/shop.svg)](https://www.npmjs.com/package/@fayz-ai/shop)
6
6
  [![license](https://img.shields.io/npm/l/@fayz-ai/shop.svg)](https://github.com/FayaLabs/fayz-sdk/blob/main/LICENSE)
7
7
 
8
+ **Status:** beta — published to npm and used across Fayz dogfood apps. Pre-1.0: minor APIs may change before 1.0.
9
+
8
10
  `@fayz-ai/shop` is the headless ecommerce engine: a `ShopProvider` contract plus the domain types for products, carts, orders, customers, discounts, and shipping. It owns *what commerce means* so the storefront (`@fayz-ai/storefront`) and admin surfaces only have to render it.
9
11
 
10
12
  It's provider-first by design. Ship a demo on `createMockShopProvider`, then drop in `createSupabaseShopProvider` (or your own `ShopProvider`) for real data — the UI doesn't change. Tenancy is resolved through a pluggable resolver, so the same engine serves one store or a marketplace of thousands.
package/dist/index.cjs CHANGED
@@ -651,6 +651,17 @@ function getShopTenantId() {
651
651
  }
652
652
  }
653
653
 
654
+ // src/tables.ts
655
+ var T = {
656
+ products: "plg_shop_products",
657
+ productImages: "plg_shop_product_images",
658
+ categories: "plg_shop_categories",
659
+ orders: "plg_shop_orders",
660
+ orderItems: "plg_shop_order_items",
661
+ customers: "plg_shop_customers",
662
+ discounts: "plg_shop_discounts"
663
+ };
664
+
654
665
  // src/supabase-provider.ts
655
666
  function getDb() {
656
667
  const supabase = core.getSupabaseClientOptional();
@@ -778,7 +789,7 @@ var SupabaseShopProvider = class {
778
789
  async listProducts(options) {
779
790
  const db = getDb();
780
791
  const tenantId = getTenantId();
781
- let q = db.from("shop_products").select("*, images:shop_product_images(*)");
792
+ let q = db.from(T.products).select(`*, images:${T.productImages}(*)`);
782
793
  if (tenantId) q = q.eq("tenant_id", tenantId);
783
794
  if (options?.status) q = q.eq("status", options.status);
784
795
  if (options?.categoryId) q = q.eq("category_id", options.categoryId);
@@ -793,14 +804,14 @@ var SupabaseShopProvider = class {
793
804
  }
794
805
  async getProduct(id) {
795
806
  const db = getDb();
796
- const { data, error } = await db.from("shop_products").select("*, images:shop_product_images(*)").eq("id", id).maybeSingle();
807
+ const { data, error } = await db.from(T.products).select(`*, images:${T.productImages}(*)`).eq("id", id).maybeSingle();
797
808
  if (error) throw error;
798
809
  return data ? rowToProduct(data) : null;
799
810
  }
800
811
  async createProduct(input) {
801
812
  const db = getDb();
802
813
  const tenantId = getTenantId();
803
- const { data, error } = await db.from("shop_products").insert({
814
+ const { data, error } = await db.from(T.products).insert({
804
815
  tenant_id: tenantId,
805
816
  name: input.name,
806
817
  slug: input.slug ?? slugify(input.name),
@@ -817,7 +828,7 @@ var SupabaseShopProvider = class {
817
828
  is_physical: input.isPhysical ?? true,
818
829
  weight: input.weight ?? null,
819
830
  weight_unit: input.weightUnit ?? "kg"
820
- }).select("*, images:shop_product_images(*)").single();
831
+ }).select(`*, images:${T.productImages}(*)`).single();
821
832
  if (error) throw error;
822
833
  return rowToProduct(data);
823
834
  }
@@ -839,12 +850,12 @@ var SupabaseShopProvider = class {
839
850
  if (input.categoryId !== void 0) updates.category_id = input.categoryId;
840
851
  if (input.isPhysical !== void 0) updates.is_physical = input.isPhysical;
841
852
  if (input.weight !== void 0) updates.weight = input.weight;
842
- const { data, error } = await db.from("shop_products").update(updates).eq("id", id).select("*, images:shop_product_images(*)").single();
853
+ const { data, error } = await db.from(T.products).update(updates).eq("id", id).select(`*, images:${T.productImages}(*)`).single();
843
854
  if (error) throw error;
844
855
  return rowToProduct(data);
845
856
  }
846
857
  async deleteProduct(id) {
847
- const { error } = await getDb().from("shop_products").delete().eq("id", id);
858
+ const { error } = await getDb().from(T.products).delete().eq("id", id);
848
859
  if (error) throw error;
849
860
  }
850
861
  async uploadProductImage(productId, file) {
@@ -854,12 +865,12 @@ var SupabaseShopProvider = class {
854
865
  const { error: uploadErr } = await db.storage.from("shop-images").upload(path, file);
855
866
  if (uploadErr) throw uploadErr;
856
867
  const { data: { publicUrl } } = db.storage.from("shop-images").getPublicUrl(path);
857
- const { data, error } = await db.from("shop_product_images").insert({ product_id: productId, tenant_id: tenantId, url: publicUrl, sort_order: 0, is_primary: false }).select().single();
868
+ const { data, error } = await db.from(T.productImages).insert({ product_id: productId, tenant_id: tenantId, url: publicUrl, sort_order: 0, is_primary: false }).select().single();
858
869
  if (error) throw error;
859
870
  return rowToImage(data);
860
871
  }
861
872
  async deleteProductImage(imageId) {
862
- const { error } = await getDb().from("shop_product_images").delete().eq("id", imageId);
873
+ const { error } = await getDb().from(T.productImages).delete().eq("id", imageId);
863
874
  if (error) throw error;
864
875
  }
865
876
  // ---------------------------------------------------------------------------
@@ -868,7 +879,7 @@ var SupabaseShopProvider = class {
868
879
  async listCategories() {
869
880
  const db = getDb();
870
881
  const tenantId = getTenantId();
871
- let q = db.from("shop_categories").select("*").order("sort_order", { ascending: true });
882
+ let q = db.from(T.categories).select("*").order("sort_order", { ascending: true });
872
883
  if (tenantId) q = q.eq("tenant_id", tenantId);
873
884
  const { data, error } = await q;
874
885
  if (error) throw error;
@@ -876,7 +887,7 @@ var SupabaseShopProvider = class {
876
887
  }
877
888
  async createCategory(input) {
878
889
  const db = getDb();
879
- const { data, error } = await db.from("shop_categories").insert({ tenant_id: getTenantId(), name: input.name, slug: input.slug ?? slugify(input.name), description: input.description ?? null, parent_id: input.parentId ?? null, sort_order: input.sortOrder ?? 0 }).select().single();
890
+ const { data, error } = await db.from(T.categories).insert({ tenant_id: getTenantId(), name: input.name, slug: input.slug ?? slugify(input.name), description: input.description ?? null, parent_id: input.parentId ?? null, sort_order: input.sortOrder ?? 0 }).select().single();
880
891
  if (error) throw error;
881
892
  return rowToCategory(data);
882
893
  }
@@ -888,12 +899,12 @@ var SupabaseShopProvider = class {
888
899
  if (input.description !== void 0) updates.description = input.description;
889
900
  if (input.parentId !== void 0) updates.parent_id = input.parentId;
890
901
  if (input.sortOrder !== void 0) updates.sort_order = input.sortOrder;
891
- const { data, error } = await db.from("shop_categories").update(updates).eq("id", id).select().single();
902
+ const { data, error } = await db.from(T.categories).update(updates).eq("id", id).select().single();
892
903
  if (error) throw error;
893
904
  return rowToCategory(data);
894
905
  }
895
906
  async deleteCategory(id) {
896
- const { error } = await getDb().from("shop_categories").delete().eq("id", id);
907
+ const { error } = await getDb().from(T.categories).delete().eq("id", id);
897
908
  if (error) throw error;
898
909
  }
899
910
  // ---------------------------------------------------------------------------
@@ -902,7 +913,7 @@ var SupabaseShopProvider = class {
902
913
  async listOrders(options) {
903
914
  const db = getDb();
904
915
  const tenantId = getTenantId();
905
- let q = db.from("shop_orders").select("*, items:shop_order_items(*)");
916
+ let q = db.from(T.orders).select(`*, items:${T.orderItems}(*)`);
906
917
  if (tenantId) q = q.eq("tenant_id", tenantId);
907
918
  if (options?.status) q = q.eq("status", options.status);
908
919
  if (options?.financialStatus) q = q.eq("financial_status", options.financialStatus);
@@ -917,7 +928,7 @@ var SupabaseShopProvider = class {
917
928
  return (data ?? []).map(rowToOrder);
918
929
  }
919
930
  async getOrder(id) {
920
- const { data, error } = await getDb().from("shop_orders").select("*, items:shop_order_items(*)").eq("id", id).maybeSingle();
931
+ const { data, error } = await getDb().from(T.orders).select(`*, items:${T.orderItems}(*)`).eq("id", id).maybeSingle();
921
932
  if (error) throw error;
922
933
  return data ? rowToOrder(data) : null;
923
934
  }
@@ -928,10 +939,10 @@ var SupabaseShopProvider = class {
928
939
  const discountTotal = input.discountTotal ?? 0;
929
940
  const shippingTotal = input.shippingTotal ?? 0;
930
941
  const total = Math.round((subtotal - discountTotal + shippingTotal) * 100) / 100;
931
- const { data: order, error: orderErr } = await db.from("shop_orders").insert({ tenant_id: tenantId, customer_id: input.customerId ?? null, customer_name: input.customerName ?? null, customer_email: input.customerEmail ?? null, currency: input.currency ?? "BRL", notes: input.notes ?? null, subtotal, discount_total: discountTotal, shipping_total: shippingTotal, discount_code: input.discountCode ?? null, total, status: "open", financial_status: "pending", fulfillment_status: "unfulfilled" }).select().single();
942
+ const { data: order, error: orderErr } = await db.from(T.orders).insert({ tenant_id: tenantId, customer_id: input.customerId ?? null, customer_name: input.customerName ?? null, customer_email: input.customerEmail ?? null, currency: input.currency ?? "BRL", notes: input.notes ?? null, subtotal, discount_total: discountTotal, shipping_total: shippingTotal, discount_code: input.discountCode ?? null, total, status: "open", financial_status: "pending", fulfillment_status: "unfulfilled" }).select().single();
932
943
  if (orderErr) throw orderErr;
933
944
  const items = input.items.map((i) => ({ order_id: order.id, product_id: asUuid(i.productId), name: i.name, sku: i.sku ?? null, quantity: i.quantity, unit_price: i.unitPrice, total: i.quantity * i.unitPrice, image_url: i.imageUrl ?? null }));
934
- await db.from("shop_order_items").insert(items);
945
+ await db.from(T.orderItems).insert(items);
935
946
  return this.getOrder(order.id);
936
947
  }
937
948
  async updateOrder(id, input) {
@@ -940,7 +951,7 @@ var SupabaseShopProvider = class {
940
951
  if (input.financialStatus !== void 0) updates.financial_status = input.financialStatus;
941
952
  if (input.fulfillmentStatus !== void 0) updates.fulfillment_status = input.fulfillmentStatus;
942
953
  if (input.notes !== void 0) updates.notes = input.notes;
943
- const { error } = await getDb().from("shop_orders").update(updates).eq("id", id);
954
+ const { error } = await getDb().from(T.orders).update(updates).eq("id", id);
944
955
  if (error) throw error;
945
956
  return this.getOrder(id);
946
957
  }
@@ -973,7 +984,7 @@ var SupabaseShopProvider = class {
973
984
  async listCustomers(options) {
974
985
  const db = getDb();
975
986
  const tenantId = getTenantId();
976
- let q = db.from("shop_customers").select("*").order("first_name", { ascending: true });
987
+ let q = db.from(T.customers).select("*").order("first_name", { ascending: true });
977
988
  if (tenantId) q = q.eq("tenant_id", tenantId);
978
989
  if (options?.search) q = q.or(`first_name.ilike.%${options.search}%,last_name.ilike.%${options.search}%,email.ilike.%${options.search}%`);
979
990
  if (options?.limit) q = q.limit(options.limit);
@@ -982,12 +993,12 @@ var SupabaseShopProvider = class {
982
993
  return (data ?? []).map(rowToCustomer);
983
994
  }
984
995
  async getCustomer(id) {
985
- const { data, error } = await getDb().from("shop_customers").select("*").eq("id", id).maybeSingle();
996
+ const { data, error } = await getDb().from(T.customers).select("*").eq("id", id).maybeSingle();
986
997
  if (error) throw error;
987
998
  return data ? rowToCustomer(data) : null;
988
999
  }
989
1000
  async createCustomer(input) {
990
- const { data, error } = await getDb().from("shop_customers").insert({ tenant_id: getTenantId(), first_name: input.firstName, last_name: input.lastName ?? "", email: input.email ?? null, phone: input.phone ?? null, notes: input.notes ?? null }).select().single();
1001
+ const { data, error } = await getDb().from(T.customers).insert({ tenant_id: getTenantId(), first_name: input.firstName, last_name: input.lastName ?? "", email: input.email ?? null, phone: input.phone ?? null, notes: input.notes ?? null }).select().single();
991
1002
  if (error) throw error;
992
1003
  return rowToCustomer(data);
993
1004
  }
@@ -998,12 +1009,12 @@ var SupabaseShopProvider = class {
998
1009
  if (input.email !== void 0) updates.email = input.email;
999
1010
  if (input.phone !== void 0) updates.phone = input.phone;
1000
1011
  if (input.notes !== void 0) updates.notes = input.notes;
1001
- const { data, error } = await getDb().from("shop_customers").update(updates).eq("id", id).select().single();
1012
+ const { data, error } = await getDb().from(T.customers).update(updates).eq("id", id).select().single();
1002
1013
  if (error) throw error;
1003
1014
  return rowToCustomer(data);
1004
1015
  }
1005
1016
  async deleteCustomer(id) {
1006
- const { error } = await getDb().from("shop_customers").delete().eq("id", id);
1017
+ const { error } = await getDb().from(T.customers).delete().eq("id", id);
1007
1018
  if (error) throw error;
1008
1019
  }
1009
1020
  // Find-or-create + link to auth.uid server-side (SECURITY DEFINER RPC), so the
@@ -1028,7 +1039,7 @@ var SupabaseShopProvider = class {
1028
1039
  async listDiscounts(options) {
1029
1040
  const db = getDb();
1030
1041
  const tenantId = getTenantId();
1031
- let q = db.from("shop_discounts").select("*").order("created_at", { ascending: false });
1042
+ let q = db.from(T.discounts).select("*").order("created_at", { ascending: false });
1032
1043
  if (tenantId) q = q.eq("tenant_id", tenantId);
1033
1044
  if (options?.status) q = q.eq("status", options.status);
1034
1045
  if (options?.type) q = q.eq("type", options.type);
@@ -1039,12 +1050,12 @@ var SupabaseShopProvider = class {
1039
1050
  return (data ?? []).map(rowToDiscount);
1040
1051
  }
1041
1052
  async getDiscount(id) {
1042
- const { data, error } = await getDb().from("shop_discounts").select("*").eq("id", id).maybeSingle();
1053
+ const { data, error } = await getDb().from(T.discounts).select("*").eq("id", id).maybeSingle();
1043
1054
  if (error) throw error;
1044
1055
  return data ? rowToDiscount(data) : null;
1045
1056
  }
1046
1057
  async createDiscount(input) {
1047
- const { data, error } = await getDb().from("shop_discounts").insert({ tenant_id: getTenantId(), title: input.title, code: input.code ?? null, type: input.type, method: input.method ?? "code", value: input.value, usage_limit: input.usageLimit ?? null, once_per_customer: input.oncePerCustomer ?? false, starts_at: input.startsAt ?? (/* @__PURE__ */ new Date()).toISOString(), ends_at: input.endsAt ?? null, status: input.status ?? "active", times_used: 0 }).select().single();
1058
+ const { data, error } = await getDb().from(T.discounts).insert({ tenant_id: getTenantId(), title: input.title, code: input.code ?? null, type: input.type, method: input.method ?? "code", value: input.value, usage_limit: input.usageLimit ?? null, once_per_customer: input.oncePerCustomer ?? false, starts_at: input.startsAt ?? (/* @__PURE__ */ new Date()).toISOString(), ends_at: input.endsAt ?? null, status: input.status ?? "active", times_used: 0 }).select().single();
1048
1059
  if (error) throw error;
1049
1060
  return rowToDiscount(data);
1050
1061
  }
@@ -1057,12 +1068,12 @@ var SupabaseShopProvider = class {
1057
1068
  if (input.usageLimit !== void 0) updates.usage_limit = input.usageLimit;
1058
1069
  if (input.status !== void 0) updates.status = input.status;
1059
1070
  if (input.endsAt !== void 0) updates.ends_at = input.endsAt;
1060
- const { data, error } = await getDb().from("shop_discounts").update(updates).eq("id", id).select().single();
1071
+ const { data, error } = await getDb().from(T.discounts).update(updates).eq("id", id).select().single();
1061
1072
  if (error) throw error;
1062
1073
  return rowToDiscount(data);
1063
1074
  }
1064
1075
  async deleteDiscount(id) {
1065
- const { error } = await getDb().from("shop_discounts").delete().eq("id", id);
1076
+ const { error } = await getDb().from(T.discounts).delete().eq("id", id);
1066
1077
  if (error) throw error;
1067
1078
  }
1068
1079
  };