@techsio/storefront-data
Version:
Medusa-first storefront data layer built on TanStack Query.
224 lines (223 loc) • 9.74 kB
JavaScript
import { hasTrimmedString, normalizePresentTrimmedString, normalizeTrimmedString } from "../shared/string-utils.js";
const defaultCheckoutAddressRequiredFields = [
"firstName",
"lastName",
"street",
"city",
"postalCode",
"country"
];
const createRequiredIssue = (scope, field)=>({
scope,
field,
code: "required",
message: `Missing ${scope} field: ${field}`
});
const normalizeCountryCode = (countryCode, options)=>{
const trimmedCountryCode = countryCode?.trim();
let transformed;
if (trimmedCountryCode) transformed = options?.countryCodeTransform ? options.countryCodeTransform(trimmedCountryCode) : trimmedCountryCode;
const normalized = transformed?.trim();
if (normalized) return normalized.toLowerCase();
const defaultCountryCode = options?.defaultCountryCode?.trim();
if (defaultCountryCode) return defaultCountryCode.toLowerCase();
};
const normalizePatchCountryCode = (countryCode, options)=>{
if ("string" != typeof countryCode) return "";
const trimmedCountryCode = countryCode.trim();
if (!trimmedCountryCode) return "";
const transformed = options?.countryCodeTransform ? options.countryCodeTransform(trimmedCountryCode) : trimmedCountryCode;
const normalized = transformed.trim();
return normalized ? normalized.toLowerCase() : "";
};
const normalizeCheckoutAddressInput = (address)=>({
...address,
firstName: normalizeTrimmedString(address.firstName),
lastName: normalizeTrimmedString(address.lastName),
street: normalizeTrimmedString(address.street),
street2: normalizeTrimmedString(address.street2),
city: normalizeTrimmedString(address.city),
postalCode: normalizeTrimmedString(address.postalCode),
country: normalizeTrimmedString(address.country),
province: normalizeTrimmedString(address.province),
company: normalizeTrimmedString(address.company),
phone: normalizeTrimmedString(address.phone)
});
const normalizeCheckoutAddressPatch = (address)=>{
const normalized = {
...address
};
for (const field of [
"firstName",
"lastName",
"street",
"street2",
"city",
"postalCode",
"country",
"province",
"company",
"phone"
])if (Object.hasOwn(address, field)) normalized[field] = normalizePresentTrimmedString(address[field]);
return normalized;
};
const getMissingCheckoutAddressFields = (address, requiredFields = defaultCheckoutAddressRequiredFields)=>requiredFields.filter((field)=>!hasTrimmedString(address[field]));
const getCheckoutAddressFieldIssues = (address, options)=>{
const scope = options?.scope ?? "shipping";
const requiredFields = options?.requiredFields ?? defaultCheckoutAddressRequiredFields;
const normalizedAddress = normalizeCheckoutAddressInput(address);
return getMissingCheckoutAddressFields(normalizedAddress, requiredFields).map((field)=>createRequiredIssue(scope, field));
};
const getCheckoutAddressPatchFieldIssues = (address, options)=>{
const scope = options?.scope ?? "shipping";
const requiredFields = options?.requiredFields ?? defaultCheckoutAddressRequiredFields;
const normalizedAddress = normalizeCheckoutAddressPatch(address);
return requiredFields.filter((field)=>Object.hasOwn(address, field) && !hasTrimmedString(normalizedAddress[field])).map((field)=>createRequiredIssue(scope, field));
};
const getCheckoutAddressValidationIssues = (data, options)=>{
const issues = [];
const requireEmail = options?.requireEmail ?? true;
const shippingRequiredFields = options?.shippingRequiredFields ?? defaultCheckoutAddressRequiredFields;
const billingRequiredFields = options?.billingRequiredFields ?? defaultCheckoutAddressRequiredFields;
issues.push(...getCheckoutAddressFieldIssues(data.shipping, {
scope: "shipping",
requiredFields: shippingRequiredFields
}));
if (false === data.useSameAddress) issues.push(...getCheckoutAddressFieldIssues(data.billing, {
scope: "billing",
requiredFields: billingRequiredFields
}));
const normalizedEmail = normalizeTrimmedString(data.email);
if (requireEmail && !normalizedEmail) issues.push({
scope: "root",
field: "email",
code: "required",
message: "Missing checkout email"
});
return issues;
};
const mapCheckoutAddressToMedusaCartAddress = (address, options)=>{
const normalized = normalizeCheckoutAddressInput(address);
return {
first_name: normalized.firstName,
last_name: normalized.lastName,
address_1: normalized.street,
address_2: normalized.street2,
city: normalized.city,
postal_code: normalized.postalCode,
country_code: normalizeCountryCode(normalized.country, options),
province: normalized.province,
company: normalized.company,
phone: normalized.phone
};
};
const mapCheckoutAddressToMedusaCustomerAddress = (address, options)=>({
...mapCheckoutAddressToMedusaCartAddress(address, options),
is_default_shipping: address.isDefaultShipping,
is_default_billing: address.isDefaultBilling,
metadata: address.metadata ?? void 0
});
const checkoutAddressPatchStringFieldMap = [
[
"firstName",
"first_name"
],
[
"lastName",
"last_name"
],
[
"street",
"address_1"
],
[
"street2",
"address_2"
],
[
"city",
"city"
],
[
"postalCode",
"postal_code"
],
[
"province",
"province"
],
[
"company",
"company"
],
[
"phone",
"phone"
]
];
const mapCheckoutAddressPatchToMedusaCustomerAddress = (address, options)=>{
const normalized = normalizeCheckoutAddressPatch(address);
const payload = {};
for (const [sourceField, targetField] of checkoutAddressPatchStringFieldMap){
if (!Object.hasOwn(normalized, sourceField)) continue;
const value = normalized[sourceField];
payload[targetField] = "string" == typeof value ? value : "";
}
if (Object.hasOwn(normalized, "country")) payload.country_code = normalizePatchCountryCode(normalized.country, options);
if (Object.hasOwn(normalized, "isDefaultShipping")) payload.is_default_shipping = address.isDefaultShipping;
if (Object.hasOwn(normalized, "isDefaultBilling")) payload.is_default_billing = address.isDefaultBilling;
if (Object.hasOwn(normalized, "metadata")) payload.metadata = address.metadata ?? void 0;
return payload;
};
const mapMedusaAddressToCheckoutAddress = (address)=>({
firstName: normalizeTrimmedString(address?.first_name),
lastName: normalizeTrimmedString(address?.last_name),
street: normalizeTrimmedString(address?.address_1),
street2: normalizeTrimmedString(address?.address_2),
city: normalizeTrimmedString(address?.city),
postalCode: normalizeTrimmedString(address?.postal_code),
country: normalizeTrimmedString(address?.country_code),
province: normalizeTrimmedString(address?.province),
company: normalizeTrimmedString(address?.company),
phone: normalizeTrimmedString(address?.phone),
isDefaultShipping: "boolean" == typeof address?.is_default_shipping ? address.is_default_shipping : void 0,
isDefaultBilling: "boolean" == typeof address?.is_default_billing ? address.is_default_billing : void 0,
metadata: address?.metadata ?? void 0
});
const createCheckoutCartAddressAdapter = (options)=>({
normalize: (input)=>normalizeCheckoutAddressInput(input),
validate: (input, context)=>getCheckoutAddressFieldIssues(input, {
scope: context.scope,
requiredFields: "shipping" === context.scope ? options?.shippingRequiredFields : options?.billingRequiredFields
}),
toPayload: (input)=>mapCheckoutAddressToMedusaCartAddress(input, options)
});
const createCheckoutCustomerAddressAdapter = (options)=>({
normalizeCreate: (input)=>normalizeCheckoutAddressInput(input),
validateCreate: (input)=>getCheckoutAddressFieldIssues(input, {
scope: "customer",
requiredFields: options?.requiredFields
}),
toCreateParams: (input)=>mapCheckoutAddressToMedusaCustomerAddress(input, options),
normalizeUpdate: (input)=>normalizeCheckoutAddressPatch(input),
validateUpdate: (input)=>getCheckoutAddressPatchFieldIssues(input, {
scope: "customer",
requiredFields: options?.requiredFields
}),
toUpdateParams: (input)=>{
const { addressId: _addressId, ...rest } = input;
return mapCheckoutAddressPatchToMedusaCustomerAddress(rest, options);
}
});
const buildCheckoutCartAddressInput = (data, options)=>{
const useSameAddress = false !== data.useSameAddress;
const shippingAddress = mapCheckoutAddressToMedusaCartAddress(data.shipping, options);
const billingAddress = false === data.useSameAddress ? mapCheckoutAddressToMedusaCartAddress(data.billing, options) : shippingAddress;
return {
email: normalizeTrimmedString(data.email),
shippingAddress,
billingAddress,
useSameAddress
};
};
export { buildCheckoutCartAddressInput, createCheckoutCartAddressAdapter, createCheckoutCustomerAddressAdapter, defaultCheckoutAddressRequiredFields, getCheckoutAddressValidationIssues, mapCheckoutAddressToMedusaCartAddress, mapMedusaAddressToCheckoutAddress };