@fayz-ai/shop 0.8.0 → 0.8.2
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.cjs +149 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +149 -3
- package/dist/index.js.map +1 -1
- package/dist/mock-provider.d.ts.map +1 -1
- package/dist/mock.cjs +5 -0
- package/dist/mock.cjs.map +1 -1
- package/dist/mock.js +5 -0
- package/dist/mock.js.map +1 -1
- package/dist/provider.d.ts +36 -1
- package/dist/provider.d.ts.map +1 -1
- package/dist/runtime.cjs +5 -0
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +5 -0
- package/dist/runtime.js.map +1 -1
- package/dist/supabase-provider.d.ts +20 -1
- package/dist/supabase-provider.d.ts.map +1 -1
- package/dist/tables.d.ts +1 -0
- package/dist/tables.d.ts.map +1 -1
- package/dist/types.d.ts +103 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/index.ts +4 -0
- package/src/mock-provider.ts +5 -0
- package/src/provider.ts +39 -0
- package/src/supabase-provider.ts +196 -5
- package/src/tables.ts +4 -0
- package/src/types.ts +110 -0
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';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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;
|