buildAndParseUrlQueryStrings

JavaScript
// Builds a query string, merging defaults with overrides and repeating arrays.
export function buildQueryString(params = {}, defaults = {}) {
  const search = new URLSearchParams();
  for (const [key, value] of Object.entries({ ...defaults, ...params })) {
    if (value === undefined || value === null) continue;
    for (const item of Array.isArray(value) ? value : [value]) {
      if (item !== undefined && item !== null) search.append(key, String(item));
    }
  }
  return search.toString();
}

// Parses full URLs, paths, or raw queries; repeated keys become arrays.
export function parseQueryString(input, defaults = {}) {
  const text = String(input);
  let raw;
  if (/^[a-z][a-z\d+.-]*:\/\//i.test(text)) {
    raw = new URL(text).search.slice(1);
  } else if (text.startsWith('/') && !text.includes('?')) {
    raw = '';
  } else {
    const mark = text.indexOf('?');
    raw = (mark === -1 ? text : text.slice(mark + 1)).split('#', 1)[0];
  }
  const params = new URLSearchParams(raw);
  const result = {};
  for (const key of new Set(params.keys())) {
    const values = params.getAll(key);
    result[key] = values.length === 1 ? values[0] : values;
  }
  return { ...defaults, ...result };
}