@rebasepro/server-postgres 0.12.1-canary.gf5f1d39 → 0.13.1-canary.g06dbe5b
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 +49 -2
- package/dist/PostgresBootstrapper.d.ts +25 -1
- package/dist/auth/services.d.ts +40 -0
- package/dist/{src-BbFOPJ1S.js → auth-users-columns-CBEOeYqa.js} +1118 -220
- package/dist/auth-users-columns-CBEOeYqa.js.map +1 -0
- package/dist/backup/backup-service.d.ts +10 -1
- package/dist/backup/pg-tools.d.ts +47 -0
- package/dist/backup-service-Bww-Lg0s.js +8999 -0
- package/dist/backup-service-Bww-Lg0s.js.map +1 -0
- package/dist/cli-helpers.d.ts +96 -1
- package/dist/connection-BuZ97wsr.js +250 -0
- package/dist/connection-BuZ97wsr.js.map +1 -0
- package/dist/connection.d.ts +42 -0
- package/dist/data-transformer.d.ts +7 -2
- package/dist/data_driver-ULAyJEi9.js +193 -0
- package/dist/data_driver-ULAyJEi9.js.map +1 -0
- package/dist/ensure-collection-policies-B_JMGa5K.js +57 -0
- package/dist/ensure-collection-policies-B_JMGa5K.js.map +1 -0
- package/dist/ensure-collection-tables-DzeTEvMv.js +800 -0
- package/dist/ensure-collection-tables-DzeTEvMv.js.map +1 -0
- package/dist/index.es.js +2460 -10233
- package/dist/index.es.js.map +1 -1
- package/dist/rls-bootstrap-sql-Bpv3nUZo.js +244 -0
- package/dist/rls-bootstrap-sql-Bpv3nUZo.js.map +1 -0
- package/dist/schema/auth-schema.d.ts +185 -144
- package/dist/schema/auth-users-columns.d.ts +97 -0
- package/dist/schema/doctor-policy-checks.d.ts +28 -0
- package/dist/schema/doctor.d.ts +23 -25
- package/dist/schema/ensure-collection-policies.d.ts +60 -0
- package/dist/schema/ensure-collection-tables.d.ts +99 -3
- package/dist/schema/generate-drizzle-schema-logic.d.ts +10 -2
- package/dist/schema/generate-postgres-ddl-logic.d.ts +185 -3
- package/dist/schema/generated-schema-staleness.d.ts +39 -0
- package/dist/schema/introspect-db-constraints.d.ts +57 -0
- package/dist/schema/introspect-db-logic.d.ts +94 -5
- package/dist/schema/introspect-db-queries.d.ts +119 -0
- package/dist/schema/introspect-db-structure.d.ts +263 -0
- package/dist/schema/introspect-db-types.d.ts +11 -0
- package/dist/schema/rls-bootstrap-sql.d.ts +135 -0
- package/dist/schema/search-column.d.ts +248 -0
- package/dist/security/rls-enforcement.d.ts +61 -5
- package/dist/services/FetchService.d.ts +38 -8
- package/dist/services/RelationService.d.ts +54 -1
- package/dist/services/channel-bus/index.d.ts +1 -7
- package/dist/services/collection-helpers.d.ts +62 -2
- package/dist/services/dataService.d.ts +8 -1
- package/dist/services/realtimeService.d.ts +131 -21
- package/dist/services/row-pipeline.d.ts +1 -1
- package/dist/{src-Zqwaw3P5.js → src-C_wvdMnl.js} +78 -145
- package/dist/src-C_wvdMnl.js.map +1 -0
- package/dist/utils/connection-string.d.ts +29 -0
- package/dist/utils/drizzle-conditions.d.ts +129 -6
- package/dist/utils/pg-error-utils.d.ts +35 -0
- package/dist/websocket-D1qbmLZ2.js +587 -0
- package/dist/websocket-D1qbmLZ2.js.map +1 -0
- package/package.json +17 -16
- package/src/PostgresAdapter.ts +21 -2
- package/src/PostgresBackendDriver.ts +176 -6
- package/src/PostgresBootstrapper.ts +245 -37
- package/src/auth/ensure-tables.ts +357 -81
- package/src/auth/services.ts +93 -7
- package/src/backup/backup-cli.ts +41 -2
- package/src/backup/backup-service.ts +38 -5
- package/src/backup/pg-tools.ts +96 -3
- package/src/cli-helpers.ts +199 -10
- package/src/cli.ts +276 -56
- package/src/collections/validate-relations.ts +136 -14
- package/src/connection.ts +73 -0
- package/src/data-transformer.ts +129 -20
- package/src/databasePoolManager.ts +5 -2
- package/src/history/ensure-history-table.ts +7 -0
- package/src/schema/auth-schema.ts +46 -19
- package/src/schema/auth-users-columns.ts +131 -0
- package/src/schema/doctor-cli.ts +12 -63
- package/src/schema/doctor-policy-checks.ts +105 -0
- package/src/schema/doctor.ts +135 -76
- package/src/schema/ensure-collection-policies.ts +105 -0
- package/src/schema/ensure-collection-tables.test.ts +105 -9
- package/src/schema/ensure-collection-tables.ts +572 -42
- package/src/schema/generate-drizzle-schema-logic.ts +169 -43
- package/src/schema/generate-postgres-ddl-logic.ts +670 -29
- package/src/schema/generate-postgres-ddl.ts +25 -2
- package/src/schema/generated-schema-staleness.ts +169 -0
- package/src/schema/introspect-db-constraints.ts +385 -0
- package/src/schema/introspect-db-inference.ts +18 -8
- package/src/schema/introspect-db-logic.ts +446 -100
- package/src/schema/introspect-db-queries.ts +326 -0
- package/src/schema/introspect-db-structure.ts +670 -0
- package/src/schema/introspect-db-types.ts +56 -0
- package/src/schema/introspect-db.ts +37 -80
- package/src/schema/introspect-runtime.test.ts +56 -8
- package/src/schema/introspect-runtime.ts +31 -9
- package/src/schema/non-sql-collections.test.ts +131 -0
- package/src/schema/rls-bootstrap-sql.ts +288 -0
- package/src/schema/search-column.ts +643 -0
- package/src/security/anonymous-grants.test.ts +4 -2
- package/src/security/policy-drift.test.ts +11 -3
- package/src/security/rls-enforcement.ts +149 -6
- package/src/services/BranchService.ts +71 -28
- package/src/services/FetchService.ts +265 -122
- package/src/services/PersistService.ts +57 -7
- package/src/services/RelationService.ts +358 -114
- package/src/services/channel-bus/index.ts +0 -9
- package/src/services/channel-history.ts +14 -0
- package/src/services/channel-presence.ts +13 -0
- package/src/services/collection-helpers.ts +123 -4
- package/src/services/dataService.ts +8 -1
- package/src/services/realtimeService.ts +346 -81
- package/src/services/row-pipeline.ts +1 -1
- package/src/utils/connection-string.ts +58 -0
- package/src/utils/drizzle-conditions.ts +396 -29
- package/src/utils/pg-error-utils.ts +73 -3
- package/src/websocket.ts +130 -24
- package/dist/chunk-DSJWtz9O.js +0 -40
- package/dist/ensure-collection-tables-CNTcZGvn.js +0 -304
- package/dist/ensure-collection-tables-CNTcZGvn.js.map +0 -1
- package/dist/schema/auth-bootstrap-sql.d.ts +0 -24
- package/dist/src-BbFOPJ1S.js.map +0 -1
- package/dist/src-Zqwaw3P5.js.map +0 -1
- package/src/schema/auth-bootstrap-sql.ts +0 -47
|
@@ -80,15 +80,6 @@ export function resolveChannelBusSetting(configured?: ChannelBusSetting): Channe
|
|
|
80
80
|
return raw === "memory" ? { type: "memory" } : { type: "postgres" };
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
/**
|
|
84
|
-
* @deprecated Use {@link resolveChannelBusSetting}, which also accepts a
|
|
85
|
-
* supplied {@link ChannelBus} instance. Kept as a narrow alias so existing
|
|
86
|
-
* config-only callers keep their exact types.
|
|
87
|
-
*/
|
|
88
|
-
export function resolveChannelBusConfig(configured?: ChannelBusConfig): ChannelBusConfig {
|
|
89
|
-
return resolveChannelBusSetting(configured) as ChannelBusConfig;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
83
|
/**
|
|
93
84
|
* Produce the bus a setting asks for.
|
|
94
85
|
*
|
|
@@ -34,6 +34,7 @@ import { sql } from "drizzle-orm";
|
|
|
34
34
|
import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
35
35
|
import type { ChannelHistoryEntry, ChannelRetentionRule } from "@rebasepro/types";
|
|
36
36
|
import { logger } from "@rebasepro/server";
|
|
37
|
+
import { revokeInternalTableSql } from "@rebasepro/common";
|
|
37
38
|
|
|
38
39
|
/** How many messages a replay returns when the caller does not say. */
|
|
39
40
|
const DEFAULT_REPLAY_LIMIT = 200;
|
|
@@ -198,6 +199,19 @@ export class ChannelHistoryStore {
|
|
|
198
199
|
)
|
|
199
200
|
`);
|
|
200
201
|
|
|
202
|
+
// Retained broadcasts for every channel in one table, with no RLS: who
|
|
203
|
+
// may replay a channel is decided before the read, by the channel gate
|
|
204
|
+
// in `realtimeService.authorizeChannelAction` — a replay is answered
|
|
205
|
+
// only for a client that has joined the channel, plus whatever an
|
|
206
|
+
// installed `ChannelAuthorizer` adds. That gate is the entire reason
|
|
207
|
+
// this table can sit outside the RLS model, so it fails closed; the
|
|
208
|
+
// rule language it does not yet have is written up in
|
|
209
|
+
// `docs/channel-authorization.md`. The driver's schema-wide
|
|
210
|
+
// grant reaches these (created here, after it ran), so take the
|
|
211
|
+
// privilege back.
|
|
212
|
+
await this.db.execute(sql.raw(revokeInternalTableSql("rebase", "channel_messages")));
|
|
213
|
+
await this.db.execute(sql.raw(revokeInternalTableSql("rebase", "channel_cursors")));
|
|
214
|
+
|
|
201
215
|
this.tablesReady = true;
|
|
202
216
|
logger.info(`✅ [ChannelHistory] Retained channels ready (${this.rules.length} rule(s)).`);
|
|
203
217
|
}
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
|
|
29
29
|
import { sql } from "drizzle-orm";
|
|
30
30
|
import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
31
|
+
import { revokeInternalTableSql } from "@rebasepro/common";
|
|
31
32
|
|
|
32
33
|
/** A tracked client, as any instance sees it. */
|
|
33
34
|
export interface PresenceRow {
|
|
@@ -71,6 +72,18 @@ export class ChannelPresenceStore {
|
|
|
71
72
|
ON rebase.channel_presence (last_seen)
|
|
72
73
|
`);
|
|
73
74
|
|
|
75
|
+
// The roster of every client on every channel, with no RLS — a row
|
|
76
|
+
// policy has nothing to match on here, since a channel name is a string
|
|
77
|
+
// a client invents rather than a row anyone owns. What guards it is the
|
|
78
|
+
// channel gate in `realtimeService.authorizeChannelAction`: presence is
|
|
79
|
+
// readable only to a client that has joined the channel, plus whatever
|
|
80
|
+
// an installed `ChannelAuthorizer` adds. That gate is the entire reason
|
|
81
|
+
// this table can sit outside the RLS model, so it fails closed —
|
|
82
|
+
// see `docs/channel-authorization.md` for what it does *not*
|
|
83
|
+
// yet decide. Revoke the schema-wide grant the driver handed out
|
|
84
|
+
// before this table existed.
|
|
85
|
+
await this.db.execute(sql.raw(revokeInternalTableSql("rebase", "channel_presence")));
|
|
86
|
+
|
|
74
87
|
this.tablesReady = true;
|
|
75
88
|
}
|
|
76
89
|
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { PgTable, AnyPgColumn } from "drizzle-orm/pg-core";
|
|
2
|
-
import {
|
|
2
|
+
import { getTableColumns } from "drizzle-orm";
|
|
3
|
+
import { CollectionConfig, Property, ResolvedHasMany, ResolvedHasOne } from "@rebasepro/types";
|
|
3
4
|
import { PostgresCollectionRegistry } from "../collections/PostgresCollectionRegistry";
|
|
4
5
|
import { getTableName } from "@rebasepro/common";
|
|
5
|
-
import { logger } from "@rebasepro/server";
|
|
6
|
+
import { ApiError, logger } from "@rebasepro/server";
|
|
6
7
|
|
|
7
8
|
// Row identity is derived on both sides of the wire — the driver parses an
|
|
8
9
|
// incoming address into key columns, the admin derives one from a served row —
|
|
9
10
|
// so the implementation lives in `common` and both agree by construction.
|
|
10
|
-
export { buildCompositeId, parseIdValues, COMPOSITE_ID_SEPARATOR } from "@rebasepro/common";
|
|
11
|
+
export { buildCompositeId, parseIdValues, isAddressableId, COMPOSITE_ID_SEPARATOR } from "@rebasepro/common";
|
|
11
12
|
export type { PrimaryKeyInfo } from "@rebasepro/common";
|
|
12
|
-
import { buildCompositeId, COMPOSITE_ID_SEPARATOR, getDeclaredPrimaryKeys } from "@rebasepro/common";
|
|
13
|
+
import { buildCompositeId, COMPOSITE_ID_SEPARATOR, getDeclaredPrimaryKeys, isAddressableId } from "@rebasepro/common";
|
|
13
14
|
import type { PrimaryKeyInfo } from "@rebasepro/common";
|
|
14
15
|
|
|
15
16
|
/**
|
|
@@ -40,6 +41,33 @@ export function getColumnMeta(col: AnyPgColumn): DrizzleColumnMeta {
|
|
|
40
41
|
};
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Whether an address could name a row in this table, judged by the columns.
|
|
46
|
+
*
|
|
47
|
+
* {@link getPrimaryKeys} lets a config's `isId: "uuid"` win over the schema, so
|
|
48
|
+
* its `isUUID` is a claim rather than a fact — right for deriving addresses,
|
|
49
|
+
* wrong for refusing a query. Here the Drizzle column type decides, because it
|
|
50
|
+
* is what Postgres will enforce: a `uuid` column meets `/c/products/new` with
|
|
51
|
+
* `22P02`, which aborts the surrounding transaction and turns every later
|
|
52
|
+
* statement into an unrelated-looking `25P02`.
|
|
53
|
+
*/
|
|
54
|
+
export function idCanAddressTable(
|
|
55
|
+
id: string | number,
|
|
56
|
+
table: PgTable,
|
|
57
|
+
idInfoArray: PrimaryKeyInfo[]
|
|
58
|
+
): boolean {
|
|
59
|
+
const columnBacked = idInfoArray.map(info => {
|
|
60
|
+
const col = table[info.fieldName as keyof typeof table] as AnyPgColumn | undefined;
|
|
61
|
+
const meta = col ? getColumnMeta(col) : undefined;
|
|
62
|
+
// No column to ask (a key the schema does not carry) leaves the id
|
|
63
|
+
// addressable: refusing on a guess would 404 rows that do exist.
|
|
64
|
+
if (!meta?.columnType) return info;
|
|
65
|
+
return { ...info,
|
|
66
|
+
isUUID: meta.columnType === "PgUUID" };
|
|
67
|
+
});
|
|
68
|
+
return isAddressableId(id, columnBacked);
|
|
69
|
+
}
|
|
70
|
+
|
|
43
71
|
export function getCollectionByPath(collectionPath: string, registry: PostgresCollectionRegistry): CollectionConfig {
|
|
44
72
|
const collection = registry.getCollectionByPath(collectionPath);
|
|
45
73
|
if (!collection) {
|
|
@@ -49,6 +77,58 @@ export function getCollectionByPath(collectionPath: string, registry: PostgresCo
|
|
|
49
77
|
return collection;
|
|
50
78
|
}
|
|
51
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Reject a write naming something that is not a column of the table.
|
|
82
|
+
*
|
|
83
|
+
* Drizzle builds INSERT from `Object.entries(table[Symbol.Columns])` and UPDATE
|
|
84
|
+
* from `Object.keys(tableColumns)`, so a key the table does not carry is not
|
|
85
|
+
* rejected by anything — it is *left out of the statement*. The insert answers
|
|
86
|
+
* 201 having stored nothing under that name; the update, if the key was the
|
|
87
|
+
* only one, builds `update "posts" set where …` and Postgres raises a syntax
|
|
88
|
+
* error (SQLSTATE 42601), which is neither class 22 nor 23 and so surfaces as a
|
|
89
|
+
* 500 for what is a caller's typo.
|
|
90
|
+
*
|
|
91
|
+
* That makes this the last honest place to check, and the only one every write
|
|
92
|
+
* passes through. `assertKnownWriteFields` in the REST layer checks the same
|
|
93
|
+
* thing against the *config* and is skipped on four paths — `strictWrites:
|
|
94
|
+
* false`, a collection declaring no properties, an auth adapter that owns the
|
|
95
|
+
* body's shape, and a nested route whose target cannot be walked — and it never
|
|
96
|
+
* sees an in-process `rebase.data` write at all.
|
|
97
|
+
*
|
|
98
|
+
* It also gives `strictWrites: false` a truthful implementation. The flag is
|
|
99
|
+
* documented for "a column that really does exist which the config never
|
|
100
|
+
* declared", and skipping the config check alone could not deliver that: the
|
|
101
|
+
* value was dropped a layer later regardless. Skipping the config check and
|
|
102
|
+
* keeping this one does exactly what the flag says — the column must exist,
|
|
103
|
+
* the property need not.
|
|
104
|
+
*/
|
|
105
|
+
export function assertWritableColumns(
|
|
106
|
+
values: Record<string, unknown>,
|
|
107
|
+
table: PgTable,
|
|
108
|
+
collectionPath: string
|
|
109
|
+
): void {
|
|
110
|
+
// Only a real Drizzle table carries a column list. A stand-in that does not
|
|
111
|
+
// (a test double, a registry entry built by hand) has nothing to check
|
|
112
|
+
// against, and inventing an answer from its own keys would reject writes
|
|
113
|
+
// over the shape of the double rather than the shape of the table.
|
|
114
|
+
const columns = getTableColumns(table) as Record<string, unknown> | undefined;
|
|
115
|
+
if (!columns || Object.keys(columns).length === 0) return;
|
|
116
|
+
|
|
117
|
+
const unknown = Object.keys(values).filter(key => !(key in columns));
|
|
118
|
+
if (unknown.length === 0) return;
|
|
119
|
+
|
|
120
|
+
// The offending keys, and deliberately not the list of real ones: this
|
|
121
|
+
// error is reachable on paths where the REST field check was skipped, and
|
|
122
|
+
// an `excludeFromApi` column is documented as never being served to a
|
|
123
|
+
// caller. Naming what was sent is the actionable half anyway.
|
|
124
|
+
throw ApiError.badRequest(
|
|
125
|
+
`'${collectionPath}' has no column${unknown.length > 1 ? "s" : ""} ` +
|
|
126
|
+
`${unknown.map(key => `'${key}'`).join(", ")}, so the value${unknown.length > 1 ? "s" : ""} ` +
|
|
127
|
+
"would have been dropped before the statement was built.",
|
|
128
|
+
"VALIDATION_UNKNOWN_FIELDS"
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
52
132
|
export function getTableForCollection(collection: CollectionConfig, registry: PostgresCollectionRegistry): PgTable<any> {
|
|
53
133
|
const tableName = getTableName(collection);
|
|
54
134
|
const table = registry.getTable(tableName);
|
|
@@ -149,6 +229,45 @@ export function requirePrimaryKeys(collection: CollectionConfig, registry: Postg
|
|
|
149
229
|
return keys;
|
|
150
230
|
}
|
|
151
231
|
|
|
232
|
+
/**
|
|
233
|
+
* The column on the *source* table that a `hasOne`/`hasMany` link points at.
|
|
234
|
+
*
|
|
235
|
+
* `sourceKey` is authored when the two sides join on a natural key — an
|
|
236
|
+
* external identity id, a SKU — and left off when they join on the row id,
|
|
237
|
+
* which is the overwhelming majority. That makes `undefined` the only optional
|
|
238
|
+
* field on a resolved relation, so it gets exactly one reader: this function.
|
|
239
|
+
* Every consumer that needs the column asks here, and none of them re-derives
|
|
240
|
+
* "or else the primary key" for itself. That is the whole point — the fallback
|
|
241
|
+
* chains this codebase removed from relation resolution were dangerous because
|
|
242
|
+
* they were *duplicated* and could disagree, not because they existed.
|
|
243
|
+
*/
|
|
244
|
+
export function sourceKeyField(
|
|
245
|
+
relation: ResolvedHasOne | ResolvedHasMany,
|
|
246
|
+
sourceCollection: CollectionConfig,
|
|
247
|
+
registry: PostgresCollectionRegistry
|
|
248
|
+
): string {
|
|
249
|
+
if (relation.sourceKey) return relation.sourceKey;
|
|
250
|
+
return requirePrimaryKeys(sourceCollection, registry)[0].fieldName;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Whether this link joins on something other than the source's primary key.
|
|
255
|
+
*
|
|
256
|
+
* Callers that hold a parent *id* — which is most of them, since an id is what
|
|
257
|
+
* a URL carries — must translate it to the source key's value before it can be
|
|
258
|
+
* compared with the target's foreign key. Those that hold the parent *row*, or
|
|
259
|
+
* that build a correlated subquery over the source table, can read the column
|
|
260
|
+
* directly and skip the lookup.
|
|
261
|
+
*/
|
|
262
|
+
export function joinsOnNaturalKey(
|
|
263
|
+
relation: ResolvedHasOne | ResolvedHasMany,
|
|
264
|
+
sourceCollection: CollectionConfig,
|
|
265
|
+
registry: PostgresCollectionRegistry
|
|
266
|
+
): boolean {
|
|
267
|
+
if (!relation.sourceKey) return false;
|
|
268
|
+
return relation.sourceKey !== requirePrimaryKeys(sourceCollection, registry)[0].fieldName;
|
|
269
|
+
}
|
|
270
|
+
|
|
152
271
|
/**
|
|
153
272
|
* Collections whose key the *browser* cannot resolve, and what it will do
|
|
154
273
|
* instead.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
2
|
-
import { FilterValues } from "@rebasepro/types";
|
|
2
|
+
import { FilterValues, LogicalCondition } from "@rebasepro/types";
|
|
3
3
|
import type { VectorSearchParams } from "@rebasepro/types";
|
|
4
4
|
import { FetchService } from "./FetchService";
|
|
5
5
|
import { PersistService } from "./PersistService";
|
|
@@ -60,6 +60,8 @@ export class DataService implements DataRepository {
|
|
|
60
60
|
collectionPath: string,
|
|
61
61
|
options: {
|
|
62
62
|
filter?: FilterValues<Extract<keyof M, string>>;
|
|
63
|
+
/** An `or(...)`/`and(...)` group, applied alongside `filter`. */
|
|
64
|
+
logical?: LogicalCondition;
|
|
63
65
|
orderBy?: string;
|
|
64
66
|
order?: "desc" | "asc";
|
|
65
67
|
limit?: number;
|
|
@@ -81,10 +83,13 @@ export class DataService implements DataRepository {
|
|
|
81
83
|
searchString: string,
|
|
82
84
|
options: {
|
|
83
85
|
filter?: FilterValues<Extract<keyof M, string>>;
|
|
86
|
+
/** An `or(...)`/`and(...)` group, applied alongside `filter`. */
|
|
87
|
+
logical?: LogicalCondition;
|
|
84
88
|
orderBy?: string;
|
|
85
89
|
order?: "desc" | "asc";
|
|
86
90
|
limit?: number;
|
|
87
91
|
databaseId?: string;
|
|
92
|
+
searchExplain?: boolean;
|
|
88
93
|
} = {}
|
|
89
94
|
): Promise<Record<string, unknown>[]> {
|
|
90
95
|
return this.fetchService.searchRows<M>(collectionPath, searchString, options);
|
|
@@ -97,6 +102,8 @@ export class DataService implements DataRepository {
|
|
|
97
102
|
collectionPath: string,
|
|
98
103
|
options: {
|
|
99
104
|
filter?: FilterValues<Extract<keyof M, string>>;
|
|
105
|
+
/** An `or(...)`/`and(...)` group, applied alongside `filter`. */
|
|
106
|
+
logical?: LogicalCondition;
|
|
100
107
|
searchString?: string;
|
|
101
108
|
databaseId?: string;
|
|
102
109
|
} = {}
|