@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.
@@ -29,7 +29,7 @@
29
29
  *
30
30
  * @group Models
31
31
  */
32
- export declare const ADMIN_COLLECTION_KEYS: readonly ["Actions", "additionalFields", "alwaysApplyDefaultValues", "components", "defaultEntityAction", "defaultFilter", "defaultSelectedView", "defaultSize", "defaultViewMode", "disableDefaultActions", "enabledViews", "entityActions", "entityViews", "exportable", "filterPresets", "fixedFilter", "form", "formAutoSave", "formView", "group", "hideFromNavigation", "hideIdFromCollection", "hideIdFromForm", "icon", "includeJsonView", "inlineEditing", "kanban", "listProperties", "localChangesBackup", "openEntityMode", "orderProperty", "pagination", "previewProperties", "propertiesOrder", "selectionController", "selectionEnabled", "sideDialogWidth", "sort", "titleProperty"];
32
+ export declare const ADMIN_COLLECTION_KEYS: readonly ["Actions", "additionalFields", "alwaysApplyDefaultValues", "components", "defaultEntityAction", "defaultFilter", "defaultSelectedView", "defaultSize", "defaultViewMode", "disableDefaultActions", "display", "enabledViews", "entityActions", "entityViews", "exportable", "filterPresets", "fixedFilter", "form", "formAutoSave", "formView", "group", "hideFromEntityViews", "hideFromNavigation", "hideIdFromCollection", "hideIdFromForm", "icon", "includeJsonView", "inlineEditing", "kanban", "listProperties", "localChangesBackup", "openEntityMode", "orderProperty", "pagination", "previewProperties", "propertiesOrder", "selectionController", "selectionEnabled", "sideDialogWidth", "sort", "titleProperty"];
33
33
  /** A key of a collection's `admin` block. @group Models */
34
34
  export type AdminCollectionKey = typeof ADMIN_COLLECTION_KEYS[number];
35
35
  /**
@@ -52,3 +52,40 @@ export type AdminCollectionKey = typeof ADMIN_COLLECTION_KEYS[number];
52
52
  export declare const ADMIN_PROPERTY_KEYS: readonly ["canAddElements", "clearable", "columnWidth", "customProps", "disabled", "expanded", "Field", "Filter", "filterOperators", "fixedFilter", "hideFromCollection", "includeEntityLink", "includeId", "markdown", "minimalistView", "multiline", "Preview", "previewAsTag", "previewProperties", "readOnly", "sortable", "span", "spreadChildren", "urlPreview", "widget"];
53
53
  /** A key of a property's `admin` block. @group Models */
54
54
  export type AdminPropertyKey = typeof ADMIN_PROPERTY_KEYS[number];
55
+ /**
56
+ * Move flattened admin keys back down into the `admin` block.
57
+ *
58
+ * The admin panel works with a *flat* view model — the block merged onto the
59
+ * collection — so what comes back from a form has `icon` and `defaultViewMode`
60
+ * at the top level while `admin` still holds whatever the file was loaded with.
61
+ * This is the way back.
62
+ *
63
+ * **The top-level value wins.** It is the one the form just wrote; the block is
64
+ * the copy the collection was loaded with, and preferring it resolves every edit
65
+ * in favour of the value the user changed away from.
66
+ *
67
+ * This lives here, next to the key lists, because it had two implementations —
68
+ * `toAdminCollectionConfig` in `@rebasepro/admin-types` and `nestAdminKeys` in
69
+ * `@rebasepro/server`'s schema editor — that agreed on everything except that
70
+ * precedence, which is the only part that decides whether a save is visible.
71
+ *
72
+ * @group Models
73
+ */
74
+ export declare function nestAdminKeysOf(source: Record<string, unknown>, adminKeys: readonly string[]): Record<string, unknown>;
75
+ /**
76
+ * {@link nestAdminKeysOf} for a collection.
77
+ *
78
+ * @group Models
79
+ */
80
+ export declare function nestAdminCollectionKeys(collection: Record<string, unknown>): Record<string, unknown>;
81
+ /**
82
+ * {@link nestAdminKeysOf} for a property, applied to its children too.
83
+ *
84
+ * A map property carries `properties`, an array property carries `of`, and both
85
+ * hold properties with `admin` blocks of their own. A flat `readOnly` left on a
86
+ * child is as dead — and as fatal at the next boot — as one left on the parent,
87
+ * so the walk goes all the way down.
88
+ *
89
+ * @group Models
90
+ */
91
+ export declare function nestAdminPropertyKeys(property: Record<string, unknown>): Record<string, unknown>;
@@ -162,6 +162,8 @@ export interface CollectionSubscriptionConfig {
162
162
  startAfter?: unknown;
163
163
  databaseId?: string;
164
164
  searchString?: string;
165
+ /** Ask each row which declared search field matched. */
166
+ searchExplain?: boolean;
165
167
  }
