medusa-product-helper 0.0.5 → 0.0.9

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.
@@ -988,6 +988,129 @@ const ValueField = ({
988
988
  adminSdk.defineWidgetConfig({
989
989
  zone: "product.details.after"
990
990
  });
991
+ const fetchJson = async (path) => {
992
+ const response = await fetch(path, {
993
+ credentials: "include"
994
+ });
995
+ const payload = await response.json().catch(() => null);
996
+ if (!response.ok) {
997
+ throw new Error(
998
+ (payload == null ? void 0 : payload.message) ?? "Unable to load wishlist statistics from the server"
999
+ );
1000
+ }
1001
+ return payload;
1002
+ };
1003
+ const ProductWishlistStatsWidget = ({ data }) => {
1004
+ const productId = data == null ? void 0 : data.id;
1005
+ const {
1006
+ data: productStats,
1007
+ isPending: isProductStatsPending,
1008
+ isError: isProductStatsError,
1009
+ error: productStatsError
1010
+ } = reactQuery.useQuery({
1011
+ queryKey: ["wishlist", "product", productId],
1012
+ enabled: Boolean(productId),
1013
+ queryFn: () => fetchJson(
1014
+ `/admin/wishlist/stats?product_id=${productId}`
1015
+ ),
1016
+ refetchInterval: 6e4
1017
+ });
1018
+ const {
1019
+ data: allStats,
1020
+ isPending: isAllStatsPending,
1021
+ isError: isAllStatsError,
1022
+ error: allStatsError
1023
+ } = reactQuery.useQuery({
1024
+ queryKey: ["wishlist", "stats"],
1025
+ queryFn: () => fetchJson("/admin/wishlist/stats"),
1026
+ staleTime: 6e4
1027
+ });
1028
+ const topFive = react.useMemo(() => {
1029
+ var _a;
1030
+ if (!((_a = allStats == null ? void 0 : allStats.stats) == null ? void 0 : _a.length)) {
1031
+ return [];
1032
+ }
1033
+ return [...allStats.stats].sort((a, b) => b.wishlist_count - a.wishlist_count).slice(0, 5);
1034
+ }, [allStats]);
1035
+ const productWishlistCount = (productStats == null ? void 0 : productStats.wishlist_count) ?? 0;
1036
+ const productIsTopWishlisted = topFive.some(
1037
+ (stat) => stat.product_id === productId
1038
+ );
1039
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "flex flex-col gap-y-4", children: [
1040
+ /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "flex flex-col gap-y-1", children: [
1041
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Wishlist performance" }),
1042
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Track how often this product appears in customer wishlists and see the current top performers." })
1043
+ ] }),
1044
+ !productId ? /* @__PURE__ */ jsxRuntime.jsx(ui.InlineTip, { variant: "info", label: "Product not loaded yet", children: "Open a product detail record to view wishlist insights." }) : isProductStatsPending ? /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-[96px] w-full rounded-lg" }) : isProductStatsError ? /* @__PURE__ */ jsxRuntime.jsx(ui.InlineTip, { variant: "error", label: "Unable to load product stats", children: productStatsError instanceof Error ? productStatsError.message : "Unknown error" }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-2 rounded-lg border border-ui-border-base bg-ui-bg-base p-4", children: [
1045
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
1046
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "txt-compact-xsmall-plus uppercase tracking-wide text-ui-fg-muted", children: "This product" }),
1047
+ productIsTopWishlisted && /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", rounded: "full", color: "green", children: "Top 5" })
1048
+ ] }),
1049
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-1", children: [
1050
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", className: "text-[32px] leading-none", children: productWishlistCount }),
1051
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: productWishlistCount === 1 ? "customer has saved this product." : "customers have saved this product." })
1052
+ ] })
1053
+ ] }),
1054
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-3 rounded-lg border border-ui-border-base bg-ui-bg-subtle p-4", children: [
1055
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
1056
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1057
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "txt-compact-xsmall-plus uppercase tracking-wide text-ui-fg-muted", children: "Storewide insights" }),
1058
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h3", children: "Top wishlisted products" })
1059
+ ] }),
1060
+ productId && /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "grey", children: productIsTopWishlisted ? "In top 5" : "Not in top 5" })
1061
+ ] }),
1062
+ isAllStatsPending ? /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-[160px] w-full rounded-lg" }) : isAllStatsError ? /* @__PURE__ */ jsxRuntime.jsx(ui.InlineTip, { variant: "error", label: "Unable to load storewide stats", children: allStatsError instanceof Error ? allStatsError.message : "Unknown error" }) : !topFive.length ? /* @__PURE__ */ jsxRuntime.jsx(ui.InlineTip, { variant: "info", label: "No wishlist activity yet", children: "Customers have not saved any products to their wishlists yet. Once they do, the most popular products will show up here." }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-hidden rounded-lg border border-ui-border-base bg-ui-bg-base", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "min-w-full divide-y divide-ui-border-base", children: [
1063
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { className: "bg-ui-bg-field", children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
1064
+ /* @__PURE__ */ jsxRuntime.jsx(
1065
+ "th",
1066
+ {
1067
+ scope: "col",
1068
+ className: "px-4 py-2 text-left text-[11px] font-semibold uppercase tracking-wide text-ui-fg-muted",
1069
+ children: "#"
1070
+ }
1071
+ ),
1072
+ /* @__PURE__ */ jsxRuntime.jsx(
1073
+ "th",
1074
+ {
1075
+ scope: "col",
1076
+ className: "px-4 py-2 text-left text-[11px] font-semibold uppercase tracking-wide text-ui-fg-muted",
1077
+ children: "Product ID"
1078
+ }
1079
+ ),
1080
+ /* @__PURE__ */ jsxRuntime.jsx(
1081
+ "th",
1082
+ {
1083
+ scope: "col",
1084
+ className: "px-4 py-2 text-right text-[11px] font-semibold uppercase tracking-wide text-ui-fg-muted",
1085
+ children: "Wishlists"
1086
+ }
1087
+ )
1088
+ ] }) }),
1089
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "divide-y divide-ui-border-subtle", children: topFive.map((stat, index) => /* @__PURE__ */ jsxRuntime.jsxs(
1090
+ "tr",
1091
+ {
1092
+ className: stat.product_id === productId ? "bg-ui-bg-subtle" : "bg-ui-bg-base",
1093
+ children: [
1094
+ /* @__PURE__ */ jsxRuntime.jsxs("td", { className: "px-4 py-3 text-sm font-medium text-ui-fg-subtle", children: [
1095
+ "#",
1096
+ index + 1
1097
+ ] }),
1098
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
1099
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-mono text-sm", children: stat.product_id }),
1100
+ stat.product_id === productId && /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "green", children: "Current" })
1101
+ ] }) }),
1102
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-3 text-right text-sm font-semibold", children: stat.wishlist_count.toLocaleString() })
1103
+ ]
1104
+ },
1105
+ stat.product_id
1106
+ )) })
1107
+ ] }) })
1108
+ ] })
1109
+ ] });
1110
+ };
1111
+ adminSdk.defineWidgetConfig({
1112
+ zone: "product.details.side.after"
1113
+ });
991
1114
  const i18nTranslations0 = {};
