@techsio/storefront-data
Version:
Medusa-first storefront data layer built on TanStack Query.
187 lines (186 loc) • 8.53 kB
JavaScript
import { compactRecord } from "../shared/object-utils.js";
const DEFAULT_PRODUCT_LISTS_PATH = "/store/product-lists";
const normalizeQuantity = (quantity)=>{
if ("number" != typeof quantity || !Number.isFinite(quantity)) return;
return Math.max(1, Math.floor(quantity));
};
const normalizeQuantityDelta = (quantity)=>{
if ("number" != typeof quantity || !Number.isFinite(quantity)) return 1;
const quantityDelta = Math.trunc(quantity);
if (0 === quantityDelta) throw new Error("Quantity change must be a non-zero integer.");
return quantityDelta;
};
const normalizeProductListsResponse = (response, fallbackLimit, fallbackOffset)=>{
const productLists = response.product_lists ?? response.productLists ?? response.lists ?? [];
return {
productLists,
count: response.count ?? productLists.length,
limit: response.limit ?? fallbackLimit,
offset: response.offset ?? fallbackOffset
};
};
const resolveProductListFromResponse = (response)=>response.product_list ?? response.productList ?? response.list ?? null;
const resolveProductListItemFromResponse = (response)=>response.product_list_item ?? response.productListItem ?? response.item ?? null;
const resolveProductListCartFromResponse = (response)=>response.cart ?? null;
function createMedusaProductListService(sdk, config) {
const basePath = config?.basePath ?? DEFAULT_PRODUCT_LISTS_PATH;
const defaultLimit = config?.defaultLimit ?? 20;
const defaultOffset = config?.defaultOffset ?? 0;
const transformProductList = config?.transformProductList ?? ((list)=>list);
const transformProductListItem = config?.transformProductListItem ?? ((item)=>item);
const transformCart = config?.transformCart ?? ((cart)=>cart);
const mapList = (list)=>transformProductList(list);
const mapItem = (item)=>transformProductListItem(item);
const resolveListQuery = (params)=>{
const normalized = config?.normalizeListQuery?.(params) ?? params;
const { limit = defaultLimit, offset = defaultOffset, ...query } = normalized;
return compactRecord({
...query,
limit,
offset
});
};
const resolveItemFromResponse = (response)=>{
const item = resolveProductListItemFromResponse(response);
return item ? mapItem(item) : null;
};
return {
async listProductLists (params, signal) {
const query = resolveListQuery(params);
const response = await sdk.client.fetch(basePath, {
query,
signal
});
const normalized = normalizeProductListsResponse(response, Number(query.limit ?? defaultLimit), Number(query.offset ?? defaultOffset));
return {
...normalized,
productLists: normalized.productLists.map(mapList)
};
},
async getProductList (params, signal) {
if (!params.id) return null;
const response = await sdk.client.fetch(`${basePath}/${params.id}`, {
signal
});
const productList = resolveProductListFromResponse(response);
return productList ? mapList(productList) : null;
},
async createFavoriteProductList (input = {}) {
const response = await sdk.client.fetch(`${basePath}/favorites`, {
method: "POST",
body: compactRecord(input)
});
const productList = resolveProductListFromResponse(response);
return productList ? mapList(productList) : null;
},
async createCustomProductList (input) {
const response = await sdk.client.fetch(`${basePath}/custom`, {
method: "POST",
body: compactRecord({
...input,
access_type: input.access_type ?? "private"
})
});
const productList = resolveProductListFromResponse(response);
return productList ? mapList(productList) : null;
},
async updateProductList (input) {
const response = await sdk.client.fetch(`${basePath}/${input.listId}`, {
method: "POST",
body: compactRecord({
title: input.title,
access_type: input.access_type,
description: input.description,
handle: input.handle,
metadata: input.metadata
})
});
const productList = resolveProductListFromResponse(response);
return productList ? mapList(productList) : null;
},
deleteProductList (input) {
return sdk.client.fetch(`${basePath}/${input.listId}`, {
method: "DELETE"
});
},
async addProductListItem (input) {
const response = await sdk.client.fetch(`${basePath}/${input.listId}/items`, {
method: "POST",
body: compactRecord({
product_id: input.productId,
variant_id: input.variantId ?? void 0,
quantity: normalizeQuantity(input.quantity),
note: input.note,
sort_order: input.sortOrder,
metadata: input.metadata
})
});
return resolveItemFromResponse(response);
},
async addFavoriteProductListItem (input) {
const response = await sdk.client.fetch(`${basePath}/favorites/items`, {
method: "POST",
body: compactRecord({
product_id: input.productId,
variant_id: input.variantId ?? void 0,
quantity: normalizeQuantity(input.quantity),
note: input.note,
sort_order: input.sortOrder,
metadata: input.metadata
})
});
return resolveItemFromResponse(response);
},
async createProductListCart (input) {
const response = await sdk.client.fetch(`${basePath}/${input.listId}/cart`, {
method: "POST",
body: compactRecord({
region_id: input.regionId ?? void 0,
country_code: input.countryCode ?? void 0,
email: input.email ?? void 0,
sales_channel_id: input.salesChannelId ?? void 0
})
});
const cart = resolveProductListCartFromResponse(response);
if (!cart) throw new Error("Product list cart response did not include a cart.");
return transformCart(cart);
},
async updateProductListItem (input) {
const response = await sdk.client.fetch(`${basePath}/items/${input.itemId}`, {
method: "POST",
body: compactRecord({
quantity: normalizeQuantity(input.quantity),
note: input.note,
sort_order: input.sortOrder,
metadata: input.metadata
})
});
return resolveItemFromResponse(response);
},
async changeProductListItemQuantity (input) {
const response = await sdk.client.fetch(`${basePath}/items/${input.itemId}/change-quantity`, {
method: "POST",
body: compactRecord({
quantity: normalizeQuantityDelta(input.quantity)
})
});
return resolveItemFromResponse(response);
},
async incrementProductListItem (input) {
const response = await sdk.client.fetch(`${basePath}/items/${input.itemId}/increment`, {
method: "POST",
body: compactRecord({
quantity: normalizeQuantity(input.quantity) ?? 1
})
});
return resolveItemFromResponse(response);
},
deleteProductListItem (input) {
const path = input.listId ? `${basePath}/${input.listId}/items/${input.itemId}` : `${basePath}/items/${input.itemId}`;
return sdk.client.fetch(path, {
method: "DELETE"
});
}
};
}
export { createMedusaProductListService, normalizeProductListsResponse, resolveProductListCartFromResponse, resolveProductListFromResponse, resolveProductListItemFromResponse };