166
168
  /**
167
169
  * Configuration for subscribing to a single entity
@@ -3,6 +3,7 @@ import type { Properties, PostgresProperties, FirebaseProperties, MongoPropertie
3
3
  import type { User } from "../users";
4
4
  import type { Relation } from "./relations";
5
5
  import type { SecurityRule } from "./security_rules";
6
+ import type { SearchConfig } from "./search";
6
7
  /**
7
8
  * Base interface containing all driver-agnostic collection properties.
8
9
  * Use {@link PostgresCollectionConfig} or {@link FirebaseCollectionConfig} for
@@ -13,9 +14,27 @@ import type { SecurityRule } from "./security_rules";
13
14
  */
14
15
  export interface BaseCollectionConfig<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User> {
15
16
  /**
16
- * You can set an alias that will be used internally instead of the collection name.
17
- * The `slug` value will be used to determine the URL of the collection.
18
- * Note that you can use this value in reference properties too.
17
+ * The collection's identity. Required, and the value nearly everything else
18
+ * keys on:
19
+ *
20
+ * - the REST path — `/api/data/<slug>`
21
+ * - the SDK accessor — `client.data.<slug>` / `client.data.collection("<slug>")`
22
+ * - the admin panel's URL
23
+ * - the target of a `reference` or `relation` property
24
+ *
25
+ * Conventionally kebab-case and plural (`blog-posts`). It is independent of
26
+ * {@link table}: the slug is what callers say, the table is where the rows
27
+ * live, and renaming one does not rename the other.
28
+ *
29
+ * Treat it as frozen once anything has shipped against it — changing a slug
30
+ * changes every URL and every generated accessor at once.
31
+ *
32
+ * @example
33
+ * defineCollection({
34
+ * slug: "blog-posts", // /api/data/blog-posts, client.data.blogPosts
35
+ * table: "posts",
36
+ * properties: { … }
37
+ * })
19
38
  */
20
39
  slug: string;
21
40
  /**
@@ -136,11 +155,16 @@ export interface BaseCollectionConfig<M extends Record<string, unknown> = Record
136
155
  * Whether a write naming a field this collection does not declare is
137
156
  * rejected with a 400. Defaults to `true`.
138
157
  *
139
- * Set to `false` to let unknown keys through to the database, which is what
140
- * happened before this existed: a typo reached the INSERT and came back as
141
- * a Postgres error about a column, or — where a column really does exist
142
- * that the config never declared, populated by a trigger or a default —
143
- * quietly worked. The second case is the reason for the escape hatch.
158
+ * Set to `false` where a column really does exist that the config never
159
+ * declared — populated by a trigger, or introspected rather than declared —
160
+ * and callers need to write it. The column still has to exist: the driver
161
+ * checks the key against the table's own columns whatever this is set to,
162
+ * because a key with no column behind it is not passed to the database and
163
+ * refused, it is dropped from the statement and answered 201.
164
+ *
165
+ * It does not let a typo through to Postgres for Postgres to judge. That is
166
+ * what this flag was documented as doing, and no such judgment ever
167
+ * happened.
144
168
  */
145
169
  strictWrites?: boolean;
146
170
  }
@@ -209,6 +233,20 @@ export interface PostgresCollectionConfig<M extends Record<string, unknown> = Re
209
233
  * @default false
210
234
  */
211
235
  disableDefaultPolicies?: boolean;
