@fayz-ai/shop 0.8.0 → 0.8.3

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/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export type { Product, ProductImage, Category, ProductEnquiry, ProductEnquiryStatus, Order, OrderItem, OrderStatus, FinancialStatus, FulfillmentStatus, ShopCustomer, Discount, DiscountType, DiscountMethod, DiscountStatus, ProductStatus, CreateProductInput, UpdateProductInput, ListProductsOptions, CreateProductEnquiryInput, ListProductEnquiriesOptions, CreateCategoryInput, UpdateCategoryInput, CreateOrderInput, UpdateOrderInput, ListOrdersOptions, CreateCustomerInput, UpdateCustomerInput, ListCustomersOptions, ResolveCustomerInput, CreateDiscountInput, UpdateDiscountInput, ListDiscountsOptions, PlaceOrderInput, PlaceOrderLine, DiscountValidation, InventoryLine, InventoryIssue, InventoryCheck, CartTotalLine, CartTotals, } from './types';
2
+ export type { ShippingZone, CreateShippingZoneInput, UpdateShippingZoneInput, ShippingQuoteOption, CustomerAddress, } from './types';
2
3
  export type { ShopProvider } from './provider';
3
4
  export { validateDiscount, applyDiscount, checkInventory, computeCartTotals, getCustomerOrders, } from './commerce';
4
5
  export { SupabaseShopProvider, createSupabaseShopProvider } from './supabase-provider';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,oBAAoB,EACrE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,eAAe,EAAE,iBAAiB,EACjE,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EACpE,aAAa,EACb,kBAAkB,EAAE,kBAAkB,EAAE,mBAAmB,EAC3D,yBAAyB,EAAE,2BAA2B,EACtD,mBAAmB,EAAE,mBAAmB,EACxC,gBAAgB,EAAE,gBAAgB,EAAE,iBAAiB,EACrD,mBAAmB,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,oBAAoB,EACpF,mBAAmB,EAAE,mBAAmB,EAAE,oBAAoB,EAC9D,eAAe,EAAE,cAAc,EAC/B,kBAAkB,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,aAAa,EAAE,UAAU,GAC7F,MAAM,SAAS,CAAA;AAEhB,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,EACL,gBAAgB,EAAE,aAAa,EAAE,cAAc,EAAE,iBAAiB,EAAE,iBAAiB,GACtF,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAA;AACtF,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AAC1E,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AACjE,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAC9C,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAG/E,OAAO,EACL,aAAa,EACb,eAAe,EACf,cAAc,EACd,cAAc,EACd,gBAAgB,GACjB,MAAM,WAAW,CAAA;AAClB,YAAY,EAAE,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,oBAAoB,EACrE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,eAAe,EAAE,iBAAiB,EACjE,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EACpE,aAAa,EACb,kBAAkB,EAAE,kBAAkB,EAAE,mBAAmB,EAC3D,yBAAyB,EAAE,2BAA2B,EACtD,mBAAmB,EAAE,mBAAmB,EACxC,gBAAgB,EAAE,gBAAgB,EAAE,iBAAiB,EACrD,mBAAmB,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,oBAAoB,EACpF,mBAAmB,EAAE,mBAAmB,EAAE,oBAAoB,EAC9D,eAAe,EAAE,cAAc,EAC/B,kBAAkB,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,aAAa,EAAE,UAAU,GAC7F,MAAM,SAAS,CAAA;AAEhB,YAAY,EACV,YAAY,EAAE,uBAAuB,EAAE,uBAAuB,EAC9D,mBAAmB,EAAE,eAAe,GACrC,MAAM,SAAS,CAAA;AAChB,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,EACL,gBAAgB,EAAE,aAAa,EAAE,cAAc,EAAE,iBAAiB,EAAE,iBAAiB,GACtF,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAA;AACtF,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AAC1E,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AACjE,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAC9C,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAG/E,OAAO,EACL,aAAa,EACb,eAAe,EACf,cAAc,EACd,cAAc,EACd,gBAAgB,GACjB,MAAM,WAAW,CAAA;AAClB,YAAY,EAAE,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA"}
package/dist/index.js CHANGED
@@ -388,6 +388,11 @@ var MockShopProvider = class {
388
388
  customerEmail: input.customerEmail ?? null,
389
389
  discountCode: input.discountCode ?? null,
390
390
  notes: input.notes ?? null,
391
+ shippingAddress: null,
392
+ // CreateOrderInput has no payment method — that is a PlaceOrderInput
393
+ // concern; the mock records none rather than inventing one.
394
+ paymentMethodKind: null,
395
+ paidAt: null,
391
396
  items: input.items.map((i) => ({ id: uid(), orderId, productId: i.productId ?? null, name: i.name, sku: i.sku ?? null, quantity: i.quantity, unitPrice: i.unitPrice, total: Math.round(i.quantity * i.unitPrice * 100) / 100, imageUrl: i.imageUrl ?? null })),
392
397
  createdAt: now(),
393
398
  updatedAt: now()
@@ -657,7 +662,11 @@ var T = {
657
662
  orders: "plg_shop_orders",
658
663
  orderItems: "plg_shop_order_items",
659
664
  customers: "plg_shop_customers",
660
- discounts: "plg_shop_discounts"
665
+ discounts: "plg_shop_discounts",
666
+ // Core, not plg_shop_*: a delivery zone is a fact about the business, and
667
+ // fulfilment and financial read it too. Same reasoning as addresses and
668
+ // orders — see migration 0021.
669
+ shippingZones: "shipping_zones"
661
670
  };
662
671
 
663
672
  // src/supabase-provider.ts
@@ -674,6 +683,40 @@ function asUuid(value) {
674
683
  if (!value) return null;
675
684
  return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value) ? value : null;
676
685
  }
