All files / src/utils/address normalize.ts

42.1% Statements 8/19
67.44% Branches 29/43
16.66% Functions 1/6
47.05% Lines 8/17

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151                                                                        779x 561x                     218x                       218x       218x             218x       218x           218x                                                                                                                                          
import { AddressType } from "@validation/schemas/address";
 
/**
 * Normalizes address data from ANY shape into the canonical AddressType type.
 *
 * This function handles all the chaos in the codebase:
 * - Nested vs flat structures
 * - line1 vs street vs address field names
 * - businessAddress vs address parent names
 * - Mixed structures with data in multiple places
 *
 * @param input - Any object that might contain address data
 * @returns Normalized Address object matching the canonical schema
 *
 * @example
 * // Handles nested line1
 * normalizeAddress({ address: { line1: "123 Main", city: "NYC" } })
 *
 * @example
 * // Handles flat line1
 * normalizeAddress({ line1: "123 Main", city: "NYC" })
 *
 * @example
 * // Handles street instead of line1
 * normalizeAddress({ address: { street: "123 Main", city: "NYC" } })
 *
 * @example
 * // Handles businessAddress
 * normalizeAddress({ businessAddress: { line1: "123 Main", city: "NYC" } })
 *
 * @example
 * // Handles TBusinessAddressInfo shape (address field contains street string)
 * normalizeAddress({ address: "123 Main", city: "NYC" })
 */
export function normalizeAddress(input: any): AddressType {
  // Handle null/undefined input
  if (!input) {
    return {
      line1: "",
      city: "",
      state: "",
      zip: "",
      country: "US",
    };
  }
 
  // Extract line1 (street address) from all possible locations
  const line1 =
    input?.address?.line1 || // Nested line1 (most common)
    input?.address?.street || // Nested street
    input?.businessAddress?.line1 || // businessAddress.line1
    input?.businessAddress?.street || // businessAddress.street
    input?.line1 || // Flat line1
    input?.street || // Flat street
    // Special case: TBusinessAddressInfo uses "address" as the street field
    (typeof input?.address === "string" ? input.address : "") ||
    "";
 
  // Extract city from all possible locations
  const city =
    input?.address?.city || input?.businessAddress?.city || input?.city || "";
 
  // Extract state from all possible locations
  const state =
    input?.address?.state ||
    input?.businessAddress?.state ||
    input?.state ||
    "";
 
  // Extract zip from all possible locations
  const zip =
    input?.address?.zip || input?.businessAddress?.zip || input?.zip || "";
 
  // Extract country from all possible locations
  const country =
    input?.address?.country ||
    input?.businessAddress?.country ||
    input?.country ||
    input?.countryOfResidence ||
    "US";
 
  return {
    line1,
    city,
    state,
    zip,
    country,
  };
}
 
/**
 * Normalizes address data and wraps it in the canonical structure.
 * Use this when you need the full form shape with nested address.
 *
 * @param input - Any object that might contain address data
 * @returns Object with address key containing normalized Address
 *
 * @example
 * const formData = normalizeAddressField(apiResponse);
 * // Returns: { address: { line1, city, state, zip, country } }
 */
export function normalizeAddressField(input: any): { address: AddressType } {
  return {
    address: normalizeAddress(input),
  };
}
 
/**
 * Type guard to check if an object has address data
 */
export function hasAddressData(input: any): boolean {
  if (!input) return false;
 
  return !!(
    input?.address ||
    input?.businessAddress ||
    input?.line1 ||
    input?.street ||
    input?.city ||
    input?.state
  );
}
 
/**
 * Merges address data from multiple sources, with priority given to the first source
 *
 * @param sources - Array of objects that might contain address data (priority order)
 * @returns Normalized Address with data from the first available source for each field
 *
 * @example
 * // Use form data if available, fall back to existing data
 * const merged = mergeAddressData([formData, existingBusinessOwner, defaultValues]);
 */
export function mergeAddressData(...sources: any[]): AddressType {
  const normalized = sources.filter(hasAddressData).map(normalizeAddress);
 
  if (normalized.length === 0) {
    return normalizeAddress(null);
  }
 
  const getField = (field: keyof AddressType) =>
    normalized.find((addr) => addr[field])?.[field] || "";
  return {
    line1: getField("line1"),
    city: getField("city"),
    state: getField("state"),
    zip: getField("zip"),
    country: getField("country") || "US",
  };
}