236
+ /**
237
+ * Opt in to Postgres full-text search for this collection.
238
+ *
239
+ * Omit it and `.search()` keeps its existing behaviour exactly — an
240
+ * `ILIKE '%term%'` across top-level string properties. Declare it and the
241
+ * collection gains one generated `tsvector` column and a GIN index, and
242
+ * `.search()` compiles to a ranked `@@ websearch_to_tsquery` against them.
243
+ *
244
+ * Postgres-only, like {@link VectorProperty}: the block is rejected at boot
245
+ * on other engines rather than silently ignored.
246
+ *
247
+ * @see SearchConfig
248
+ */
249
+ search?: SearchConfig;
212
250
  }
213
251
  /**
214
252
  * A collection backed by Firebase / Firestore.
@@ -1,4 +1,5 @@
1
- import type { RebaseClient } from "../controllers/client";
1
+ import type { RebaseServerClient } from "../controllers/client";
2
+ import type { RebaseSdkData } from "../controllers/data";
2
3
  /**
3
4
  * Cron Job type definitions for Rebase.
4
5
  *
@@ -75,16 +76,56 @@ export interface CronJobContext {
75
76
  /** A simple logger scoped to this job run. */
76
77
  log: (...args: unknown[]) => void;
77
78
  /**
78
- * The server-side {@link RebaseClient}. This is the **same singleton**
79
- * exposed as `rebase` (imported from `@rebasepro/server`) and as
80
- * `context` in collection callbacks — it is only named `client` here.
79
+ * The server-side Rebase singleton — the **same object** `import { rebase }
80
+ * from "@rebasepro/server"` returns, and the same one `defineFunction`
81
+ * hands its callback. Spelled the same way here so that one thing has one
82
+ * name across every server-side authoring surface.
81
83
  *
82
- * Its data plane (`client.data`) runs with **admin privileges and bypasses
83
- * RLS** (`{ uid: "service", roles: ["admin"] }`). There is no per-request
84
- * user in a cron, so treat every query as fully trusted and scope your own
85
- * filters explicitly.
84
+ * Its data plane is {@link RebaseServerClient.dataAsAdmin}, which runs with
85
+ * **admin privileges and bypasses RLS** (`{ uid: "service", roles:
86
+ * ["admin"] }`). A cron has no per-request user, so there is no user-scoped
87
+ * alternative here and no policy to fall back on: scope every query's
88
+ * filters yourself.
89
+ *
90
+ * @example
91
+ * export default defineCron({
92
+ * name: "Nightly cleanup",
93
+ * schedule: "0 3 * * *",
94
+ * async handler({ rebase, log }) {
95
+ * const expired = await rebase.dataAsAdmin.sessions.findAll({
96
+ * where: { expired: ["==", true] }
97
+ * });
98
+ * for (const session of expired) {
99
+ * await rebase.dataAsAdmin.sessions.delete(session.id as string);
100
+ * }
101
+ * log(`Deleted ${expired.length} expired sessions`);
102
+ * }
103
+ * });
104
+ */
105
+ rebase: RebaseServerClient;
106
+ /**
107
+ * The same object as {@link rebase}, under the name this context used
108
+ * before.
109
+ *
110
+ * @deprecated Use `rebase` instead. Two things made the old name a problem,
111
+ * and neither was cosmetic. It contradicted every other server surface,
112
+ * where the singleton is `rebase` — the previous docstring had to end with
113
+ * *"it is only named `client` here"*. And typing it as `RebaseClient`
114
+ * re-exposed `client.data`, the alias that {@link RebaseServerClient}
115
+ * deliberately `Omit`s so the RLS-bypassing plane has exactly one name and
116
+ * the privilege is visible at the call site. A reader who learned
117
+ * `client.data` here carried it to a collection callback, where
118
+ * `context.data` is the *user-scoped* plane — same spelling, opposite
119
+ * privilege.
120
+ *
121
+ * Still the full server client at runtime, and `data` still resolves, so
122
+ * existing cron files keep working and keep compiling. It will be removed
123
+ * in the next major.
86
124
  */
87
- client: RebaseClient;
125
+ client: RebaseServerClient & {
126
+ /** @deprecated Use `rebase.dataAsAdmin` — the name states the privilege. */
127
+ data: RebaseSdkData;
128
+ };
88
129
  }
89
130
  export type CronJobRunState = "idle" | "running" | "success" | "error" | "disabled";
