@rebasepro/server-postgres 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/PostgresBackendDriver.d.ts +1 -1
- package/dist/PostgresBootstrapper.d.ts +19 -0
- package/dist/auth/services.d.ts +10 -0
- package/dist/{auth-users-columns-BfQHf9JE.js → auth-users-columns-C-FDnL_e.js} +245 -15
- package/dist/auth-users-columns-C-FDnL_e.js.map +1 -0
- package/dist/data_driver-ULAyJEi9.js.map +1 -1
- package/dist/{ensure-collection-policies-8vuu-n4r.js → ensure-collection-policies-DoHwhVf8.js} +3 -3
- package/dist/{ensure-collection-policies-8vuu-n4r.js.map → ensure-collection-policies-DoHwhVf8.js.map} +1 -1
- package/dist/{ensure-collection-tables-CbvaGuVn.js → ensure-collection-tables-DT2eq859.js} +45 -5
- package/dist/{ensure-collection-tables-CbvaGuVn.js.map → ensure-collection-tables-DT2eq859.js.map} +1 -1
- package/dist/index.es.js +543 -105
- package/dist/index.es.js.map +1 -1
- package/dist/{rls-enforcement-BJ_3wxwg.js → rls-enforcement-gUNDfm7l.js} +2 -2
- package/dist/{rls-enforcement-BJ_3wxwg.js.map → rls-enforcement-gUNDfm7l.js.map} +1 -1
- package/dist/schema/drizzle-ddl.d.ts +9 -0
- package/dist/services/FetchService.d.ts +81 -5
- package/dist/services/RelationService.d.ts +3 -3
- package/dist/services/channel-presence.d.ts +16 -1
- package/dist/services/dataService.d.ts +6 -4
- package/dist/services/realtimeService.d.ts +54 -10
- package/dist/src-DCdn3Val.js.map +1 -1
- package/dist/utils/drizzle-conditions.d.ts +25 -0
- package/dist/{websocket-C8ZqVBiV.js → websocket-D2jXv0Ds.js} +29 -2
- package/dist/websocket-D2jXv0Ds.js.map +1 -0
- package/package.json +6 -6
- package/src/PostgresBackendDriver.ts +7 -3
- package/src/PostgresBootstrapper.ts +105 -41
- package/src/auth/services.ts +26 -5
- package/src/schema/drizzle-ddl.ts +33 -0
- package/src/schema/ensure-collection-tables.test.ts +99 -0
- package/src/schema/ensure-collection-tables.ts +65 -3
- package/src/schema/generate-drizzle-schema-logic.ts +19 -1
- package/src/services/FetchService.ts +310 -63
- package/src/services/RelationService.ts +3 -3
- package/src/services/channel-history.ts +38 -6
- package/src/services/channel-presence.ts +31 -7
- package/src/services/dataService.ts +6 -4
- package/src/services/pg-notify-listener.ts +14 -0
- package/src/services/realtimeService.ts +161 -41
- package/src/utils/drizzle-conditions.ts +155 -5
- package/src/websocket.ts +44 -1
- package/dist/auth-users-columns-BfQHf9JE.js.map +0 -1
- package/dist/websocket-C8ZqVBiV.js.map +0 -1
|
@@ -152,7 +152,7 @@ export declare class PostgresBackendDriver implements DataDriver {
|
|
|
152
152
|
delete<M extends Record<string, unknown>>({ row, collection }: DeleteProps<M>): Promise<void>;
|
|
153
153
|
deleteAll(path: string): Promise<void>;
|
|
154
154
|
checkUniqueField(path: string, name: string, value: unknown, id?: string, collection?: CollectionConfig): Promise<boolean>;
|
|
155
|
-
count<M extends Record<string, unknown>>({ path, collection, filter, logical, searchString }: FetchCollectionProps<M>): Promise<number>;
|
|
155
|
+
count<M extends Record<string, unknown>>({ path, collection, filter, logical, searchString, vectorSearch }: FetchCollectionProps<M>): Promise<number>;
|
|
156
156
|
private getTargetDb;
|
|
157
157
|
executeSql(sqlText: string, options?: {
|
|
158
158
|
database?: string;
|
|
@@ -89,6 +89,25 @@ export interface PostgresDriverInternals {
|
|
|
89
89
|
* and this is the part that was wrong.
|
|
90
90
|
*/
|
|
91
91
|
export declare function resolveDriftCheckName(col: CollectionConfig, registeredTableNames: string[]): string;
|
|
92
|
+
/**
|
|
93
|
+
* Why the tables this backend serves are not in the database — the part of the
|
|
94
|
+
* drift warning that has to be true rather than merely plausible.
|
|
95
|
+
*
|
|
96
|
+
* The three answers need three different actions, and only the caller knows
|
|
97
|
+
* which one applies. This warning used to assert the first ("this runtime
|
|
98
|
+
* applies the collection schema at boot unless REBASE_MIGRATE_ON_BOOT=none")
|
|
99
|
+
* and then point at that variable and at driver-version skew. For an app whose
|
|
100
|
+
* boot path contained no provisioning step at all, every word of that was a
|
|
101
|
+
* dead end: nothing read the variable, and the driver was current. The advice
|
|
102
|
+
* cost an investigation, which is a strictly worse outcome than saying less.
|
|
103
|
+
*
|
|
104
|
+
* Exported for its own test: the surrounding check needs a live pool and a real
|
|
105
|
+
* database, and this is the part that was wrong.
|
|
106
|
+
*/
|
|
107
|
+
export declare function describeSchemaDriftCause(provisioning: {
|
|
108
|
+
attempted: boolean;
|
|
109
|
+
reason?: string;
|
|
110
|
+
} | undefined): string[];
|
|
92
111
|
/**
|
|
93
112
|
* Is this the local database `rebase init` scaffolds — i.e. the one case where
|
|
94
113
|
* "you are connected as a superuser" is not news?
|
package/dist/auth/services.d.ts
CHANGED
|
@@ -58,6 +58,16 @@ export declare class UserService implements UserRepository {
|
|
|
58
58
|
private withServerContext;
|
|
59
59
|
private mapRowToUser;
|
|
60
60
|
private mapPayload;
|
|
61
|
+
/**
|
|
62
|
+
* @see UserRepository.createUser — an email already in use is a 409.
|
|
63
|
+
*
|
|
64
|
+
* The route checks first and answers 409; this is the same answer for the
|
|
65
|
+
* requests that get past the check, which two clicks on a signup button
|
|
66
|
+
* are enough to produce. `PersistService` has mapped `23505` to a conflict
|
|
67
|
+
* for collection writes since the layer that holds the SQLSTATE was made
|
|
68
|
+
* responsible for saying whose fault a failure is; the auth writes never
|
|
69
|
+
* got the same treatment and reached the client as "Internal Server Error".
|
|
70
|
+
*/
|
|
61
71
|
createUser(data: CreateUserData): Promise<UserData>;
|
|
62
72
|
getUserById(id: string): Promise<UserData | null>;
|
|
63
73
|
getUserByEmail(email: string): Promise<UserData | null>;
|
|
@@ -2505,8 +2505,147 @@ function schemaOf(collection) {
|
|
|
2505
2505
|
}
|
|
2506
2506
|
function resolveColumnName(propName, collection) {
|
|
2507
2507
|
const prop = collection?.properties?.[propName];
|
|
2508
|
-
if (prop && "columnName" in prop && typeof prop.columnName === "string") return prop.columnName;
|
|
2509
|
-
return toSnakeCase(propName);
|
|
2508
|
+
if (prop && "columnName" in prop && typeof prop.columnName === "string") return quoteColumnIdentifier(prop.columnName);
|
|
2509
|
+
return quoteColumnIdentifier(toSnakeCase(propName));
|
|
2510
|
+
}
|
|
2511
|
+
/**
|
|
2512
|
+
* Every PostgreSQL keyword that cannot stand as a bare column reference.
|
|
2513
|
+
* Appendix C's two reserved categories — plain "reserved", and "reserved (can
|
|
2514
|
+
* be function or type name)" — since neither may name a column unquoted.
|
|
2515
|
+
*/
|
|
2516
|
+
var RESERVED_SQL_WORDS = /* @__PURE__ */ new Set([
|
|
2517
|
+
"all",
|
|
2518
|
+
"analyse",
|
|
2519
|
+
"analyze",
|
|
2520
|
+
"and",
|
|
2521
|
+
"any",
|
|
2522
|
+
"array",
|
|
2523
|
+
"as",
|
|
2524
|
+
"asc",
|
|
2525
|
+
"asymmetric",
|
|
2526
|
+
"authorization",
|
|
2527
|
+
"binary",
|
|
2528
|
+
"both",
|
|
2529
|
+
"case",
|
|
2530
|
+
"cast",
|
|
2531
|
+
"check",
|
|
2532
|
+
"collate",
|
|
2533
|
+
"collation",
|
|
2534
|
+
"column",
|
|
2535
|
+
"concurrently",
|
|
2536
|
+
"constraint",
|
|
2537
|
+
"create",
|
|
2538
|
+
"cross",
|
|
2539
|
+
"current_catalog",
|
|
2540
|
+
"current_date",
|
|
2541
|
+
"current_role",
|
|
2542
|
+
"current_schema",
|
|
2543
|
+
"current_time",
|
|
2544
|
+
"current_timestamp",
|
|
2545
|
+
"current_user",
|
|
2546
|
+
"default",
|
|
2547
|
+
"deferrable",
|
|
2548
|
+
"desc",
|
|
2549
|
+
"distinct",
|
|
2550
|
+
"do",
|
|
2551
|
+
"else",
|
|
2552
|
+
"end",
|
|
2553
|
+
"except",
|
|
2554
|
+
"false",
|
|
2555
|
+
"fetch",
|
|
2556
|
+
"for",
|
|
2557
|
+
"foreign",
|
|
2558
|
+
"freeze",
|
|
2559
|
+
"from",
|
|
2560
|
+
"full",
|
|
2561
|
+
"grant",
|
|
2562
|
+
"group",
|
|
2563
|
+
"having",
|
|
2564
|
+
"ilike",
|
|
2565
|
+
"in",
|
|
2566
|
+
"initially",
|
|
2567
|
+
"inner",
|
|
2568
|
+
"intersect",
|
|
2569
|
+
"into",
|
|
2570
|
+
"is",
|
|
2571
|
+
"isnull",
|
|
2572
|
+
"join",
|
|
2573
|
+
"lateral",
|
|
2574
|
+
"leading",
|
|
2575
|
+
"left",
|
|
2576
|
+
"like",
|
|
2577
|
+
"limit",
|
|
2578
|
+
"localtime",
|
|
2579
|
+
"localtimestamp",
|
|
2580
|
+
"natural",
|
|
2581
|
+
"not",
|
|
2582
|
+
"notnull",
|
|
2583
|
+
"null",
|
|
2584
|
+
"offset",
|
|
2585
|
+
"on",
|
|
2586
|
+
"only",
|
|
2587
|
+
"or",
|
|
2588
|
+
"order",
|
|
2589
|
+
"outer",
|
|
2590
|
+
"overlaps",
|
|
2591
|
+
"placing",
|
|
2592
|
+
"primary",
|
|
2593
|
+
"references",
|
|
2594
|
+
"returning",
|
|
2595
|
+
"right",
|
|
2596
|
+
"select",
|
|
2597
|
+
"session_user",
|
|
2598
|
+
"similar",
|
|
2599
|
+
"some",
|
|
2600
|
+
"symmetric",
|
|
2601
|
+
"system_user",
|
|
2602
|
+
"table",
|
|
2603
|
+
"tablesample",
|
|
2604
|
+
"then",
|
|
2605
|
+
"to",
|
|
2606
|
+
"trailing",
|
|
2607
|
+
"true",
|
|
2608
|
+
"union",
|
|
2609
|
+
"unique",
|
|
2610
|
+
"user",
|
|
2611
|
+
"using",
|
|
2612
|
+
"variadic",
|
|
2613
|
+
"verbose",
|
|
2614
|
+
"when",
|
|
2615
|
+
"where",
|
|
2616
|
+
"window",
|
|
2617
|
+
"with"
|
|
2618
|
+
]);
|
|
2619
|
+
/** An identifier Postgres reads back unchanged without quotes. */
|
|
2620
|
+
var BARE_IDENTIFIER = /^[a-z_][a-z0-9_$]*$/;
|
|
2621
|
+
/**
|
|
2622
|
+
* Quote a column reference when Postgres would not read the bare name as that
|
|
2623
|
+
* column — and only then.
|
|
2624
|
+
*
|
|
2625
|
+
* Three ways a bare name goes wrong, in ascending order of how long it takes to
|
|
2626
|
+
* notice:
|
|
2627
|
+
*
|
|
2628
|
+
* - **Case.** `columnName` is used verbatim, and `rebase schema introspect`
|
|
2629
|
+
* populates it from a live database, so a legacy `"createdAt"` column arrives
|
|
2630
|
+
* spelled exactly that way. Unquoted, Postgres folds it to `createdat` and
|
|
2631
|
+
* `CREATE POLICY` fails with "column does not exist" — the collection keeps
|
|
2632
|
+
* RLS enabled with no policy, which denies every row.
|
|
2633
|
+
* - **Syntax.** A column named `order` or `default` is a syntax error mid-clause.
|
|
2634
|
+
* - **Silent rebinding.** `user`, `current_user`, `session_user`, `current_date`
|
|
2635
|
+
* and friends are *valid bare expressions*, so the policy compiles, applies,
|
|
2636
|
+
* and is reported as a success — while comparing against the connected role
|
|
2637
|
+
* or the wall clock instead of the column. Under RLS every request runs as the
|
|
2638
|
+
* same `rebase_user` role, so `USING (user = rebase.uid())` is a constant: it
|
|
2639
|
+
* denies everything, and its negation admits everything.
|
|
2640
|
+
*
|
|
2641
|
+
* Only the names that need it are quoted, so an ordinary snake_case policy body
|
|
2642
|
+
* is emitted byte-for-byte as before. That keeps generated artifacts and the
|
|
2643
|
+
* policies already stored in shipped databases stable — this fix reaches the
|
|
2644
|
+
* clauses that were broken and no others.
|
|
2645
|
+
*/
|
|
2646
|
+
function quoteColumnIdentifier(name) {
|
|
2647
|
+
if (BARE_IDENTIFIER.test(name) && !RESERVED_SQL_WORDS.has(name)) return name;
|
|
2648
|
+
return `"${name.replace(/"/g, "\"\"")}"`;
|
|
2510
2649
|
}
|
|
2511
2650
|
function quoteLiteral(value) {
|
|
2512
2651
|
if (value === null) return "NULL";
|
|
@@ -3867,6 +4006,90 @@ var CollectionRegistry = class {
|
|
|
3867
4006
|
}
|
|
3868
4007
|
};
|
|
3869
4008
|
//#endregion
|
|
4009
|
+
//#region ../common/src/data/sort-dialect.ts
|
|
4010
|
+
/**
|
|
4011
|
+
* Sort-order wire codec.
|
|
4012
|
+
*
|
|
4013
|
+
* This is the ONLY module that knows about the colon-delimited wire format
|
|
4014
|
+
* (`"field:direction"`) used in HTTP query parameters, and about the JSON-array
|
|
4015
|
+
* form that carries a multi-column sort over the same parameter.
|
|
4016
|
+
* Everything else speaks {@link OrderByTuple} exclusively.
|
|
4017
|
+
*
|
|
4018
|
+
* Mirrors the filter architecture in `filter-dialect.ts`.
|
|
4019
|
+
*
|
|
4020
|
+
* @module
|
|
4021
|
+
*/
|
|
4022
|
+
/**
|
|
4023
|
+
* Collapse the one-key and many-key spellings of a sort into the list form.
|
|
4024
|
+
*
|
|
4025
|
+
* `["a", "desc"]` and `[["a", "desc"]]` mean the same thing and normalize to
|
|
4026
|
+
* the same value; the two are told apart by whether the first element is
|
|
4027
|
+
* itself an array, which no field name ever is.
|
|
4028
|
+
*
|
|
4029
|
+
* @returns The keys in order of significance, or `undefined` for no sort. An
|
|
4030
|
+
* empty list also returns `undefined` — "sort by nothing" is no sort, and
|
|
4031
|
+
* letting `[]` through would have every layer below re-deciding what it meant.
|
|
4032
|
+
*/
|
|
4033
|
+
function normalizeOrderBy(orderBy) {
|
|
4034
|
+
if (!orderBy || orderBy.length === 0) return void 0;
|
|
4035
|
+
const list = Array.isArray(orderBy[0]) ? orderBy : [orderBy];
|
|
4036
|
+
return list.length > 0 ? list : void 0;
|
|
4037
|
+
}
|
|
4038
|
+
/**
|
|
4039
|
+
* Collapse the driver-level `{orderBy, order}` pair into the list form.
|
|
4040
|
+
*
|
|
4041
|
+
* The driver contract spells a single-column sort as a field name plus a
|
|
4042
|
+
* separate direction, and a multi-column one as a list of tuples that leaves
|
|
4043
|
+
* `order` meaningless. Every driver reads both through here so neither
|
|
4044
|
+
* spelling has to be handled twice.
|
|
4045
|
+
*
|
|
4046
|
+
* An absent direction means ascending — the same thing a bare `?orderBy=name`
|
|
4047
|
+
* has always meant over HTTP. The Postgres driver used to read the same pair as
|
|
4048
|
+
* *descending* while Mongo read it as ascending, so one field name and no
|
|
4049
|
+
* direction described two different queries depending on which database was
|
|
4050
|
+
* underneath. Neither had a caller: every path in the workspace passes a
|
|
4051
|
+
* direction, which is why the disagreement went unnoticed rather than being
|
|
4052
|
+
* load-bearing.
|
|
4053
|
+
*/
|
|
4054
|
+
function normalizeDriverOrderBy(orderBy, order) {
|
|
4055
|
+
if (!orderBy) return void 0;
|
|
4056
|
+
if (typeof orderBy === "string") return [[orderBy, order === "desc" ? "desc" : "asc"]];
|
|
4057
|
+
return orderBy.length > 0 ? orderBy : void 0;
|
|
4058
|
+
}
|
|
4059
|
+
/** A sort whose *shape* is unusable, as opposed to one naming a field that does not exist. */
|
|
4060
|
+
var OrderBySpecError = class extends Error {
|
|
4061
|
+
code = "INVALID_ORDER_BY";
|
|
4062
|
+
constructor(detail) {
|
|
4063
|
+
super(`Invalid \`orderBy\`: ${detail}. Expected a field name, or a list of [field, direction] pairs like [["roles","asc"],["created_at","desc"]]`);
|
|
4064
|
+
this.name = "OrderBySpecError";
|
|
4065
|
+
}
|
|
4066
|
+
};
|
|
4067
|
+
/**
|
|
4068
|
+
* Validate an `orderBy` that arrived from outside this process — a WebSocket
|
|
4069
|
+
* subscribe frame, a driver call from untyped JavaScript — and return it in the
|
|
4070
|
+
* list form.
|
|
4071
|
+
*
|
|
4072
|
+
* Strict on purpose, in the same way the REST `parseOrderByParam` is: the
|
|
4073
|
+
* failure mode for a shape nobody checks is not a crash but a *silently
|
|
4074
|
+
* different query*. A malformed entry read as a field name resolves to no
|
|
4075
|
+
* column, and under the lenient unknown-field mode the sort is then dropped and
|
|
4076
|
+
* the rows come back in whatever order the database pleased — sorted, as far as
|
|
4077
|
+
* the subscriber can tell, by whatever they asked for.
|
|
4078
|
+
*/
|
|
4079
|
+
function parseOrderBySpecStrict(raw, order) {
|
|
4080
|
+
if (raw === void 0 || raw === null || raw === "") return void 0;
|
|
4081
|
+
if (typeof raw === "string") return normalizeDriverOrderBy(raw, order);
|
|
4082
|
+
if (!Array.isArray(raw) || raw.length === 0) throw new OrderBySpecError(`${typeof raw} is not a field name or a list of sort keys`);
|
|
4083
|
+
if (typeof raw[0] === "string") return [toStrictTuple(raw, 0)];
|
|
4084
|
+
return raw.map(toStrictTuple);
|
|
4085
|
+
}
|
|
4086
|
+
function toStrictTuple(raw, index) {
|
|
4087
|
+
if (!Array.isArray(raw) || typeof raw[0] !== "string" || raw[0].trim() === "") throw new OrderBySpecError(`entry ${index} has no field name`);
|
|
4088
|
+
const direction = raw[1];
|
|
4089
|
+
if (direction !== void 0 && direction !== "asc" && direction !== "desc") throw new OrderBySpecError(`entry ${index} has direction '${String(direction)}'`);
|
|
4090
|
+
return [raw[0], direction ?? "asc"];
|
|
4091
|
+
}
|
|
4092
|
+
//#endregion
|
|
3870
4093
|
//#region ../common/src/data/query_builder.ts
|
|
3871
4094
|
var QueryBuilder = class {
|
|
3872
4095
|
collection;
|
|
@@ -3895,11 +4118,18 @@ var QueryBuilder = class {
|
|
|
3895
4118
|
}
|
|
3896
4119
|
/**
|
|
3897
4120
|
* Order the results by a specific column.
|
|
4121
|
+
*
|
|
4122
|
+
* Called again, this adds a tie-breaker rather than replacing the sort:
|
|
4123
|
+
* keys apply in the order they were added.
|
|
4124
|
+
*
|
|
3898
4125
|
* @example
|
|
3899
4126
|
* client.collection('users').orderBy('createdAt', 'desc').find()
|
|
4127
|
+
* @example
|
|
4128
|
+
* client.collection('users').orderBy('roles').orderBy('createdAt', 'desc').find()
|
|
3900
4129
|
*/
|
|
3901
4130
|
orderBy(column, direction = "asc") {
|
|
3902
|
-
this.params.orderBy
|
|
4131
|
+
const existing = normalizeOrderBy(this.params.orderBy) ?? [];
|
|
4132
|
+
this.params.orderBy = [...existing, [column, direction]];
|
|
3903
4133
|
return this;
|
|
3904
4134
|
}
|
|
3905
4135
|
/**
|
|
@@ -4073,9 +4303,10 @@ async function* paginateFind(find, params, label = "collection") {
|
|
|
4073
4303
|
const requestedDirection = typeof cursor === "object" && cursor !== null ? cursor.direction : void 0;
|
|
4074
4304
|
let direction = "asc";
|
|
4075
4305
|
if (cursorField) {
|
|
4076
|
-
const orderBy = findParams.orderBy;
|
|
4077
|
-
if (orderBy && orderBy
|
|
4078
|
-
|
|
4306
|
+
const orderBy = normalizeOrderBy(findParams.orderBy);
|
|
4307
|
+
if (orderBy && orderBy.length > 1) throw new RebasePaginationError("cursor-order-mismatch", `Cannot seek on "${cursorField}" while ordering "${label}" by ${orderBy.map(([field]) => `"${field}"`).join(", ")}: keyset pagination advances along a single column. Order by "${cursorField}" alone, or drop the cursor and page by offset.`);
|
|
4308
|
+
if (orderBy && orderBy[0][0] !== cursorField) throw new RebasePaginationError("cursor-order-mismatch", `Cannot seek on "${cursorField}" while ordering "${label}" by "${orderBy[0][0]}": keyset pagination only advances along the column the query is sorted by. Order by "${cursorField}", or drop the cursor and page by offset.`);
|
|
4309
|
+
direction = requestedDirection ?? orderBy?.[0][1] ?? "asc";
|
|
4079
4310
|
findParams.orderBy = [cursorField, direction];
|
|
4080
4311
|
}
|
|
4081
4312
|
const seekOp = direction === "desc" ? "<" : ">";
|
|
@@ -4552,8 +4783,7 @@ function createDriverAccessor(driver, slug, getPks = () => []) {
|
|
|
4552
4783
|
logical: params?.logical,
|
|
4553
4784
|
limit,
|
|
4554
4785
|
offset: driverOffset,
|
|
4555
|
-
orderBy: params?.orderBy
|
|
4556
|
-
order: params?.orderBy?.[1],
|
|
4786
|
+
orderBy: normalizeOrderBy(params?.orderBy),
|
|
4557
4787
|
searchString: params?.searchString
|
|
4558
4788
|
}, params?.include) : await driver.fetchCollection({
|
|
4559
4789
|
path: slug,
|
|
@@ -4561,8 +4791,7 @@ function createDriverAccessor(driver, slug, getPks = () => []) {
|
|
|
4561
4791
|
offset: driverOffset,
|
|
4562
4792
|
filter,
|
|
4563
4793
|
logical: params?.logical,
|
|
4564
|
-
orderBy: params?.orderBy
|
|
4565
|
-
order: params?.orderBy?.[1],
|
|
4794
|
+
orderBy: normalizeOrderBy(params?.orderBy),
|
|
4566
4795
|
searchString: params?.searchString
|
|
4567
4796
|
});
|
|
4568
4797
|
let total = rows.length + offset;
|
|
@@ -4657,8 +4886,7 @@ function createDriverAccessor(driver, slug, getPks = () => []) {
|
|
|
4657
4886
|
offset: driverOffset,
|
|
4658
4887
|
filter: params?.where,
|
|
4659
4888
|
logical: params?.logical,
|
|
4660
|
-
orderBy: params?.orderBy
|
|
4661
|
-
order: params?.orderBy?.[1],
|
|
4889
|
+
orderBy: normalizeOrderBy(params?.orderBy),
|
|
4662
4890
|
searchString: params?.searchString,
|
|
4663
4891
|
searchExplain: params?.searchExplain,
|
|
4664
4892
|
onUpdate: (entities) => {
|
|
@@ -4778,8 +5006,10 @@ var SdkQueryBuilder = class {
|
|
|
4778
5006
|
}
|
|
4779
5007
|
return this;
|
|
4780
5008
|
}
|
|
5009
|
+
/** Called again, this adds a tie-breaker rather than replacing the sort. */
|
|
4781
5010
|
orderBy(column, direction = "asc") {
|
|
4782
|
-
this.params.orderBy
|
|
5011
|
+
const existing = normalizeOrderBy(this.params.orderBy) ?? [];
|
|
5012
|
+
this.params.orderBy = [...existing, [column, direction]];
|
|
4783
5013
|
return this;
|
|
4784
5014
|
}
|
|
4785
5015
|
limit(count) {
|
|
@@ -5430,6 +5660,6 @@ function isAuthCollection(collection) {
|
|
|
5430
5660
|
return typeof auth === "object" && auth !== null && auth.enabled === true;
|
|
5431
5661
|
}
|
|
5432
5662
|
//#endregion
|
|
5433
|
-
export {
|
|
5663
|
+
export { mergeDeep as $, fieldKeyForColumn as A, parseIdValues as B, getJunctionCollectionConfig as C, policyToPostgres as D, getEffectiveSecurityRules as E, getTableVarName as F, updateDateAutoValues as G, createRelationRef as H, resolveCollectionRelations as I, legacyForeignKeyName as J, firstFreeKey as K, buildCompositeId as L, getColumnName as M, getEnumVarName as N, securityRuleToConditions as O, getTableName as P, isPrototypePollutingKey as Q, getDeclaredPrimaryKeys as R, resolveStringColumnLength as S, resolveJunctionSpecs as T, createRelationRefWithData as U, sortCollectionsBySlug as V, normalizeToEntityRelation as W, toWireKey as X, toPostgresIdentifier as Y, getPolicyNamesForRule as Z, OrderBySpecError as _, SEARCH_STAMP_PREFIX as a, isManyToMany as at, CollectionRegistry as b, assertSearchIsPostgresOnly as c, searchColumnStamps as d, camelCase as et, searchExtensionStatements as f, buildSdkData as g, visibleColumnProjection as h, isAuthCollection as i, hasForeignKeyOnTarget as it, findRelation as j, findAnonymousGrants as k, buildSearchColumnSpec as l, searchIndexStatements as m, authUsersColumnDefinition as n, DEFAULT_ONE_OF_TYPE as nt, SEARCH_TEXT_FN as o, Vector as ot, searchHelperFunctions as p, generateForeignKeyName as q, authUsersColumnSql as r, DEFAULT_ONE_OF_VALUE as rt, SEARCH_UNACCENT_FN as s, AUTH_USERS_COLUMNS as t, toSnakeCase as tt, hiddenColumnsOption as u, normalizeDriverOrderBy as v, getJunctionSecurityRules as w, relationalCollections as x, parseOrderBySpecStrict as y, isAddressableId as z };
|
|
5434
5664
|
|
|
5435
|
-
//# sourceMappingURL=auth-users-columns-
|
|
5665
|
+
//# sourceMappingURL=auth-users-columns-C-FDnL_e.js.map
|