992
1115
  const widgetModule = { widgets: [
993
1116
  {
@@ -1005,6 +1128,10 @@ const widgetModule = { widgets: [
1005
1128
  {
1006
1129
  Component: ProductMetadataTableWidget,
1007
1130
  zone: ["product.details.after"]
1131
+ },
1132
+ {
1133
+ Component: ProductWishlistStatsWidget,
1134
+ zone: ["product.details.side.after"]
1008
1135
  }
1009
1136
  ] };
1010
1137
  const routeModule = {
@@ -987,6 +987,129 @@ const ValueField = ({
987
987
  defineWidgetConfig({
988
988
  zone: "product.details.after"
989
989
  });
990
+ const fetchJson = async (path) => {
991
+ const response = await fetch(path, {
992
+ credentials: "include"
993
+ });
994
+ const payload = await response.json().catch(() => null);
995
+ if (!response.ok) {
996
+ throw new Error(
997
+ (payload == null ? void 0 : payload.message) ?? "Unable to load wishlist statistics from the server"
998
+ );
999
+ }
1000
+ return payload;
1001
+ };
1002
+ const ProductWishlistStatsWidget = ({ data }) => {
1003
+ const productId = data == null ? void 0 : data.id;
1004
+ const {
1005
+ data: productStats,
1006
+ isPending: isProductStatsPending,
1007
+ isError: isProductStatsError,
1008
+ error: productStatsError
1009
+ } = useQuery({
1010
+ queryKey: ["wishlist", "product", productId],
1011
+ enabled: Boolean(productId),
1012
+ queryFn: () => fetchJson(
1013
+ `/admin/wishlist/stats?product_id=${productId}`
1014
+ ),
1015
+ refetchInterval: 6e4
1016
+ });
1017
+ const {
1018
+ data: allStats,
1019
+ isPending: isAllStatsPending,
1020
+ isError: isAllStatsError,
1021
+ error: allStatsError
1022
+ } = useQuery({
1023
+ queryKey: ["wishlist", "stats"],
1024
+ queryFn: () => fetchJson("/admin/wishlist/stats"),
1025
+ staleTime: 6e4
1026
+ });
1027
+ const topFive = useMemo(() => {
1028
+ var _a;
1029
+ if (!((_a = allStats == null ? void 0 : allStats.stats) == null ? void 0 : _a.length)) {
1030
+ return [];
1031
+ }
1032
+ return [...allStats.stats].sort((a, b) => b.wishlist_count - a.wishlist_count).slice(0, 5);
1033
+ }, [allStats]);
1034
+ const productWishlistCount = (productStats == null ? void 0 : productStats.wishlist_count) ?? 0;
1035
+ const productIsTopWishlisted = topFive.some(
1036
+ (stat) => stat.product_id === productId
1037
+ );
1038
+ return /* @__PURE__ */ jsxs(Container, { className: "flex flex-col gap-y-4", children: [
1039
+ /* @__PURE__ */ jsxs("header", { className: "flex flex-col gap-y-1", children: [
1040
+ /* @__PURE__ */ jsx(Heading, { level: "h2", children: "Wishlist performance" }),
1041
+ /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: "Track how often this product appears in customer wishlists and see the current top performers." })
1042
+ ] }),
1043
+ !productId ? /* @__PURE__ */ jsx(InlineTip, { variant: "info", label: "Product not loaded yet", children: "Open a product detail record to view wishlist insights." }) : isProductStatsPending ? /* @__PURE__ */ jsx(Skeleton, { className: "h-[96px] w-full rounded-lg" }) : isProductStatsError ? /* @__PURE__ */ jsx(InlineTip, { variant: "error", label: "Unable to load product stats", children: productStatsError instanceof Error ? productStatsError.message : "Unknown error" }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-2 rounded-lg border border-ui-border-base bg-ui-bg-base p-4", children: [
1044
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
1045
+ /* @__PURE__ */ jsx(Text, { className: "txt-compact-xsmall-plus uppercase tracking-wide text-ui-fg-muted", children: "This product" }),
1046
+ productIsTopWishlisted && /* @__PURE__ */ jsx(Badge, { size: "2xsmall", rounded: "full", color: "green", children: "Top 5" })
1047
+ ] }),
1048
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-1", children: [
1049
+ /* @__PURE__ */ jsx(Heading, { level: "h1", className: "text-[32px] leading-none", children: productWishlistCount }),
1050
+ /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: productWishlistCount === 1 ? "customer has saved this product." : "customers have saved this product." })
1051
+ ] })
1052
+ ] }),
1053
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-3 rounded-lg border border-ui-border-base bg-ui-bg-subtle p-4", children: [
1054
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
1055
+ /* @__PURE__ */ jsxs("div", { children: [
1056
+ /* @__PURE__ */ jsx(Text, { className: "txt-compact-xsmall-plus uppercase tracking-wide text-ui-fg-muted", children: "Storewide insights" }),
1057
+ /* @__PURE__ */ jsx(Heading, { level: "h3", children: "Top wishlisted products" })
1058
+ ] }),
1059
+ productId && /* @__PURE__ */ jsx(Badge, { size: "2xsmall", color: "grey", children: productIsTopWishlisted ? "In top 5" : "Not in top 5" })
1060
+ ] }),
1061
+ isAllStatsPending ? /* @__PURE__ */ jsx(Skeleton, { className: "h-[160px] w-full rounded-lg" }) : isAllStatsError ? /* @__PURE__ */ jsx(InlineTip, { variant: "error", label: "Unable to load storewide stats", children: allStatsError instanceof Error ? allStatsError.message : "Unknown error" }) : !topFive.length ? /* @__PURE__ */ jsx(InlineTip, { variant: "info", label: "No wishlist activity yet", children: "Customers have not saved any products to their wishlists yet. Once they do, the most popular products will show up here." }) : /* @__PURE__ */ jsx("div", { className: "overflow-hidden rounded-lg border border-ui-border-base bg-ui-bg-base", children: /* @__PURE__ */ jsxs("table", { className: "min-w-full divide-y divide-ui-border-base", children: [
1062
+ /* @__PURE__ */ jsx("thead", { className: "bg-ui-bg-field", children: /* @__PURE__ */ jsxs("tr", { children: [
1063
+ /* @__PURE__ */ jsx(
1064
+ "th",
1065
+ {
1066
+ scope: "col",
1067
+ className: "px-4 py-2 text-left text-[11px] font-semibold uppercase tracking-wide text-ui-fg-muted",
1068
+ children: "#"
1069
+ }
1070
+ ),
1071
+ /* @__PURE__ */ jsx(
1072
+ "th",
1073
+ {
1074
+ scope: "col",
1075
+ className: "px-4 py-2 text-left text-[11px] font-semibold uppercase tracking-wide text-ui-fg-muted",
1076
+ children: "Product ID"
1077
+ }
1078
+ ),
1079
+ /* @__PURE__ */ jsx(
1080
+ "th",
1081
+ {
1082
+ scope: "col",
1083
+ className: "px-4 py-2 text-right text-[11px] font-semibold uppercase tracking-wide text-ui-fg-muted",
1084
+ children: "Wishlists"
1085
+ }
1086
+ )
1087
+ ] }) }),
1088
+ /* @__PURE__ */ jsx("tbody", { className: "divide-y divide-ui-border-subtle", children: topFive.map((stat, index) => /* @__PURE__ */ jsxs(
1089
+ "tr",
1090
+ {
1091
+ className: stat.product_id === productId ? "bg-ui-bg-subtle" : "bg-ui-bg-base",
1092
+ children: [
1093
+ /* @__PURE__ */ jsxs("td", { className: "px-4 py-3 text-sm font-medium text-ui-fg-subtle", children: [
1094
+ "#",
1095
+ index + 1
1096
+ ] }),
1097
+ /* @__PURE__ */ jsx("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
1098
+ /* @__PURE__ */ jsx(Text, { className: "font-mono text-sm", children: stat.product_id }),
1099
+ stat.product_id === productId && /* @__PURE__ */ jsx(Badge, { size: "2xsmall", color: "green", children: "Current" })
1100
+ ] }) }),
1101
+ /* @__PURE__ */ jsx("td", { className: "px-4 py-3 text-right text-sm font-semibold", children: stat.wishlist_count.toLocaleString() })
1102
+ ]
1103
+ },
1104
+ stat.product_id
1105
+ )) })
1106
+ ] }) })
1107
+ ] })
1108
+ ] });
1109
+ };
1110
+ defineWidgetConfig({
1111
+ zone: "product.details.side.after"
1112
+ });
990
1113
  const i18nTranslations0 = {};
