@rebasepro/client 0.14.0 → 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 CHANGED
@@ -1,5 +1,5 @@
1
1
  import { DEFAULT_STORAGE_SOURCE_KEY, EntityReference, EntityRelation, GeoPoint, PUBLIC_STORAGE_PREFIX, RebaseApiError, RebaseApiError as RebaseApiError$1, RebaseClientError, RebaseClientError as RebaseClientError$1, Vector, isPublicStoragePath, toCanonicalOp } from "@rebasepro/types";
2
- import { COMPOSITE_ID_SEPARATOR, QueryBuilder, RebasePaginationError, and, buildCompositeId, collectAllPages, cond, or, paginateFind, resolveFindWindow, serializeFilter, serializeLogicalCondition, serializeOrderBy } from "@rebasepro/common";
2
+ import { COMPOSITE_ID_SEPARATOR, QueryBuilder, RebasePaginationError, and, buildCompositeId, collectAllPages, cond, normalizeOrderBy, or, paginateFind, resolveFindWindow, serializeFilter, serializeLogicalCondition, serializeOrderBy } from "@rebasepro/common";
3
3
  import { toSnakeCase } from "@rebasepro/utils";
4
4
  //#region src/reviver.ts
5
5
  function rebaseReviver(_key, value) {
@@ -1277,9 +1277,15 @@ var SDKQueryBuilder = class {
1277
1277
  }
1278
1278
  /**
1279
1279
  * Order the results by a specific column.
1280
+ *
1281
+ * Call it again to add a tie-breaker rather than replace the sort: keys
1282
+ * apply in the order they were added, so
1283
+ * `.orderBy("roles").orderBy("created_at", "desc")` sorts by role and
1284
+ * shows the newest first within each one.
1280
1285
  */
1281
1286
  orderBy(column, direction = "asc") {
1282
- this.params.orderBy = [column, direction];
1287
+ const existing = normalizeOrderBy(this.params.orderBy) ?? [];
1288
+ this.params.orderBy = [...existing, [column, direction]];
1283
1289
  return this;
1284
1290
  }
1285
1291
  /**
@@ -1596,8 +1602,7 @@ function createCollectionClient(transport, slug, ws) {
1596
1602
  logical: params?.logical,
1597
1603
  limit: params?.limit,
1598
1604
  offset: window.driverOffset,
1599
- orderBy: params?.orderBy?.[0],
1600
- order: params?.orderBy?.[1],
1605
+ orderBy: normalizeOrderBy(params?.orderBy),
1601
1606
  searchString: params?.searchString,
1602
1607
  searchExplain: params?.searchExplain
1603
1608
  }, (incomingRows) => {
@@ -4099,27 +4104,43 @@ function matchesParams(row, params) {
4099
4104
  * Sort in place, Postgres-style: nulls last ascending, first descending, with
4100
4105
  * the row id as a tiebreak so paging through an unsorted-but-equal run does
4101
4106
  * not shuffle rows between pages.
4107
+ *
4108
+ * The tiebreak runs *descending*, which is not a taste: every server-side sort
4109
+ * ends on `id DESC` — `FetchService.buildOrderExpressions` appends it to make
4110
+ * the ordering total, and the keyset cursor is built to match. This ran
4111
+ * ascending, so two rows sharing a sort value came back from the local overlay
4112
+ * in the opposite order to the server's, and {@link isLocallySortable} called
4113
+ * that page exactly reproducible while it was not.
4102
4114
  */
4103
4115
  function sortRows(rows, orderBy) {
4104
- if (!orderBy) return rows;
4105
- const [field, direction = "asc"] = orderBy;
4106
- const sign = direction === "desc" ? -1 : 1;
4116
+ const keys = normalizeOrderBy(orderBy);
4117
+ if (!keys) return rows;
4107
4118
  return rows.sort((a, b) => {
4108
- const av = a[field];
4109
- const bv = b[field];
4110
- const aNull = isNullish(toComparable(av));
4111
- const bNull = isNullish(toComparable(bv));
4112
- if (aNull || bNull) {
4113
- if (aNull && bNull) return tiebreak(a, b);
4114
- return (aNull ? 1 : -1) * (direction === "desc" ? -1 : 1);
4119
+ for (const [field, direction = "asc"] of keys) {
4120
+ const cmp = compareOnKey(a, b, field, direction);
4121
+ if (cmp !== 0) return cmp;
4115
4122
  }
4116
- const cmp = compareValues(av, bv);
4117
- if (cmp === void 0 || cmp === 0) return tiebreak(a, b);
4118
- return cmp * sign;
4123
+ return tiebreak(a, b);
4119
4124
  });
4120
4125
  }
4126
+ /** One key's verdict: negative, positive, or 0 for "these two are equal here". */
4127
+ function compareOnKey(a, b, field, direction) {
4128
+ const av = a[field];
4129
+ const bv = b[field];
4130
+ const aNull = isNullish(toComparable(av));
4131
+ const bNull = isNullish(toComparable(bv));
4132
+ if (aNull || bNull) {
4133
+ if (aNull && bNull) return 0;
4134
+ return (aNull ? 1 : -1) * (direction === "desc" ? -1 : 1);
4135
+ }
4136
+ const cmp = compareValues(av, bv);
4137
+ if (cmp === void 0 || cmp === 0) return 0;
4138
+ return cmp * (direction === "desc" ? -1 : 1);
4139
+ }
4140
+ /** The last word, and the server's: `id DESC`. */
4121
4141
  function tiebreak(a, b) {
4122
- return compareValues(a.id, b.id) ?? 0;
4142
+ const cmp = compareValues(a.id, b.id);
4143
+ return cmp === void 0 ? 0 : -cmp;
4123
4144
  }
4124
4145
  /**
4125
4146
  * Resolve `page`/`offset`/`limit` the way the server does.
@@ -4214,17 +4235,16 @@ function isExactlyEvaluable(params) {
4214
4235
  * comparator.
4215
4236
  */
4216
4237
  function isLocallySortable(rows, orderBy) {
4217
- if (!orderBy) return true;
4218
- const [field] = orderBy;
4219
- for (const row of rows) {
4238
+ const keys = normalizeOrderBy(orderBy);
4239
+ if (!keys) return true;
4240
+ return keys.every(([field]) => rows.every((row) => {
4220
4241
  const value = toComparable(row[field]);
4221
- if (isNullish(value)) continue;
4222
- if (typeof value === "number" || typeof value === "boolean") continue;
4223
- if (typeof value === "bigint") continue;
4224
- if (typeof value === "string" && value.trim() !== "" && !Number.isNaN(Number(value))) continue;
4242
+ if (isNullish(value)) return true;
4243
+ if (typeof value === "number" || typeof value === "boolean") return true;
4244
+ if (typeof value === "bigint") return true;
4245
+ if (typeof value === "string" && value.trim() !== "" && !Number.isNaN(Number(value))) return true;
4225
4246
  return false;
4226
- }
4227
- return true;
4247
+ }));
4228
4248
  }
4229
4249
  /** Run a full query — filter, sort, paginate — over a set of rows. */
4230
4250
  function runLocalQuery(rows, params) {