686
+ function toPostalDigits(value) {
687
+ const digits = String(value ?? "").replace(/\D/g, "");
688
+ return digits ? digits.padStart(8, "0").slice(0, 8) : void 0;
689
+ }
690
+ function rowToShippingZone(row) {
691
+ return {
692
+ id: row.id,
693
+ tenantId: row.tenant_id,
694
+ name: row.name,
695
+ carrier: row.carrier ?? null,
696
+ postalFrom: String(row.postal_from ?? "").trim(),
697
+ postalTo: String(row.postal_to ?? "").trim(),
698
+ rate: Number(row.rate ?? 0),
699
+ freeAbove: row.free_above == null ? null : Number(row.free_above),
700
+ etaMinDays: row.eta_min_days ?? null,
701
+ etaMaxDays: row.eta_max_days ?? null,
702
+ active: row.active ?? true,
703
+ sortOrder: row.sort_order ?? 0
704
+ };
705
+ }
706
+ function shippingZonePayload(input) {
707
+ const payload = {};
708
+ if (input.name !== void 0) payload.name = input.name;
709
+ if (input.carrier !== void 0) payload.carrier = input.carrier || null;
710
+ if (input.postalFrom !== void 0) payload.postal_from = toPostalDigits(input.postalFrom);
711
+ if (input.postalTo !== void 0) payload.postal_to = toPostalDigits(input.postalTo);
712
+ if (input.rate !== void 0) payload.rate = input.rate;
713
+ if (input.freeAbove !== void 0) payload.free_above = input.freeAbove ?? null;
714
+ if (input.etaMinDays !== void 0) payload.eta_min_days = input.etaMinDays ?? null;
715
+ if (input.etaMaxDays !== void 0) payload.eta_max_days = input.etaMaxDays ?? null;
716
+ if (input.active !== void 0) payload.active = input.active;
717
+ if (input.sortOrder !== void 0) payload.sort_order = input.sortOrder;
718
+ return payload;
719
+ }
677
720
  function rowToProduct(row) {
678
721
  return {
679
722
  id: row.id,
@@ -722,6 +765,19 @@ function rowToCategory(row) {
722
765
  createdAt: row.created_at
723
766
  };
724
767
  }
768
+ function rowToOrderItem(row) {
769
+ return {
770
+ id: row.id,
771
+ orderId: row.order_id,
772
+ productId: row.product_id ?? null,
773
+ name: row.name,
774
+ sku: row.sku ?? null,
775
+ quantity: row.quantity ?? 0,
776
+ unitPrice: row.unit_price ?? 0,
777
+ total: row.total ?? 0,
778
+ imageUrl: row.image_url ?? null
779
+ };
780
+ }
725
781
  function rowToOrder(row) {
726
782
  return {
727
783
  id: row.id,
@@ -741,7 +797,10 @@ function rowToOrder(row) {
741
797
  customerEmail: row.customer_email,
742
798
  discountCode: row.discount_code,
743
799
  notes: row.notes,
744
- items: Array.isArray(row.items) ? row.items : [],
800
+ shippingAddress: row.shipping_address ?? null,
801
+ paymentMethodKind: row.payment_method_kind ?? null,
802
+ paidAt: row.paid_at ?? null,
803
+ items: Array.isArray(row.items) ? row.items.map(rowToOrderItem) : [],
745
804
  createdAt: row.created_at,
746
805
  updatedAt: row.updated_at
747
806
  };
@@ -863,7 +922,32 @@ var SupabaseShopProvider = class {
863
922
  const { error: uploadErr } = await db.storage.from("shop-images").upload(path, file);
864
923
  if (uploadErr) throw uploadErr;
865
924
  const { data: { publicUrl } } = db.storage.from("shop-images").getPublicUrl(path);
866
- 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();
925
+ const { data: existing } = await db.from(T.productImages).select("id,sort_order").eq("product_id", productId).order("sort_order", { ascending: false }).limit(1);
926
+ const first = !existing || existing.length === 0;
927
+ const nextOrder = first ? 0 : (existing[0].sort_order ?? 0) + 1;
928
+ const { data, error } = await db.from(T.productImages).insert({
929
+ product_id: productId,
930
+ tenant_id: tenantId,
931
+ url: publicUrl,
932
+ sort_order: nextOrder,
933
+ is_primary: first
934
+ }).select().single();
935
+ if (error) throw error;
936
+ return rowToImage(data);
937
+ }
938
+ async updateProductImage(imageId, input) {
939
+ const db = getDb();
940
+ const updates = {};
941
+ if (input.sortOrder !== void 0) updates.sort_order = input.sortOrder;
942
+ if (input.altText !== void 0) updates.alt_text = input.altText;
943
+ if (input.isPrimary !== void 0) updates.is_primary = input.isPrimary;
944
+ if (input.isPrimary) {
945
+ const { data: target } = await db.from(T.productImages).select("product_id").eq("id", imageId).single();
946
+ if (target?.product_id) {
947
+ await db.from(T.productImages).update({ is_primary: false }).eq("product_id", target.product_id).neq("id", imageId);
948
+ }
949
+ }
950
+ const { data, error } = await db.from(T.productImages).update(updates).eq("id", imageId).select().single();
867
951
  if (error) throw error;
868
952
  return rowToImage(data);
869
953
  }
@@ -953,6 +1037,52 @@ var SupabaseShopProvider = class {
953
1037
  if (error) throw error;
954
1038
  return this.getOrder(id);
955
1039
  }
1040
+ // -------------------------------------------------------------------------
1041
+ // Delivery zones
1042
+ //
1043
+ // Postal codes are stored as 8 bare digits so the range test in
1044
+ // shop_quote_shipping is a plain BETWEEN. Accepting '22.041-001' from the
1045
+ // form and normalising here means the merchant never has to think about it.
1046
+ // -------------------------------------------------------------------------
1047
+ async listShippingZones() {
1048
+ const tenantId = getTenantId();
1049
+ const { data, error } = await getDb().from(T.shippingZones).select("*").eq("tenant_id", tenantId).order("sort_order", { ascending: true }).order("postal_from", { ascending: true });
1050
+ if (error) throw error;
1051
+ return (data ?? []).map(rowToShippingZone);
1052
+ }
1053
+ async createShippingZone(input) {
1054
+ const tenantId = getTenantId();
1055
+ if (!tenantId) throw new Error("@fayz-ai/shop: createShippingZone requires a tenant id.");
1056
+ const { data, error } = await getDb().from(T.shippingZones).insert({ tenant_id: tenantId, ...shippingZonePayload(input) }).select().single();
1057
+ if (error) throw error;
1058
+ return rowToShippingZone(data);
1059
+ }
1060
+ async updateShippingZone(id, input) {
1061
+ const { data, error } = await getDb().from(T.shippingZones).update(shippingZonePayload(input)).eq("id", id).select().single();
1062
+ if (error) throw error;
1063
+ return rowToShippingZone(data);
1064
+ }
1065
+ async deleteShippingZone(id) {
1066
+ const { error } = await getDb().from(T.shippingZones).delete().eq("id", id);
1067
+ if (error) throw error;
1068
+ }
1069
+ /**
1070
+ * Settle a pending order: the merchant confirming that the money arrived.
1071
+ *
1072
+ * Goes through the RPC rather than an UPDATE so the payment ledger row in
1073
+ * public.transactions closes in the same transaction as the order — set
1074
+ * financial_status by hand and the order says paid while the ledger still
1075
+ * says pending. Since 0019 the RPC also enforces who may call it: a member of
1076
+ * the owning tenant, or a PSP webhook with the service role.
1077
+ */
1078
+ async confirmPayment(id, reference) {
1079
+ const { error } = await getDb().rpc("shop_confirm_payment", {
1080
+ p_order_id: id,
1081
+ p_reference: reference ?? null
1082
+ });
1083
+ if (error) throw error;
1084
+ return this.getOrder(id);
1085
+ }
956
1086
  // Trusted placement — delegates all price/discount/inventory authority to the
957
1087
  // shop_place_order RPC (SECURITY DEFINER). The browser never sets a price.
958
1088
  async placeOrder(input) {
@@ -968,6 +1098,22 @@ var SupabaseShopProvider = class {
968
1098
  p_currency: input.currency ?? "BRL",
969
1099
  p_discount_code: input.discountCode ?? null,
970
1100
  p_shipping_total: input.shippingTotal ?? 0,
1101
+ // snake_case keys on purpose: the RPC freezes this jsonb onto the
1102
+ // order and public.addresses uses these exact column names.
1103
+ p_shipping_address: input.shippingAddress ? {
1104
+ postal_code: input.shippingAddress.postalCode,
1105
+ street: input.shippingAddress.street,
1106
+ number: input.shippingAddress.number ?? null,
1107
+ complement: input.shippingAddress.complement ?? null,
1108
+ district: input.shippingAddress.district ?? null,
1109
+ city: input.shippingAddress.city,
1110
+ state: input.shippingAddress.state,
1111
+ country: input.shippingAddress.country ?? "BR",
1112
+ recipient: input.shippingAddress.recipient ?? null,
1113
+ phone: input.shippingAddress.phone ?? null,
1114
+ label: input.shippingAddress.label ?? null
1115
+ } : null,
1116
+ p_payment_method: input.paymentMethod ?? null,
971
1117
  p_notes: input.notes ?? null
972
1118
  });
973
1119
  if (error) throw error;