{"version":3,"file":"compare-DLCePYSR.cjs","names":["getPath","toAggregateNumber","value","Number","isFinite","trim","n","undefined","numbers","values","out","v","push","BUILT_INS","sum","reduce","a","b","avg","ns","length","count","min","Math","max","AGGREGATE_NAMES","Object","keys","resolveAggregateValue","row","key","column","sortValue","aggregate","spec","options","columns","format","byKey","Map","map","c","entries","rows","fn","aggregator","get","result","defaultLabels","table","search","searchPlaceholder","noData","noResults","pageSelected","count","selectAllMatching","total","allMatchingSelected","expandRow","collapseRow","operator","value","from","to","opEqual","opAtLeast","opAtMost","opBetween","opOn","opOnOrAfter","opOnOrBefore","opNotEqual","opGreater","opLess","opContains","opNotContains","opStartsWith","opEndsWith","opEmpty","opNotEmpty","opIn","opNotIn","opBefore","opAfter","opRelative","relToday","relYesterday","relTomorrow","relThisWeek","relThisMonth","relPreviousMonth","relLastN","relNextN","boolAny","boolTrue","boolFalse","savedViews","saveView","viewName","deleteView","renameView","applyView","moveViewUp","moveViewDown","setDefaultView","defaultViewBadge","readOnlyViewBadge","loading","loadMore","filters","clearAll","removeFilter","label","filtersDone","filterTree","filterAddCondition","filterAddGroup","filterCombinatorAnd","filterCombinatorOr","filterRemoveCondition","filterRemoveGroup","filterField","checklistSearch","checklistClear","checklistNoValues","headerFilters","sortBy","rowsPerPage","actions","selectAll","selectRow","selectColumn","cancel","retry","errorTitle","errorMessage","previousPage","nextPage","goToPage","page","selectedCount","showing","pageOf","columns","pinStart","pinEnd","unpin","moveStart","moveEnd","resetColumns","autoSizeColumns","autoSizeColumn","resizeColumn","showColumn","hideColumn","searchColumns","showAllColumns","hideAllColumns","unpinAllColumns","resetColumn","sortAscending","sortDescending","filterColumn","columnActions","exportCsv","exportFile","format","toUpperCase","exportDone","exportFailed","editCell","undoEdit","redoEdit","editRow","saveRow","pendingRows","String","saveAll","cancelAll","addRow","duplicateRow","deleteRow","deleteRowConfirm","rowActionsMenu","editConflict","keepMine","takeTheirs","theirsValue","reorderRow","moveRowUp","moveRowDown","rowLifted","position","rowMoved","rowReorderCancelled","pinToTop","pinToBottom","unpinRow","rowSeparator","expandColumnGroup","collapseColumnGroup","gridRangeCopied","cells","gridRangeCopyFailed","gridRangePasted","gridRangePasteFailed","gridRangeFilled","gridFillHandle","selectionCount","selectionSum","selectionAverage","selectionMin","selectionMax","editUndone","editRedone","editNothingToUndo","findInTable","findPlaceholder","findMatchCount","current","findPrevious","findNext","findClose","sidePanel","contextMenu","commandPalette","commandSearch","commandEmpty","print","density","densityComfortable","densityCompact","enterFullscreen","exitFullscreen","copyCells","cutCells","closePanel","pivotRows","pivotColumns","pivotMeasures","pivotAdd","pivotRemove","pivotMoveUp","pivotMoveDown","pivotAggregation","pivotTotal","pivotGrandTotal","gridCellPosition","row","gridRangeSelection","fromRow","toRow","fromColumn","toColumn","moreGroups","remaining","moreRowsInGroup","groupTotal","expandGroup","collapseGroup","groupCount","noticeVirtualizePaged","noticePinNested","noticeReorderNested","noticeGroupingUnavailable","noticeExportAllPage","noticeEditWithoutWriter","exportThisPage","resolveLabels","overrides","merged","key","Object","keys","undefined","sortsLast","value","undefined","Number","isNaN","compareValues","a","b","aLast","bLast","String","localeCompare","compareSortEntries","direction","index","cmp","compareSortLevel","sortedInsertIndex","items","compare","lo","hi","length","mid","sortRows","rows","getValue","map","row","sort","x","y","entry","sortRowsMulti","levels","values","l","key","i","level","entries","decided","dir"],"sources":["../src/aggregate/aggregate.ts","../src/labels.ts","../src/sort/compare.ts"],"sourcesContent":["/**\n * The aggregation library — the batteries for `summaryRow` and\n * `groupAggregates`.\n *\n * Both props take the same mapper: rows in, a record of cells out. Writing\n * that mapper by hand is fine for one total and tedious for five, and every\n * hand-rolled version re-solves the same edge cases — non-numeric values,\n * blanks, an empty group. {@link aggregate} builds the mapper from a\n * declaration instead:\n *\n * ```ts\n * summaryRow={aggregate({ budget: \"sum\", headcount: \"avg\" })}\n * ```\n *\n * The mapper API is untouched and still accepted anywhere: this returns one.\n *\n * Values are found the same way the table finds them elsewhere — the column's\n * `sortValue` if it has one, else the key's data path — so a formatted cell\n * (`accessor: r => money.format(r.budget)`) still aggregates on its number.\n */\nimport type { ReactNode } from \"react\";\n\nimport type { ColumnDef, SortableValue } from \"../types\";\nimport { getPath } from \"../utils/path\";\n\n/** The aggregate functions available by name. */\nexport type AggregateName = \"sum\" | \"avg\" | \"count\" | \"min\" | \"max\";\n\n/**\n * A custom aggregator: the values found for one column across the rows being\n * aggregated, already narrowed to those that are present.\n *\n * Return whatever the cell should show — a number, a formatted string, a\n * node. Return `undefined` for \"no cell here\".\n *\n * The return type is `ReactNode` so the built mapper is directly assignable\n * to `summaryRow` and `groupAggregates`, which is the whole point of it.\n */\nexport type Aggregator<TValue = SortableValue> = (\n  values: readonly TValue[]\n) => ReactNode;\n\n/** What to compute per column: a built-in name, or your own function. */\nexport type AggregateSpec = Partial<Record<string, AggregateName | Aggregator>>;\n\n/** Options for {@link aggregate}. */\nexport interface AggregateOptions<TRow> {\n  /**\n   * Columns, so values resolve through `sortValue` exactly as sorting and\n   * grouping do. Without them, values come from the key's data path.\n   */\n  columns?: readonly ColumnDef<TRow>[];\n  /**\n   * Format a computed value for display. Receives the raw result and the\n   * column key: `format: (v, key) => key === \"budget\" ? money.format(v) : v`.\n   */\n  format?: (value: ReactNode, key: string) => ReactNode;\n}\n\n/**\n * Coerce one cell to a finite number the way the built-in aggregators do.\n * Non-numeric values are absent, never zero — a missing budget is not a\n * $0 row.\n *\n * @param value - The resolved cell value.\n * @returns The number, or `undefined` when it is not summable.\n */\nexport function toAggregateNumber(value: SortableValue): number | undefined {\n  if (typeof value === \"number\" && Number.isFinite(value)) return value;\n  if (typeof value === \"string\" && value.trim() !== \"\") {\n    const n = Number(value);\n    if (Number.isFinite(n)) return n;\n  }\n  return undefined;\n}\n\n/** Numbers only — everything else is not summable, and silently skipped. */\nfunction numbers(values: readonly SortableValue[]): number[] {\n  const out: number[] = [];\n  for (const v of values) {\n    const n = toAggregateNumber(v);\n    if (n !== undefined) out.push(n);\n  }\n  return out;\n}\n\n/**\n * The built-ins.\n *\n * `count` counts rows that have a value, not rows in the group — a column\n * that is blank for half the group reports the half that is filled, which is\n * what a \"count\" cell under that column is asking about. `sum` of nothing is\n * `0`; `avg`, `min` and `max` of nothing are `undefined`, because an average\n * of no numbers is not zero, it is unanswerable.\n */\nconst BUILT_INS: Record<AggregateName, Aggregator> = {\n  sum: (values) => numbers(values).reduce((a, b) => a + b, 0),\n  avg: (values) => {\n    const ns = numbers(values);\n    return ns.length ? ns.reduce((a, b) => a + b, 0) / ns.length : undefined;\n  },\n  count: (values) => values.length,\n  min: (values) => {\n    const ns = numbers(values);\n    return ns.length ? Math.min(...ns) : undefined;\n  },\n  max: (values) => {\n    const ns = numbers(values);\n    return ns.length ? Math.max(...ns) : undefined;\n  },\n};\n\n/** Every built-in aggregate name, for a UI that offers a choice. */\nexport const AGGREGATE_NAMES = Object.keys(BUILT_INS) as AggregateName[];\n\n/**\n * Resolve one column's value from a row the way the rest of the table does.\n * Incremental aggregates use the same path so a patched total matches a\n * full `aggregate()` pass.\n *\n * @typeParam TRow - The row type.\n * @param row - The row to read.\n * @param key - The column key / data path.\n * @param column - The matching column, when the host passed one.\n */\nexport function resolveAggregateValue<TRow>(\n  row: TRow,\n  key: string,\n  column: ColumnDef<TRow> | undefined\n): SortableValue {\n  if (column?.sortValue) return column.sortValue(row);\n  return getPath(row, key) as SortableValue;\n}\n\n/**\n * Build a `summaryRow` / `groupAggregates` mapper from a declaration.\n *\n * @example\n * ```tsx\n * <DataTable\n *   summaryRow={aggregate({ budget: \"sum\", team: \"count\" }, { columns })}\n *   groupAggregates={aggregate({ budget: \"sum\" }, { columns })}\n * />\n * ```\n */\nexport function aggregate<TRow>(\n  spec: AggregateSpec,\n  options: AggregateOptions<TRow> = {}\n): (rows: readonly TRow[]) => Partial<Record<string, ReactNode>> {\n  const { columns, format } = options;\n  const byKey = new Map(columns?.map((c) => [c.key, c]));\n  const entries = Object.entries(spec);\n\n  return (rows) => {\n    const out: Partial<Record<string, ReactNode>> = {};\n    for (const [key, fn] of entries) {\n      if (!fn) continue;\n      const aggregator = typeof fn === \"string\" ? BUILT_INS[fn] : fn;\n      const values: SortableValue[] = [];\n      for (const row of rows) {\n        const value = resolveAggregateValue(row, key, byKey.get(key));\n        // A missing value is not a zero — skip it and let the aggregator see\n        // only what is really there.\n        if (value !== undefined && value !== null) values.push(value);\n      }\n      const result = aggregator(values);\n      out[key] = format ? format(result, key) : result;\n    }\n    return out;\n  };\n}\n","import type { TableLabels } from \"./types\";\n\n/**\n * English default strings. Consumers override any subset via the\n * `labels` option; {@link resolveLabels} merges their overrides on top.\n */\nexport const defaultLabels: Required<TableLabels> = {\n  table: \"Data table\",\n  search: \"Search\",\n  searchPlaceholder: \"Search…\",\n  noData: \"No data\",\n  noResults: \"No results match your filters\",\n  pageSelected: (count) => `All ${count} on this page selected`,\n  selectAllMatching: (total) => `Select all ${total} matching`,\n  allMatchingSelected: (total) => `All ${total} matching selected`,\n  expandRow: \"Expand row\",\n  collapseRow: \"Collapse row\",\n  operator: \"Operator\",\n  value: \"Value\",\n  from: \"From\",\n  to: \"To\",\n  opEqual: \"Equal\",\n  opAtLeast: \"At least\",\n  opAtMost: \"At most\",\n  opBetween: \"Between\",\n  opOn: \"On\",\n  opOnOrAfter: \"On or after\",\n  opOnOrBefore: \"On or before\",\n  opNotEqual: \"Not equal\",\n  opGreater: \"Greater than\",\n  opLess: \"Less than\",\n  opContains: \"Contains\",\n  opNotContains: \"Does not contain\",\n  opStartsWith: \"Starts with\",\n  opEndsWith: \"Ends with\",\n  opEmpty: \"Is empty\",\n  opNotEmpty: \"Is not empty\",\n  opIn: \"Is any of\",\n  opNotIn: \"Is none of\",\n  opBefore: \"Before\",\n  opAfter: \"After\",\n  opRelative: \"Relative\",\n  relToday: \"Today\",\n  relYesterday: \"Yesterday\",\n  relTomorrow: \"Tomorrow\",\n  relThisWeek: \"This week\",\n  relThisMonth: \"This month\",\n  relPreviousMonth: \"Previous month\",\n  relLastN: \"Last N days\",\n  relNextN: \"Next N days\",\n  boolAny: \"Any\",\n  boolTrue: \"True\",\n  boolFalse: \"False\",\n  savedViews: \"Saved views\",\n  saveView: \"Save view\",\n  viewName: \"View name\",\n  deleteView: \"Delete view\",\n  renameView: \"Rename view\",\n  applyView: \"Apply view\",\n  moveViewUp: \"Move view up\",\n  moveViewDown: \"Move view down\",\n  setDefaultView: \"Set as default\",\n  defaultViewBadge: \"Default\",\n  readOnlyViewBadge: \"Read-only\",\n  loading: \"Loading…\",\n  loadMore: \"Load more\",\n  filters: \"Filters\",\n  clearAll: \"Clear all\",\n  removeFilter: (label) => `Remove filter: ${label}`,\n  filtersDone: \"Done\",\n  filterTree: \"Advanced\",\n  filterAddCondition: \"Add condition\",\n  filterAddGroup: \"Add group\",\n  filterCombinatorAnd: \"AND\",\n  filterCombinatorOr: \"OR\",\n  filterRemoveCondition: \"Remove condition\",\n  filterRemoveGroup: \"Remove group\",\n  filterField: \"Field\",\n  checklistSearch: \"Search values\",\n  checklistClear: \"Clear\",\n  checklistNoValues: \"No matching values\",\n  headerFilters: \"Column filters\",\n  sortBy: \"Sort by\",\n  rowsPerPage: \"Rows per page\",\n  actions: \"Actions\",\n  selectAll: \"Select all\",\n  selectRow: \"Select row\",\n  selectColumn: \"Select column\",\n  cancel: \"Cancel\",\n  retry: \"Retry\",\n  errorTitle: \"Something went wrong\",\n  errorMessage: \"We couldn't load this data.\",\n  previousPage: \"Previous page\",\n  nextPage: \"Next page\",\n  goToPage: (page) => `Go to page ${page}`,\n  selectedCount: (count) => `${count} selected`,\n  showing: ({ from, to, total }) => `Showing ${from}–${to} of ${total}`,\n  pageOf: ({ page, total }) => `Page ${page} of ${total}`,\n  columns: \"Columns\",\n  pinStart: \"Pin to start\",\n  pinEnd: \"Pin to end\",\n  unpin: \"Unpin\",\n  moveStart: \"Move to start\",\n  moveEnd: \"Move to end\",\n  resetColumns: \"Reset columns\",\n  autoSizeColumns: \"Size columns to content\",\n  autoSizeColumn: \"Size column to content\",\n  resizeColumn: \"Resize column\",\n  showColumn: \"Show column\",\n  hideColumn: \"Hide column\",\n  searchColumns: \"Search columns\",\n  showAllColumns: \"Show all\",\n  hideAllColumns: \"Hide all\",\n  unpinAllColumns: \"Unpin all\",\n  resetColumn: \"Reset column\",\n  sortAscending: \"Sort ascending\",\n  sortDescending: \"Sort descending\",\n  filterColumn: \"Filter column\",\n  columnActions: \"Column actions\",\n  exportCsv: \"Export CSV\",\n  exportFile: (format) => `Export ${format.toUpperCase()}`,\n  exportDone: \"Export complete\",\n  exportFailed: \"Export failed\",\n  editCell: \"Edit cell\",\n  undoEdit: \"Undo\",\n  redoEdit: \"Redo\",\n  editRow: \"Edit row\",\n  saveRow: \"Save row\",\n  pendingRows: (count) =>\n    count === 1 ? \"1 unsaved row\" : `${String(count)} unsaved rows`,\n  saveAll: \"Save all\",\n  cancelAll: \"Cancel all\",\n  addRow: \"Add row\",\n  duplicateRow: \"Duplicate row\",\n  deleteRow: \"Delete row\",\n  deleteRowConfirm: \"Delete this row? This cannot be undone.\",\n  rowActionsMenu: \"Row actions\",\n  editConflict: \"This row changed while you were editing\",\n  keepMine: \"Keep mine\",\n  takeTheirs: \"Take theirs\",\n  theirsValue: (value) => `Theirs: ${value}`,\n  reorderRow: \"Reorder row\",\n  moveRowUp: \"Move row up\",\n  moveRowDown: \"Move row down\",\n  rowLifted: (position) => `Row ${String(position)} lifted`,\n  rowMoved: (from, to) => `Row moved from ${String(from)} to ${String(to)}`,\n  rowReorderCancelled: \"Reorder cancelled\",\n  pinToTop: \"Pin to top\",\n  pinToBottom: \"Pin to bottom\",\n  unpinRow: \"Unpin row\",\n  rowSeparator: \"Separator\",\n  expandColumnGroup: \"Expand column group\",\n  collapseColumnGroup: \"Collapse column group\",\n  gridRangeCopied: (cells) =>\n    `${cells} ${cells === 1 ? \"cell\" : \"cells\"} copied`,\n  gridRangeCopyFailed: \"Copy failed\",\n  gridRangePasted: (cells) =>\n    `${cells} ${cells === 1 ? \"cell\" : \"cells\"} pasted`,\n  gridRangePasteFailed: \"Paste failed\",\n  gridRangeFilled: (cells) =>\n    `${cells} ${cells === 1 ? \"cell\" : \"cells\"} filled`,\n  gridFillHandle: \"Fill from selection\",\n  selectionCount: \"Count\",\n  selectionSum: \"Sum\",\n  selectionAverage: \"Avg\",\n  selectionMin: \"Min\",\n  selectionMax: \"Max\",\n  editUndone: (cells) => `${cells} ${cells === 1 ? \"cell\" : \"cells\"} restored`,\n  editRedone: (cells) => `${cells} ${cells === 1 ? \"cell\" : \"cells\"} redone`,\n  editNothingToUndo: \"Nothing to undo\",\n  findInTable: \"Find in table\",\n  findPlaceholder: \"Find in table\",\n  findMatchCount: (current, total) =>\n    total === 0 ? \"No matches\" : `${current} of ${total}`,\n  findPrevious: \"Previous match\",\n  findNext: \"Next match\",\n  findClose: \"Close find\",\n  sidePanel: \"Table settings\",\n  contextMenu: \"Table actions\",\n  commandPalette: \"Command palette\",\n  commandSearch: \"Search commands\",\n  commandEmpty: \"No matching command\",\n  print: \"Print\",\n  density: \"Density\",\n  densityComfortable: \"Comfortable\",\n  densityCompact: \"Compact\",\n  enterFullscreen: \"Enter fullscreen\",\n  exitFullscreen: \"Exit fullscreen\",\n  copyCells: \"Copy\",\n  cutCells: \"Cut\",\n  closePanel: \"Close panel\",\n  pivotRows: \"Rows\",\n  pivotColumns: \"Columns\",\n  pivotMeasures: \"Measures\",\n  pivotAdd: \"Add field\",\n  pivotRemove: \"Remove field\",\n  pivotMoveUp: \"Move up\",\n  pivotMoveDown: \"Move down\",\n  pivotAggregation: \"Aggregation\",\n  pivotTotal: \"Total\",\n  pivotGrandTotal: \"Grand total\",\n  gridCellPosition: (row, total) => `row ${row} of ${total}`,\n  gridRangeSelection: ({ fromRow, toRow, fromColumn, toColumn, cells }) =>\n    `selected rows ${fromRow} to ${toRow}, columns ${fromColumn} to ${toColumn}, ${cells} cells`,\n  moreGroups: (remaining) => `Show ${remaining} more groups`,\n  moreRowsInGroup: (remaining) => `Show ${remaining} more in this group`,\n  groupTotal: (label) => `${label} total`,\n  expandGroup: \"Expand group\",\n  collapseGroup: \"Collapse group\",\n  groupCount: (count) => `(${count})`,\n  noticeVirtualizePaged:\n    \"Virtualization is off — this paged table shows one page at a time.\",\n  noticePinNested: \"Row pinning is off while grouping or a tree is on.\",\n  noticeReorderNested: \"Row reorder is off while grouping or a tree is on.\",\n  noticeGroupingUnavailable:\n    \"Grouping is off — this source does not provide the full filtered set.\",\n  noticeExportAllPage:\n    \"Export all is this page — the full filtered set is not available.\",\n  noticeEditWithoutWriter: \"Editing is off — no write handler is wired.\",\n  exportThisPage: \"Export this page\",\n};\n\n/**\n * Merge caller overrides over {@link defaultLabels}. Undefined entries in\n * the override are ignored, so partial `labels` objects are safe.\n *\n * @param overrides - A partial set of label overrides.\n * @returns A fully-populated, immutable label set.\n */\nexport function resolveLabels(\n  overrides: TableLabels | undefined\n): Required<TableLabels> {\n  if (!overrides) return defaultLabels;\n  const merged = { ...defaultLabels };\n  for (const key of Object.keys(overrides) as (keyof TableLabels)[]) {\n    const value = overrides[key];\n    if (value !== undefined) {\n      // Each key's value type matches the same key in the target.\n      (merged[key] as unknown) = value;\n    }\n  }\n  return merged;\n}\n","import type { SortableValue, SortDirection } from \"../types\";\n\n/** `null` / `undefined` / `NaN` are unorderable and always sort last. */\nfunction sortsLast(value: SortableValue): boolean {\n  return (\n    value === null ||\n    value === undefined ||\n    (typeof value === \"number\" && Number.isNaN(value))\n  );\n}\n\n/**\n * Compare two sortable primitives for ascending order. `null` / `undefined` /\n * `NaN` sort last. Numbers compare numerically; everything else compares via\n * locale-aware string comparison.\n *\n * @returns Negative if `a < b`, positive if `a > b`, `0` if equal.\n */\nexport function compareValues(a: SortableValue, b: SortableValue): number {\n  // All unorderable values are EQUAL to each other (null vs undefined vs\n  // NaN), keeping the comparator symmetric — `a === b` alone would let a\n  // NaN pair fall through to the one-sided branches below.\n  const aLast = sortsLast(a);\n  const bLast = sortsLast(b);\n  if (aLast || bLast) {\n    if (aLast && bLast) return 0;\n    return aLast ? 1 : -1;\n  }\n  if (a === b) return 0;\n  if (typeof a === \"number\" && typeof b === \"number\") return a - b;\n  if (typeof a === \"boolean\" && typeof b === \"boolean\") {\n    return Number(a) - Number(b);\n  }\n  return String(a).localeCompare(String(b));\n}\n\n/**\n * Compare two already-extracted sort keys the way {@link sortRows} does,\n * including \"nulls last\" regardless of direction and a stable index\n * tie-break. Incremental repositioning uses this so a patched row lands\n * where a full re-sort would have put it.\n *\n * @param a - The first key and its index in the unsorted (filtered) list.\n * @param b - The second key and its index.\n * @param direction - Sort direction.\n * @returns Negative if `a` belongs before `b`.\n */\nexport function compareSortEntries(\n  a: { value: SortableValue; index: number },\n  b: { value: SortableValue; index: number },\n  direction: SortDirection\n): number {\n  const aLast = sortsLast(a.value);\n  const bLast = sortsLast(b.value);\n  if (aLast || bLast) {\n    if (aLast && bLast) return a.index - b.index;\n    return aLast ? 1 : -1;\n  }\n  const cmp = compareValues(a.value, b.value);\n  if (cmp === 0) return a.index - b.index;\n  return direction === \"asc\" ? cmp : -cmp;\n}\n\n/**\n * Compare one multi-sort level. `undefined` means a tie — including two\n * unorderable values — so the caller falls through to the next level.\n *\n * @param a - The first key.\n * @param b - The second key.\n * @param direction - Sort direction for this level.\n * @returns The ordering, or `undefined` when this level does not decide.\n */\nexport function compareSortLevel(\n  a: SortableValue,\n  b: SortableValue,\n  direction: SortDirection\n): number | undefined {\n  const cmp = compareValues(a, b);\n  if (cmp === 0) return undefined;\n  if (sortsLast(a) || sortsLast(b)) return cmp;\n  return direction === \"asc\" ? cmp : -cmp;\n}\n\n/**\n * First index in a sorted list where `compare(item)` is positive — the\n * insertion point that keeps the list ordered the way {@link sortRows} would.\n *\n * @typeParam T - The item type.\n * @param items - A list already in comparator order.\n * @param compare - Negative/zero when `item` belongs at or before the target.\n * @returns The index to splice at.\n */\nexport function sortedInsertIndex<T>(\n  items: readonly T[],\n  compare: (item: T) => number\n): number {\n  let lo = 0;\n  let hi = items.length;\n  while (lo < hi) {\n    const mid = (lo + hi) >> 1;\n    if (compare(items[mid]!) <= 0) lo = mid + 1;\n    else hi = mid;\n  }\n  return lo;\n}\n\n/**\n * Return a new array sorted by the given value extractor and direction.\n * The sort is stable (input order is preserved for equal keys). The input\n * array is not mutated.\n *\n * @typeParam TRow - The row type.\n * @param rows - The rows to sort.\n * @param getValue - Extracts the comparison key for a row.\n * @param direction - Sort direction.\n * @returns A new, sorted array.\n */\nexport function sortRows<TRow>(\n  rows: readonly TRow[],\n  getValue: (row: TRow) => SortableValue,\n  direction: SortDirection\n): TRow[] {\n  return [...rows]\n    .map((row, index) => ({ row, index, value: getValue(row) }))\n    .sort((x, y) => compareSortEntries(x, y, direction))\n    .map((entry) => entry.row);\n}\n\n/** One level of a multi-column sort. */\nexport interface SortLevel {\n  key: string;\n  dir: SortDirection;\n}\n\n/**\n * Sort rows by a CHAIN of levels: ties at level N fall through to level\n * N+1. Null-ish values sort last per level regardless of direction, same\n * as {@link sortRows}.\n */\nexport function sortRowsMulti<TRow>(\n  rows: readonly TRow[],\n  levels: readonly SortLevel[],\n  getValue: (row: TRow, key: string) => SortableValue\n): TRow[] {\n  if (levels.length === 0) return [...rows];\n  return [...rows]\n    .map((row, index) => ({\n      row,\n      index,\n      values: levels.map((l) => getValue(row, l.key)),\n    }))\n    .sort((x, y) => {\n      for (const [i, level] of levels.entries()) {\n        const decided = compareSortLevel(x.values[i], y.values[i], level.dir);\n        if (decided !== undefined) return decided;\n      }\n      // Stable: preserve the original order for full ties.\n      return x.index - y.index;\n    })\n    .map((entry) => entry.row);\n}\n"],"mappings":"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"}