90
131
  /**
@@ -1,3 +1,4 @@
1
+ import type { SearchMatch } from "./search";
1
2
  /**
2
3
  * New or existing status
3
4
  * @group Models
@@ -21,6 +22,16 @@ export interface Entity<M extends Record<string, unknown> = Record<string, unkno
21
22
  * Current values
22
23
  */
23
24
  values: EntityValues<M>;
25
+ /**
26
+ * Why this entity is in a search result: which declared fields matched, and
27
+ * the text around each hit.
28
+ *
29
+ * Present only on rows returned by a search that asked for it. A sibling of
30
+ * `values` rather than a key inside it, because it describes the *query*,
31
+ * not the record — nothing in the collection declares it, no form edits it,
32
+ * and a record fetched by id never has one.
33
+ */
34
+ searchMatches?: SearchMatch[];
24
35
  /**
25
36
  * Which driver this entity belongs to (e.g., 'postgres', 'firestore').
26
37
  * If not specified, the default driver is assumed.
@@ -7,7 +7,8 @@ import type { RebaseCallContext } from "../call_context";
7
7
  *
8
8
  * Register per-collection on the collection's `callbacks` field, or globally
9
9
  * via `initializeRebaseBackend({ callbacks })`. Fires on **every** data path — REST API,
10
- * WebSocket / realtime subscriptions, and server-side `rebase.data`.
10
+ * WebSocket / realtime subscriptions, and server-side writes through
11
+ * `rebase.dataAsAdmin`.
11
12
  *
12
13
  * When both global and per-collection callbacks are registered, execution
13
14
  * order is: **global → collection → property callbacks**.
@@ -4,8 +4,10 @@ export * from "./chips";
4
4
  export * from "./properties";
5
5
  export * from "./admin_block";
6
6
  export * from "./collections";
7
+ export * from "./search";
7
8
  export * from "./relations";
8
9
  export * from "./policy";
10
+ export * from "./rls-functions";
9
11
  export * from "./security_rules";
10
12
  export * from "./entity_callbacks";
11
13
  export * from "./websockets";
@@ -19,7 +19,7 @@
19
19
  */
20
20
  export type PolicyExpression = TruePolicyExpression | FalsePolicyExpression | AndPolicyExpression | OrPolicyExpression | NotPolicyExpression | ComparePolicyExpression | RolesOverlapPolicyExpression | RolesContainPolicyExpression | AuthenticatedPolicyExpression | ServerContextPolicyExpression | ExistsInPolicyExpression | RawPolicyExpression;
21
21
  /**
22
- * The id a request without a logged-in user reports as `auth.uid()`.
22
+ * The id a request without a logged-in user reports as `rebase.uid()`.
23
23
  *
24
24
  * A user-context request always sets `app.uid`: blank would read back as
25
25
  * `NULL`, and `NULL` is how the trusted server context is recognised, so an
@@ -27,7 +27,7 @@ export type PolicyExpression = TruePolicyExpression | FalsePolicyExpression | An
27
27
  * therefore substitutes this sentinel at the single chokepoint where the GUC
28
28
  * is set.
29
29
  *
30
- * The consequence for policy authors is that **`auth.uid() IS NOT NULL` is a
30
+ * The consequence for policy authors is that **`rebase.uid() IS NOT NULL` is a
31
31
  * tautology on the user path** — it is true for anonymous visitors too. Use
32
32
  * {@link policy.authenticated} to mean "signed in", and
33
33
  * {@link policy.serverContext} to mean "the trusted server context". Do not
@@ -44,7 +44,7 @@ export declare const ANONYMOUS_USER_ID = "anonymous";
44
44
  * JavaScript evaluator and the linter were all built on
45
45
  * {@link ANONYMOUS_USER_ID}, while the request path scoped unauthenticated
46
46
  * callers as `'anon'` — so `policy.authenticated()`, which compiled to
47
- * `auth.uid() <> 'anonymous'`, was *true* for an anonymous visitor. The
47
+ * `rebase.uid() <> 'anonymous'`, was *true* for an anonymous visitor. The
48
48
  * sanctioned way to write "signed in" granted to everyone, and the linter
49
49
  * flagged the spelling that actually worked as a foreign convention.
50
50
  *
@@ -93,7 +93,7 @@ export interface NotPolicyExpression {
93
93
  /** Comparison operators available to {@link ComparePolicyExpression}. @group Models */
