UNPKG

@techsio/storefront-data

Version:

Medusa-first storefront data layer built on TanStack Query.

224 lines (223 loc) • 9.74 kB
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 };