@rebasepro/types 0.17.3 → 0.18.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/README.md +4 -0
- package/dist/call_context.d.ts +20 -0
- package/dist/controllers/client.d.ts +36 -4
- package/dist/controllers/data.d.ts +120 -10
- package/dist/errors.d.ts +83 -4
- package/dist/index.es.js +522 -160
- package/dist/index.es.js.map +1 -1
- package/dist/types/admin_block.d.ts +2 -2
- package/dist/types/auth_adapter.d.ts +41 -6
- package/dist/types/backend.d.ts +48 -0
- package/dist/types/collections.d.ts +25 -1
- package/dist/types/cron.d.ts +34 -0
- package/dist/types/database_adapter.d.ts +39 -0
- package/dist/types/entity_callbacks.d.ts +14 -1
- package/dist/types/filter-operators.d.ts +24 -1
- package/dist/types/policy.d.ts +29 -1
- package/dist/types/properties.d.ts +216 -3
- package/dist/types/relations.d.ts +65 -7
- package/dist/types/resource_kinds.d.ts +173 -17
- package/dist/types/resources.d.ts +108 -7
- package/dist/types/rls-functions.d.ts +11 -0
- package/dist/types/storage_source.d.ts +12 -23
- package/package.json +24 -23
- package/src/call_context.ts +0 -120
- package/src/controllers/auth_state.ts +0 -24
- package/src/controllers/client.ts +0 -494
- package/src/controllers/collection_registry.ts +0 -62
- package/src/controllers/data.ts +0 -1012
- package/src/controllers/data_driver.ts +0 -576
- package/src/controllers/effective_role.ts +0 -4
- package/src/controllers/email.ts +0 -91
- package/src/controllers/index.ts +0 -11
- package/src/controllers/storage.ts +0 -252
- package/src/errors.ts +0 -119
- package/src/index.ts +0 -5
- package/src/types/admin_block.ts +0 -209
- package/src/types/api_keys.ts +0 -108
- package/src/types/auth_adapter.ts +0 -580
- package/src/types/backend.ts +0 -987
- package/src/types/backup.ts +0 -26
- package/src/types/channel_bus.ts +0 -202
- package/src/types/chips.ts +0 -34
- package/src/types/collection_contract.ts +0 -278
- package/src/types/collections.ts +0 -763
- package/src/types/component_ref.ts +0 -92
- package/src/types/cron.ts +0 -213
- package/src/types/data_source.ts +0 -357
- package/src/types/database_adapter.ts +0 -267
- package/src/types/entities.ts +0 -226
- package/src/types/entity_callbacks.ts +0 -229
- package/src/types/filter-operators.ts +0 -444
- package/src/types/history.ts +0 -66
- package/src/types/index.ts +0 -36
- package/src/types/indexes.ts +0 -180
- package/src/types/policy.ts +0 -328
- package/src/types/postgres_introspection.ts +0 -101
- package/src/types/project_manifest.ts +0 -598
- package/src/types/properties.ts +0 -1368
- package/src/types/relations.ts +0 -417
- package/src/types/resource_kinds.ts +0 -390
- package/src/types/resources.ts +0 -368
- package/src/types/rls-functions.ts +0 -98
- package/src/types/schema_editing.ts +0 -157
- package/src/types/schema_version.ts +0 -112
- package/src/types/search.ts +0 -247
- package/src/types/security_rules.ts +0 -344
- package/src/types/storage_authorize.ts +0 -77
- package/src/types/storage_source.ts +0 -248
- package/src/types/websockets.ts +0 -117
- package/src/users/index.ts +0 -2
- package/src/users/user.ts +0 -69
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
import type { CollectionConfig } from "./collections";
|
|
2
|
-
import type { EntityStatus, EntityValues } from "./entities";
|
|
3
|
-
import type { User } from "../users";
|
|
4
|
-
import type { RebaseCallContext } from "../call_context";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Lifecycle callbacks for entity CRUD operations.
|
|
8
|
-
*
|
|
9
|
-
* Register per-collection on the collection's `callbacks` field, or globally
|
|
10
|
-
* via `initializeRebaseBackend({ callbacks })`. Fires on **every** data path — REST API,
|
|
11
|
-
* WebSocket / realtime subscriptions, and server-side writes through
|
|
12
|
-
* `rebase.dataAsAdmin`.
|
|
13
|
-
*
|
|
14
|
-
* When both global and per-collection callbacks are registered, execution
|
|
15
|
-
* order is: **global → collection → property callbacks**.
|
|
16
|
-
*
|
|
17
|
-
* @group Models
|
|
18
|
-
*/
|
|
19
|
-
export type CollectionCallbacks<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User> = {
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Callback used after fetching data.
|
|
23
|
-
*
|
|
24
|
-
* Fires on every read path. Use this for security-critical redaction
|
|
25
|
-
* (PII masking, row filtering) — no read path bypasses it.
|
|
26
|
-
*
|
|
27
|
-
* @param props
|
|
28
|
-
*/
|
|
29
|
-
afterRead?(props: AfterReadProps<M, USER>)
|
|
30
|
-
: Promise<Record<string, unknown>> | Record<string, unknown>;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Callback used before saving, you need to return the values that will get
|
|
35
|
-
* saved. If you throw an error in this method the process stops, and an
|
|
36
|
-
* HTTP error response is returned to the client.
|
|
37
|
-
* This runs after schema validation.
|
|
38
|
-
*
|
|
39
|
-
* @param props
|
|
40
|
-
*/
|
|
41
|
-
beforeSave?(props: BeforeSaveProps<M, USER>)
|
|
42
|
-
: Promise<Partial<EntityValues<M>>> | Partial<EntityValues<M>>;
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Callback used when save is successful.
|
|
46
|
-
*
|
|
47
|
-
* @param props
|
|
48
|
-
*/
|
|
49
|
-
afterSave?(props: AfterSaveProps<M, USER>)
|
|
50
|
-
: Promise<void> | void;
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Callback used when saving fails
|
|
54
|
-
* @param props
|
|
55
|
-
*/
|
|
56
|
-
afterSaveError?(props: AfterSaveErrorProps<M, USER>)
|
|
57
|
-
: Promise<void> | void;
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Callback used before the entity is deleted.
|
|
61
|
-
* If you throw an error in this method the process stops, and an
|
|
62
|
-
* HTTP error response is returned to the client.
|
|
63
|
-
*
|
|
64
|
-
* @param props
|
|
65
|
-
*/
|
|
66
|
-
beforeDelete?(props: BeforeDeleteProps<M, USER>): Promise<boolean | void> | boolean | void;
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Callback used after the entity is deleted.
|
|
70
|
-
*
|
|
71
|
-
* @param props
|
|
72
|
-
*/
|
|
73
|
-
afterDelete?(props: AfterDeleteProps<M, USER>): Promise<void> | void;
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Parameters passed to hooks when a entity is fetched
|
|
79
|
-
* @group Models
|
|
80
|
-
*/
|
|
81
|
-
export interface AfterReadProps<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User> {
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Collection of the entity
|
|
85
|
-
*/
|
|
86
|
-
collection: CollectionConfig<M>;
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Full path of the admin where this collection is being fetched.
|
|
90
|
-
* Might contain unresolved aliases.
|
|
91
|
-
*/
|
|
92
|
-
path: string;
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Fetched row (flat — the table's columns)
|
|
96
|
-
*/
|
|
97
|
-
row: Record<string, unknown>
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Context of the app status
|
|
101
|
-
*/
|
|
102
|
-
context: RebaseCallContext<USER>;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Parameters passed to hooks before a entity is saved
|
|
107
|
-
* @group Models
|
|
108
|
-
*/
|
|
109
|
-
export type BeforeSaveProps<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User> =
|
|
110
|
-
Omit<AfterSaveProps<M, USER>, "id">
|
|
111
|
-
& {
|
|
112
|
-
id?: string | number;
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Parameters passed to hooks before a entity is saved
|
|
116
|
-
* @group Models
|
|
117
|
-
*/
|
|
118
|
-
export type AfterSaveErrorProps<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User> =
|
|
119
|
-
Omit<AfterSaveProps<M, USER>, "id">
|
|
120
|
-
& {
|
|
121
|
-
id?: string | number;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Parameters passed to hooks when a entity is saved
|
|
126
|
-
* @group Models
|
|
127
|
-
*/
|
|
128
|
-
export interface AfterSaveProps<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User> {
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Resolved collection of the entity
|
|
132
|
-
*/
|
|
133
|
-
collection: CollectionConfig<M>;
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Full path of the admin where this entity is being saved.
|
|
137
|
-
* Might contain unresolved aliases.
|
|
138
|
-
*/
|
|
139
|
-
path: string;
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* ID of the entity
|
|
143
|
-
*/
|
|
144
|
-
id: string | number;
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Values being saved
|
|
148
|
-
*/
|
|
149
|
-
values: Partial<EntityValues<M>>;
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Previous values
|
|
153
|
-
*/
|
|
154
|
-
previousValues?: Partial<EntityValues<M>>;
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* New or existing entity
|
|
158
|
-
*/
|
|
159
|
-
status: EntityStatus;
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Context of the app status
|
|
163
|
-
*/
|
|
164
|
-
context: RebaseCallContext<USER>;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Parameters passed to hooks when a entity is deleted
|
|
169
|
-
* @group Models
|
|
170
|
-
*/
|
|
171
|
-
export interface BeforeDeleteProps<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User> {
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* collection of the entity being deleted
|
|
175
|
-
*/
|
|
176
|
-
collection: CollectionConfig<M>;
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Path of the parent collection
|
|
180
|
-
*/
|
|
181
|
-
path: string;
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Deleted entity id
|
|
185
|
-
*/
|
|
186
|
-
id: string | number;
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Deleted row (flat — the table's columns)
|
|
190
|
-
*/
|
|
191
|
-
row: Record<string, unknown>;
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Context of the app status
|
|
195
|
-
*/
|
|
196
|
-
context: RebaseCallContext<USER>;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Parameters passed to hooks after a entity is deleted
|
|
201
|
-
* @group Models
|
|
202
|
-
*/
|
|
203
|
-
export interface AfterDeleteProps<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User> {
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* collection of the entity being deleted
|
|
207
|
-
*/
|
|
208
|
-
collection: CollectionConfig<M>;
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Path of the parent collection
|
|
212
|
-
*/
|
|
213
|
-
path: string;
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Deleted entity id
|
|
217
|
-
*/
|
|
218
|
-
id: string | number;
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Deleted row (flat — the table's columns)
|
|
222
|
-
*/
|
|
223
|
-
row: Record<string, unknown>;
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Context of the app status
|
|
227
|
-
*/
|
|
228
|
-
context: RebaseCallContext<USER>;
|
|
229
|
-
}
|
|
@@ -1,444 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Canonical filter operators and REST wire-format mappings.
|
|
3
|
-
*
|
|
4
|
-
* `WhereFilterOp` is THE operator type used at every layer — from React
|
|
5
|
-
* components through the SDK, server, and down to the database driver.
|
|
6
|
-
*
|
|
7
|
-
* PostgREST short-codes (`eq`, `gt`, `cs`, …) exist **only** at the
|
|
8
|
-
* HTTP wire boundary, handled by `serializeFilter` / `deserializeFilter`
|
|
9
|
-
* in `@rebasepro/common`.
|
|
10
|
-
*
|
|
11
|
-
* ┌──────────────────────┬───────────────┬──────────────────────────────┐
|
|
12
|
-
* │ Canonical │ REST short │ Meaning │
|
|
13
|
-
* ├──────────────────────┼───────────────┼──────────────────────────────┤
|
|
14
|
-
* │ "==" │ "eq" │ Equal │
|
|
15
|
-
* │ "!=" │ "neq" │ Not equal │
|
|
16
|
-
* │ ">" │ "gt" │ Greater than │
|
|
17
|
-
* │ ">=" │ "gte" │ Greater than or equal │
|
|
18
|
-
* │ "<" │ "lt" │ Less than │
|
|
19
|
-
* │ "<=" │ "lte" │ Less than or equal │
|
|
20
|
-
* │ "in" │ "in" │ Value in list │
|
|
21
|
-
* │ "not-in" │ "nin" │ Value not in list │
|
|
22
|
-
* │ "array-contains" │ "cs" │ Array contains element │
|
|
23
|
-
* │ "array-contains-any" │ "csa" │ Array contains any of │
|
|
24
|
-
* │ "like" │ "like" │ SQL LIKE (case-sensitive) │
|
|
25
|
-
* │ "ilike" │ "ilike" │ SQL ILIKE (case-insensitive) │
|
|
26
|
-
* │ "not-like" │ "nlike" │ NOT LIKE (case-sensitive) │
|
|
27
|
-
* │ "not-ilike" │ "nilike" │ NOT ILIKE (case-insensitive) │
|
|
28
|
-
* │ "is-null" │ "isnull" │ Field IS NULL │
|
|
29
|
-
* │ "is-not-null" │ "notnull" │ Field IS NOT NULL │
|
|
30
|
-
* └──────────────────────┴───────────────┴──────────────────────────────┘
|
|
31
|
-
*
|
|
32
|
-
* Pattern matching (`like`/`ilike`) uses SQL wildcard syntax: `%` matches any
|
|
33
|
-
* sequence of characters, `_` matches a single character. On MongoDB these are
|
|
34
|
-
* translated to anchored regular expressions; Firestore has no native pattern
|
|
35
|
-
* matching and rejects these operators (use `searchString` instead).
|
|
36
|
-
*
|
|
37
|
-
* @module
|
|
38
|
-
*/
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Canonical sort representation: `[fieldName, direction]`.
|
|
42
|
-
*
|
|
43
|
-
* Used in `FindParams.orderBy`, `collection.sort`, and `FilterPreset.sort`.
|
|
44
|
-
* The colon-string form (`"field:direction"`) exists only at the HTTP wire
|
|
45
|
-
* boundary, handled by `serializeOrderBy` / `deserializeOrderBy` in
|
|
46
|
-
* `@rebasepro/common`.
|
|
47
|
-
*
|
|
48
|
-
* @group Models
|
|
49
|
-
*/
|
|
50
|
-
export type OrderByTuple<Key extends string = string> = [Key, "asc" | "desc"];
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* One sort key, or several applied in order of significance.
|
|
54
|
-
*
|
|
55
|
-
* ```ts
|
|
56
|
-
* orderBy: ["created_at", "desc"] // one key
|
|
57
|
-
* orderBy: [["roles", "asc"], ["created_at", "desc"]] // roles, then newest first
|
|
58
|
-
* ```
|
|
59
|
-
*
|
|
60
|
-
* The two forms are told apart by whether the first element is itself an
|
|
61
|
-
* array, so a single tuple never needs wrapping and every existing caller
|
|
62
|
-
* keeps working unchanged. `normalizeOrderBy` in `@rebasepro/common` collapses
|
|
63
|
-
* both to the list form, which is what every layer below the call site speaks.
|
|
64
|
-
*
|
|
65
|
-
* Ties on the last key are broken by the row id, so a multi-key sort is a
|
|
66
|
-
* total order and pages over it neither repeat nor skip rows.
|
|
67
|
-
*
|
|
68
|
-
* @group Models
|
|
69
|
-
*/
|
|
70
|
-
export type OrderBySpec<Key extends string = string> =
|
|
71
|
-
| OrderBySortTuple<Key>
|
|
72
|
-
| OrderBySortTuple<Key>[];
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* A sort key: a field name, or an aggregate over a to-many relation.
|
|
76
|
-
*
|
|
77
|
-
* @group Models
|
|
78
|
-
*/
|
|
79
|
-
export type SortKey<Key extends string = string> = Key | RelationAggregateSort;
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* `[sortKey, direction]` — the authoring form of {@link OrderByTuple}, which
|
|
83
|
-
* additionally accepts a {@link RelationAggregateSort} object.
|
|
84
|
-
*
|
|
85
|
-
* The object never reaches a driver: `normalizeOrderBy` in `@rebasepro/common`
|
|
86
|
-
* encodes it to its string spelling on the way down, and everything below that
|
|
87
|
-
* point speaks plain `OrderByTuple`. See {@link RelationAggregateSort} for why
|
|
88
|
-
* the wire form is a string.
|
|
89
|
-
*
|
|
90
|
-
* @group Models
|
|
91
|
-
*/
|
|
92
|
-
export type OrderBySortTuple<Key extends string = string> = [SortKey<Key>, "asc" | "desc"];
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* The aggregate functions a relation sort can apply.
|
|
96
|
-
*
|
|
97
|
-
* Five, and no `array_agg`/`string_agg`: an aggregate used as a sort key has to
|
|
98
|
-
* produce something with an order, and these are the ones that do.
|
|
99
|
-
*
|
|
100
|
-
* @group Models
|
|
101
|
-
*/
|
|
102
|
-
export type RelationAggregateFn = "min" | "max" | "count" | "sum" | "avg";
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Order rows by an aggregate over the rows a to-many relation reaches —
|
|
106
|
-
* "candidates, oldest waiting first", "clients, busiest first".
|
|
107
|
-
*
|
|
108
|
-
* ```ts
|
|
109
|
-
* // The date of each candidate's earliest open application.
|
|
110
|
-
* orderBy: [[{ relation: "applications", field: "created_at", agg: "min" }, "asc"]]
|
|
111
|
-
*
|
|
112
|
-
* // How many applications each candidate has.
|
|
113
|
-
* orderBy: [[{ relation: "applications", agg: "count" }, "desc"]]
|
|
114
|
-
* ```
|
|
115
|
-
*
|
|
116
|
-
* This is the half of a queue that cannot be worked around client-side. A
|
|
117
|
-
* *filter* over a relation can be approximated by denormalising a flag onto the
|
|
118
|
-
* row; an *ordering* cannot be approximated at all once the result set is
|
|
119
|
-
* paged, because the client only ever holds one page and the page was chosen by
|
|
120
|
-
* the wrong order.
|
|
121
|
-
*
|
|
122
|
-
* Rows the relation reaches nothing from sort last ascending and first
|
|
123
|
-
* descending — the placement Postgres gives a `NULL`, stated rather than
|
|
124
|
-
* inherited, because the keyset comparison behind cursor paging has to agree
|
|
125
|
-
* with it exactly. Ties are broken by the row id, so the order is total and
|
|
126
|
-
* paging over it neither repeats nor skips.
|
|
127
|
-
*
|
|
128
|
-
* Compiled by the driver into a correlated subquery, so it is subject to the
|
|
129
|
-
* reader's own row-level security on the target table: a related row the reader
|
|
130
|
-
* cannot see does not contribute to the aggregate. Offered only where
|
|
131
|
-
* {@link DataSourceCapabilities.relationAggregateSorts} says the driver can
|
|
132
|
-
* compile it.
|
|
133
|
-
*
|
|
134
|
-
* @group Models
|
|
135
|
-
*/
|
|
136
|
-
export interface RelationAggregateSort {
|
|
137
|
-
/** The to-many relation to aggregate over, by its name on this collection. */
|
|
138
|
-
relation: string;
|
|
139
|
-
|
|
140
|
-
/** The aggregate to apply. */
|
|
141
|
-
agg: RelationAggregateFn;
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* The column of the *target* to aggregate. Required by every function
|
|
145
|
-
* except `count`, which counts the related rows themselves when it is
|
|
146
|
-
* omitted — and counts the rows whose column is non-null when it is not.
|
|
147
|
-
*/
|
|
148
|
-
field?: string;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/** The wire spelling of a {@link RelationAggregateSort}: `min(applications.created_at)`. */
|
|
152
|
-
const RELATION_AGGREGATE_SORT_PATTERN = /^(min|max|count|sum|avg)\(([^().]+)(?:\.([^()]+))?\)$/;
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* A {@link RelationAggregateSort} as a single string — `min(applications.created_at)`,
|
|
156
|
-
* `count(applications)`.
|
|
157
|
-
*
|
|
158
|
-
* The wire form is a string because every layer below the call site already is
|
|
159
|
-
* one: `OrderByTuple` is `[string, direction]`, the REST parameter is
|
|
160
|
-
* `?orderBy=key:direction`, the driver contract takes `orderBy?: string |
|
|
161
|
-
* OrderByTuple[]`, and a cursor names its keys by string. `_score` established
|
|
162
|
-
* the same pattern — a sort key that is not a column, spelled as one — and this
|
|
163
|
-
* reuses it rather than widening five signatures to carry an object that would
|
|
164
|
-
* be flattened at the end anyway.
|
|
165
|
-
*
|
|
166
|
-
* SQL's own spelling, so the key reads as what it compiles to. Neither `:` nor
|
|
167
|
-
* `,` appears in it, which is what keeps it safe in the colon-delimited wire
|
|
168
|
-
* shorthand.
|
|
169
|
-
*
|
|
170
|
-
* @group Models
|
|
171
|
-
*/
|
|
172
|
-
export function encodeRelationAggregateSort(sort: RelationAggregateSort): string {
|
|
173
|
-
return `${sort.agg}(${sort.relation}${sort.field ? `.${sort.field}` : ""})`;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Read the string spelling back, or `undefined` if it is not one.
|
|
178
|
-
*
|
|
179
|
-
* `undefined` rather than a throw: this is asked of *every* sort key to find
|
|
180
|
-
* out which kind it is, and an ordinary column name is not an error.
|
|
181
|
-
*
|
|
182
|
-
* @group Models
|
|
183
|
-
*/
|
|
184
|
-
export function parseRelationAggregateSort(key: string): RelationAggregateSort | undefined {
|
|
185
|
-
const match = RELATION_AGGREGATE_SORT_PATTERN.exec(key);
|
|
186
|
-
if (!match) return undefined;
|
|
187
|
-
const [, agg, relation, field] = match;
|
|
188
|
-
// `min()` and friends have nothing to aggregate without a column, and a
|
|
189
|
-
// key that parses to a half-built sort would resolve to no expression and
|
|
190
|
-
// be dropped — leaving the rows unsorted while the caller believes
|
|
191
|
-
// otherwise. `count` is the one function that means something on its own.
|
|
192
|
-
if (!field && agg !== "count") return undefined;
|
|
193
|
-
return { agg: agg as RelationAggregateFn, relation, ...(field && { field }) };
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/** Is this sort key the object form rather than a field name? */
|
|
197
|
-
export function isRelationAggregateSort(key: unknown): key is RelationAggregateSort {
|
|
198
|
-
return typeof key === "object" && key !== null &&
|
|
199
|
-
typeof (key as RelationAggregateSort).relation === "string" &&
|
|
200
|
-
typeof (key as RelationAggregateSort).agg === "string";
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/** A sort key in the single-string form every layer below the call site speaks. */
|
|
204
|
-
export function sortKeyToString(key: SortKey): string {
|
|
205
|
-
return isRelationAggregateSort(key) ? encodeRelationAggregateSort(key) : key;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Canonical filter operators supported across all database backends.
|
|
210
|
-
* Each DB driver translates these to its native query format.
|
|
211
|
-
*
|
|
212
|
-
* @group Models
|
|
213
|
-
*/
|
|
214
|
-
export type WhereFilterOp =
|
|
215
|
-
| "<"
|
|
216
|
-
| "<="
|
|
217
|
-
| "=="
|
|
218
|
-
| "!="
|
|
219
|
-
| ">="
|
|
220
|
-
| ">"
|
|
221
|
-
| "array-contains"
|
|
222
|
-
| "in"
|
|
223
|
-
| "not-in"
|
|
224
|
-
| "array-contains-any"
|
|
225
|
-
| "like"
|
|
226
|
-
| "ilike"
|
|
227
|
-
| "not-like"
|
|
228
|
-
| "not-ilike"
|
|
229
|
-
| "is-null"
|
|
230
|
-
| "is-not-null";
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Used to define filters applied in collections.
|
|
234
|
-
*
|
|
235
|
-
* A single condition is a tuple `[operator, value]`.
|
|
236
|
-
* Multiple conditions on the same field use an array of tuples.
|
|
237
|
-
*
|
|
238
|
-
* @example
|
|
239
|
-
* // Single condition per field
|
|
240
|
-
* { status: ["==", "active"], price: [">=", 9.99] }
|
|
241
|
-
*
|
|
242
|
-
* // Multiple conditions on one field
|
|
243
|
-
* { age: [[">=", 18], ["<", 65]] }
|
|
244
|
-
*
|
|
245
|
-
* // Array operators
|
|
246
|
-
* { role: ["in", ["admin", "editor"]] }
|
|
247
|
-
* { tags: ["array-contains", "featured"] }
|
|
248
|
-
*
|
|
249
|
-
* // Pattern matching (SQL wildcards: % and _)
|
|
250
|
-
* { name: ["ilike", "%john%"] }
|
|
251
|
-
* { slug: ["like", "post-%"] }
|
|
252
|
-
*
|
|
253
|
-
* // Null checks (the value is ignored; `null` is conventional)
|
|
254
|
-
* { deleted_at: ["is-null", null] }
|
|
255
|
-
* { published_at: ["is-not-null", null] }
|
|
256
|
-
*
|
|
257
|
-
* @group Models
|
|
258
|
-
*/
|
|
259
|
-
export type FilterValues<Key extends string> =
|
|
260
|
-
Partial<Record<Key, [WhereFilterOp, unknown] | [WhereFilterOp, unknown][]>>;
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* The field names a query may address on a row type: every column, plus a
|
|
264
|
-
* dotted path reaching inside one — or *through a relation* to a column of the
|
|
265
|
-
* related row.
|
|
266
|
-
*
|
|
267
|
-
* A dotted path is not checked at all, in either direction. That is a
|
|
268
|
-
* deliberate loosening, and it is worth being exact about what it costs. The
|
|
269
|
-
* root used to be checked: `"meta.tag"` required a `meta` column. It cannot
|
|
270
|
-
* stay checked, because the other thing a dotted path now means is
|
|
271
|
-
* `"applications.status"` — and `applications` is a *relation*, which comes
|
|
272
|
-
* from the collection's `relations` and is not a column of `M` at all. There is
|
|
273
|
-
* nothing in a generated row type that could validate one. `FindParams.include`
|
|
274
|
-
* is `string[]` for exactly this reason and says so.
|
|
275
|
-
*
|
|
276
|
-
* So the guarantee moves rather than disappears: an unresolvable path is a 400
|
|
277
|
-
* from the driver, not a silently dropped condition. See
|
|
278
|
-
* `UnknownFilterFieldsMode` in `@rebasepro/server-postgres` — dropping a filter
|
|
279
|
-
* key *widens* the read to every row, which is why that resolution fails
|
|
280
|
-
* closed. A typo'd relation path is refused at runtime with the target
|
|
281
|
-
* collection's real column list in the message.
|
|
282
|
-
*
|
|
283
|
-
* Undotted keys are unaffected and still checked against `keyof M`.
|
|
284
|
-
*
|
|
285
|
-
* When `M` is left at its default `Record<string, unknown>`, `keyof M` is
|
|
286
|
-
* `string` and this collapses to `string`, so every query stays permissive.
|
|
287
|
-
* That is what keeps an untyped `createRebaseClient()` behaving exactly as it
|
|
288
|
-
* did before the row type was threaded through.
|
|
289
|
-
*
|
|
290
|
-
* @group Models
|
|
291
|
-
*/
|
|
292
|
-
export type FieldPath<M extends Record<string, unknown> = Record<string, unknown>> =
|
|
293
|
-
| Extract<keyof M, string>
|
|
294
|
-
| `${string}.${string}`;
|
|
295
|
-
|
|
296
|
-
/**
|
|
297
|
-
* Relaxed filter type that also accepts pre-serialized PostgREST strings.
|
|
298
|
-
* **Internal only** — used at the wire-format boundary
|
|
299
|
-
* (`serializeFilter` / `deserializeFilter` in `@rebasepro/common`).
|
|
300
|
-
*
|
|
301
|
-
* Application code, UI components, and SDK consumers should use
|
|
302
|
-
* {@link FilterValues} instead.
|
|
303
|
-
*
|
|
304
|
-
* @internal
|
|
305
|
-
*/
|
|
306
|
-
export type WireFilterValues<Key extends string> =
|
|
307
|
-
Partial<Record<Key, [WhereFilterOp, unknown] | [WhereFilterOp, unknown][] | string>>;
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* A pre-defined filter preset for quick access in the collection toolbar.
|
|
311
|
-
* Users can select a preset to instantly apply a set of filters and
|
|
312
|
-
* optionally a sort order.
|
|
313
|
-
*
|
|
314
|
-
* @group Models
|
|
315
|
-
*/
|
|
316
|
-
export interface FilterPreset<Key extends string = string> {
|
|
317
|
-
/**
|
|
318
|
-
* Display label shown in the preset menu.
|
|
319
|
-
* If omitted, a summary is auto-generated from the filter keys.
|
|
320
|
-
*/
|
|
321
|
-
label?: string;
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* The filter values to apply when this preset is selected.
|
|
325
|
-
*/
|
|
326
|
-
filterValues: FilterValues<Key>;
|
|
327
|
-
|
|
328
|
-
/**
|
|
329
|
-
* Optional sort override to apply alongside the filter values.
|
|
330
|
-
* One key, or several in order of significance.
|
|
331
|
-
*/
|
|
332
|
-
sort?: OrderBySpec<Key>;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* PostgREST short-code operators. Wire format only — these never appear
|
|
337
|
-
* in application code. Used by `serializeFilter`/`deserializeFilter`
|
|
338
|
-
* in `@rebasepro/common`.
|
|
339
|
-
*/
|
|
340
|
-
export type RestFilterOp =
|
|
341
|
-
| "eq" | "neq"
|
|
342
|
-
| "gt" | "gte"
|
|
343
|
-
| "lt" | "lte"
|
|
344
|
-
| "in" | "nin"
|
|
345
|
-
| "cs" | "csa"
|
|
346
|
-
| "like" | "ilike"
|
|
347
|
-
| "nlike" | "nilike"
|
|
348
|
-
| "isnull" | "notnull";
|
|
349
|
-
|
|
350
|
-
/** Maps canonical operators to their REST short-code equivalents. */
|
|
351
|
-
export const CANONICAL_TO_REST: Readonly<Record<WhereFilterOp, RestFilterOp>> = {
|
|
352
|
-
"==": "eq",
|
|
353
|
-
"!=": "neq",
|
|
354
|
-
">": "gt",
|
|
355
|
-
">=": "gte",
|
|
356
|
-
"<": "lt",
|
|
357
|
-
"<=": "lte",
|
|
358
|
-
"in": "in",
|
|
359
|
-
"not-in": "nin",
|
|
360
|
-
"array-contains": "cs",
|
|
361
|
-
"array-contains-any": "csa",
|
|
362
|
-
"like": "like",
|
|
363
|
-
"ilike": "ilike",
|
|
364
|
-
"not-like": "nlike",
|
|
365
|
-
"not-ilike": "nilike",
|
|
366
|
-
"is-null": "isnull",
|
|
367
|
-
"is-not-null": "notnull"
|
|
368
|
-
};
|
|
369
|
-
|
|
370
|
-
/** Maps REST short-code operators to their canonical equivalents. */
|
|
371
|
-
export const REST_TO_CANONICAL: Readonly<Record<RestFilterOp, WhereFilterOp>> = {
|
|
372
|
-
"eq": "==",
|
|
373
|
-
"neq": "!=",
|
|
374
|
-
"gt": ">",
|
|
375
|
-
"gte": ">=",
|
|
376
|
-
"lt": "<",
|
|
377
|
-
"lte": "<=",
|
|
378
|
-
"in": "in",
|
|
379
|
-
"nin": "not-in",
|
|
380
|
-
"cs": "array-contains",
|
|
381
|
-
"csa": "array-contains-any",
|
|
382
|
-
"like": "like",
|
|
383
|
-
"ilike": "ilike",
|
|
384
|
-
"nlike": "not-like",
|
|
385
|
-
"nilike": "not-ilike",
|
|
386
|
-
"isnull": "is-null",
|
|
387
|
-
"notnull": "is-not-null"
|
|
388
|
-
};
|
|
389
|
-
|
|
390
|
-
/**
|
|
391
|
-
* Operators that test for null/not-null and therefore ignore their value.
|
|
392
|
-
* Codecs normalize the value of these conditions to `null`.
|
|
393
|
-
*/
|
|
394
|
-
export const NULL_OPS: ReadonlySet<WhereFilterOp> = new Set<WhereFilterOp>([
|
|
395
|
-
"is-null", "is-not-null"
|
|
396
|
-
]);
|
|
397
|
-
|
|
398
|
-
/**
|
|
399
|
-
* Every canonical operator, in a stable order. Useful for engine capability
|
|
400
|
-
* declarations ({@link DataSourceCapabilities.filterOperators}) and for
|
|
401
|
-
* building operator subsets.
|
|
402
|
-
* @group Models
|
|
403
|
-
*/
|
|
404
|
-
export const ALL_WHERE_FILTER_OPS: readonly WhereFilterOp[] = [
|
|
405
|
-
"<", "<=", "==", "!=", ">=", ">",
|
|
406
|
-
"in", "not-in",
|
|
407
|
-
"array-contains", "array-contains-any",
|
|
408
|
-
"like", "ilike", "not-like", "not-ilike",
|
|
409
|
-
"is-null", "is-not-null"
|
|
410
|
-
];
|
|
411
|
-
|
|
412
|
-
/** All canonical operator strings for runtime validation. */
|
|
413
|
-
const CANONICAL_OPS: ReadonlySet<string> = new Set<WhereFilterOp>(ALL_WHERE_FILTER_OPS);
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* The REST table as a `Map`, because the key `toCanonicalOp` is handed comes
|
|
417
|
-
* off the wire.
|
|
418
|
-
*
|
|
419
|
-
* Indexed as a plain object, every `Object.prototype` member answered:
|
|
420
|
-
* `toCanonicalOp("valueOf")` returned the inherited *function* as though it
|
|
421
|
-
* were a `WhereFilterOp`, and every caller here treats a defined result as
|
|
422
|
-
* "known operator". Same defect the REST codec's own lookup tables were
|
|
423
|
-
* converted away from in `filter-dialect.ts`; this is the copy that survived
|
|
424
|
-
* one package over, and it now sits under the operator validation the REST
|
|
425
|
-
* parser does, which would otherwise have admitted `["constructor", x]`.
|
|
426
|
-
*/
|
|
427
|
-
const REST_OP_LOOKUP: ReadonlyMap<string, WhereFilterOp> = new Map<string, WhereFilterOp>(
|
|
428
|
-
Object.entries(REST_TO_CANONICAL) as [string, WhereFilterOp][]
|
|
429
|
-
);
|
|
430
|
-
|
|
431
|
-
/**
|
|
432
|
-
* Resolve any operator string (canonical or REST short-code) to its
|
|
433
|
-
* canonical `WhereFilterOp` form. Returns `undefined` for unknown operators.
|
|
434
|
-
*
|
|
435
|
-
* @example
|
|
436
|
-
* toCanonicalOp("==") // "=="
|
|
437
|
-
* toCanonicalOp("eq") // "=="
|
|
438
|
-
* toCanonicalOp("cs") // "array-contains"
|
|
439
|
-
* toCanonicalOp("xyz") // undefined
|
|
440
|
-
*/
|
|
441
|
-
export function toCanonicalOp(op: string): WhereFilterOp | undefined {
|
|
442
|
-
if (CANONICAL_OPS.has(op)) return op as WhereFilterOp;
|
|
443
|
-
return REST_OP_LOOKUP.get(op);
|
|
444
|
-
}
|