4dff30f676
Fixes #20109 The entry was repeating because in the database we store DateTime fields with microsecond precision (timestamptz), but when JS parses timestamptz into a Date object it only keeps millisecond precision. ### Example If previous cursor was: ``` { name: "Quick Lead", createdAt: "2026-05-21T15:33:00.708Z", } ``` The resulting query look something like: ``` ... WHERE ( "workflow"."name" > "Quick Lead" OR ( "workflow"."name" = "Quick Lead" AND "workflow"."createdAt" > "2026-05-21T15:33:00.708Z" ) OR ( "workflow"."name" = "Quick Lead" AND "workflow"."createdAt" = "2026-05-21T15:33:00.708Z" AND "workflow"."id" > "8b213cac-a68b-4ffe-817a-3ec994e9932d" ) ) ``` So, when comparing the 2nd condition the `"workflow"."createdAt" > "2026-05-21T15:33:00.708Z"` would always result to true because in db the data for createdAt is `2026-05-21 21:03:00.708 +0530` which will always be greater than `2026-05-21T15:33:00.708Z` The second condition `"workflow"."createdAt" > "2026-05-21T15:33:00.708Z"` always evaluates to true, because the value actually stored in the DB for createdAt is something like `2026-05-21 21:03:00.708264 +0530`, which is always greater than `2026-05-21T15:33:00.708Z` in the cursor. The row used to generate the cursor therefore reappears on the next page. ### My solution Truncate the column to milliseconds in the comparison so both sides have the same precision: `date_trunc('milliseconds', ${fieldReference})`. For the issue of nested sorting filters, when ordering by a composite field (e.g. `createdBy.name`), `encodeCursor` stored the entire composite object (`source`, `workspaceMemberId`, `name`, `context`). The where-condition builder later iterated those sub-keys and threw "Invalid cursor" because only name had an orderBy direction. P.S: Duplicate of #20867 because last fork got polluted. --------- Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com>