@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.
Files changed (71) hide show
  1. package/README.md +4 -0
  2. package/dist/call_context.d.ts +20 -0
  3. package/dist/controllers/client.d.ts +36 -4
  4. package/dist/controllers/data.d.ts +120 -10
  5. package/dist/errors.d.ts +83 -4
  6. package/dist/index.es.js +522 -160
  7. package/dist/index.es.js.map +1 -1
  8. package/dist/types/admin_block.d.ts +2 -2
  9. package/dist/types/auth_adapter.d.ts +41 -6
  10. package/dist/types/backend.d.ts +48 -0
  11. package/dist/types/collections.d.ts +25 -1
  12. package/dist/types/cron.d.ts +34 -0
  13. package/dist/types/database_adapter.d.ts +39 -0
  14. package/dist/types/entity_callbacks.d.ts +14 -1
  15. package/dist/types/filter-operators.d.ts +24 -1
  16. package/dist/types/policy.d.ts +29 -1
  17. package/dist/types/properties.d.ts +216 -3
  18. package/dist/types/relations.d.ts +65 -7
  19. package/dist/types/resource_kinds.d.ts +173 -17
  20. package/dist/types/resources.d.ts +108 -7
  21. package/dist/types/rls-functions.d.ts +11 -0
  22. package/dist/types/storage_source.d.ts +12 -23
  23. package/package.json +24 -23
  24. package/src/call_context.ts +0 -120
  25. package/src/controllers/auth_state.ts +0 -24
  26. package/src/controllers/client.ts +0 -494
  27. package/src/controllers/collection_registry.ts +0 -62
  28. package/src/controllers/data.ts +0 -1012
  29. package/src/controllers/data_driver.ts +0 -576
  30. package/src/controllers/effective_role.ts +0 -4
  31. package/src/controllers/email.ts +0 -91
  32. package/src/controllers/index.ts +0 -11
  33. package/src/controllers/storage.ts +0 -252
  34. package/src/errors.ts +0 -119
  35. package/src/index.ts +0 -5
  36. package/src/types/admin_block.ts +0 -209
  37. package/src/types/api_keys.ts +0 -108
  38. package/src/types/auth_adapter.ts +0 -580
  39. package/src/types/backend.ts +0 -987
  40. package/src/types/backup.ts +0 -26
  41. package/src/types/channel_bus.ts +0 -202
  42. package/src/types/chips.ts +0 -34
  43. package/src/types/collection_contract.ts +0 -278
  44. package/src/types/collections.ts +0 -763
  45. package/src/types/component_ref.ts +0 -92
  46. package/src/types/cron.ts +0 -213
  47. package/src/types/data_source.ts +0 -357
  48. package/src/types/database_adapter.ts +0 -267
  49. package/src/types/entities.ts +0 -226
  50. package/src/types/entity_callbacks.ts +0 -229
  51. package/src/types/filter-operators.ts +0 -444
  52. package/src/types/history.ts +0 -66
  53. package/src/types/index.ts +0 -36
  54. package/src/types/indexes.ts +0 -180
  55. package/src/types/policy.ts +0 -328
  56. package/src/types/postgres_introspection.ts +0 -101
  57. package/src/types/project_manifest.ts +0 -598
  58. package/src/types/properties.ts +0 -1368
  59. package/src/types/relations.ts +0 -417
  60. package/src/types/resource_kinds.ts +0 -390
  61. package/src/types/resources.ts +0 -368
  62. package/src/types/rls-functions.ts +0 -98
  63. package/src/types/schema_editing.ts +0 -157
  64. package/src/types/schema_version.ts +0 -112
  65. package/src/types/search.ts +0 -247
  66. package/src/types/security_rules.ts +0 -344
  67. package/src/types/storage_authorize.ts +0 -77
  68. package/src/types/storage_source.ts +0 -248
  69. package/src/types/websockets.ts +0 -117
  70. package/src/users/index.ts +0 -2
  71. 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
- }