94
94
  export type PolicyCompareOperator = "eq" | "neq" | "lt" | "lte" | "gt" | "gte";
95
95
  /**
96
- * Compares two operands, e.g. `owner_id = auth.uid()`.
96
+ * Compares two operands, e.g. `owner_id = rebase.uid()`.
97
97
  * @group Models
98
98
  */
99
99
  export interface ComparePolicyExpression {
@@ -104,7 +104,7 @@ export interface ComparePolicyExpression {
104
104
  }
105
105
  /**
106
106
  * True when the user holds *at least one* of the given application roles.
107
- * Compiles to `string_to_array(auth.roles(), ',') && ARRAY[...]`.
107
+ * Compiles to `string_to_array(rebase.roles(), ',') && ARRAY[...]`.
108
108
  * @group Models
109
109
  */
110
110
  export interface RolesOverlapPolicyExpression {
@@ -113,7 +113,7 @@ export interface RolesOverlapPolicyExpression {
113
113
  }
114
114
  /**
115
115
  * True when the user holds *all* of the given application roles.
116
- * Compiles to `string_to_array(auth.roles(), ',') @> ARRAY[...]`.
116
+ * Compiles to `string_to_array(rebase.roles(), ',') @> ARRAY[...]`.
117
117
  * @group Models
118
118
  */
119
119
  export interface RolesContainPolicyExpression {
@@ -122,11 +122,11 @@ export interface RolesContainPolicyExpression {
122
122
  }
123
123
  /**
124
124
  * True when a signed-in user is making the request. Compiles to
125
- * `auth.uid() IS NOT NULL AND auth.uid() <> 'anonymous'`.
125
+ * `rebase.uid() IS NOT NULL AND rebase.uid() <> 'anonymous'`.
126
126
  *
127
127
  * Both halves are load-bearing. `IS NOT NULL` excludes the server context;
128
128
  * the {@link ANONYMOUS_USER_ID} comparison excludes anonymous visitors, who
129
- * *do* carry a non-null `auth.uid()`. Checking only `IS NOT NULL` grants to
129
+ * *do* carry a non-null `rebase.uid()`. Checking only `IS NOT NULL` grants to
130
130
  * everyone — see {@link ANONYMOUS_USER_ID}.
131
131
  *
132
132
  * `policy.not(policy.authenticated())` therefore means "anonymous visitor or
@@ -140,8 +140,8 @@ export interface AuthenticatedPolicyExpression {
140
140
  /**
141
141
  * True only in the trusted **server context** — the built-in flows that run
142
142
  * without a user (signup, migrations, `dataAsAdmin`) set no user GUC, so
143
- * `auth.uid()` is `NULL` for them and only for them. Compiles to
144
- * `auth.uid() IS NULL`.
143
+ * `rebase.uid()` is `NULL` for them and only for them. Compiles to
144
+ * `rebase.uid() IS NULL`.
145
145
  *
146
146
  * This is what lets the owner connection satisfy a policy even under FORCE RLS.
147
147
  * It is deliberately a primitive rather than `not(authenticated())`: the two
@@ -178,7 +178,7 @@ export interface ServerContextPolicyExpression {
178
178
  * ),
179
179
  * })
180
180
  * // → EXISTS (SELECT 1 FROM team_members _ex0
181
- * // WHERE _ex0.team_id = documents.team_id AND _ex0.user_id = auth.uid())
181
+ * // WHERE _ex0.team_id = documents.team_id AND _ex0.user_id = rebase.uid())
182
182
  * ```
183
183
  *
184
184
  * Postgres-authoritative: like {@link RawPolicyExpression}, the JavaScript
@@ -232,13 +232,13 @@ export interface LiteralPolicyOperand {
232
232
  kind: "literal";
233
233
  value: string | number | boolean | null;
234
234
  }
235
- /** The current user's id — compiles to `auth.uid()`. @group Models */
235
+ /** The current user's id — compiles to `rebase.uid()`. @group Models */
236
236
  export interface AuthUidPolicyOperand {
237
237
  kind: "authUid";
238
238
  }
239
239
  /**
240
240
  * The current user's roles as an array — compiles to
241
- * `string_to_array(auth.roles(), ',')`.
241
+ * `string_to_array(rebase.roles(), ',')`.
242
242
  * @group Models
243
243
  */
244
244
  export interface AuthRolesPolicyOperand {
@@ -159,14 +159,21 @@ export interface BaseProperty<CustomProps = unknown> {
159
159
  */
160
160
  validation?: PropertyValidationSchema;
161
161
  /**
162
- * Never include this column in an API response.
162
+ * Never mention this column on the API surface, in either direction.
163
163
  *
164
164
  * For secrets the server must store and read but no client should ever
165
165
  * receive — password hashes, verification tokens. The value is still
166
166
  * written and queryable server-side; it is stripped from every row the API
167
- * serves, for every caller, including admins and service keys.
167
+ * serves, for every caller, including admins and service keys, and it is
168
+ * absent from every generated description of the surface: the SDK's `Row`,
169
+ * `Insert` and `Update` types, and the OpenAPI schemas, filters and
170
+ * parameters.
168
171
  *
169
- * This is a server-side guarantee, unlike `ui.hideFromCollection`, which
172
+ * The generated types are a *description*, not a second enforcement point:
173
+ * the server still accepts such a field on a write, because that is how the
174
+ * value gets written in the first place. Nothing generated offers it.
175
+ *
176
+ * This is a server-side guarantee, unlike `admin.hideFromCollection`, which
170
177
  * only stops the admin panel from *rendering* a field and leaves it in the
171
178
  * JSON payload.
172
179
  */
@@ -904,6 +911,17 @@ export interface ImageResize {
904
911
  * @group Entity properties
905
912
  */
906
913
  export type JsonLogicRule = Record<string, any>;
914
+ /**
915
+ * A condition that is either a JSON Logic rule or a literal answer.
916
+ *
917
+ * The unconditional case is the common one — "this field is never editable",
918
+ * "this field is never shown" — and with only a rule accepted it had to be
919
+ * spelled `{ "==": [1, 1] }`, which reads as a puzzle at the call site. A plain
920
+ * `true` says the same thing.
921
+ *
922
+ * @group Entity properties
923
+ */
924
+ export type ConditionRule = JsonLogicRule | boolean;
907
925
  /**
908
926
  * Conditions for individual enum values within a property.
909
927
  * @group Entity properties
@@ -945,8 +963,10 @@ export interface PropertyConditions {
945
963
  * \`\`\`json
946
964
  * { "==": [{ "var": "values.status" }, "archived"] }
947
965
  * \`\`\`
966
+ *
967
+ * A literal `true` disables it unconditionally.
948
968
  */
949
- disabled?: JsonLogicRule;
969
+ disabled?: ConditionRule;
950
970
  /**
951
971
  * Message to display when the field is disabled by a condition.
952
972
  */
@@ -959,13 +979,18 @@ export interface PropertyConditions {
959
979
  /**
960
980
  * Hide the field completely when this condition evaluates to true.
961
981
  * The field is removed from the form (not just visually hidden).
982
+ *
983
+ * A literal `true` hides it unconditionally. This is the way to keep a
984
+ * property out of the form without keeping it out of the collection.
962
985
  */
963
- hidden?: JsonLogicRule;
986
+ hidden?: ConditionRule;
964
987
  /**
965
988
  * Make the field read-only when this condition evaluates to true.
966
989
  * Renders as a preview instead of an input.
990
+ *
991
+ * A literal `true` makes it read-only unconditionally.
967
992
  */
968
- readOnly?: JsonLogicRule;
993
+ readOnly?: ConditionRule;
969
994
  /**
970
995
  * Make the field required when this condition evaluates to true.
971
996
  * Overrides the static `validation.required` setting.
@@ -0,0 +1,84 @@
1
+ /**
2
+ * The SQL helper functions RLS policies call, and the schema they live in.
3
+ *
4
+ * ## One schema, and it is ours
5
+ *
6
+ * Rebase creates exactly one schema in a project's database: `rebase`. These
7
+ * three functions live in it alongside the framework's own tables, and that is
8
+ * the whole contract — a reader can look at a database and know precisely which
9
+ * namespace belongs to the framework and that nothing else was touched.
10
+ *
11
+ * It used to be two. `uid()`, `jwt()` and `roles()` sat in a schema called
12
+ * `auth`, which is Supabase's name, chosen so that a developer who had written
13
+ * Supabase RLS would recognise `auth.uid()`. The familiarity was real but the
14
+ * name was not Rebase's to take, and taking it had a concrete cost: pointing
15
+ * Rebase at a database that already had a Supabase `auth` schema meant
16
+ * `CREATE OR REPLACE FUNCTION auth.uid() RETURNS text` against Supabase's
17
+ * `RETURNS uuid`, which Postgres rejects outright —
18
+ *
19
+ * ERROR: cannot change return type of existing function
20
+ * HINT: Use DROP FUNCTION auth.uid() first.
21
+ *
22
+ * — and the failure landed inside a catch-all that logged a warning and carried
23
+ * on, leaving a database with auth tables, no helper functions, and policies
24
+ * calling functions that did not exist. Under `rebase db migrate` the same
25
+ * statements aborted the migration instead.
26
+ *
27
+ * `rebase.uid()` collides with nobody. A Supabase database keeps its `auth`
28
+ * schema untouched and gains a `rebase` one, which is what a gradual migration
29
+ * needs.
30
+ *
31
+ * ## Why functions at all, rather than inlining `current_setting`
32
+ *
33
+ * Because the indirection has already been spent once. `uid()` resolves
34
+ * `app.uid` and falls back to the pre-rename `app.user_id`, so that during a
35
+ * rolling deploy — old and new pods serving one database — both eras resolve
36
+ * the principal. That was a single `CREATE OR REPLACE`. Inlined into policy
37
+ * bodies it would have been a rewrite of every policy on every table.
38
+ *
39
+ * ## Why the name is not configurable
40
+ *
41
+ * A policy body is stored SQL: Postgres parses `USING (…)` once and keeps it, so
42
+ * these strings are written into every policy in every database Rebase has
43
+ * provisioned. Everything that reads policies back — the SQL-to-policy parser
44
+ * behind the admin UI, the drift checker, `rls-check` — would have to know the
45
+ * configured value to recognise its own output. One frozen name is the feature.
46
+ */
47
+ /** The schema Rebase owns. The only schema Rebase creates. */
48
+ export declare const REBASE_SCHEMA = "rebase";
49
+ /**
50
+ * The principal of the current request, as text, or NULL in the server context.
51
+ *
52
+ * Never NULL for a user request — an anonymous one carries
53
+ * {@link ANONYMOUS_USER_ID} — which is what makes `IS NULL` a reliable test for
54
+ * the trusted server plane and `IS NOT NULL` a tautology.
55
+ */
56
+ export declare const RLS_UID_SQL = "rebase.uid()";
57
+ /** The request's roles as a comma-separated string, for `string_to_array`. */
58
+ export declare const RLS_ROLES_SQL = "rebase.roles()";
59
+ /** The request's JWT claims as `jsonb`, or `{}`. */
60
+ export declare const RLS_JWT_SQL = "rebase.jwt()";
61
+ /**
62
+ * The pre-1.0 spellings, for recognising policies and hand-written SQL that
63
+ * predate the move.
64
+ *
65
+ * Kept because policies outlive the server that wrote them: a database migrated
66
+ * by an older release still holds `auth.uid()` in its policy bodies until the
67
+ * next push or boot recompiles them, and anything that reads policies back has
68
+ * to recognise both eras or report the framework's own output as foreign drift.
69
+ * Also used to give a project whose `securityRules` contain raw `auth.uid()` a
70
+ * message naming the replacement, instead of a parse failure.
71
+ */
72
+ export declare const LEGACY_RLS_SCHEMA = "auth";
73
+ export declare const LEGACY_RLS_UID_SQL = "auth.uid()";
74
+ export declare const LEGACY_RLS_ROLES_SQL = "auth.roles()";
75
+ export declare const LEGACY_RLS_JWT_SQL = "auth.jwt()";
76
+ /**
77
+ * Rewrites the pre-1.0 function calls in a fragment of policy SQL.
78
+ *
79
+ * Deliberately anchored on a word boundary and the schema qualifier, so a column
80
+ * called `auth_uid` or a table named `auth` is left alone.
81
+ */
82
+ export declare function rewriteLegacyRlsFunctions(sql: string): string;
83
+ /** Whether a fragment of SQL still calls the pre-1.0 functions. */
84
+ export declare function usesLegacyRlsFunctions(sql: string): boolean;