@rebasepro/client 0.14.1-canary.g7e666eb → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.es.js +10 -1
- package/dist/index.es.js.map +1 -1
- package/dist/offline-query.d.ts +7 -0
- package/package.json +4 -4
- package/src/offline-query.test.ts +20 -3
- package/src/offline-query.ts +9 -1
package/dist/offline-query.d.ts
CHANGED
|
@@ -38,6 +38,13 @@ export declare function matchesParams(row: Record<string, unknown>, params?: Fin
|
|
|
38
38
|
* Sort in place, Postgres-style: nulls last ascending, first descending, with
|
|
39
39
|
* the row id as a tiebreak so paging through an unsorted-but-equal run does
|
|
40
40
|
* not shuffle rows between pages.
|
|
41
|
+
*
|
|
42
|
+
* The tiebreak runs *descending*, which is not a taste: every server-side sort
|
|
43
|
+
* ends on `id DESC` — `FetchService.buildOrderExpressions` appends it to make
|
|
44
|
+
* the ordering total, and the keyset cursor is built to match. This ran
|
|
45
|
+
* ascending, so two rows sharing a sort value came back from the local overlay
|
|
46
|
+
* in the opposite order to the server's, and {@link isLocallySortable} called
|
|
47
|
+
* that page exactly reproducible while it was not.
|
|
41
48
|
*/
|
|
42
49
|
export declare function sortRows<M extends Record<string, unknown>>(rows: M[], orderBy?: OrderBySpec): M[];
|
|
43
50
|
/**
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/client",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.14.1
|
|
4
|
+
"version": "0.14.1",
|
|
5
5
|
"description": "HTTP SDK client for the Rebase custom backend",
|
|
6
6
|
"funding": {
|
|
7
7
|
"url": "https://github.com/sponsors/rebaseco"
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
"./package.json": "./package.json"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@rebasepro/
|
|
33
|
-
"@rebasepro/
|
|
34
|
-
"@rebasepro/types": "0.14.1
|
|
32
|
+
"@rebasepro/common": "0.14.1",
|
|
33
|
+
"@rebasepro/utils": "0.14.1",
|
|
34
|
+
"@rebasepro/types": "0.14.1"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@jest/globals": "^30.4.1",
|
|
@@ -158,9 +158,26 @@ describe("local query engine", () => {
|
|
|
158
158
|
expect(sortRows([...rows], ["n", "desc"]).map((r) => r.id)).toEqual([2, 1, 3]);
|
|
159
159
|
});
|
|
160
160
|
|
|
161
|
-
it("breaks ties on id
|
|
162
|
-
|
|
163
|
-
|
|
161
|
+
it("breaks ties on id descending, which is what the server orders by", () => {
|
|
162
|
+
// Not an arbitrary direction: `FetchService.buildOrderExpressions`
|
|
163
|
+
// appends `id DESC` to every sort it builds, and the keyset cursor
|
|
164
|
+
// is written to match it. Ascending here meant the overlay handed
|
|
165
|
+
// back tied rows in the opposite order to the network answer for
|
|
166
|
+
// the same query — while `isLocallySortable` reported the page as
|
|
167
|
+
// exactly reproducible.
|
|
168
|
+
const rows: Row[] = [{ id: "a", n: 1 }, { id: "c", n: 1 }, { id: "b", n: 1 }];
|
|
169
|
+
expect(sortRows(rows, ["n", "asc"]).map((r) => r.id)).toEqual(["c", "b", "a"]);
|
|
170
|
+
expect(sortRows(rows, ["n", "desc"]).map((r) => r.id)).toEqual(["c", "b", "a"]);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("lets a later key decide before the id tiebreak does", () => {
|
|
174
|
+
const rows: Row[] = [
|
|
175
|
+
{ id: 1, role: "admin", at: 2 },
|
|
176
|
+
{ id: 2, role: "admin", at: 1 },
|
|
177
|
+
{ id: 3, role: "user", at: 9 }
|
|
178
|
+
];
|
|
179
|
+
expect(sortRows(rows, [["role", "asc"], ["at", "desc"]]).map((r) => r.id))
|
|
180
|
+
.toEqual([1, 2, 3]);
|
|
164
181
|
});
|
|
165
182
|
|
|
166
183
|
it("orders dates chronologically, not lexicographically", () => {
|
package/src/offline-query.ts
CHANGED
|
@@ -307,6 +307,13 @@ export function matchesParams(row: Record<string, unknown>, params?: FindParams)
|
|
|
307
307
|
* Sort in place, Postgres-style: nulls last ascending, first descending, with
|
|
308
308
|
* the row id as a tiebreak so paging through an unsorted-but-equal run does
|
|
309
309
|
* not shuffle rows between pages.
|
|
310
|
+
*
|
|
311
|
+
* The tiebreak runs *descending*, which is not a taste: every server-side sort
|
|
312
|
+
* ends on `id DESC` — `FetchService.buildOrderExpressions` appends it to make
|
|
313
|
+
* the ordering total, and the keyset cursor is built to match. This ran
|
|
314
|
+
* ascending, so two rows sharing a sort value came back from the local overlay
|
|
315
|
+
* in the opposite order to the server's, and {@link isLocallySortable} called
|
|
316
|
+
* that page exactly reproducible while it was not.
|
|
310
317
|
*/
|
|
311
318
|
export function sortRows<M extends Record<string, unknown>>(rows: M[], orderBy?: OrderBySpec): M[] {
|
|
312
319
|
const keys = normalizeOrderBy(orderBy);
|
|
@@ -345,9 +352,10 @@ function compareOnKey(
|
|
|
345
352
|
return cmp * (direction === "desc" ? -1 : 1);
|
|
346
353
|
}
|
|
347
354
|
|
|
355
|
+
/** The last word, and the server's: `id DESC`. */
|
|
348
356
|
function tiebreak(a: Record<string, unknown>, b: Record<string, unknown>): number {
|
|
349
357
|
const cmp = compareValues(a.id, b.id);
|
|
350
|
-
return cmp
|
|
358
|
+
return cmp === undefined ? 0 : -cmp;
|
|
351
359
|
}
|
|
352
360
|
|
|
353
361
|
/**
|