@rebasepro/types 0.9.0 → 0.9.1-canary.0de22e0
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 -4
- package/dist/controllers/auth.d.ts +6 -0
- package/dist/controllers/client.d.ts +21 -1
- package/dist/controllers/data.d.ts +44 -2
- package/dist/controllers/data_driver.d.ts +45 -3
- package/dist/controllers/email.d.ts +2 -2
- package/dist/controllers/registry.d.ts +5 -5
- package/dist/index.es.js +19 -1
- package/dist/index.es.js.map +1 -1
- package/dist/types/auth_adapter.d.ts +25 -3
- package/dist/types/backend.d.ts +55 -1
- package/dist/types/backup.d.ts +20 -0
- package/dist/types/collections.d.ts +34 -19
- package/dist/types/cron.d.ts +2 -2
- package/dist/types/database_adapter.d.ts +17 -1
- package/dist/types/entity_callbacks.d.ts +3 -3
- package/dist/types/index.d.ts +1 -0
- package/dist/types/policy.d.ts +48 -2
- package/dist/types/properties.d.ts +13 -0
- package/dist/types/translations.d.ts +5 -1
- package/dist/types/user_management_delegate.d.ts +8 -2
- package/dist/types/websockets.d.ts +77 -0
- package/dist/users/user.d.ts +1 -1
- package/package.json +5 -6
- package/src/controllers/auth.tsx +6 -0
- package/src/controllers/client.ts +22 -2
- package/src/controllers/data.ts +42 -2
- package/src/controllers/data_driver.ts +47 -3
- package/src/controllers/email.ts +2 -2
- package/src/controllers/registry.ts +5 -5
- package/src/types/auth_adapter.ts +25 -3
- package/src/types/backend.ts +59 -1
- package/src/types/backup.ts +26 -0
- package/src/types/collections.ts +35 -19
- package/src/types/cron.ts +2 -2
- package/src/types/database_adapter.ts +15 -1
- package/src/types/entity_callbacks.ts +3 -3
- package/src/types/index.ts +1 -0
- package/src/types/policy.ts +50 -1
- package/src/types/properties.ts +14 -0
- package/src/types/translations.ts +5 -1
- package/src/types/user_management_delegate.ts +8 -2
- package/src/types/websockets.ts +76 -0
- package/src/users/user.ts +1 -1
- package/dist/index.umd.js +0 -591
- package/dist/index.umd.js.map +0 -1
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*
|
|
18
18
|
* @example Custom auth
|
|
19
19
|
* ```ts
|
|
20
|
-
* import { createCustomAuthAdapter } from "@rebasepro/server
|
|
20
|
+
* import { createCustomAuthAdapter } from "@rebasepro/server";
|
|
21
21
|
*
|
|
22
22
|
* initializeRebaseBackend({
|
|
23
23
|
* auth: createCustomAuthAdapter({
|
|
@@ -77,8 +77,25 @@ export interface AuthAdapterCapabilities {
|
|
|
77
77
|
emailPasswordLogin: boolean;
|
|
78
78
|
/** Supports new user registration. */
|
|
79
79
|
registration: boolean;
|
|
80
|
-
/**
|
|
80
|
+
/**
|
|
81
|
+
* Supports the end-user password reset flow (emailing a reset link).
|
|
82
|
+
*
|
|
83
|
+
* This is about *self-service* reset, so it is typically tied to whether an
|
|
84
|
+
* email service is configured. It says nothing about whether an admin can
|
|
85
|
+
* reset someone else's password — see `adminPasswordReset`.
|
|
86
|
+
*/
|
|
81
87
|
passwordReset: boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Whether the adapter exposes `POST /admin/users/:userId/reset-password`,
|
|
90
|
+
* letting an admin reset another user's password.
|
|
91
|
+
*
|
|
92
|
+
* Independent of `passwordReset`: the built-in adapter supports this even
|
|
93
|
+
* with no email service configured (it returns a one-time temporary
|
|
94
|
+
* password instead of sending a link). Adapters that mount their own admin
|
|
95
|
+
* routes must set this to `true` only once that route actually exists —
|
|
96
|
+
* the admin UI hides the "Reset Password" action when it is `false`.
|
|
97
|
+
*/
|
|
98
|
+
adminPasswordReset: boolean;
|
|
82
99
|
/** Supports session listing/revocation. */
|
|
83
100
|
sessionManagement: boolean;
|
|
84
101
|
/** Supports profile updates (display name, photo). */
|
|
@@ -196,6 +213,11 @@ export interface UserCreationFinalizeResult {
|
|
|
196
213
|
temporaryPassword?: string;
|
|
197
214
|
/** Whether an invitation email was sent. */
|
|
198
215
|
invitationSent: boolean;
|
|
216
|
+
/**
|
|
217
|
+
* Whether an email service was configured but delivery failed, causing the
|
|
218
|
+
* fallback to `temporaryPassword`. Absent when no email service is configured.
|
|
219
|
+
*/
|
|
220
|
+
emailDeliveryFailed?: boolean;
|
|
199
221
|
}
|
|
200
222
|
/**
|
|
201
223
|
* The auth response payload shape that flows through `transformAuthResponse`.
|
|
@@ -266,7 +288,7 @@ export interface AuthAdapter {
|
|
|
266
288
|
/**
|
|
267
289
|
* Verify an incoming request and extract the authenticated user.
|
|
268
290
|
*
|
|
269
|
-
* This replaces the hardcoded JWT verification in server
|
|
291
|
+
* This replaces the hardcoded JWT verification in server's middleware.
|
|
270
292
|
* Each adapter implements its own token verification strategy:
|
|
271
293
|
* - Built-in: verify Rebase JWT
|
|
272
294
|
* - Clerk: call Clerk's `verifyToken()`
|
package/dist/types/backend.d.ts
CHANGED
|
@@ -162,6 +162,40 @@ export interface SingleSubscriptionConfig {
|
|
|
162
162
|
path: string;
|
|
163
163
|
id: string | number;
|
|
164
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Opt-in retention for one set of broadcast channels.
|
|
167
|
+
*
|
|
168
|
+
* Retention is configured on the server and nowhere else. A channel is created
|
|
169
|
+
* by whoever names it, so letting a client ask for its own history depth would
|
|
170
|
+
* let any visitor commit the backend to unbounded storage; and presence-only or
|
|
171
|
+
* notification-only channels — the overwhelming majority — must not pay for a
|
|
172
|
+
* feature they never use. With no rules configured nothing is written, no table
|
|
173
|
+
* is created, and broadcast behaves exactly as it did before history existed.
|
|
174
|
+
*/
|
|
175
|
+
export interface ChannelRetentionRule {
|
|
176
|
+
/**
|
|
177
|
+
* Channel name to match. Either exact (`"doc:42"`) or a trailing-`*` prefix
|
|
178
|
+
* (`"doc:*"`). Deliberately not a full glob or RegExp: this decides what
|
|
179
|
+
* gets written to disk, and a rule whose blast radius is not obvious at a
|
|
180
|
+
* glance is the wrong shape for that.
|
|
181
|
+
*/
|
|
182
|
+
match: string;
|
|
183
|
+
/** Keep at most this many of the most recent messages per channel. */
|
|
184
|
+
limit?: number;
|
|
185
|
+
/**
|
|
186
|
+
* Keep messages for at most this long. Accepts a millisecond count or a
|
|
187
|
+
* short duration string (`"30s"`, `"15m"`, `"24h"`, `"7d"`).
|
|
188
|
+
*/
|
|
189
|
+
ttl?: number | string;
|
|
190
|
+
}
|
|
191
|
+
/** Server-side realtime options. */
|
|
192
|
+
export interface RealtimeChannelsConfig {
|
|
193
|
+
/**
|
|
194
|
+
* Retention rules, most specific first — the first match wins. Omitted or
|
|
195
|
+
* empty means no channel retains anything.
|
|
196
|
+
*/
|
|
197
|
+
channels?: ChannelRetentionRule[];
|
|
198
|
+
}
|
|
165
199
|
/**
|
|
166
200
|
* Abstract realtime provider interface.
|
|
167
201
|
* Handles real-time subscriptions and notifications for entity changes.
|
|
@@ -258,9 +292,22 @@ export interface SQLAdmin {
|
|
|
258
292
|
*/
|
|
259
293
|
fetchAvailableDatabases?(): Promise<string[]>;
|
|
260
294
|
/**
|
|
261
|
-
* Fetch the available database roles.
|
|
295
|
+
* Fetch the available *native PostgreSQL* database roles (from `pg_roles`).
|
|
296
|
+
*
|
|
297
|
+
* These are connection-level roles — what the SQL editor can `SET ROLE` to,
|
|
298
|
+
* and what `SecurityRule.pgRoles` targets. They are NOT application roles;
|
|
299
|
+
* for those use {@link fetchApplicationRoles}.
|
|
262
300
|
*/
|
|
263
301
|
fetchAvailableRoles?(): Promise<string[]>;
|
|
302
|
+
/**
|
|
303
|
+
* Fetch the *application-level* roles in use in this project.
|
|
304
|
+
*
|
|
305
|
+
* These are the strings stored on the users table's `roles` column and
|
|
306
|
+
* exposed to policies as `auth.roles()` — what `SecurityRule.roles`
|
|
307
|
+
* matches against. Distinct from {@link fetchAvailableRoles}; the two are
|
|
308
|
+
* not interchangeable.
|
|
309
|
+
*/
|
|
310
|
+
fetchApplicationRoles?(): Promise<string[]>;
|
|
264
311
|
/**
|
|
265
312
|
* Fetch the current database name.
|
|
266
313
|
*/
|
|
@@ -551,6 +598,13 @@ export interface InitializedDriver {
|
|
|
551
598
|
realtimeProvider?: RealtimeProvider;
|
|
552
599
|
/** A collection registry to register schema / tables into. */
|
|
553
600
|
collectionRegistry?: CollectionRegistryInterface;
|
|
601
|
+
/**
|
|
602
|
+
* Collections the driver derived from the live database schema.
|
|
603
|
+
*
|
|
604
|
+
* Set by drivers that introspect in `baas` mode; the server serves these
|
|
605
|
+
* instead of collections loaded from config files.
|
|
606
|
+
*/
|
|
607
|
+
collections?: import("./collections").CollectionConfig[];
|
|
554
608
|
/** The underlying database connection (for lifecycle management). */
|
|
555
609
|
connection?: DatabaseConnection;
|
|
556
610
|
/**
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backup type definitions shared across server, client, and studio.
|
|
3
|
+
*/
|
|
4
|
+
/** Where a backup lives — a local path or an object-storage bucket. */
|
|
5
|
+
export type BackupDestinationKind = "local" | "s3" | "gcs";
|
|
6
|
+
/**
|
|
7
|
+
* A single backup as surfaced by the admin API / Studio Backups panel.
|
|
8
|
+
*/
|
|
9
|
+
export interface BackupInfo {
|
|
10
|
+
/** Storage key (object storage) or absolute file path (local). */
|
|
11
|
+
key: string;
|
|
12
|
+
/** Display name — the file's basename. */
|
|
13
|
+
name: string;
|
|
14
|
+
/** Size in bytes, when known. */
|
|
15
|
+
sizeBytes?: number;
|
|
16
|
+
/** ISO timestamp the backup was created, when recoverable. */
|
|
17
|
+
createdAt?: string;
|
|
18
|
+
/** The kind of destination this backup was read from. */
|
|
19
|
+
destinationKind: BackupDestinationKind;
|
|
20
|
+
}
|
|
@@ -154,24 +154,23 @@ export interface BaseCollectionConfig<M extends Record<string, unknown> = Record
|
|
|
154
154
|
*/
|
|
155
155
|
auth?: boolean | AuthCollectionConfig;
|
|
156
156
|
/**
|
|
157
|
-
* Opt out of the framework's default Row Level Security policies
|
|
158
|
-
* authentication collections.
|
|
157
|
+
* Opt out of the framework's default Row Level Security policies.
|
|
159
158
|
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
* `admin` role
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
159
|
+
* The schema generator automatically injects, for every collection, a
|
|
160
|
+
* baseline SELECT policy granting the trusted server context and the
|
|
161
|
+
* `admin` role read access (reads run under a restricted role, so RLS
|
|
162
|
+
* default-denies without it). For auth collections it additionally injects
|
|
163
|
+
* a self-read policy (`id = auth.uid()`) and an admin-only write gate
|
|
164
|
+
* (INSERT/UPDATE/DELETE require the `admin` role or the trusted server
|
|
165
|
+
* context), making privileged columns such as `roles` safe by default.
|
|
167
166
|
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
167
|
+
* Author-defined `securityRules` are permissive and broaden access on top
|
|
168
|
+
* of these defaults. Set this flag to `true` to remove the defaults
|
|
170
169
|
* entirely and take full responsibility for the collection's RLS.
|
|
171
170
|
*
|
|
172
171
|
* @default false
|
|
173
172
|
*/
|
|
174
|
-
|
|
173
|
+
disableDefaultPolicies?: boolean;
|
|
175
174
|
/**
|
|
176
175
|
* Order in which the properties are displayed.
|
|
177
176
|
* If you are specifying your collection as code, the order is the same as the
|
|
@@ -331,6 +330,17 @@ export interface BaseCollectionConfig<M extends Record<string, unknown> = Record
|
|
|
331
330
|
* This prop has no effect if the history plugin is not enabled
|
|
332
331
|
*/
|
|
333
332
|
history?: boolean;
|
|
333
|
+
/**
|
|
334
|
+
* Whether a write naming a field this collection does not declare is
|
|
335
|
+
* rejected with a 400. Defaults to `true`.
|
|
336
|
+
*
|
|
337
|
+
* Set to `false` to let unknown keys through to the database, which is what
|
|
338
|
+
* happened before this existed: a typo reached the INSERT and came back as
|
|
339
|
+
* a Postgres error about a column, or — where a column really does exist
|
|
340
|
+
* that the config never declared, populated by a trigger or a default —
|
|
341
|
+
* quietly worked. The second case is the reason for the escape hatch.
|
|
342
|
+
*/
|
|
343
|
+
strictWrites?: boolean;
|
|
334
344
|
/**
|
|
335
345
|
* Should local changes be backed up in local storage, to prevent data loss on
|
|
336
346
|
* accidental navigations.
|
|
@@ -899,9 +909,14 @@ export interface SecurityRuleBase {
|
|
|
899
909
|
* **Shortcut.** Restrict this rule to users that have one of these
|
|
900
910
|
* application-level roles.
|
|
901
911
|
*
|
|
902
|
-
* **Important:** These are NOT native PostgreSQL database roles
|
|
903
|
-
*
|
|
904
|
-
*
|
|
912
|
+
* **Important:** These are NOT native PostgreSQL database roles — names
|
|
913
|
+
* like `public`, `anon` or `authenticated` belong to {@link pgRoles} and
|
|
914
|
+
* produce a condition no user can satisfy if used here. These are
|
|
915
|
+
* application roles managed by Rebase, stored as an inline `roles TEXT[]`
|
|
916
|
+
* column on the users table, and injected into each transaction as
|
|
917
|
+
* `app.user_roles` — which `auth.roles()` reads.
|
|
918
|
+
*
|
|
919
|
+
* There is no roles registry: a role exists once it is assigned to a user.
|
|
905
920
|
*
|
|
906
921
|
* Generates a safe array-overlap condition — the user passes if they hold
|
|
907
922
|
* *any* of the listed roles:
|
|
@@ -930,10 +945,10 @@ export interface SecurityRuleBase {
|
|
|
930
945
|
* every database connection). This is correct for most setups where
|
|
931
946
|
* a single database role is used for all connections.
|
|
932
947
|
*
|
|
933
|
-
* **Important:** These are NOT the same as the application-level
|
|
934
|
-
* (admin, editor, viewer, etc.) — those are enforced in the
|
|
935
|
-
* CHECK clauses via `auth.roles()`. This field controls the
|
|
936
|
-
* `TO` clause in `CREATE POLICY ... TO role_name`.
|
|
948
|
+
* **Important:** These are NOT the same as the application-level
|
|
949
|
+
* {@link roles} (admin, editor, viewer, etc.) — those are enforced in the
|
|
950
|
+
* USING/WITH CHECK clauses via `auth.roles()`. This field controls the
|
|
951
|
+
* PostgreSQL `TO` clause in `CREATE POLICY ... TO role_name`.
|
|
937
952
|
*
|
|
938
953
|
* Use this if you have dedicated PostgreSQL roles (e.g. `app_read`,
|
|
939
954
|
* `app_write`) and want policies to target specific ones.
|
package/dist/types/cron.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { RebaseClient } from "../controllers/client";
|
|
|
3
3
|
* Cron Job type definitions for Rebase.
|
|
4
4
|
*
|
|
5
5
|
* These types define the shape of cron job definitions, their runtime
|
|
6
|
-
* status, and execution log entries — used across server
|
|
6
|
+
* status, and execution log entries — used across server, client,
|
|
7
7
|
* and studio packages.
|
|
8
8
|
*/
|
|
9
9
|
/**
|
|
@@ -45,7 +45,7 @@ export interface CronJobContext {
|
|
|
45
45
|
log: (...args: unknown[]) => void;
|
|
46
46
|
/**
|
|
47
47
|
* The server-side {@link RebaseClient}. This is the **same singleton**
|
|
48
|
-
* exposed as `rebase` (imported from `@rebasepro/server
|
|
48
|
+
* exposed as `rebase` (imported from `@rebasepro/server`) and as
|
|
49
49
|
* `context` in collection callbacks — it is only named `client` here.
|
|
50
50
|
*
|
|
51
51
|
* Its data plane (`client.data`) runs with **admin privileges and bypasses
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* @example
|
|
11
11
|
* ```ts
|
|
12
|
-
* import { createPostgresAdapter } from "@rebasepro/server-
|
|
12
|
+
* import { createPostgresAdapter } from "@rebasepro/server-postgres";
|
|
13
13
|
*
|
|
14
14
|
* initializeRebaseBackend({
|
|
15
15
|
* database: createPostgresAdapter({ connection: db, schema }),
|
|
@@ -92,4 +92,20 @@ export interface DatabaseAdapterInitConfig {
|
|
|
92
92
|
collections: CollectionConfig[];
|
|
93
93
|
/** The shared collection registry to register into. */
|
|
94
94
|
collectionRegistry: CollectionRegistryInterface;
|
|
95
|
+
/**
|
|
96
|
+
* How the server is being run — see `RebaseBackendConfig.mode`.
|
|
97
|
+
*
|
|
98
|
+
* In `"baas"` mode `collections` is empty by design: a driver that can
|
|
99
|
+
* describe its own schema should introspect the database and report the
|
|
100
|
+
* collections it found back on `InitializedDriver.collections`. Drivers
|
|
101
|
+
* that cannot introspect may ignore this.
|
|
102
|
+
*/
|
|
103
|
+
mode?: "cms" | "baas";
|
|
104
|
+
/**
|
|
105
|
+
* `baas`-mode options — see `RebaseBackendConfig.baas`. Drivers that
|
|
106
|
+
* introspect should honour `unprotectedTables`.
|
|
107
|
+
*/
|
|
108
|
+
baas?: {
|
|
109
|
+
unprotectedTables?: "exclude" | "serve";
|
|
110
|
+
};
|
|
95
111
|
}
|
|
@@ -74,7 +74,7 @@ export interface AfterReadProps<M extends Record<string, unknown> = Record<strin
|
|
|
74
74
|
*/
|
|
75
75
|
path: string;
|
|
76
76
|
/**
|
|
77
|
-
* Fetched row (flat —
|
|
77
|
+
* Fetched row (flat — the table's columns)
|
|
78
78
|
*/
|
|
79
79
|
row: Record<string, unknown>;
|
|
80
80
|
/**
|
|
@@ -149,7 +149,7 @@ export interface BeforeDeleteProps<M extends Record<string, unknown> = Record<st
|
|
|
149
149
|
*/
|
|
150
150
|
id: string | number;
|
|
151
151
|
/**
|
|
152
|
-
* Deleted row (flat —
|
|
152
|
+
* Deleted row (flat — the table's columns)
|
|
153
153
|
*/
|
|
154
154
|
row: Record<string, unknown>;
|
|
155
155
|
/**
|
|
@@ -175,7 +175,7 @@ export interface AfterDeleteProps<M extends Record<string, unknown> = Record<str
|
|
|
175
175
|
*/
|
|
176
176
|
id: string | number;
|
|
177
177
|
/**
|
|
178
|
-
* Deleted row (flat —
|
|
178
|
+
* Deleted row (flat — the table's columns)
|
|
179
179
|
*/
|
|
180
180
|
row: Record<string, unknown>;
|
|
181
181
|
/**
|
package/dist/types/index.d.ts
CHANGED
|
@@ -24,6 +24,7 @@ export * from "./entity_views";
|
|
|
24
24
|
export * from "./data_source";
|
|
25
25
|
export * from "./storage_source";
|
|
26
26
|
export * from "./cron";
|
|
27
|
+
export * from "./backup";
|
|
27
28
|
export * from "./component_ref";
|
|
28
29
|
export * from "./auth_adapter";
|
|
29
30
|
export * from "./database_adapter";
|
package/dist/types/policy.d.ts
CHANGED
|
@@ -17,7 +17,24 @@
|
|
|
17
17
|
*
|
|
18
18
|
* @group Models
|
|
19
19
|
*/
|
|
20
|
-
export type PolicyExpression = TruePolicyExpression | FalsePolicyExpression | AndPolicyExpression | OrPolicyExpression | NotPolicyExpression | ComparePolicyExpression | RolesOverlapPolicyExpression | RolesContainPolicyExpression | AuthenticatedPolicyExpression | ExistsInPolicyExpression | RawPolicyExpression;
|
|
20
|
+
export type PolicyExpression = TruePolicyExpression | FalsePolicyExpression | AndPolicyExpression | OrPolicyExpression | NotPolicyExpression | ComparePolicyExpression | RolesOverlapPolicyExpression | RolesContainPolicyExpression | AuthenticatedPolicyExpression | ServerContextPolicyExpression | ExistsInPolicyExpression | RawPolicyExpression;
|
|
21
|
+
/**
|
|
22
|
+
* The id a request without a logged-in user reports as `auth.uid()`.
|
|
23
|
+
*
|
|
24
|
+
* A user-context request always sets `app.user_id`: blank would read back as
|
|
25
|
+
* `NULL`, and `NULL` is how the trusted server context is recognised, so an
|
|
26
|
+
* anonymous visitor would be promoted to server privileges. The driver
|
|
27
|
+
* therefore substitutes this sentinel at the single chokepoint where the GUC
|
|
28
|
+
* is set.
|
|
29
|
+
*
|
|
30
|
+
* The consequence for policy authors is that **`auth.uid() IS NOT NULL` is a
|
|
31
|
+
* tautology on the user path** — it is true for anonymous visitors too. Use
|
|
32
|
+
* {@link policy.authenticated} (or `auth.uid() <> 'anonymous'`) to mean "signed
|
|
33
|
+
* in", and {@link policy.serverContext} to mean "the trusted server context".
|
|
34
|
+
*
|
|
35
|
+
* @group Models
|
|
36
|
+
*/
|
|
37
|
+
export declare const ANONYMOUS_USER_ID = "anonymous";
|
|
21
38
|
/** Always allows. Compiles to `true`. @group Models */
|
|
22
39
|
export interface TruePolicyExpression {
|
|
23
40
|
kind: "true";
|
|
@@ -72,12 +89,40 @@ export interface RolesContainPolicyExpression {
|
|
|
72
89
|
roles: readonly string[];
|
|
73
90
|
}
|
|
74
91
|
/**
|
|
75
|
-
* True when
|
|
92
|
+
* True when a signed-in user is making the request. Compiles to
|
|
93
|
+
* `auth.uid() IS NOT NULL AND auth.uid() <> 'anonymous'`.
|
|
94
|
+
*
|
|
95
|
+
* Both halves are load-bearing. `IS NOT NULL` excludes the server context;
|
|
96
|
+
* the {@link ANONYMOUS_USER_ID} comparison excludes anonymous visitors, who
|
|
97
|
+
* *do* carry a non-null `auth.uid()`. Checking only `IS NOT NULL` grants to
|
|
98
|
+
* everyone — see {@link ANONYMOUS_USER_ID}.
|
|
99
|
+
*
|
|
100
|
+
* `policy.not(policy.authenticated())` therefore means "anonymous visitor or
|
|
101
|
+
* the server context". To single out the server context, use
|
|
102
|
+
* {@link ServerContextPolicyExpression}.
|
|
76
103
|
* @group Models
|
|
77
104
|
*/
|
|
78
105
|
export interface AuthenticatedPolicyExpression {
|
|
79
106
|
kind: "authenticated";
|
|
80
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* True only in the trusted **server context** — the built-in flows that run
|
|
110
|
+
* without a user (signup, migrations, `dataAsAdmin`) set no user GUC, so
|
|
111
|
+
* `auth.uid()` is `NULL` for them and only for them. Compiles to
|
|
112
|
+
* `auth.uid() IS NULL`.
|
|
113
|
+
*
|
|
114
|
+
* This is what lets the owner connection satisfy a policy even under FORCE RLS.
|
|
115
|
+
* It is deliberately a primitive rather than `not(authenticated())`: the two
|
|
116
|
+
* meant the same thing while `authenticated` ignored {@link ANONYMOUS_USER_ID},
|
|
117
|
+
* and conflating them is what turns a server-only grant into an anonymous one.
|
|
118
|
+
*
|
|
119
|
+
* The JavaScript evaluator always returns `false` for this node — a client is
|
|
120
|
+
* never the server context.
|
|
121
|
+
* @group Models
|
|
122
|
+
*/
|
|
123
|
+
export interface ServerContextPolicyExpression {
|
|
124
|
+
kind: "serverContext";
|
|
125
|
+
}
|
|
81
126
|
/**
|
|
82
127
|
* Membership / relational access: true when at least one row exists in another
|
|
83
128
|
* collection (a join/membership table) matching `where`. This is what lets you
|
|
@@ -178,6 +223,7 @@ export declare const policy: {
|
|
|
178
223
|
rolesOverlap: (roles: readonly string[]) => RolesOverlapPolicyExpression;
|
|
179
224
|
rolesContain: (roles: readonly string[]) => RolesContainPolicyExpression;
|
|
180
225
|
authenticated: () => AuthenticatedPolicyExpression;
|
|
226
|
+
serverContext: () => ServerContextPolicyExpression;
|
|
181
227
|
existsIn: (args: {
|
|
182
228
|
collection: string;
|
|
183
229
|
where: PolicyExpression;
|
|
@@ -175,6 +175,19 @@ export interface BaseProperty<CustomProps = unknown> {
|
|
|
175
175
|
* Rules for validating this property
|
|
176
176
|
*/
|
|
177
177
|
validation?: PropertyValidationSchema;
|
|
178
|
+
/**
|
|
179
|
+
* Never include this column in an API response.
|
|
180
|
+
*
|
|
181
|
+
* For secrets the server must store and read but no client should ever
|
|
182
|
+
* receive — password hashes, verification tokens. The value is still
|
|
183
|
+
* written and queryable server-side; it is stripped from every row the API
|
|
184
|
+
* serves, for every caller, including admins and service keys.
|
|
185
|
+
*
|
|
186
|
+
* This is a server-side guarantee, unlike `ui.hideFromCollection`, which
|
|
187
|
+
* only stops the admin panel from *rendering* a field and leaves it in the
|
|
188
|
+
* JSON payload.
|
|
189
|
+
*/
|
|
190
|
+
excludeFromApi?: boolean;
|
|
178
191
|
/**
|
|
179
192
|
* Use this to define dynamic properties that change based on certain conditions
|
|
180
193
|
* or on the entity's values. For example, you can make a field read-only if
|
|
@@ -6,7 +6,7 @@ export type DeepPartial<T> = T extends object ? {
|
|
|
6
6
|
[K in keyof T]?: DeepPartial<T[K]>;
|
|
7
7
|
} : T;
|
|
8
8
|
/**
|
|
9
|
-
* All user-visible strings used internally by @rebasepro/
|
|
9
|
+
* All user-visible strings used internally by @rebasepro/app.
|
|
10
10
|
* Pass a `DeepPartial<RebaseTranslations>` via the `translations` prop
|
|
11
11
|
* on your Rebase entry-point component to override any key, or to add
|
|
12
12
|
* a new locale.
|
|
@@ -487,6 +487,7 @@ export interface RebaseTranslations {
|
|
|
487
487
|
invitation_sent_title?: string;
|
|
488
488
|
temporary_password?: string;
|
|
489
489
|
temporary_password_description?: string;
|
|
490
|
+
temporary_password_email_failed_description?: string;
|
|
490
491
|
copy_password?: string;
|
|
491
492
|
password_copied?: string;
|
|
492
493
|
/** UsersView — pagination & search */
|
|
@@ -497,6 +498,9 @@ export interface RebaseTranslations {
|
|
|
497
498
|
reset_password?: string;
|
|
498
499
|
reset_password_success?: string;
|
|
499
500
|
reset_password_confirmation?: string;
|
|
501
|
+
reset_password_send_email?: string;
|
|
502
|
+
reset_password_set_manually?: string;
|
|
503
|
+
reset_password_set_manually_description?: string;
|
|
500
504
|
error_resetting_password?: string;
|
|
501
505
|
/** Permission-denied empty states */
|
|
502
506
|
no_permission_to_view_users?: string;
|
|
@@ -9,8 +9,14 @@ export interface UserCreationResult<USER extends User = User> {
|
|
|
9
9
|
/** Whether an invitation email was sent to the user */
|
|
10
10
|
invitationSent: boolean;
|
|
11
11
|
/**
|
|
12
|
-
* Temporary password
|
|
13
|
-
*
|
|
12
|
+
* Temporary password, present when no invitation email could be delivered —
|
|
13
|
+
* either because no email service is configured, or because delivery failed
|
|
14
|
+
* (see `emailDeliveryFailed`). Returned one-time, to be shared manually.
|
|
14
15
|
*/
|
|
15
16
|
temporaryPassword?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Whether an email service was configured but delivery failed, causing the
|
|
19
|
+
* fallback to `temporaryPassword`. Absent when no email service is configured.
|
|
20
|
+
*/
|
|
21
|
+
emailDeliveryFailed?: boolean;
|
|
16
22
|
}
|
|
@@ -14,11 +14,42 @@ export interface WebSocketMessage {
|
|
|
14
14
|
rows?: Record<string, unknown>[];
|
|
15
15
|
row?: Record<string, unknown> | null;
|
|
16
16
|
error?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Channel name, on broadcast and presence frames.
|
|
19
|
+
*
|
|
20
|
+
* These are addressed by channel rather than by `requestId` or
|
|
21
|
+
* `subscriptionId`, so this is the only field that routes them.
|
|
22
|
+
*/
|
|
23
|
+
channel?: string;
|
|
17
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* The key columns a collection's rows are addressed by.
|
|
27
|
+
*
|
|
28
|
+
* A row is exactly its columns and carries no address, so a subscriber that has
|
|
29
|
+
* to recognise one — to patch it, or to keep its reference across a refetch —
|
|
30
|
+
* derives the address from these. The SDK is usable with no collections
|
|
31
|
+
* declared at all, so the server is the only side that knows them.
|
|
32
|
+
*
|
|
33
|
+
* Undefined when the server cannot resolve them: a table with no primary key
|
|
34
|
+
* and no `id` column has no address, and rows of it cannot be recognised by
|
|
35
|
+
* anyone.
|
|
36
|
+
*/
|
|
37
|
+
export type WirePrimaryKeys = {
|
|
38
|
+
fieldName: string;
|
|
39
|
+
type: "string" | "number";
|
|
40
|
+
isUUID?: boolean;
|
|
41
|
+
}[];
|
|
18
42
|
export interface CollectionUpdateMessage extends WebSocketMessage {
|
|
19
43
|
type: "collection_update";
|
|
20
44
|
subscriptionId: string;
|
|
21
45
|
rows: Record<string, unknown>[];
|
|
46
|
+
/**
|
|
47
|
+
* See {@link WirePrimaryKeys}. Sent with the rows themselves — and not only
|
|
48
|
+
* with a patch — because a CDC-originated change sends no patch at all: it
|
|
49
|
+
* invalidates and goes straight to a refetch, and the merge that preserves
|
|
50
|
+
* unchanged rows' references needs an address to match them by.
|
|
51
|
+
*/
|
|
52
|
+
pks?: WirePrimaryKeys;
|
|
22
53
|
}
|
|
23
54
|
export interface SingleUpdateMessage extends WebSocketMessage {
|
|
24
55
|
type: "single_update";
|
|
@@ -34,9 +65,55 @@ export interface SingleUpdateMessage extends WebSocketMessage {
|
|
|
34
65
|
export interface CollectionPatchMessage extends WebSocketMessage {
|
|
35
66
|
type: "collection_patch";
|
|
36
67
|
subscriptionId: string;
|
|
68
|
+
/** The address of the row this patch refers to — derived, never read off it. */
|
|
37
69
|
id: string;
|
|
38
70
|
/** The updated row, or null if deleted */
|
|
39
71
|
row: Record<string, unknown> | null;
|
|
72
|
+
/** See {@link WirePrimaryKeys}: how the subscriber finds {@link id} in its cache. */
|
|
73
|
+
pks?: WirePrimaryKeys;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* One retained broadcast, as it travels on the wire.
|
|
77
|
+
*
|
|
78
|
+
* `seq` is per-channel, dense and monotonically increasing — it is the only
|
|
79
|
+
* thing a reconnecting client needs to say where it got to. See
|
|
80
|
+
* {@link ChannelHistoryMessage}.
|
|
81
|
+
*/
|
|
82
|
+
export interface ChannelHistoryEntry {
|
|
83
|
+
seq: number;
|
|
84
|
+
event: string;
|
|
85
|
+
payload: unknown;
|
|
86
|
+
/**
|
|
87
|
+
* The server-side client id of whoever sent it, for information only.
|
|
88
|
+
*
|
|
89
|
+
* Deliberately not used to filter a client's own messages out of a replay:
|
|
90
|
+
* a reconnect assigns a brand-new client id, so the very case replay exists
|
|
91
|
+
* for is the case where this would fail to match. Consumers that cannot
|
|
92
|
+
* tolerate re-applying their own operations must make them idempotent.
|
|
93
|
+
*/
|
|
94
|
+
senderId?: string;
|
|
95
|
+
/** When the server accepted it, ISO-8601. */
|
|
96
|
+
at: string;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Server → client: the retained messages a client missed.
|
|
100
|
+
*
|
|
101
|
+
* `retained: false` means the channel has no retention rule configured, so
|
|
102
|
+
* there is no history to replay and there never will be — an explicit answer
|
|
103
|
+
* rather than an empty one, so a client can tell "nothing missed" apart from
|
|
104
|
+
* "this channel does not keep history".
|
|
105
|
+
*/
|
|
106
|
+
export interface ChannelHistoryMessage extends WebSocketMessage {
|
|
107
|
+
type: "channel_history";
|
|
108
|
+
channel: string;
|
|
109
|
+
messages: ChannelHistoryEntry[];
|
|
110
|
+
retained: boolean;
|
|
111
|
+
/**
|
|
112
|
+
* The highest seq the server holds for this channel, whether or not it was
|
|
113
|
+
* returned. Lets a client that capped its request with `limit` see that it
|
|
114
|
+
* is still behind, and decide to resync wholesale instead of paging.
|
|
115
|
+
*/
|
|
116
|
+
latestSeq?: number;
|
|
40
117
|
}
|
|
41
118
|
/**
|
|
42
119
|
* Column metadata returned by table introspection.
|
package/dist/users/user.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The canonical representation of an authenticated user in the Rebase ecosystem.
|
|
3
3
|
*
|
|
4
4
|
* Used by {@link AuthController}, collections, callbacks, and both the
|
|
5
|
-
* `@rebasepro/client` and `@rebasepro/
|
|
5
|
+
* `@rebasepro/client` and `@rebasepro/app` packages. All other user types
|
|
6
6
|
* (`RebaseUser`, `UserInfo`) are deprecated aliases of this type.
|
|
7
7
|
*
|
|
8
8
|
* **Backend-managed fields** (`uid`, `email`, `roles`, `metadata`, `createdAt`)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/types",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.1-canary.0de22e0",
|
|
5
5
|
"description": "Rebase type definitions — shared interfaces and controller types",
|
|
6
6
|
"funding": {
|
|
7
7
|
"url": "https://github.com/sponsors/rebaseco"
|
|
@@ -13,12 +13,12 @@
|
|
|
13
13
|
"url": "https://github.com/rebasepro/rebase.git",
|
|
14
14
|
"directory": "packages/types"
|
|
15
15
|
},
|
|
16
|
-
"main": "./dist/index.
|
|
16
|
+
"main": "./dist/index.es.js",
|
|
17
17
|
"module": "./dist/index.es.js",
|
|
18
18
|
"types": "./dist/index.d.ts",
|
|
19
19
|
"source": "src/index.ts",
|
|
20
20
|
"engines": {
|
|
21
|
-
"node": ">=
|
|
21
|
+
"node": ">=20"
|
|
22
22
|
},
|
|
23
23
|
"keywords": [
|
|
24
24
|
"rebase",
|
|
@@ -33,8 +33,7 @@
|
|
|
33
33
|
".": {
|
|
34
34
|
"types": "./dist/index.d.ts",
|
|
35
35
|
"development": "./dist/index.es.js",
|
|
36
|
-
"import": "./dist/index.es.js"
|
|
37
|
-
"require": "./dist/index.umd.js"
|
|
36
|
+
"import": "./dist/index.es.js"
|
|
38
37
|
},
|
|
39
38
|
"./package.json": "./package.json"
|
|
40
39
|
},
|
|
@@ -92,7 +91,7 @@
|
|
|
92
91
|
},
|
|
93
92
|
"scripts": {
|
|
94
93
|
"watch": "vite build --watch",
|
|
95
|
-
"build": "vite build && tsc --emitDeclarationOnly -p tsconfig.prod.json",
|
|
94
|
+
"build": "vite build && tsc --emitDeclarationOnly -p tsconfig.prod.json && node ../../scripts/assert-build-output.mjs",
|
|
96
95
|
"test:lint": "eslint \"src/**\" --quiet",
|
|
97
96
|
"test": "jest --passWithNoTests",
|
|
98
97
|
"clean": "rm -rf dist && find ./src -name '*.js' -type f | xargs rm -f"
|
package/src/controllers/auth.tsx
CHANGED
|
@@ -10,7 +10,13 @@ export interface AuthCapabilities {
|
|
|
10
10
|
emailPasswordLogin?: boolean;
|
|
11
11
|
googleLogin?: boolean;
|
|
12
12
|
registration?: boolean;
|
|
13
|
+
/** Self-service password reset (emailing a reset link) is available. */
|
|
13
14
|
passwordReset?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* An admin can reset another user's password. Gates the "Reset Password"
|
|
17
|
+
* entity action in the admin UI. See `AuthAdapterCapabilities`.
|
|
18
|
+
*/
|
|
19
|
+
adminPasswordReset?: boolean;
|
|
14
20
|
sessionManagement?: boolean;
|
|
15
21
|
profileUpdate?: boolean;
|
|
16
22
|
emailVerification?: boolean;
|