991
1114
  const widgetModule = { widgets: [
992
1115
  {
@@ -1004,6 +1127,10 @@ const widgetModule = { widgets: [
1004
1127
  {
1005
1128
  Component: ProductMetadataTableWidget,
1006
1129
  zone: ["product.details.after"]
1130
+ },
1131
+ {
1132
+ Component: ProductWishlistStatsWidget,
1133
+ zone: ["product.details.side.after"]
1007
1134
  }
1008
1135
  ] };
1009
1136
  const routeModule = {
@@ -4,6 +4,18 @@ exports.GET = void 0;
4
4
  const wishlist_1 = require("../../../../modules/wishlist");
5
5
  const GET = async (req, res) => {
6
6
  try {
7
+ const authContext = req.auth_context;
8
+ const actorId = authContext?.actor_id;
9
+ const actorType = authContext?.actor_type;
10
+ if (!actorId) {
11
+ res.status(401).json({ message: "Unauthorized" });
12
+ return;
13
+ }
14
+ const adminActorTypes = ["user", "api-key"];
15
+ if (!actorType || !adminActorTypes.includes(actorType)) {
16
+ res.status(403).json({ message: "Only admin actors can access wishlist stats" });
17
+ return;
18
+ }
7
19
  const wishlistService = req.scope.resolve(wishlist_1.WISHLIST_MODULE);
8
20
  const productId = req.query.product_id;
9
21
  // If product_id is provided, return count for that product only
@@ -29,4 +41,4 @@ const GET = async (req, res) => {
29
41
  }
30
42
  };
31
43
  exports.GET = GET;
32
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL3dpc2hsaXN0L3N0YXRzL3JvdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUNBLDJEQUE4RDtBQUd2RCxNQUFNLEdBQUcsR0FBRyxLQUFLLEVBQ3RCLEdBQWtCLEVBQ2xCLEdBQW1CLEVBQ0osRUFBRTtJQUNqQixJQUFJLENBQUM7UUFDSCxNQUFNLGVBQWUsR0FBMEIsR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQzlELDBCQUFlLENBQ2hCLENBQUE7UUFFRCxNQUFNLFNBQVMsR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLFVBQWdDLENBQUE7UUFFNUQsZ0VBQWdFO1FBQ2hFLElBQUksU0FBUyxFQUFFLENBQUM7WUFDZCxNQUFNLE1BQU0sR0FBRyxNQUFNLGVBQWUsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUE7WUFDbkUsTUFBTSxLQUFLLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLFVBQVUsS0FBSyxTQUFTLENBQUMsQ0FBQTtZQUU1RCxHQUFHLENBQUMsSUFBSSxDQUFDO2dCQUNQLFVBQVUsRUFBRSxTQUFTO2dCQUNyQixjQUFjLEVBQUUsS0FBSyxFQUFFLGNBQWMsSUFBSSxDQUFDO2FBQzNDLENBQUMsQ0FBQTtZQUNGLE9BQU07UUFDUixDQUFDO1FBRUQsNENBQTRDO1FBQzVDLE1BQU0sTUFBTSxHQUFHLE1BQU0sZUFBZSxDQUFDLGlCQUFpQixFQUFFLENBQUE7UUFFeEQsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsQ0FBQyxDQUFBO0lBQzdCLENBQUM7SUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1FBQ2YsSUFBSSxLQUFLLFlBQVksS0FBSyxFQUFFLENBQUM7WUFDM0IsR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxPQUFPLEVBQUUsS0FBSyxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUE7WUFDaEQsT0FBTTtRQUNSLENBQUM7UUFDRCxHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxDQUFDLENBQUE7SUFDNUQsQ0FBQztBQUNILENBQUMsQ0FBQTtBQWxDWSxRQUFBLEdBQUcsT0FrQ2YifQ==
44
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL3dpc2hsaXN0L3N0YXRzL3JvdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUNBLDJEQUE4RDtBQUd2RCxNQUFNLEdBQUcsR0FBRyxLQUFLLEVBQ3RCLEdBQWtCLEVBQ2xCLEdBQW1CLEVBQ0osRUFBRTtJQUNqQixJQUFJLENBQUM7UUFDSCxNQUFNLFdBQVcsR0FBSSxHQUFXLENBQUMsWUFBWSxDQUFBO1FBQzdDLE1BQU0sT0FBTyxHQUFHLFdBQVcsRUFBRSxRQUFRLENBQUE7UUFDckMsTUFBTSxTQUFTLEdBQUcsV0FBVyxFQUFFLFVBQVUsQ0FBQTtRQUV6QyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDYixHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLE9BQU8sRUFBRSxjQUFjLEVBQUUsQ0FBQyxDQUFBO1lBQ2pELE9BQU07UUFDUixDQUFDO1FBRUQsTUFBTSxlQUFlLEdBQUcsQ0FBQyxNQUFNLEVBQUUsU0FBUyxDQUFDLENBQUE7UUFDM0MsSUFBSSxDQUFDLFNBQVMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQztZQUN2RCxHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLE9BQU8sRUFBRSw2Q0FBNkMsRUFBRSxDQUFDLENBQUE7WUFDaEYsT0FBTTtRQUNSLENBQUM7UUFFRCxNQUFNLGVBQWUsR0FBMEIsR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQzlELDBCQUFlLENBQ2hCLENBQUE7UUFFRCxNQUFNLFNBQVMsR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLFVBQWdDLENBQUE7UUFFNUQsZ0VBQWdFO1FBQ2hFLElBQUksU0FBUyxFQUFFLENBQUM7WUFDZCxNQUFNLE1BQU0sR0FBRyxNQUFNLGVBQWUsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUE7WUFDbkUsTUFBTSxLQUFLLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLFVBQVUsS0FBSyxTQUFTLENBQUMsQ0FBQTtZQUU1RCxHQUFHLENBQUMsSUFBSSxDQUFDO2dCQUNQLFVBQVUsRUFBRSxTQUFTO2dCQUNyQixjQUFjLEVBQUUsS0FBSyxFQUFFLGNBQWMsSUFBSSxDQUFDO2FBQzNDLENBQUMsQ0FBQTtZQUNGLE9BQU07UUFDUixDQUFDO1FBRUQsNENBQTRDO1FBQzVDLE1BQU0sTUFBTSxHQUFHLE1BQU0sZUFBZSxDQUFDLGlCQUFpQixFQUFFLENBQUE7UUFFeEQsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsQ0FBQyxDQUFBO0lBQzdCLENBQUM7SUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1FBQ2YsSUFBSSxLQUFLLFlBQVksS0FBSyxFQUFFLENBQUM7WUFDM0IsR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxPQUFPLEVBQUUsS0FBSyxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUE7WUFDaEQsT0FBTTtRQUNSLENBQUM7UUFDRCxHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxDQUFDLENBQUE7SUFDNUQsQ0FBQztBQUNILENBQUMsQ0FBQTtBQWpEWSxRQUFBLEdBQUcsT0FpRGYifQ==
@@ -9,12 +9,17 @@ const DELETE = async (req, res) => {
9
9
  res.status(400).json({ message: "product_id is required" });
10
10
  return;
11
11
  }
12
- // Get customer ID from auth context
13
- const customerId = req.auth_context?.actor_id;
12
+ // Enforce that only authenticated customers can delete wishlist entries
13
+ const authContext = req.auth_context;
14
+ const customerId = authContext?.actor_id;
14
15
  if (!customerId) {
15
16
  res.status(401).json({ message: "Unauthorized" });
16
17
  return;
17
18
  }
19
+ if (authContext?.actor_type !== "customer") {
20
+ res.status(403).json({ message: "Only customers can remove wishlist items" });
21
+ return;
22
+ }
18
23
  // Execute remove from wishlist workflow
19
24
  const { result } = await (0, remove_from_wishlist_1.removeFromWishlistWorkflow)(req.scope).run({
20
25
  input: {
@@ -33,4 +38,4 @@ const DELETE = async (req, res) => {
33
38
  }
34
39
  };
35
40
  exports.DELETE = DELETE;
36
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3N0b3JlL3dpc2hsaXN0L1twcm9kdWN0X2lkXS9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFDQSxxRkFBdUY7QUFFaEYsTUFBTSxNQUFNLEdBQUcsS0FBSyxFQUN6QixHQUFrQixFQUNsQixHQUFtQixFQUNKLEVBQUU7SUFDakIsSUFBSSxDQUFDO1FBQ0gsTUFBTSxFQUFFLFVBQVUsRUFBRSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUE7UUFFakMsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1lBQ2hCLEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsT0FBTyxFQUFFLHdCQUF3QixFQUFFLENBQUMsQ0FBQTtZQUMzRCxPQUFNO1FBQ1IsQ0FBQztRQUVELG9DQUFvQztRQUNwQyxNQUFNLFVBQVUsR0FBSSxHQUFXLENBQUMsWUFBWSxFQUFFLFFBQVEsQ0FBQTtRQUV0RCxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7WUFDaEIsR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxPQUFPLEVBQUUsY0FBYyxFQUFFLENBQUMsQ0FBQTtZQUNqRCxPQUFNO1FBQ1IsQ0FBQztRQUVELHdDQUF3QztRQUN4QyxNQUFNLEVBQUUsTUFBTSxFQUFFLEdBQUcsTUFBTSxJQUFBLGlEQUEwQixFQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxHQUFHLENBQUM7WUFDakUsS0FBSyxFQUFFO2dCQUNMLFdBQVcsRUFBRSxVQUFVO2dCQUN2QixVQUFVLEVBQUUsVUFBVTthQUN2QjtTQUNGLENBQUMsQ0FBQTtRQUVGLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxPQUFPLEVBQUUsTUFBTSxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUE7SUFDdkMsQ0FBQztJQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7UUFDZixJQUFJLEtBQUssWUFBWSxLQUFLLEVBQUUsQ0FBQztZQUMzQixHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLE9BQU8sRUFBRSxLQUFLLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQTtZQUNoRCxPQUFNO1FBQ1IsQ0FBQztRQUNELEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsT0FBTyxFQUFFLHVCQUF1QixFQUFFLENBQUMsQ0FBQTtJQUM1RCxDQUFDO0FBQ0gsQ0FBQyxDQUFBO0FBcENZLFFBQUEsTUFBTSxVQW9DbEIifQ==
41
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3N0b3JlL3dpc2hsaXN0L1twcm9kdWN0X2lkXS9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFDQSxxRkFBdUY7QUFFaEYsTUFBTSxNQUFNLEdBQUcsS0FBSyxFQUN6QixHQUFrQixFQUNsQixHQUFtQixFQUNKLEVBQUU7SUFDakIsSUFBSSxDQUFDO1FBQ0gsTUFBTSxFQUFFLFVBQVUsRUFBRSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUE7UUFFakMsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1lBQ2hCLEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsT0FBTyxFQUFFLHdCQUF3QixFQUFFLENBQUMsQ0FBQTtZQUMzRCxPQUFNO1FBQ1IsQ0FBQztRQUVELHdFQUF3RTtRQUN4RSxNQUFNLFdBQVcsR0FBSSxHQUFXLENBQUMsWUFBWSxDQUFBO1FBQzdDLE1BQU0sVUFBVSxHQUFHLFdBQVcsRUFBRSxRQUFRLENBQUE7UUFFeEMsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1lBQ2hCLEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsT0FBTyxFQUFFLGNBQWMsRUFBRSxDQUFDLENBQUE7WUFDakQsT0FBTTtRQUNSLENBQUM7UUFFRCxJQUFJLFdBQVcsRUFBRSxVQUFVLEtBQUssVUFBVSxFQUFFLENBQUM7WUFDM0MsR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxPQUFPLEVBQUUsMENBQTBDLEVBQUUsQ0FBQyxDQUFBO1lBQzdFLE9BQU07UUFDUixDQUFDO1FBRUQsd0NBQXdDO1FBQ3hDLE1BQU0sRUFBRSxNQUFNLEVBQUUsR0FBRyxNQUFNLElBQUEsaURBQTBCLEVBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDLEdBQUcsQ0FBQztZQUNqRSxLQUFLLEVBQUU7Z0JBQ0wsV0FBVyxFQUFFLFVBQVU7Z0JBQ3ZCLFVBQVUsRUFBRSxVQUFVO2FBQ3ZCO1NBQ0YsQ0FBQyxDQUFBO1FBRUYsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLE9BQU8sRUFBRSxNQUFNLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQTtJQUN2QyxDQUFDO0lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztRQUNmLElBQUksS0FBSyxZQUFZLEtBQUssRUFBRSxDQUFDO1lBQzNCLEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsT0FBTyxFQUFFLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFBO1lBQ2hELE9BQU07UUFDUixDQUFDO1FBQ0QsR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsQ0FBQyxDQUFBO0lBQzVELENBQUM7QUFDSCxDQUFDLENBQUE7QUExQ1ksUUFBQSxNQUFNLFVBMENsQiJ9
@@ -8,12 +8,17 @@ const POST = async (req, res) => {
8
8
  try {
9
9
  // Validate request body
10
10
  const validated = validators_1.AddToWishlistSchema.parse(req.body);
11
- // Get customer ID from auth context
12
- const customerId = req.auth_context?.actor_id;
11
+ // Enforce that only authenticated customers can mutate wishlists
12
+ const authContext = req.auth_context;
13
+ const customerId = authContext?.actor_id;
13
14
  if (!customerId) {
14
15
  res.status(401).json({ message: "Unauthorized" });
15
16
  return;
16
17
  }
18
+ if (authContext?.actor_type !== "customer") {
19
+ res.status(403).json({ message: "Only customers can update wishlists" });
20
+ return;
21
+ }
17
22
  // Execute add to wishlist workflow
18
23
  const { result } = await (0, add_to_wishlist_1.addToWishlistWorkflow)(req.scope).run({
19
24
  input: {
@@ -34,12 +39,17 @@ const POST = async (req, res) => {
34
39
  exports.POST = POST;
35
40
  const GET = async (req, res) => {
36
41
  try {
37
- // Get customer ID from auth context
38
- const customerId = req.auth_context?.actor_id;
42
+ // Restrict wishlist reads to authenticated customers
43
+ const authContext = req.auth_context;
44
+ const customerId = authContext?.actor_id;
39
45
  if (!customerId) {
40
46
  res.status(401).json({ message: "Unauthorized" });
41
47
  return;
42
48
  }
49
+ if (authContext?.actor_type !== "customer") {
50
+ res.status(403).json({ message: "Only customers can read their wishlist" });
51
+ return;
52
+ }
43
53
  // Parse query parameters
44
54
  const includeDetails = req.query.include_details === "true" ||
45
55
  req.query.include_details === "1";
@@ -63,4 +73,4 @@ const GET = async (req, res) => {
63
73
  }
64
74
  };
65
75
  exports.GET = GET;
66
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3N0b3JlL3dpc2hsaXN0L3JvdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUNBLHdFQUEwRTtBQUMxRSxrRUFBcUU7QUFDckUsNkNBQWtEO0FBRTNDLE1BQU0sSUFBSSxHQUFHLEtBQUssRUFDdkIsR0FBa0IsRUFDbEIsR0FBbUIsRUFDSixFQUFFO0lBQ2pCLElBQUksQ0FBQztRQUNILHdCQUF3QjtRQUN4QixNQUFNLFNBQVMsR0FBRyxnQ0FBbUIsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFBO1FBRXJELG9DQUFvQztRQUNwQyxNQUFNLFVBQVUsR0FBSSxHQUFXLENBQUMsWUFBWSxFQUFFLFFBQVEsQ0FBQTtRQUV0RCxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7WUFDaEIsR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxPQUFPLEVBQUUsY0FBYyxFQUFFLENBQUMsQ0FBQTtZQUNqRCxPQUFNO1FBQ1IsQ0FBQztRQUVELG1DQUFtQztRQUNuQyxNQUFNLEVBQUUsTUFBTSxFQUFFLEdBQUcsTUFBTSxJQUFBLHVDQUFxQixFQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxHQUFHLENBQUM7WUFDNUQsS0FBSyxFQUFFO2dCQUNMLFdBQVcsRUFBRSxVQUFVO2dCQUN2QixVQUFVLEVBQUUsU0FBUyxDQUFDLFVBQVU7YUFDakM7U0FDRixDQUFDLENBQUE7UUFFRixHQUFHLENBQUMsSUFBSSxDQUFDLEVBQUUsYUFBYSxFQUFFLE1BQU0sQ0FBQyxhQUFhLEVBQUUsQ0FBQyxDQUFBO0lBQ25ELENBQUM7SUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1FBQ2YsSUFBSSxLQUFLLFlBQVksS0FBSyxFQUFFLENBQUM7WUFDM0IsR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxPQUFPLEVBQUUsS0FBSyxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUE7WUFDaEQsT0FBTTtRQUNSLENBQUM7UUFDRCxHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxDQUFDLENBQUE7SUFDNUQsQ0FBQztBQUNILENBQUMsQ0FBQTtBQWhDWSxRQUFBLElBQUksUUFnQ2hCO0FBRU0sTUFBTSxHQUFHLEdBQUcsS0FBSyxFQUN0QixHQUFrQixFQUNsQixHQUFtQixFQUNKLEVBQUU7SUFDakIsSUFBSSxDQUFDO1FBQ0gsb0NBQW9DO1FBQ3BDLE1BQU0sVUFBVSxHQUFJLEdBQVcsQ0FBQyxZQUFZLEVBQUUsUUFBUSxDQUFBO1FBRXRELElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztZQUNoQixHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLE9BQU8sRUFBRSxjQUFjLEVBQUUsQ0FBQyxDQUFBO1lBQ2pELE9BQU07UUFDUixDQUFDO1FBRUQseUJBQXlCO1FBQ3pCLE1BQU0sY0FBYyxHQUNsQixHQUFHLENBQUMsS0FBSyxDQUFDLGVBQWUsS0FBSyxNQUFNO1lBQ3BDLEdBQUcsQ0FBQyxLQUFLLENBQUMsZUFBZSxLQUFLLEdBQUcsQ0FBQTtRQUVuQyxnQ0FBZ0M7UUFDaEMsTUFBTSxFQUFFLE1BQU0sRUFBRSxHQUFHLE1BQU0sSUFBQSxrQ0FBbUIsRUFBQyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUMsR0FBRyxDQUFDO1lBQzFELEtBQUssRUFBRTtnQkFDTCxXQUFXLEVBQUUsVUFBVTtnQkFDdkIsT0FBTyxFQUFFO29CQUNQLGNBQWMsRUFBRSxjQUFjO2lCQUMvQjthQUNGO1NBQ0YsQ0FBQyxDQUFBO1FBRUYsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLFFBQVEsRUFBRSxNQUFNLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQTtJQUN6QyxDQUFDO0lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztRQUNmLElBQUksS0FBSyxZQUFZLEtBQUssRUFBRSxDQUFDO1lBQzNCLEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsT0FBTyxFQUFFLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFBO1lBQ2hELE9BQU07UUFDUixDQUFDO1FBQ0QsR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsQ0FBQyxDQUFBO0lBQzVELENBQUM7QUFDSCxDQUFDLENBQUE7QUFwQ1ksUUFBQSxHQUFHLE9Bb0NmIn0=
76
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3N0b3JlL3dpc2hsaXN0L3JvdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUNBLHdFQUEwRTtBQUMxRSxrRUFBcUU7QUFDckUsNkNBQWtEO0FBRTNDLE1BQU0sSUFBSSxHQUFHLEtBQUssRUFDdkIsR0FBa0IsRUFDbEIsR0FBbUIsRUFDSixFQUFFO0lBQ2pCLElBQUksQ0FBQztRQUNILHdCQUF3QjtRQUN4QixNQUFNLFNBQVMsR0FBRyxnQ0FBbUIsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFBO1FBRXJELGlFQUFpRTtRQUNqRSxNQUFNLFdBQVcsR0FBSSxHQUFXLENBQUMsWUFBWSxDQUFBO1FBQzdDLE1BQU0sVUFBVSxHQUFHLFdBQVcsRUFBRSxRQUFRLENBQUE7UUFFeEMsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1lBQ2hCLEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsT0FBTyxFQUFFLGNBQWMsRUFBRSxDQUFDLENBQUE7WUFDakQsT0FBTTtRQUNSLENBQUM7UUFFRCxJQUFJLFdBQVcsRUFBRSxVQUFVLEtBQUssVUFBVSxFQUFFLENBQUM7WUFDM0MsR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxPQUFPLEVBQUUscUNBQXFDLEVBQUUsQ0FBQyxDQUFBO1lBQ3hFLE9BQU07UUFDUixDQUFDO1FBRUQsbUNBQW1DO1FBQ25DLE1BQU0sRUFBRSxNQUFNLEVBQUUsR0FBRyxNQUFNLElBQUEsdUNBQXFCLEVBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDLEdBQUcsQ0FBQztZQUM1RCxLQUFLLEVBQUU7Z0JBQ0wsV0FBVyxFQUFFLFVBQVU7Z0JBQ3ZCLFVBQVUsRUFBRSxTQUFTLENBQUMsVUFBVTthQUNqQztTQUNGLENB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
@@ -1,40 +1,98 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getWishlist = getWishlist;
4
- const wishlist_1 = require("../../modules/wishlist");
5
- /**
6
- * Helper function to get customer's wishlist
7
- * This function can be used directly by UI components without implementing the entire logic
8
- *
9
- * @param container - Medusa container instance
10
- * @param customerId - Customer ID
11
- * @param options - Options for getting wishlist
12
- * @returns Promise of wishlist items (IDs or full details based on options)
13
- *
14
- * @example
15
- * // Get only product IDs
16
- * const wishlist = await getWishlist(container, customerId)
17
- * // Returns: ['prod_123', 'prod_456']
18
- *
19
- * @example
20
- * // Get full product details
21
- * const wishlist = await getWishlist(container, customerId, { includeDetails: true })
22
- * // Returns: [{ product_id: 'prod_123', product: {...}, ... }]
23
- */
24
- async function getWishlist(container, customerId, options = {}) {
25
- const wishlistService = container.resolve(wishlist_1.WISHLIST_MODULE);
26
- const serviceOptions = {
27
- includeDetails: options.includeDetails ?? false,
28
- productIds: options.productIds,
3
+ exports.createWishlistHelpers = exports.createRemoveFromWishlist = exports.createAddToWishlist = exports.createGetWishlist = exports.removeFromWishlist = exports.addToWishlist = exports.getWishlist = void 0;
4
+ const WISHLIST_ENDPOINT = "/store/wishlist";
5
+ const normalizeBaseUrl = (baseUrl) => {
6
+ if (!baseUrl) {
7
+ return "";
8
+ }
9
+ return baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
10
+ };
11
+ const getClientRequest = (client) => {
12
+ if (!client) {
13
+ return undefined;
14
+ }
15
+ if ("request" in client && typeof client.request === "function") {
16
+ return client.request.bind(client);
17
+ }
18
+ if ("client" in client && typeof client.client?.request === "function") {
19
+ return client.client.request.bind(client.client);
20
+ }
21
+ return undefined;
22
+ };
23
+ const execute = async (path, init, options) => {
24
+ const headers = {
25
+ ...(init.headers ?? {}),
26
+ ...(options.headers ?? {}),
27
+ };
28
+ const normalizedInit = {
29
+ ...init,
30
+ headers,
29
31
  };
30
- const wishlist = await wishlistService.getWishlist(customerId, serviceOptions);
31
- // If includeDetails is false, wishlist is an array of product IDs
32
- if (!options.includeDetails) {
33
- return wishlist;
34
- }
35
- // If includeDetails is true, we need to enrich with product details
36
- // For now, return the wishlist items as-is (they should already have details if includeDetails was true)
37
- // In a real implementation, you might want to use remote query to enrich here
38
- return wishlist;
39
- }
40
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVscGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL3NoYXJlZC93aXNobGlzdC9oZWxwZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUE4Q0Esa0NBeUJDO0FBdEVELHFEQUF3RDtBQTBCeEQ7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWtCRztBQUNJLEtBQUssVUFBVSxXQUFXLENBQy9CLFNBQXdCLEVBQ3hCLFVBQWtCLEVBQ2xCLFVBQW9DLEVBQUU7SUFFdEMsTUFBTSxlQUFlLEdBQTBCLFNBQVMsQ0FBQyxPQUFPLENBQzlELDBCQUFlLENBQ2hCLENBQUE7SUFFRCxNQUFNLGNBQWMsR0FBdUI7UUFDekMsY0FBYyxFQUFFLE9BQU8sQ0FBQyxjQUFjLElBQUksS0FBSztRQUMvQyxVQUFVLEVBQUUsT0FBTyxDQUFDLFVBQVU7S0FDL0IsQ0FBQTtJQUVELE1BQU0sUUFBUSxHQUFHLE1BQU0sZUFBZSxDQUFDLFdBQVcsQ0FBQyxVQUFVLEVBQUUsY0FBYyxDQUFDLENBQUE7SUFFOUUsa0VBQWtFO0lBQ2xFLElBQUksQ0FBQyxPQUFPLENBQUMsY0FBYyxFQUFFLENBQUM7UUFDNUIsT0FBTyxRQUFvQixDQUFBO0lBQzdCLENBQUM7SUFFRCxvRUFBb0U7SUFDcEUseUdBQXlHO0lBQ3pHLDhFQUE4RTtJQUM5RSxPQUFPLFFBQWtDLENBQUE7QUFDM0MsQ0FBQyJ9
32
+ const clientRequest = getClientRequest(options.client);
33
+ if (clientRequest) {
34
+ return (await clientRequest(path, normalizedInit));
35
+ }
36
+ const fetchImpl = options.fetchImpl ?? globalThis.fetch;
37
+ if (!fetchImpl) {
38
+ throw new Error("No fetch implementation available. Provide `fetchImpl` or a Medusa client.");
39
+ }
40
+ const url = `${normalizeBaseUrl(options.baseUrl)}${path}`;
41
+ const response = await fetchImpl(url || path, normalizedInit);
42
+ if (!response.ok) {
43
+ const body = await response.text();
44
+ throw new Error(body || `Request failed with status ${response.status}`);
45
+ }
46
+ return (await response.json());
47
+ };
48
+ const getWishlist = async (input = {}, options = {}) => {
49
+ const params = new URLSearchParams();
50
+ if (input.includeDetails) {
51
+ params.set("include_details", "true");
52
+ }
53
+ const endpoint = params.toString()
54
+ ? `${WISHLIST_ENDPOINT}?${params.toString()}`
55
+ : WISHLIST_ENDPOINT;
56
+ return execute(endpoint, {
57
+ method: "GET",
58
+ }, options);
59
+ };
60
+ exports.getWishlist = getWishlist;
61
+ const addToWishlist = async (input, options = {}) => {
62
+ if (!input.product_id) {
63
+ throw new Error("product_id is required");
64
+ }
65
+ return execute(WISHLIST_ENDPOINT, {
66
+ method: "POST",
67
+ headers: {
68
+ "Content-Type": "application/json",
69
+ },
70
+ body: JSON.stringify({
71
+ product_id: input.product_id,
72
+ }),
73
+ }, options);
74
+ };
75
+ exports.addToWishlist = addToWishlist;
76
+ const removeFromWishlist = async (input, options = {}) => {
77
+ if (!input.product_id) {
78
+ throw new Error("product_id is required");
79
+ }
80
+ const endpoint = `${WISHLIST_ENDPOINT}/${encodeURIComponent(input.product_id)}`;
81
+ return execute(endpoint, {
82
+ method: "DELETE",
83
+ }, options);
84
+ };
85
+ exports.removeFromWishlist = removeFromWishlist;
86
+ const createGetWishlist = (options = {}) => (input) => (0, exports.getWishlist)(input, options);
87
+ exports.createGetWishlist = createGetWishlist;
88
+ const createAddToWishlist = (options = {}) => (input) => (0, exports.addToWishlist)(input, options);
89
+ exports.createAddToWishlist = createAddToWishlist;
90
+ const createRemoveFromWishlist = (options = {}) => (input) => (0, exports.removeFromWishlist)(input, options);
91
+ exports.createRemoveFromWishlist = createRemoveFromWishlist;
92
+ const createWishlistHelpers = (options = {}) => ({
93
+ getWishlist: (0, exports.createGetWishlist)(options),
94
+ addToWishlist: (0, exports.createAddToWishlist)(options),
95
+ removeFromWishlist: (0, exports.createRemoveFromWishlist)(options),
96
+ });
97
+ exports.createWishlistHelpers = createWishlistHelpers;
98
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVscGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL3NoYXJlZC93aXNobGlzdC9oZWxwZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsTUFBTSxpQkFBaUIsR0FBRyxpQkFBaUIsQ0FBQTtBQXFFM0MsTUFBTSxnQkFBZ0IsR0FBRyxDQUFDLE9BQWdCLEVBQUUsRUFBRTtJQUM1QyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDYixPQUFPLEVBQUUsQ0FBQTtJQUNYLENBQUM7SUFDRCxPQUFPLE9BQU8sQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQTtBQUMvRCxDQUFDLENBQUE7QUFFRCxNQUFNLGdCQUFnQixHQUFHLENBQUMsTUFBeUIsRUFBRSxFQUFFO0lBQ3JELElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUNaLE9BQU8sU0FBUyxDQUFBO0lBQ2xCLENBQUM7SUFFRCxJQUFJLFNBQVMsSUFBSSxNQUFNLElBQUksT0FBTyxNQUFNLENBQUMsT0FBTyxLQUFLLFVBQVUsRUFBRSxDQUFDO1FBQ2hFLE9BQU8sTUFBTSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUE7SUFDcEMsQ0FBQztJQUVELElBQUksUUFBUSxJQUFJLE1BQU0sSUFBSSxPQUFPLE1BQU0sQ0FBQyxNQUFNLEVBQUUsT0FBTyxLQUFLLFVBQVUsRUFBRSxDQUFDO1FBQ3ZFLE9BQU8sTUFBTSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQTtJQUNsRCxDQUFDO0lBRUQsT0FBTyxTQUFTLENBQUE7QUFDbEIsQ0FBQyxDQUFBO0FBRUQsTUFBTSxPQUFPLEdBQUcsS0FBSyxFQUNuQixJQUFZLEVBQ1os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
@@ -1,6 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getWishlist = void 0;
3
+ exports.removeFromWishlist = exports.getWishlist = exports.createWishlistHelpers = exports.createRemoveFromWishlist = exports.createGetWishlist = exports.createAddToWishlist = exports.addToWishlist = void 0;
4
4
  var helper_1 = require("./helper");
5
+ Object.defineProperty(exports, "addToWishlist", { enumerable: true, get: function () { return helper_1.addToWishlist; } });
6
+ Object.defineProperty(exports, "createAddToWishlist", { enumerable: true, get: function () { return helper_1.createAddToWishlist; } });
7
+ Object.defineProperty(exports, "createGetWishlist", { enumerable: true, get: function () { return helper_1.createGetWishlist; } });
8
+ Object.defineProperty(exports, "createRemoveFromWishlist", { enumerable: true, get: function () { return helper_1.createRemoveFromWishlist; } });
9
+ Object.defineProperty(exports, "createWishlistHelpers", { enumerable: true, get: function () { return helper_1.createWishlistHelpers; } });
5
10
  Object.defineProperty(exports, "getWishlist", { enumerable: true, get: function () { return helper_1.getWishlist; } });
6
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvc2hhcmVkL3dpc2hsaXN0L2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLG1DQUFzQztBQUE3QixxR0FBQSxXQUFXLE9BQUEifQ==
11
+ Object.defineProperty(exports, "removeFromWishlist", { enumerable: true, get: function () { return helper_1.removeFromWishlist; } });
12
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvc2hhcmVkL3dpc2hsaXN0L2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLG1DQVFpQjtBQVBmLHVHQUFBLGFBQWEsT0FBQTtBQUNiLDZHQUFBLG1CQUFtQixPQUFBO0FBQ25CLDJHQUFBLGlCQUFpQixPQUFBO0FBQ2pCLGtIQUFBLHdCQUF3QixPQUFBO0FBQ3hCLCtHQUFBLHFCQUFxQixPQUFBO0FBQ3JCLHFHQUFBLFdBQVcsT0FBQTtBQUNYLDRHQUFBLGtCQUFrQixPQUFBIn0=
package/README.md CHANGED
@@ -194,6 +194,18 @@ inputs. No extra configuration is required beyond defining descriptors under
194
194
  `metadata.products.descriptors` or `metadata.categories.descriptors` in
195
195
  `medusa-config.ts`.
196
196
 
197
+ #### Wishlist Insights Widget
198
+
199
+ The admin extension also injects a **Wishlist performance** card into the product
200
+ details sidebar (`product.details.side.after`). The widget:
201
+
202
+ - Shows how many unique customers saved the current product.
203
+ - Highlights whether the product ranks within the most wishlisted items.
204
+ - Displays a live leaderboard (top 5) powered by `/admin/wishlist/stats`.
205
+
206
+ No additional configuration is required—install the plugin, run the wishlist
207
+ migration, and open any product inside Medusa Admin to see the UI.
208
+
197
209
  #### Promotion Window
198
210
 
199
211
  Configure how promotion dates are tracked using product metadata:
@@ -222,6 +234,12 @@ Configure rating-based filtering:
222
234
 
223
235
  The plugin includes a comprehensive wishlist feature that allows customers to save products they're interested in and admins to view wishlist statistics.
224
236
 
237
+ ### Security & Access Control
238
+
239
+ - Store-facing wishlist routes authenticate strictly as customers. Customer IDs are always derived from the session or JWT auth context and never taken from the request payload, so an admin or API client cannot spoof a customer identifier.
240
+ - Admin-facing wishlist statistics are available only to admin actors (`user` sessions or secret API keys). Customer tokens receive a `403` response when attempting to access `/admin/wishlist/stats`.
241
+ - This separation ensures only customers can manage their wishlist entries while only admins can inspect aggregate wishlist state.
242
+
225
243
  ### Module Registration
226
244
 
227
245
  The wishlist module is automatically registered when you add the plugin to your Medusa configuration. No additional configuration is required for basic usage.
@@ -410,59 +428,79 @@ curl "https://your-store.com/admin/wishlist/stats?product_id=prod_123" \
410
428
  -H "Authorization: Bearer ADMIN_TOKEN"
411
429
  ```
412
430
 
413
- ### Helper Function
431
+ ### Helper Functions
414
432
 
415
- The plugin provides a helper function that can be used directly by UI components without implementing the entire logic.
433
+ The plugin exposes lightweight helpers that wrap the Store API endpoints. They are ideal for server-side storefronts that need to call the wishlist endpoints while relying on the authenticated customer's session (no customer ID is ever passed in the payload).
416
434
 
417
- #### Import the Helper
435
+ #### Imports
418
436
 
419
437
  ```typescript
420
- import { getWishlist } from "medusa-product-helper/wishlist-helper"
438
+ import {
439
+ addToWishlist,
440
+ getWishlist,
441
+ removeFromWishlist,
442
+ createWishlistHelpers,
443
+ } from "medusa-product-helper/wishlist-helper"
421
444
  ```
422
445
 
423
- #### Usage Examples
446
+ #### Example Usage
424
447
 
425
- **Get only product IDs**:
426
448
  ```typescript
427
- import { getWishlist } from "medusa-product-helper/wishlist-helper"
449
+ // Add a product
450
+ await addToWishlist(
451
+ { product_id: "prod_123" },
452
+ {
453
+ baseUrl: "https://store.example.com",
454
+ headers: {
455
+ Cookie: "connect.sid=...", // or Authorization header
456
+ },
457
+ }
458
+ )
459
+
460
+ // Read wishlist with details
461
+ const { wishlist } = await getWishlist(
462
+ { includeDetails: true },
463
+ {
464
+ baseUrl: "https://store.example.com",
465
+ headers: {
466
+ Cookie: "connect.sid=...",
467
+ },
468
+ }
469
+ )
428
470
 
429
- // In your component or service
430
- const productIds = await getWishlist(container, customerId)
431
- // Returns: ['prod_123', 'prod_456']
471
+ // Remove a product
472
+ await removeFromWishlist(
473
+ { product_id: "prod_123" },
474
+ {
475
+ baseUrl: "https://store.example.com",
476
+ headers: {
477
+ Cookie: "connect.sid=...",
478
+ },
479
+ }
480
+ )
432
481
  ```
433
482
 
434
- **Get full product details**:
435
- ```typescript
436
- import { getWishlist } from "medusa-product-helper/wishlist-helper"
483
+ #### Configuration Options
437
484
 
438
- const wishlist = await getWishlist(container, customerId, {
439
- includeDetails: true
440
- })
441
- // Returns: [
442
- // {
443
- // product_id: 'prod_123',
444
- // product: { ... },
445
- // id: 'wish_123',
446
- // created_at: Date
447
- // },
448
- // ...
449
- // ]
450
- ```
485
+ All helper calls accept the following options:
451
486
 
452
- **Filter by specific products**:
453
- ```typescript
454
- import { getWishlist } from "medusa-product-helper/wishlist-helper"
487
+ - `client`: Medusa JS/SDK client instance. When provided, network requests are delegated to `client.request`.
488
+ - `baseUrl`: Base URL for the Store API (e.g., `https://store.example.com`). Required when a client is not provided.
489
+ - `fetchImpl`: Custom `fetch` implementation for SSR or React Native environments. Defaults to `globalThis.fetch`.
490
+ - `headers`: Additional headers appended to every request (useful for `Cookie` / `Authorization` headers).
455
491
 
456
- const wishlist = await getWishlist(container, customerId, {
457
- includeDetails: true,
458
- productIds: ['prod_123', 'prod_456']
459
- })
460
- ```
492
+ You can also generate pre-configured helpers:
461
493
 
462
- #### Helper Options
494
+ ```typescript
495
+ const wishlist = createWishlistHelpers({
496
+ baseUrl: "https://store.example.com",
497
+ headers: { Cookie: "connect.sid=..." },
498
+ })
463
499
 
464
- - `includeDetails` (boolean, default: `false`): If `true`, returns full product details. If `false`, returns only product IDs.
465
- - `productIds` (string[], optional): Filter wishlist by specific product IDs.
500
+ await wishlist.addToWishlist({ product_id: "prod_123" })
501
+ const { wishlist: items } = await wishlist.getWishlist({ includeDetails: true })
502
+ await wishlist.removeFromWishlist({ product_id: "prod_123" })
503
+ ```
466
504
 
467
505
  ### Using Workflows Directly
468
506
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "medusa-product-helper",
3
- "version": "0.0.5",
3
+ "version": "0.0.9",
4
4
  "description": "A starter for Medusa plugins.",
5
5
  "author": "Medusa (https://medusajs.com)",
6
6
  "license": "MIT",