{"version":3,"file":"cursor.cjs","sources":["../../../src/utils/cursor.ts"],"sourcesContent":["import { and, eq, gt, lt, or } from '../query/builder/functions.js'\nimport { Value } from '../query/ir.js'\nimport type { BasicExpression, OrderBy } from '../query/ir.js'\n\n/**\n * Builds a cursor expression for paginating through ordered results.\n * For multi-column orderBy, creates a composite cursor that respects all columns.\n *\n * For [col1 ASC, col2 DESC] with values [v1, v2], produces:\n *   or(\n *     gt(col1, v1),                         // col1 > v1\n *     and(eq(col1, v1), lt(col2, v2))       // col1 = v1 AND col2 < v2 (DESC)\n *   )\n *\n * This creates a precise cursor that works with composite indexes on the backend.\n *\n * @param orderBy - The order-by clauses defining sort columns and directions\n * @param values - The cursor values corresponding to each order-by column\n * @returns A filter expression for rows after the cursor position, or undefined if empty\n */\nexport function buildCursor(\n  orderBy: OrderBy,\n  values: Array<unknown>,\n): BasicExpression<boolean> | undefined {\n  if (values.length === 0 || orderBy.length === 0) {\n    return undefined\n  }\n\n  // For single column, just use simple gt/lt\n  if (orderBy.length === 1) {\n    const { expression, compareOptions } = orderBy[0]!\n    const operator = compareOptions.direction === `asc` ? gt : lt\n    return operator(expression, new Value(values[0]))\n  }\n\n  // For multi-column, build the composite cursor:\n  // or(\n  //   gt(col1, v1),\n  //   and(eq(col1, v1), gt(col2, v2)),\n  //   and(eq(col1, v1), eq(col2, v2), gt(col3, v3)),\n  //   ...\n  // )\n  const clauses: Array<BasicExpression<boolean>> = []\n\n  for (let i = 0; i < orderBy.length && i < values.length; i++) {\n    const clause = orderBy[i]!\n    const value = values[i]\n\n    // Build equality conditions for all previous columns\n    const eqConditions: Array<BasicExpression<boolean>> = []\n    for (let j = 0; j < i; j++) {\n      const prevClause = orderBy[j]!\n      const prevValue = values[j]\n      eqConditions.push(eq(prevClause.expression, new Value(prevValue)))\n    }\n\n    // Add the comparison for the current column (respecting direction)\n    const operator = clause.compareOptions.direction === `asc` ? gt : lt\n    const comparison = operator(clause.expression, new Value(value))\n\n    if (eqConditions.length === 0) {\n      // First column: just the comparison\n      clauses.push(comparison)\n    } else {\n      // Subsequent columns: and(eq(prev...), comparison)\n      // We need to spread into and() which expects at least 2 args\n      const allConditions = [...eqConditions, comparison]\n      clauses.push(allConditions.reduce((acc, cond) => and(acc, cond)))\n    }\n  }\n\n  // Combine all clauses with OR\n  if (clauses.length === 1) {\n    return clauses[0]!\n  }\n  // Use reduce to combine with or() which expects exactly 2 args\n  return clauses.reduce((acc, clause) => or(acc, clause))\n}\n"],"names":["gt","lt","Value","eq","and","or"],"mappings":";;;;AAoBO,SAAS,YACd,SACA,QACsC;AACtC,MAAI,OAAO,WAAW,KAAK,QAAQ,WAAW,GAAG;AAC/C,WAAO;AAAA,EACT;AAGA,MAAI,QAAQ,WAAW,GAAG;AACxB,UAAM,EAAE,YAAY,mBAAmB,QAAQ,CAAC;AAChD,UAAM,WAAW,eAAe,cAAc,QAAQA,UAAAA,KAAKC,UAAAA;AAC3D,WAAO,SAAS,YAAY,IAAIC,GAAAA,MAAM,OAAO,CAAC,CAAC,CAAC;AAAA,EAClD;AASA,QAAM,UAA2C,CAAA;AAEjD,WAAS,IAAI,GAAG,IAAI,QAAQ,UAAU,IAAI,OAAO,QAAQ,KAAK;AAC5D,UAAM,SAAS,QAAQ,CAAC;AACxB,UAAM,QAAQ,OAAO,CAAC;AAGtB,UAAM,eAAgD,CAAA;AACtD,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,YAAM,aAAa,QAAQ,CAAC;AAC5B,YAAM,YAAY,OAAO,CAAC;AAC1B,mBAAa,KAAKC,UAAAA,GAAG,WAAW,YAAY,IAAID,GAAAA,MAAM,SAAS,CAAC,CAAC;AAAA,IACnE;AAGA,UAAM,WAAW,OAAO,eAAe,cAAc,QAAQF,UAAAA,KAAKC,UAAAA;AAClE,UAAM,aAAa,SAAS,OAAO,YAAY,IAAIC,GAAAA,MAAM,KAAK,CAAC;AAE/D,QAAI,aAAa,WAAW,GAAG;AAE7B,cAAQ,KAAK,UAAU;AAAA,IACzB,OAAO;AAGL,YAAM,gBAAgB,CAAC,GAAG,cAAc,UAAU;AAClD,cAAQ,KAAK,cAAc,OAAO,CAAC,KAAK,SAASE,cAAI,KAAK,IAAI,CAAC,CAAC;AAAA,IAClE;AAAA,EACF;AAGA,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,QAAQ,CAAC;AAAA,EAClB;AAEA,SAAO,QAAQ,OAAO,CAAC,KAAK,WAAWC,aAAG,KAAK,MAAM,CAAC;AACxD;;"}