@rebasepro/types 0.13.0 → 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/call_context.d.ts +70 -4
- package/dist/controllers/client.d.ts +47 -74
- package/dist/controllers/data.d.ts +379 -36
- package/dist/controllers/data_driver.d.ts +71 -5
- package/dist/errors.d.ts +30 -4
- package/dist/index.es.js +204 -11
- package/dist/index.es.js.map +1 -1
- package/dist/types/admin_block.d.ts +38 -1
- package/dist/types/backend.d.ts +2 -0
- package/dist/types/collections.d.ts +46 -8
- package/dist/types/cron.d.ts +50 -9
- package/dist/types/entities.d.ts +11 -0
- package/dist/types/entity_callbacks.d.ts +2 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/policy.d.ts +13 -13
- package/dist/types/properties.d.ts +31 -6
- package/dist/types/rls-functions.d.ts +84 -0
- package/dist/types/search.d.ts +231 -0
- package/package.json +2 -2
- package/src/call_context.ts +68 -4
- package/src/controllers/client.ts +47 -95
- package/src/controllers/data.ts +390 -36
- package/src/controllers/data_driver.ts +109 -8
- package/src/errors.ts +43 -4
- package/src/types/admin_block.ts +85 -0
- package/src/types/backend.ts +2 -0
- package/src/types/collections.ts +47 -8
- package/src/types/cron.ts +51 -9
- package/src/types/entities.ts +12 -0
- package/src/types/entity_callbacks.ts +2 -1
- package/src/types/index.ts +2 -0
- package/src/types/policy.ts +13 -13
- package/src/types/properties.ts +32 -6
- package/src/types/rls-functions.ts +98 -0
- package/src/types/search.ts +247 -0
|
@@ -1,6 +1,67 @@
|
|
|
1
|
+
import type { VectorSearchParams } from "./data_driver";
|
|
2
|
+
import type { ComputedSortField, SearchMatch } from "../types/search";
|
|
1
3
|
import { Entity, EntityValues } from "../types/entities";
|
|
2
4
|
import { WhereFilterOp, FieldPath, FilterValues, OrderByTuple } from "../types/filter-operators";
|
|
5
|
+
/**
|
|
6
|
+
* Operator-blind filter value: whatever the column holds, a list of it, or null.
|
|
7
|
+
*
|
|
8
|
+
* @deprecated Superseded by {@link WhereValueFor}, which correlates the value
|
|
9
|
+
* with the operator. Kept exported because it is public API and downstream code
|
|
10
|
+
* annotates with it; every `where()` overload in this file uses `WhereValueFor`.
|
|
11
|
+
*/
|
|
3
12
|
export type WhereValue<T> = T | T[] | null;
|
|
13
|
+
/**
|
|
14
|
+
* The element type of an array column, and the column's own type otherwise.
|
|
15
|
+
*
|
|
16
|
+
* A generated SDK emits an `array` property as `Array<X>` and a to-many
|
|
17
|
+
* relation as `Array<TargetRow>`, so this is what `array-contains` compares
|
|
18
|
+
* against on either.
|
|
19
|
+
*/
|
|
20
|
+
export type ElementOf<T> = T extends readonly (infer E)[] ? E : T;
|
|
21
|
+
/**
|
|
22
|
+
* The `id` of a row-shaped element, and `never` for anything else.
|
|
23
|
+
*
|
|
24
|
+
* A to-many relation is emitted as `Array<TargetRow>`, but the filter compilers
|
|
25
|
+
* compare a relation by **id** — `buildRelationFilterPredicate` in
|
|
26
|
+
* `@rebasepro/server-postgres` unwraps a relation value down to its id — so
|
|
27
|
+
* `where("tags", "array-contains", tagId)` is the call that works, and the
|
|
28
|
+
* element type alone would refuse it.
|
|
29
|
+
*/
|
|
30
|
+
export type IdOf<E> = E extends {
|
|
31
|
+
id: infer I;
|
|
32
|
+
} ? I : never;
|
|
33
|
+
/**
|
|
34
|
+
* One member of an array column: its element, or — when the element is a row —
|
|
35
|
+
* that row's id, which is what a relation filter is actually compared against.
|
|
36
|
+
*/
|
|
37
|
+
export type WhereElementOf<T> = ElementOf<T> | IdOf<ElementOf<T>>;
|
|
38
|
+
/**
|
|
39
|
+
* The value a given operator takes on a column of type `T`.
|
|
40
|
+
*
|
|
41
|
+
* `WhereValue<T>` was one value type for all sixteen operators, which made
|
|
42
|
+
* `array-contains` uncallable from a generated SDK — it is the one operator
|
|
43
|
+
* whose value is an *element* of the column rather than the column's own type,
|
|
44
|
+
* so on `tags: string[]` it wanted a `string[]` and the documented
|
|
45
|
+
* `.where("tags", "array-contains", "featured")` was a compile error. The
|
|
46
|
+
* spelling that did compile, `["featured"]`, builds `@> ARRAY[$1]` with the
|
|
47
|
+
* whole array bound as the single element and matches nothing: the correct
|
|
48
|
+
* query rejected, the accepted query silently wrong.
|
|
49
|
+
*
|
|
50
|
+
* The branches mirror `buildSingleFilterCondition` in `@rebasepro/server-postgres`:
|
|
51
|
+
*
|
|
52
|
+
* - `array-contains` → one element of the column (or a related row's id).
|
|
53
|
+
* - `in` / `not-in` / `array-contains-any` → a list of elements; a bare element
|
|
54
|
+
* is read as the one-element list, and `null` is a null check.
|
|
55
|
+
* - `like` / `ilike` / `not-like` / `not-ilike` → a SQL pattern. Always a
|
|
56
|
+
* string, including on numeric and date columns, which the driver casts.
|
|
57
|
+
* - `is-null` / `is-not-null` → nothing; the value is ignored everywhere.
|
|
58
|
+
* - everything else → the column's own type, or `null` for a null comparison.
|
|
59
|
+
*
|
|
60
|
+
* Distributes over `Op`, so a caller holding an unnarrowed `WhereFilterOp`
|
|
61
|
+
* (a dynamic filter UI, say) gets the union of every branch and stays as
|
|
62
|
+
* permissive as it was.
|
|
63
|
+
*/
|
|
64
|
+
export type WhereValueFor<Op extends WhereFilterOp, T> = Op extends "array-contains" ? WhereElementOf<T> : Op extends "in" | "not-in" | "array-contains-any" ? readonly WhereElementOf<T>[] | WhereElementOf<T> | null : Op extends "like" | "ilike" | "not-like" | "not-ilike" ? string : Op extends "is-null" | "is-not-null" ? null | undefined : T | null;
|
|
4
65
|
export interface LogicalCondition {
|
|
5
66
|
type: "and" | "or";
|
|
6
67
|
conditions: (FilterCondition | LogicalCondition)[];
|
|
@@ -34,13 +95,24 @@ export interface FilterCondition {
|
|
|
34
95
|
*
|
|
35
96
|
* `limit`/`offset` and `page` describe the same window two ways. If **both
|
|
36
97
|
* `offset` and `page` are provided, `page` wins** — the backend computes
|
|
37
|
-
* `offset = (page - 1) * (limit ??
|
|
38
|
-
* Pick one style per query.
|
|
98
|
+
* `offset = (page - 1) * (limit ?? DEFAULT_LIST_LIMIT)` and ignores the
|
|
99
|
+
* explicit `offset`. Pick one style per query.
|
|
39
100
|
*
|
|
40
101
|
* @group Data
|
|
41
102
|
*/
|
|
42
103
|
export interface FindParams<M extends Record<string, unknown> = Record<string, unknown>> {
|
|
43
|
-
/**
|
|
104
|
+
/**
|
|
105
|
+
* Maximum number of items to return.
|
|
106
|
+
*
|
|
107
|
+
* Omit it and the backend applies {@link DEFAULT_LIST_LIMIT}, so a read is
|
|
108
|
+
* never unbounded. Provide it and it must be a whole number between 1 and
|
|
109
|
+
* {@link MAX_LIST_LIMIT}: the backend **rejects** anything else with a 400
|
|
110
|
+
* rather than trimming it to fit, because a page quietly smaller than the
|
|
111
|
+
* one you asked for is indistinguishable from having reached the end of the
|
|
112
|
+
* collection. To read past the ceiling, page with `offset` — or let
|
|
113
|
+
* {@link SDKCollectionClient.iterate} / {@link SDKCollectionClient.findAll}
|
|
114
|
+
* do it for you.
|
|
115
|
+
*/
|
|
44
116
|
limit?: number;
|
|
45
117
|
/**
|
|
46
118
|
* Number of items to skip. Ignored when {@link FindParams.page} is also
|
|
@@ -49,7 +121,7 @@ export interface FindParams<M extends Record<string, unknown> = Record<string, u
|
|
|
49
121
|
offset?: number;
|
|
50
122
|
/**
|
|
51
123
|
* Page number (1-indexed), alternative to {@link FindParams.offset}.
|
|
52
|
-
* When set, overrides `offset` as `(page - 1) * (limit ??
|
|
124
|
+
* When set, overrides `offset` as `(page - 1) * (limit ?? DEFAULT_LIST_LIMIT)`.
|
|
53
125
|
*/
|
|
54
126
|
page?: number;
|
|
55
127
|
/**
|
|
@@ -76,7 +148,7 @@ export interface FindParams<M extends Record<string, unknown> = Record<string, u
|
|
|
76
148
|
* Sort order as a `[field, direction]` tuple.
|
|
77
149
|
* @example orderBy: ["created_at", "desc"]
|
|
78
150
|
*/
|
|
79
|
-
orderBy?: OrderByTuple<FieldPath<M
|
|
151
|
+
orderBy?: OrderByTuple<FieldPath<M> | ComputedSortField>;
|
|
80
152
|
/**
|
|
81
153
|
* Relations to include in the response.
|
|
82
154
|
*
|
|
@@ -86,11 +158,45 @@ export interface FindParams<M extends Record<string, unknown> = Record<string, u
|
|
|
86
158
|
*/
|
|
87
159
|
include?: string[];
|
|
88
160
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
161
|
+
* Text search string, AND-ed with `where`/`logical`. This is the value
|
|
162
|
+
* behind the query builder's `.search()` method.
|
|
163
|
+
*
|
|
164
|
+
* What it compiles to depends on the collection. By default — matching
|
|
165
|
+
* every collection that has not said otherwise — it is a case-insensitive
|
|
166
|
+
* substring match OR-ed across the collection's top-level `string`
|
|
167
|
+
* properties: it does not reach inside `map` or `array` properties, it does
|
|
168
|
+
* not stem or rank, and it cannot use an index.
|
|
169
|
+
*
|
|
170
|
+
* A Postgres collection that declares a `search` block instead gets a
|
|
171
|
+
* ranked full-text match over exactly the fields it named, and rows come
|
|
172
|
+
* back with a {@link FindParams.orderBy}-able `_score`.
|
|
92
173
|
*/
|
|
93
174
|
searchString?: string;
|
|
175
|
+
/**
|
|
176
|
+
* Nearest-neighbour search over a `vector` property.
|
|
177
|
+
*
|
|
178
|
+
* Postgres only, and only for a collection that declares a property of
|
|
179
|
+
* type `vector`. Rows come back ordered by distance, closest first, each
|
|
180
|
+
* carrying a `_distance`. Combines with `where` and `logical`, which are
|
|
181
|
+
* applied as filters before the ordering — so this is "the nearest rows
|
|
182
|
+
* that also match", not "the nearest rows, then filtered".
|
|
183
|
+
*
|
|
184
|
+
* Supplying the query vector is the caller's job: rebase stores and
|
|
185
|
+
* searches embeddings, it does not compute them.
|
|
186
|
+
*/
|
|
187
|
+
vectorSearch?: VectorSearchParams;
|
|
188
|
+
/**
|
|
189
|
+
* Ask each returned row to explain itself: which declared search fields
|
|
190
|
+
* matched, with a highlighted snippet from each. Populates `_matches`.
|
|
191
|
+
*
|
|
192
|
+
* Off by default because it is not free — one `ts_headline` per declared
|
|
193
|
+
* field per returned row, and `ts_headline` re-parses the document rather
|
|
194
|
+
* than reading the index. Fine for a page of results, not for an export.
|
|
195
|
+
*
|
|
196
|
+
* Ignored unless the collection declares a `search` block and the query
|
|
197
|
+
* carries a `searchString`; there is nothing to explain otherwise.
|
|
198
|
+
*/
|
|
199
|
+
searchExplain?: boolean;
|
|
94
200
|
}
|
|
95
201
|
/**
|
|
96
202
|
* Paginated response from a collection query.
|
|
@@ -108,34 +214,49 @@ export interface FindResponse<M extends Record<string, unknown> = Record<string,
|
|
|
108
214
|
};
|
|
109
215
|
}
|
|
110
216
|
/**
|
|
111
|
-
* Fluent query builder for the **admin
|
|
217
|
+
* Fluent query builder for the **admin panel** — resolves to `FindResponse<M>`
|
|
112
218
|
* (Snapshot-wrapped rows).
|
|
113
219
|
*
|
|
114
220
|
* @internal App developers should use {@link SDKQueryBuilderInterface}
|
|
115
221
|
* (flat rows, returned by `client.data.*` / `context.data.*`). This
|
|
116
|
-
* Snapshot-flavored variant backs the admin
|
|
222
|
+
* Snapshot-flavored variant backs the admin panel internals only.
|
|
117
223
|
*
|
|
118
224
|
* @group Data
|
|
119
225
|
*/
|
|
120
226
|
export interface QueryBuilderInterface<M extends Record<string, unknown> = Record<string, unknown>> {
|
|
121
|
-
where<K extends keyof M & string>(column: K, operator:
|
|
227
|
+
where<K extends keyof M & string, Op extends WhereFilterOp>(column: K, operator: Op, value: WhereValueFor<Op, M[K]>): this;
|
|
122
228
|
where(logicalCondition: LogicalCondition): this;
|
|
123
|
-
orderBy(column: keyof M & string, direction?: "asc" | "desc"): this;
|
|
229
|
+
orderBy(column: (keyof M & string) | ComputedSortField, direction?: "asc" | "desc"): this;
|
|
124
230
|
limit(count: number): this;
|
|
125
231
|
offset(count: number): this;
|
|
126
|
-
search(searchString: string
|
|
232
|
+
search(searchString: string, options?: {
|
|
233
|
+
explain?: boolean;
|
|
234
|
+
}): this;
|
|
235
|
+
/**
|
|
236
|
+
* Order rows by nearest-neighbour distance to `vector`, closest first.
|
|
237
|
+
*
|
|
238
|
+
* Postgres only, over a property declared as `type: "vector"`. Each row
|
|
239
|
+
* comes back with a `_distance`. Any `where` on the same query filters
|
|
240
|
+
* before the ordering; distance decides the order.
|
|
241
|
+
*
|
|
242
|
+
* The query embedding is the caller's to produce.
|
|
243
|
+
*/
|
|
244
|
+
vectorSearch(property: string, vector: number[], options?: {
|
|
245
|
+
distance?: "cosine" | "l2" | "inner_product";
|
|
246
|
+
threshold?: number;
|
|
247
|
+
}): this;
|
|
127
248
|
include(...relations: string[]): this;
|
|
128
249
|
find(): Promise<FindResponse<M>>;
|
|
129
250
|
listen(onUpdate: (data: FindResponse<M>) => void, onError?: (error: Error) => void): () => void;
|
|
130
251
|
}
|
|
131
252
|
/**
|
|
132
|
-
* A single collection's CRUD accessor for the **admin
|
|
253
|
+
* A single collection's CRUD accessor for the **admin panel** — every method
|
|
133
254
|
* resolves to `Snapshot`-wrapped rows (`FindResponse<M>` / `Snapshot<M>`).
|
|
134
255
|
*
|
|
135
256
|
* @internal App developers do **not** use this. The public, symmetric surface
|
|
136
257
|
* is {@link SDKCollectionClient} (flat rows), exposed as `client.data.products`
|
|
137
258
|
* in the SDK and `context.data.products` in framework callbacks. This
|
|
138
|
-
* Snapshot-flavored accessor backs the admin
|
|
259
|
+
* Snapshot-flavored accessor backs the admin panel view-model only.
|
|
139
260
|
*
|
|
140
261
|
* @group Data
|
|
141
262
|
*/
|
|
@@ -169,6 +290,21 @@ export interface CollectionAccessor<M extends Record<string, unknown> = Record<s
|
|
|
169
290
|
* @returns The updated entity
|
|
170
291
|
*/
|
|
171
292
|
update(id: string | number, data: Partial<EntityValues<M>>): Promise<Entity<M>>;
|
|
293
|
+
/**
|
|
294
|
+
* Update many records in a single transaction.
|
|
295
|
+
*
|
|
296
|
+
* See {@link SDKCollectionClient.updateMany}. Optional, as `createMany` is.
|
|
297
|
+
*/
|
|
298
|
+
updateMany?(updates: {
|
|
299
|
+
id: string | number;
|
|
300
|
+
data: Partial<EntityValues<M>>;
|
|
301
|
+
}[]): Promise<Entity<M>[]>;
|
|
302
|
+
/**
|
|
303
|
+
* Delete many records in a single transaction.
|
|
304
|
+
*
|
|
305
|
+
* See {@link SDKCollectionClient.deleteMany}. Optional, as `createMany` is.
|
|
306
|
+
*/
|
|
307
|
+
deleteMany?(ids: (string | number)[]): Promise<void>;
|
|
172
308
|
/**
|
|
173
309
|
* Delete a record by ID.
|
|
174
310
|
*/
|
|
@@ -185,14 +321,35 @@ export interface CollectionAccessor<M extends Record<string, unknown> = Record<s
|
|
|
185
321
|
listenById?(id: string | number, onUpdate: (entity: Entity<M> | undefined) => void, onError?: (error: Error) => void): () => void;
|
|
186
322
|
/**
|
|
187
323
|
* Count the number of records matching the given filter.
|
|
324
|
+
*
|
|
325
|
+
* Optional on this contract because a data source need not support it, and
|
|
326
|
+
* required on `CollectionClient` — the HTTP implementation always has it.
|
|
327
|
+
* So `client.data.posts.count()` compiles in the browser while the same
|
|
328
|
+
* call through a `context.data` accessor needs `count?.()`, which is the
|
|
329
|
+
* one place the two halves of this API are not interchangeable.
|
|
188
330
|
*/
|
|
189
331
|
count?(params?: FindParams<M>): Promise<number>;
|
|
190
|
-
where<K extends keyof M & string>(column: K, operator:
|
|
332
|
+
where<K extends keyof M & string, Op extends WhereFilterOp>(column: K, operator: Op, value: WhereValueFor<Op, M[K]>): QueryBuilderInterface<M>;
|
|
191
333
|
where(logicalCondition: LogicalCondition): QueryBuilderInterface<M>;
|
|
192
|
-
orderBy(column: keyof M & string, direction?: "asc" | "desc"): QueryBuilderInterface<M>;
|
|
334
|
+
orderBy(column: (keyof M & string) | ComputedSortField, direction?: "asc" | "desc"): QueryBuilderInterface<M>;
|
|
193
335
|
limit(count: number): QueryBuilderInterface<M>;
|
|
194
336
|
offset(count: number): QueryBuilderInterface<M>;
|
|
195
|
-
search(searchString: string
|
|
337
|
+
search(searchString: string, options?: {
|
|
338
|
+
explain?: boolean;
|
|
339
|
+
}): QueryBuilderInterface<M>;
|
|
340
|
+
/**
|
|
341
|
+
* Order rows by nearest-neighbour distance to `vector`, closest first.
|
|
342
|
+
*
|
|
343
|
+
* Postgres only, over a property declared as `type: "vector"`. Each row
|
|
344
|
+
* comes back with a `_distance`. Any `where` on the same query filters
|
|
345
|
+
* before the ordering; distance decides the order.
|
|
346
|
+
*
|
|
347
|
+
* The query embedding is the caller's to produce.
|
|
348
|
+
*/
|
|
349
|
+
vectorSearch(property: string, vector: number[], options?: {
|
|
350
|
+
distance?: "cosine" | "l2" | "inner_product";
|
|
351
|
+
threshold?: number;
|
|
352
|
+
}): QueryBuilderInterface<M>;
|
|
196
353
|
include(...relations: string[]): QueryBuilderInterface<M>;
|
|
197
354
|
}
|
|
198
355
|
/**
|
|
@@ -217,11 +374,53 @@ export interface PaginationMeta {
|
|
|
217
374
|
* @group Data
|
|
218
375
|
*/
|
|
219
376
|
export interface FindResult<M extends Record<string, unknown> = Record<string, unknown>> {
|
|
220
|
-
/**
|
|
221
|
-
|
|
377
|
+
/**
|
|
378
|
+
* Flat rows matching the query, each carrying whatever the query computed
|
|
379
|
+
* for it — see {@link QueryComputedFields}.
|
|
380
|
+
*/
|
|
381
|
+
data: (M & QueryComputedFields)[];
|
|
222
382
|
/** Pagination metadata */
|
|
223
383
|
meta: PaginationMeta;
|
|
224
384
|
}
|
|
385
|
+
/**
|
|
386
|
+
* Values a query attaches to a row that are not columns of it.
|
|
387
|
+
*
|
|
388
|
+
* Both are absent unless the query asked for the thing that produces them, so
|
|
389
|
+
* both are optional — and reading one on a query that did not ask returns
|
|
390
|
+
* `undefined` rather than a wrong number.
|
|
391
|
+
*
|
|
392
|
+
* They live here rather than on the row type because a generated row type
|
|
393
|
+
* describes a *table*, and neither of these is in one. Without this, a caller
|
|
394
|
+
* who sorted by relevance could not then read the relevance.
|
|
395
|
+
*
|
|
396
|
+
* A `type` alias, deliberately, not an `interface`. TypeScript grants an
|
|
397
|
+
* implicit index signature to a type alias and withholds it from an interface,
|
|
398
|
+
* so `Row & QueryComputedFields` stops being assignable to
|
|
399
|
+
* `Record<string, unknown>` the moment this becomes an interface. Seven casts
|
|
400
|
+
* in one downstream app broke on exactly that.
|
|
401
|
+
*
|
|
402
|
+
* @group Data
|
|
403
|
+
*/
|
|
404
|
+
export type QueryComputedFields = {
|
|
405
|
+
/**
|
|
406
|
+
* Relevance, when the collection declares a {@link SearchConfig} and the
|
|
407
|
+
* query carried a search string. Higher is better; the scale is not
|
|
408
|
+
* comparable between two different search strings.
|
|
409
|
+
*/
|
|
410
|
+
_score?: number;
|
|
411
|
+
/**
|
|
412
|
+
* Which declared fields matched, and the text around each hit. Present only
|
|
413
|
+
* when the query asked for it — `.search(term, { explain: true })` — because
|
|
414
|
+
* it costs a `ts_headline` per field per row.
|
|
415
|
+
*/
|
|
416
|
+
_matches?: SearchMatch[];
|
|
417
|
+
/**
|
|
418
|
+
* Distance to the query vector, when the query used
|
|
419
|
+
* {@link FindParams.vectorSearch}. Lower is closer, and the rows are
|
|
420
|
+
* already ordered by it.
|
|
421
|
+
*/
|
|
422
|
+
_distance?: number;
|
|
423
|
+
};
|
|
225
424
|
/**
|
|
226
425
|
* Which column an iteration seeks on, for keyset ("seek") pagination.
|
|
227
426
|
*
|
|
@@ -309,12 +508,27 @@ export type FindAllParams<M extends Record<string, unknown> = Record<string, unk
|
|
|
309
508
|
* @group Data
|
|
310
509
|
*/
|
|
311
510
|
export interface SDKQueryBuilderInterface<M extends Record<string, unknown> = Record<string, unknown>> {
|
|
312
|
-
where<K extends keyof M & string>(column: K, operator:
|
|
511
|
+
where<K extends keyof M & string, Op extends WhereFilterOp>(column: K, operator: Op, value: WhereValueFor<Op, M[K]>): this;
|
|
313
512
|
where(logicalCondition: LogicalCondition): this;
|
|
314
|
-
orderBy(column: keyof M & string, direction?: "asc" | "desc"): this;
|
|
513
|
+
orderBy(column: (keyof M & string) | ComputedSortField, direction?: "asc" | "desc"): this;
|
|
315
514
|
limit(count: number): this;
|
|
316
515
|
offset(count: number): this;
|
|
317
|
-
search(searchString: string
|
|
516
|
+
search(searchString: string, options?: {
|
|
517
|
+
explain?: boolean;
|
|
518
|
+
}): this;
|
|
519
|
+
/**
|
|
520
|
+
* Order rows by nearest-neighbour distance to `vector`, closest first.
|
|
521
|
+
*
|
|
522
|
+
* Postgres only, over a property declared as `type: "vector"`. Each row
|
|
523
|
+
* comes back with a `_distance`. Any `where` on the same query filters
|
|
524
|
+
* before the ordering; distance decides the order.
|
|
525
|
+
*
|
|
526
|
+
* The query embedding is the caller's to produce.
|
|
527
|
+
*/
|
|
528
|
+
vectorSearch(property: string, vector: number[], options?: {
|
|
529
|
+
distance?: "cosine" | "l2" | "inner_product";
|
|
530
|
+
threshold?: number;
|
|
531
|
+
}): this;
|
|
318
532
|
include(...relations: string[]): this;
|
|
319
533
|
find(): Promise<FindResult<M>>;
|
|
320
534
|
count(): Promise<number>;
|
|
@@ -359,9 +573,20 @@ export interface WriteOptions {
|
|
|
359
573
|
* it performs it again. On a table with a server-assigned id that is a
|
|
360
574
|
* duplicate row, because the id the client chose was never used.
|
|
361
575
|
*
|
|
576
|
+
* A key names **one** request, not a job. It records the method, the path
|
|
577
|
+
* and the body it was claimed for, so re-sending that exact request replays
|
|
578
|
+
* its answer, while the same key on a different one is refused with
|
|
579
|
+
* `IDEMPOTENCY_KEY_REUSED` (422) rather than silently answered with the
|
|
580
|
+
* first request's result. Pass a fresh key — a uuid — per call; a reusable
|
|
581
|
+
* business id shared by the create and the delete of one import means the
|
|
582
|
+
* second of them never runs.
|
|
583
|
+
*
|
|
362
584
|
* Set by the offline queue on every replay. Honoured for 24 hours and scoped
|
|
363
|
-
* to the authenticated user
|
|
364
|
-
*
|
|
585
|
+
* to the authenticated user — an unauthenticated caller has no principal to
|
|
586
|
+
* scope it to, so the key is ignored there. A retry sent while the first
|
|
587
|
+
* attempt is still being answered gets `IDEMPOTENCY_KEY_IN_PROGRESS` (409)
|
|
588
|
+
* and should be sent again. A server that cannot store keys ignores the
|
|
589
|
+
* header rather than refusing the write.
|
|
365
590
|
*/
|
|
366
591
|
idempotencyKey?: string;
|
|
367
592
|
}
|
|
@@ -461,6 +686,12 @@ export interface SDKCollectionClient<M extends Record<string, unknown> = Record<
|
|
|
461
686
|
* Batches are capped server-side (1000 rows by default) because one batch
|
|
462
687
|
* holds its locks for the whole transaction — chunk larger jobs.
|
|
463
688
|
*
|
|
689
|
+
* Pass {@link WriteOptions.idempotencyKey} on anything that may be retried.
|
|
690
|
+
* A client that never sees the response cannot know whether the batch
|
|
691
|
+
* committed, and without a key the server cannot tell the retry from a
|
|
692
|
+
* second genuine import — so it performs it again, duplicating every row in
|
|
693
|
+
* the batch rather than just one.
|
|
694
|
+
*
|
|
464
695
|
* @returns The written rows, in the order given.
|
|
465
696
|
*
|
|
466
697
|
* @example
|
|
@@ -472,7 +703,7 @@ export interface SDKCollectionClient<M extends Record<string, unknown> = Record<
|
|
|
472
703
|
*/
|
|
473
704
|
createMany(data: I[], options?: {
|
|
474
705
|
upsert?: boolean;
|
|
475
|
-
}): Promise<M[]>;
|
|
706
|
+
} & WriteOptions): Promise<M[]>;
|
|
476
707
|
/**
|
|
477
708
|
* Update an existing record by ID.
|
|
478
709
|
* @param data The fields to update (the collection's `Update` shape).
|
|
@@ -480,33 +711,120 @@ export interface SDKCollectionClient<M extends Record<string, unknown> = Record<
|
|
|
480
711
|
* @throws {RebaseApiError} with status 404 when the record does not exist.
|
|
481
712
|
*/
|
|
482
713
|
update(id: string | number, data: U): Promise<M>;
|
|
714
|
+
/**
|
|
715
|
+
* Update many records in a single request and a single transaction.
|
|
716
|
+
*
|
|
717
|
+
* The counterpart to {@link createMany}, and the reason it exists is the
|
|
718
|
+
* same: one call per row means one HTTP round trip and one transaction per
|
|
719
|
+
* row. Every record still runs the normal pipeline — callbacks, relations,
|
|
720
|
+
* row-level security — and the batch is all-or-nothing, so a rejected
|
|
721
|
+
* record leaves none of them written and the error names the offending
|
|
722
|
+
* index.
|
|
723
|
+
*
|
|
724
|
+
* Each entry is `{ id, data }` rather than a flat row carrying its own key.
|
|
725
|
+
* That is deliberate: on a table keyed on something other than `id` — a
|
|
726
|
+
* `sku`, a composite key — a flat row cannot say whether a column is the
|
|
727
|
+
* address or a value to write. Naming the address separately mirrors
|
|
728
|
+
* single-row `update(id, data)` exactly and leaves nothing to infer.
|
|
729
|
+
*
|
|
730
|
+
* An id that matches no row fails the batch with a 404 rather than being
|
|
731
|
+
* skipped, for the same reason `update()` does: silently updating four of
|
|
732
|
+
* five rows is worse than updating none.
|
|
733
|
+
*
|
|
734
|
+
* Batches share `createMany`'s server-side cap (1000 rows by default),
|
|
735
|
+
* because one batch holds its locks for the whole transaction.
|
|
736
|
+
*
|
|
737
|
+
* Pass {@link WriteOptions.idempotencyKey} on anything that may be retried.
|
|
738
|
+
* An update replayed in full is naturally idempotent, but one interleaved
|
|
739
|
+
* with another writer's is not — the key is what stops a lost ACK from
|
|
740
|
+
* re-applying a stale batch over newer data.
|
|
741
|
+
*
|
|
742
|
+
* @returns The updated rows, in the order given.
|
|
743
|
+
*
|
|
744
|
+
* @example
|
|
745
|
+
* ```ts
|
|
746
|
+
* await client.data.orders.updateMany([
|
|
747
|
+
* { id: "o-1", data: { status: "shipped" } },
|
|
748
|
+
* { id: "o-2", data: { status: "shipped" } }
|
|
749
|
+
* ]);
|
|
750
|
+
* ```
|
|
751
|
+
*/
|
|
752
|
+
updateMany(updates: {
|
|
753
|
+
id: string | number;
|
|
754
|
+
data: U;
|
|
755
|
+
}[], options?: WriteOptions): Promise<M[]>;
|
|
483
756
|
/**
|
|
484
757
|
* Delete a record by ID.
|
|
485
758
|
* @throws {RebaseApiError} with status 404 when the record does not exist.
|
|
486
759
|
*/
|
|
487
760
|
delete(id: string | number): Promise<void>;
|
|
488
761
|
/**
|
|
489
|
-
*
|
|
762
|
+
* Delete many records in a single request and a single transaction.
|
|
763
|
+
*
|
|
764
|
+
* Takes ids, not a filter. A filter-shaped bulk delete is a different and
|
|
765
|
+
* far more dangerous operation — the failure mode is an omitted or
|
|
766
|
+
* mistyped condition emptying a table, and it cannot be reviewed at the
|
|
767
|
+
* call site the way an explicit list can. Read first, then pass the ids you
|
|
768
|
+
* meant.
|
|
769
|
+
*
|
|
770
|
+
* `beforeDelete` and `afterDelete` fire per row, exactly as they do for
|
|
771
|
+
* single deletes, and returning `false` from `beforeDelete` fails the batch
|
|
772
|
+
* rather than quietly dropping one row from it. All-or-nothing, so an id
|
|
773
|
+
* that matches no row 404s the whole call.
|
|
774
|
+
*
|
|
775
|
+
* Shares `createMany`'s row cap.
|
|
776
|
+
*
|
|
777
|
+
* @example
|
|
778
|
+
* ```ts
|
|
779
|
+
* const stale = await client.data.sessions.findAll({
|
|
780
|
+
* where: { expires_at: ["<", cutoff] }
|
|
781
|
+
* });
|
|
782
|
+
* await client.data.sessions.deleteMany(stale.map(s => s.id as string));
|
|
783
|
+
* ```
|
|
490
784
|
*/
|
|
491
|
-
|
|
785
|
+
deleteMany(ids: (string | number)[], options?: WriteOptions): Promise<void>;
|
|
492
786
|
/**
|
|
493
|
-
*
|
|
787
|
+
* The low-level realtime subscription: raw server pushes, nothing else.
|
|
788
|
+
*
|
|
789
|
+
* **Prefer `observe()`** on a client from `@rebasepro/client`, which wraps
|
|
790
|
+
* this one and is what a UI actually wants — it emits from the local
|
|
791
|
+
* database first when offline is enabled, re-emits on local writes and
|
|
792
|
+
* rollbacks, and de-duplicates emissions so a refresh that changes nothing
|
|
793
|
+
* does not call back. `listen` does none of that; it forwards what the
|
|
794
|
+
* socket sends.
|
|
795
|
+
*
|
|
796
|
+
* Optional because it is only present when realtime is enabled. `observe()`
|
|
797
|
+
* is not — it degrades to a single fetch — which is the other reason to
|
|
798
|
+
* reach for it instead.
|
|
494
799
|
*/
|
|
800
|
+
listen?(params: FindParams<M> | undefined, onUpdate: (response: FindResult<M>) => void, onError?: (error: Error) => void): () => void;
|
|
801
|
+
/** {@link listen} for a single row. Prefer `observeById()`. */
|
|
495
802
|
listenById?(id: string | number, onUpdate: (row: M | undefined) => void, onError?: (error: Error) => void): () => void;
|
|
496
803
|
/**
|
|
497
804
|
* Count the number of records matching the given filter.
|
|
498
805
|
*/
|
|
499
806
|
count?(params?: FindParams<M>): Promise<number>;
|
|
500
|
-
where<K extends keyof M & string>(column: K, operator:
|
|
807
|
+
where<K extends keyof M & string, Op extends WhereFilterOp>(column: K, operator: Op, value: WhereValueFor<Op, M[K]>): SDKQueryBuilderInterface<M>;
|
|
501
808
|
where(logicalCondition: LogicalCondition): SDKQueryBuilderInterface<M>;
|
|
502
|
-
orderBy(column: keyof M & string, direction?: "asc" | "desc"): SDKQueryBuilderInterface<M>;
|
|
809
|
+
orderBy(column: (keyof M & string) | ComputedSortField, direction?: "asc" | "desc"): SDKQueryBuilderInterface<M>;
|
|
503
810
|
limit(count: number): SDKQueryBuilderInterface<M>;
|
|
504
811
|
offset(count: number): SDKQueryBuilderInterface<M>;
|
|
505
|
-
search(searchString: string
|
|
812
|
+
search(searchString: string, options?: {
|
|
813
|
+
explain?: boolean;
|
|
814
|
+
}): SDKQueryBuilderInterface<M>;
|
|
815
|
+
/**
|
|
816
|
+
* Order rows by nearest-neighbour distance to `vector`, closest first.
|
|
817
|
+
* Postgres only, over a `type: "vector"` property. See
|
|
818
|
+
* {@link SDKQueryBuilderInterface.vectorSearch}.
|
|
819
|
+
*/
|
|
820
|
+
vectorSearch(property: string, vector: number[], options?: {
|
|
821
|
+
distance?: "cosine" | "l2" | "inner_product";
|
|
822
|
+
threshold?: number;
|
|
823
|
+
}): SDKQueryBuilderInterface<M>;
|
|
506
824
|
include(...relations: string[]): SDKQueryBuilderInterface<M>;
|
|
507
825
|
}
|
|
508
826
|
/**
|
|
509
|
-
* The unified data access object for the **admin
|
|
827
|
+
* The unified data access object for the **admin panel** (Entity-shaped).
|
|
510
828
|
*
|
|
511
829
|
* Access collections as dynamic properties: `data.products.find(...)`. Each
|
|
512
830
|
* accessor returns `Entity`-wrapped records (`{ id, path, values }`) — the
|
|
@@ -515,7 +833,7 @@ export interface SDKCollectionClient<M extends Record<string, unknown> = Record<
|
|
|
515
833
|
*
|
|
516
834
|
* @internal App developers do **not** use this — they use
|
|
517
835
|
* {@link RebaseSdkData} (flat rows), which is what the SDK client and backend
|
|
518
|
-
* `context.data` expose. This Entity-shaped map backs the admin
|
|
836
|
+
* `context.data` expose. This Entity-shaped map backs the admin panel only.
|
|
519
837
|
*
|
|
520
838
|
* @group Data
|
|
521
839
|
*/
|
|
@@ -539,10 +857,15 @@ export type RebaseData<DB = unknown> = {
|
|
|
539
857
|
* Dynamic collection accessor.
|
|
540
858
|
* Access any collection by its slug as a property.
|
|
541
859
|
*
|
|
860
|
+
* The index signature is `CollectionAccessor` alone, for the reason
|
|
861
|
+
* spelled out on {@link RebaseSdkData}: unioning in the `collection`
|
|
862
|
+
* method's own signature is unnecessary across an intersection, and it
|
|
863
|
+
* costs `data.products.find()` — the access this `@example` documents.
|
|
864
|
+
*
|
|
542
865
|
* @example
|
|
543
866
|
* data.products.find({ where: { status: ["==", "published"] } })
|
|
544
867
|
*/
|
|
545
|
-
[collectionSlug: string]: CollectionAccessor
|
|
868
|
+
[collectionSlug: string]: CollectionAccessor;
|
|
546
869
|
});
|
|
547
870
|
/**
|
|
548
871
|
* The unified data access object for the **SDK** — flat rows, no Entity wrapper.
|
|
@@ -595,6 +918,26 @@ export type InsertOf<T> = T extends {
|
|
|
595
918
|
export type UpdateOf<T> = T extends {
|
|
596
919
|
Update: infer U extends Record<string, unknown>;
|
|
597
920
|
} ? U : Partial<RowOf<T>>;
|
|
921
|
+
/**
|
|
922
|
+
* Note on the untyped branch below: its index signature is
|
|
923
|
+
* `SDKCollectionClient`, NOT `SDKCollectionClient | ((slug: string) => …)`.
|
|
924
|
+
*
|
|
925
|
+
* The union looks like it is needed so `collection` — a method on this same
|
|
926
|
+
* object — satisfies the index signature. It is not, because `collection` is
|
|
927
|
+
* declared in a *separate* member of the intersection, and TypeScript only
|
|
928
|
+
* requires named properties to be assignable to an index signature declared
|
|
929
|
+
* alongside them. Including the function arm cost the documented accessor:
|
|
930
|
+
*
|
|
931
|
+
* rebase.dataAsAdmin.projects.find()
|
|
932
|
+
* // ^ Property 'find' does not exist on type
|
|
933
|
+
* // 'SDKCollectionClient | ((slug: string) => …)'
|
|
934
|
+
*
|
|
935
|
+
* Every project without a generated `Database` type lands on this branch, so
|
|
936
|
+
* property-style access — the form used by the `@example` below, by the
|
|
937
|
+
* scaffolded function template, and by the 0.13 migration note — did not
|
|
938
|
+
* compile for any of them. Do not restore the arm; use `collection(slug)` if a
|
|
939
|
+
* caller genuinely needs the by-slug function.
|
|
940
|
+
*/
|
|
598
941
|
export type RebaseSdkData<DB = unknown> = {
|
|
599
942
|
/**
|
|
600
943
|
* Get a flat collection accessor by slug.
|
|
@@ -614,5 +957,5 @@ export type RebaseSdkData<DB = unknown> = {
|
|
|
614
957
|
* @example
|
|
615
958
|
* data.products.find({ where: { status: ["==", "published"] } })
|
|
616
959
|
*/
|
|
617
|
-
[collectionSlug: string]: SDKCollectionClient
|
|
960
|
+
[collectionSlug: string]: SDKCollectionClient;
|
|
618
961
|
});
|