{"version":3,"sources":["../../src/query/server.ts"],"sourcesContent":["/**\n * Farm.js Server-Side Query State Management\n *\n * Server-side utilities for parsing and handling URL search parameters\n */\n\nimport type { Parser } from \"./parsers\";\nexport { parseRouteParams, loadRouteParams, type RouteParamsInput } from \"./params\";\n\nexport {\n  asString,\n  asInteger,\n  asFloat,\n  asBoolean,\n  asArrayOf,\n  asJson,\n  asIsoDate,\n  asIsoDateTime,\n  createParser,\n  type ArrayParserOptions,\n  type Parser,\n  type inferParserType,\n} from \"./parsers\";\n\nexport type SearchParams = Record<string, string | string[] | undefined>;\n\n/**\n * Server-side utility to load and parse search parameters\n *\n * @example\n * ```tsx\n * // In a server component\n * import { loadSearchParams, parseAsString, parseAsInteger } from 'farm/query/server';\n *\n * export default async function MyPage({ searchParams }: PageProps) {\n *   const params = await loadSearchParams(searchParams, {\n *     search: parseAsString.withDefault!(''),\n *     page: parseAsInteger.withDefault!(1),\n *   });\n *\n *   return <div>Search: {params.search}, Page: {params.page}</div>;\n * }\n * ```\n */\nexport async function loadSearchParams<T extends Record<string, Parser<any>>>(\n  searchParams: Promise<URLSearchParams | Record<string, string | string[] | undefined>>,\n  parsers: T,\n): Promise<{ [K in keyof T]: ReturnType<T[K][\"parse\"]> }> {\n  const params = await searchParams;\n\n  let urlParams: URLSearchParams;\n  if (params instanceof URLSearchParams) {\n    urlParams = params;\n  } else {\n    urlParams = new URLSearchParams();\n    for (const [key, value] of Object.entries(params)) {\n      if (Array.isArray(value)) {\n        for (const v of value) {\n          urlParams.append(key, v);\n        }\n      } else if (value !== undefined && value !== null) {\n        urlParams.set(key, String(value));\n      }\n    }\n  }\n\n  const result = {} as { [K in keyof T]: ReturnType<T[K][\"parse\"]> };\n\n  for (const [key, parser] of Object.entries(parsers)) {\n    // Read every value, not just the first. A repeated key is joined with commas,\n    // the format `asArrayOf` parses and its `serialize` writes, so a repeated\n    // parameter round-trips. A single value is passed through untouched, so a\n    // value that already contains commas keeps its meaning.\n    const values = urlParams.getAll(key);\n    const value = values.length > 1 ? values.join(\",\") : (values[0] ?? null);\n\n    if (value !== null && value !== \"\") {\n      try {\n        result[key as keyof T] = parser.parse(value);\n      } catch (error) {\n        console.error(`Failed to parse parameter \"${key}\":`, error);\n        throw error;\n      }\n    } else {\n      result[key as keyof T] = parser.parse(\"\");\n    }\n  }\n\n  return result;\n}\n\n/**\n * Creates pagination metadata for server components.\n *\n * @param searchParams The search parameters promise.\n * @param options Configuration options for pagination.\n * @returns Pagination metadata.\n */\nexport async function createPaginationMeta(\n  searchParams: Promise<URLSearchParams | Record<string, string | string[] | undefined>>,\n  options: {\n    totalItems: number;\n    itemsPerPage?: number;\n    pageParam?: string;\n  },\n) {\n  const params = await searchParams;\n\n  let urlParams: URLSearchParams;\n  if (params instanceof URLSearchParams) {\n    urlParams = params;\n  } else {\n    urlParams = new URLSearchParams();\n    for (const [key, value] of Object.entries(params)) {\n      if (Array.isArray(value)) {\n        for (const v of value) {\n          urlParams.append(key, v);\n        }\n      } else if (value !== undefined && value !== null) {\n        urlParams.set(key, String(value));\n      }\n    }\n  }\n\n  const pageParam = options.pageParam || \"page\";\n  const itemsPerPage = options.itemsPerPage ?? 10;\n  assertPaginationInteger(options.totalItems, \"totalItems\", true);\n  assertPaginationInteger(itemsPerPage, \"itemsPerPage\", false);\n\n  const requestedPage = parsePositivePaginationInteger(urlParams.get(pageParam));\n  const currentPage =\n    requestedPage !== null && Number.isSafeInteger((requestedPage - 1) * itemsPerPage)\n      ? requestedPage\n      : 1;\n\n  const totalPages = Math.ceil(options.totalItems / itemsPerPage);\n  const offset = (currentPage - 1) * itemsPerPage;\n  const limit = itemsPerPage;\n\n  return {\n    currentPage,\n    itemsPerPage,\n    totalItems: options.totalItems,\n    totalPages,\n    offset,\n    limit,\n    hasNextPage: currentPage < totalPages,\n    hasPreviousPage: currentPage > 1,\n  };\n}\n\nfunction parsePositivePaginationInteger(value: string | null): number | null {\n  if (!value || !/^[+]?[0-9]+$/.test(value)) return null;\n  const parsed = Number(value);\n  return Number.isSafeInteger(parsed) && parsed >= 1 ? parsed : null;\n}\n\nfunction assertPaginationInteger(value: number, name: string, allowZero: boolean): void {\n  const minimum = allowZero ? 0 : 1;\n  if (!Number.isSafeInteger(value) || value < minimum) {\n    throw new RangeError(\n      `Pagination ${name} must be a safe integer greater than or equal to ${minimum}.`,\n    );\n  }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA4CA,eAAsB,iBACpB,cACA,SACwD;AACxD,QAAM,SAAS,MAAM;AAErB,MAAI;AACJ,MAAI,kBAAkB,iBAAiB;AACrC,gBAAY;AAAA,EACd,OAAO;AACL,gBAAY,IAAI,gBAAgB;AAChC,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,UAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,mBAAW,KAAK,OAAO;AACrB,oBAAU,OAAO,KAAK,CAAC;AAAA,QACzB;AAAA,MACF,WAAW,UAAU,UAAa,UAAU,MAAM;AAChD,kBAAU,IAAI,KAAK,OAAO,KAAK,CAAC;AAAA,MAClC;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAS,CAAC;AAEhB,aAAW,CAAC,KAAK,MAAM,KAAK,OAAO,QAAQ,OAAO,GAAG;AAKnD,UAAM,SAAS,UAAU,OAAO,GAAG;AACnC,UAAM,QAAQ,OAAO,SAAS,IAAI,OAAO,KAAK,GAAG,IAAK,OAAO,CAAC,KAAK;AAEnE,QAAI,UAAU,QAAQ,UAAU,IAAI;AAClC,UAAI;AACF,eAAO,GAAc,IAAI,OAAO,MAAM,KAAK;AAAA,MAC7C,SAAS,OAAO;AACd,gBAAQ,MAAM,8BAA8B,GAAG,MAAM,KAAK;AAC1D,cAAM;AAAA,MACR;AAAA,IACF,OAAO;AACL,aAAO,GAAc,IAAI,OAAO,MAAM,EAAE;AAAA,IAC1C;AAAA,EACF;AAEA,SAAO;AACT;AA7CsB;AAsDtB,eAAsB,qBACpB,cACA,SAKA;AACA,QAAM,SAAS,MAAM;AAErB,MAAI;AACJ,MAAI,kBAAkB,iBAAiB;AACrC,gBAAY;AAAA,EACd,OAAO;AACL,gBAAY,IAAI,gBAAgB;AAChC,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,UAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,mBAAW,KAAK,OAAO;AACrB,oBAAU,OAAO,KAAK,CAAC;AAAA,QACzB;AAAA,MACF,WAAW,UAAU,UAAa,UAAU,MAAM;AAChD,kBAAU,IAAI,KAAK,OAAO,KAAK,CAAC;AAAA,MAClC;AAAA,IACF;AAAA,EACF;AAEA,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,eAAe,QAAQ,gBAAgB;AAC7C,0BAAwB,QAAQ,YAAY,cAAc,IAAI;AAC9D,0BAAwB,cAAc,gBAAgB,KAAK;AAE3D,QAAM,gBAAgB,+BAA+B,UAAU,IAAI,SAAS,CAAC;AAC7E,QAAM,cACJ,kBAAkB,QAAQ,OAAO,eAAe,gBAAgB,KAAK,YAAY,IAC7E,gBACA;AAEN,QAAM,aAAa,KAAK,KAAK,QAAQ,aAAa,YAAY;AAC9D,QAAM,UAAU,cAAc,KAAK;AACnC,QAAM,QAAQ;AAEd,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,YAAY,QAAQ;AAAA,IACpB;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa,cAAc;AAAA,IAC3B,iBAAiB,cAAc;AAAA,EACjC;AACF;AAnDsB;AAqDtB,SAAS,+BAA+B,OAAqC;AAC3E,MAAI,CAAC,SAAS,CAAC,eAAe,KAAK,KAAK,EAAG,QAAO;AAClD,QAAM,SAAS,OAAO,KAAK;AAC3B,SAAO,OAAO,cAAc,MAAM,KAAK,UAAU,IAAI,SAAS;AAChE;AAJS;AAMT,SAAS,wBAAwB,OAAe,MAAc,WAA0B;AACtF,QAAM,UAAU,YAAY,IAAI;AAChC,MAAI,CAAC,OAAO,cAAc,KAAK,KAAK,QAAQ,SAAS;AACnD,UAAM,IAAI;AAAA,MACR,cAAc,IAAI,oDAAoD,OAAO;AAAA,IAC/E;AAAA,EACF;AACF;AAPS;","names":[]}