@rebasepro/types 0.9.0 → 0.9.1-canary.0fce67c
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 +7 -0
- package/dist/types/backup.d.ts +20 -0
- package/dist/types/collections.d.ts +22 -12
- 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 +27 -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 +8 -0
- package/src/types/backup.ts +26 -0
- package/src/types/collections.ts +23 -12
- 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 +24 -0
- package/src/users/user.ts +1 -1
- package/dist/index.umd.js +0 -591
- package/dist/index.umd.js.map +0 -1
|
@@ -77,6 +77,31 @@ export interface SaveProps<M extends Record<string, unknown> = Record<string, un
|
|
|
77
77
|
previousValues?: Partial<EntityValues<M>>;
|
|
78
78
|
collection?: CollectionConfig<M>;
|
|
79
79
|
status: EntityStatus;
|
|
80
|
+
/**
|
|
81
|
+
* Write the row with INSERT ... ON CONFLICT DO UPDATE on the primary key
|
|
82
|
+
* instead of choosing between insert and update up front.
|
|
83
|
+
*
|
|
84
|
+
* One statement, so it does not lose the race a read-then-write can, and it
|
|
85
|
+
* succeeds whether or not the row is already there — what a re-runnable
|
|
86
|
+
* import needs. Requires every primary key column to be present; without
|
|
87
|
+
* them there is no conflict target and the row is inserted normally.
|
|
88
|
+
*/
|
|
89
|
+
upsert?: boolean;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @internal
|
|
94
|
+
*/
|
|
95
|
+
export interface SaveManyProps<M extends Record<string, unknown> = Record<string, unknown>> {
|
|
96
|
+
path: string;
|
|
97
|
+
/**
|
|
98
|
+
* The rows to write. A row carrying its primary key updates (or, with
|
|
99
|
+
* `upsert`, inserts-or-updates) that row; one without inserts.
|
|
100
|
+
*/
|
|
101
|
+
rows: Partial<EntityValues<M>>[];
|
|
102
|
+
collection?: CollectionConfig<M>;
|
|
103
|
+
/** Apply every row as INSERT ... ON CONFLICT DO UPDATE. See {@link SaveProps.upsert}. */
|
|
104
|
+
upsert?: boolean;
|
|
80
105
|
}
|
|
81
106
|
|
|
82
107
|
/**
|
|
@@ -154,6 +179,20 @@ export interface DataDriver {
|
|
|
154
179
|
*/
|
|
155
180
|
save<M extends Record<string, unknown> = Record<string, unknown>>(props: SaveProps<M>): Promise<Record<string, unknown>>;
|
|
156
181
|
|
|
182
|
+
/**
|
|
183
|
+
* Save many rows as one unit of work.
|
|
184
|
+
*
|
|
185
|
+
* Every row runs the same pipeline as {@link save} — callbacks, relations
|
|
186
|
+
* and row-level security all still apply — but they share a single
|
|
187
|
+
* transaction, so the batch either lands whole or not at all. That, and the
|
|
188
|
+
* single round trip, is what makes importing tens of thousands of rows
|
|
189
|
+
* viable without dropping to raw SQL.
|
|
190
|
+
*
|
|
191
|
+
* Optional: drivers that cannot do this leave it undefined and callers fall
|
|
192
|
+
* back to `save` per row.
|
|
193
|
+
*/
|
|
194
|
+
saveMany?<M extends Record<string, unknown> = Record<string, unknown>>(props: SaveManyProps<M>): Promise<Record<string, unknown>[]>;
|
|
195
|
+
|
|
157
196
|
/**
|
|
158
197
|
* Delete a entity
|
|
159
198
|
* @param props
|
|
@@ -252,9 +291,14 @@ export interface DataDriver {
|
|
|
252
291
|
* REST-optimised fetch service exposed by drivers that support
|
|
253
292
|
* eager-loading of relations via `include`.
|
|
254
293
|
*
|
|
255
|
-
* The methods return flattened rows
|
|
256
|
-
*
|
|
257
|
-
*
|
|
294
|
+
* The methods return flattened rows — exactly the table's columns, under their
|
|
295
|
+
* own names and with the types the database returned — and included relations
|
|
296
|
+
* inlined as plain nested rows. This is the shape served to app developers
|
|
297
|
+
* through the REST API / SDK client.
|
|
298
|
+
*
|
|
299
|
+
* No synthesized `id`: identity is a primary key, which may be named anything
|
|
300
|
+
* and span several columns, so an address is derived by whoever needs one (see
|
|
301
|
+
* `buildCompositeId`) rather than written into the row on top of the data.
|
|
258
302
|
*
|
|
259
303
|
* @group DataDriver
|
|
260
304
|
*/
|
package/src/controllers/email.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Email service types — portable interface shared by RebaseClient and server
|
|
2
|
+
* Email service types — portable interface shared by RebaseClient and server.
|
|
3
3
|
*
|
|
4
|
-
* The concrete SMTP implementation lives in `@rebasepro/server
|
|
4
|
+
* The concrete SMTP implementation lives in `@rebasepro/server/email`.
|
|
5
5
|
* This file provides only the consumer-facing contract so that it can be
|
|
6
6
|
* referenced from `RebaseClient` without dragging in nodemailer.
|
|
7
7
|
*/
|
|
@@ -7,7 +7,7 @@ import type { AppView, NavigationGroupMapping } from "./navigation";
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Options to enable the built-in collection editor.
|
|
10
|
-
* When provided to `<
|
|
10
|
+
* When provided to `<RebaseAdmin>`, the editor is auto-wired as a native feature.
|
|
11
11
|
*/
|
|
12
12
|
export interface CollectionEditorOptions {
|
|
13
13
|
/**
|
|
@@ -21,7 +21,7 @@ export interface CollectionEditorOptions {
|
|
|
21
21
|
pathSuggestions?: string[];
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export interface
|
|
24
|
+
export interface RebaseAdminConfig<EC extends CollectionConfig = CollectionConfig> {
|
|
25
25
|
collections?: EC[] | CollectionConfigsBuilder<EC>;
|
|
26
26
|
|
|
27
27
|
/**
|
|
@@ -68,7 +68,7 @@ export interface RebaseCMSConfig<EC extends CollectionConfig = CollectionConfig>
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
export interface RebaseStudioConfig {
|
|
71
|
-
tools?: ("sql" | "js" | "rls" | "schema" | "storage" | "cron" | "schema-visualizer" | "branches" | "api" | "logs" | "api-keys")[];
|
|
71
|
+
tools?: ("sql" | "js" | "rls" | "schema" | "storage" | "cron" | "schema-visualizer" | "branches" | "backups" | "api" | "logs" | "api-keys")[];
|
|
72
72
|
homePage?: ReactNode;
|
|
73
73
|
devViews?: AppView[];
|
|
74
74
|
}
|
|
@@ -79,12 +79,12 @@ export interface RebaseAuthConfig {
|
|
|
79
79
|
|
|
80
80
|
export interface RebaseRegistryController {
|
|
81
81
|
// Current state
|
|
82
|
-
cmsConfig:
|
|
82
|
+
cmsConfig: RebaseAdminConfig | null;
|
|
83
83
|
studioConfig: RebaseStudioConfig | null;
|
|
84
84
|
authConfig: RebaseAuthConfig | null;
|
|
85
85
|
|
|
86
86
|
// Registration functions
|
|
87
|
-
registerCMS: (config:
|
|
87
|
+
registerCMS: (config: RebaseAdminConfig) => void;
|
|
88
88
|
unregisterCMS: () => void;
|
|
89
89
|
|
|
90
90
|
registerStudio: (config: RebaseStudioConfig) => void;
|
|
@@ -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({
|
|
@@ -85,8 +85,25 @@ export interface AuthAdapterCapabilities {
|
|
|
85
85
|
emailPasswordLogin: boolean;
|
|
86
86
|
/** Supports new user registration. */
|
|
87
87
|
registration: boolean;
|
|
88
|
-
/**
|
|
88
|
+
/**
|
|
89
|
+
* Supports the end-user password reset flow (emailing a reset link).
|
|
90
|
+
*
|
|
91
|
+
* This is about *self-service* reset, so it is typically tied to whether an
|
|
92
|
+
* email service is configured. It says nothing about whether an admin can
|
|
93
|
+
* reset someone else's password — see `adminPasswordReset`.
|
|
94
|
+
*/
|
|
89
95
|
passwordReset: boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Whether the adapter exposes `POST /admin/users/:userId/reset-password`,
|
|
98
|
+
* letting an admin reset another user's password.
|
|
99
|
+
*
|
|
100
|
+
* Independent of `passwordReset`: the built-in adapter supports this even
|
|
101
|
+
* with no email service configured (it returns a one-time temporary
|
|
102
|
+
* password instead of sending a link). Adapters that mount their own admin
|
|
103
|
+
* routes must set this to `true` only once that route actually exists —
|
|
104
|
+
* the admin UI hides the "Reset Password" action when it is `false`.
|
|
105
|
+
*/
|
|
106
|
+
adminPasswordReset: boolean;
|
|
90
107
|
/** Supports session listing/revocation. */
|
|
91
108
|
sessionManagement: boolean;
|
|
92
109
|
/** Supports profile updates (display name, photo). */
|
|
@@ -218,6 +235,11 @@ export interface UserCreationFinalizeResult {
|
|
|
218
235
|
temporaryPassword?: string;
|
|
219
236
|
/** Whether an invitation email was sent. */
|
|
220
237
|
invitationSent: boolean;
|
|
238
|
+
/**
|
|
239
|
+
* Whether an email service was configured but delivery failed, causing the
|
|
240
|
+
* fallback to `temporaryPassword`. Absent when no email service is configured.
|
|
241
|
+
*/
|
|
242
|
+
emailDeliveryFailed?: boolean;
|
|
221
243
|
}
|
|
222
244
|
|
|
223
245
|
// ─── Auth Response Transform ─────────────────────────────────────────────────
|
|
@@ -298,7 +320,7 @@ export interface AuthAdapter {
|
|
|
298
320
|
/**
|
|
299
321
|
* Verify an incoming request and extract the authenticated user.
|
|
300
322
|
*
|
|
301
|
-
* This replaces the hardcoded JWT verification in server
|
|
323
|
+
* This replaces the hardcoded JWT verification in server's middleware.
|
|
302
324
|
* Each adapter implements its own token verification strategy:
|
|
303
325
|
* - Built-in: verify Rebase JWT
|
|
304
326
|
* - Clerk: call Clerk's `verifyToken()`
|
package/src/types/backend.ts
CHANGED
|
@@ -736,6 +736,14 @@ export interface InitializedDriver {
|
|
|
736
736
|
/** A collection registry to register schema / tables into. */
|
|
737
737
|
collectionRegistry?: CollectionRegistryInterface;
|
|
738
738
|
|
|
739
|
+
/**
|
|
740
|
+
* Collections the driver derived from the live database schema.
|
|
741
|
+
*
|
|
742
|
+
* Set by drivers that introspect in `baas` mode; the server serves these
|
|
743
|
+
* instead of collections loaded from config files.
|
|
744
|
+
*/
|
|
745
|
+
collections?: import("./collections").CollectionConfig[];
|
|
746
|
+
|
|
739
747
|
/** The underlying database connection (for lifecycle management). */
|
|
740
748
|
connection?: DatabaseConnection;
|
|
741
749
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backup type definitions shared across server, client, and studio.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/** Where a backup lives — a local path or an object-storage bucket. */
|
|
6
|
+
export type BackupDestinationKind = "local" | "s3" | "gcs";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A single backup as surfaced by the admin API / Studio Backups panel.
|
|
10
|
+
*/
|
|
11
|
+
export interface BackupInfo {
|
|
12
|
+
/** Storage key (object storage) or absolute file path (local). */
|
|
13
|
+
key: string;
|
|
14
|
+
|
|
15
|
+
/** Display name — the file's basename. */
|
|
16
|
+
name: string;
|
|
17
|
+
|
|
18
|
+
/** Size in bytes, when known. */
|
|
19
|
+
sizeBytes?: number;
|
|
20
|
+
|
|
21
|
+
/** ISO timestamp the backup was created, when recoverable. */
|
|
22
|
+
createdAt?: string;
|
|
23
|
+
|
|
24
|
+
/** The kind of destination this backup was read from. */
|
|
25
|
+
destinationKind: BackupDestinationKind;
|
|
26
|
+
}
|
package/src/types/collections.ts
CHANGED
|
@@ -179,24 +179,23 @@ export interface BaseCollectionConfig<M extends Record<string, unknown> = Record
|
|
|
179
179
|
auth?: boolean | AuthCollectionConfig;
|
|
180
180
|
|
|
181
181
|
/**
|
|
182
|
-
* Opt out of the framework's default Row Level Security policies
|
|
183
|
-
* authentication collections.
|
|
182
|
+
* Opt out of the framework's default Row Level Security policies.
|
|
184
183
|
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
* `admin` role
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
184
|
+
* The schema generator automatically injects, for every collection, a
|
|
185
|
+
* baseline SELECT policy granting the trusted server context and the
|
|
186
|
+
* `admin` role read access (reads run under a restricted role, so RLS
|
|
187
|
+
* default-denies without it). For auth collections it additionally injects
|
|
188
|
+
* a self-read policy (`id = auth.uid()`) and an admin-only write gate
|
|
189
|
+
* (INSERT/UPDATE/DELETE require the `admin` role or the trusted server
|
|
190
|
+
* context), making privileged columns such as `roles` safe by default.
|
|
192
191
|
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
192
|
+
* Author-defined `securityRules` are permissive and broaden access on top
|
|
193
|
+
* of these defaults. Set this flag to `true` to remove the defaults
|
|
195
194
|
* entirely and take full responsibility for the collection's RLS.
|
|
196
195
|
*
|
|
197
196
|
* @default false
|
|
198
197
|
*/
|
|
199
|
-
|
|
198
|
+
disableDefaultPolicies?: boolean;
|
|
200
199
|
|
|
201
200
|
|
|
202
201
|
/**
|
|
@@ -383,6 +382,18 @@ export interface BaseCollectionConfig<M extends Record<string, unknown> = Record
|
|
|
383
382
|
*/
|
|
384
383
|
history?: boolean;
|
|
385
384
|
|
|
385
|
+
/**
|
|
386
|
+
* Whether a write naming a field this collection does not declare is
|
|
387
|
+
* rejected with a 400. Defaults to `true`.
|
|
388
|
+
*
|
|
389
|
+
* Set to `false` to let unknown keys through to the database, which is what
|
|
390
|
+
* happened before this existed: a typo reached the INSERT and came back as
|
|
391
|
+
* a Postgres error about a column, or — where a column really does exist
|
|
392
|
+
* that the config never declared, populated by a trigger or a default —
|
|
393
|
+
* quietly worked. The second case is the reason for the escape hatch.
|
|
394
|
+
*/
|
|
395
|
+
strictWrites?: boolean;
|
|
396
|
+
|
|
386
397
|
/**
|
|
387
398
|
* Should local changes be backed up in local storage, to prevent data loss on
|
|
388
399
|
* accidental navigations.
|
package/src/types/cron.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { RebaseClient } from "../controllers/client";
|
|
|
4
4
|
* Cron Job type definitions for Rebase.
|
|
5
5
|
*
|
|
6
6
|
* These types define the shape of cron job definitions, their runtime
|
|
7
|
-
* status, and execution log entries — used across server
|
|
7
|
+
* status, and execution log entries — used across server, client,
|
|
8
8
|
* and studio packages.
|
|
9
9
|
*/
|
|
10
10
|
|
|
@@ -62,7 +62,7 @@ export interface CronJobContext {
|
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
64
|
* The server-side {@link RebaseClient}. This is the **same singleton**
|
|
65
|
-
* exposed as `rebase` (imported from `@rebasepro/server
|
|
65
|
+
* exposed as `rebase` (imported from `@rebasepro/server`) and as
|
|
66
66
|
* `context` in collection callbacks — it is only named `client` here.
|
|
67
67
|
*
|
|
68
68
|
* 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 }),
|
|
@@ -118,4 +118,18 @@ export interface DatabaseAdapterInitConfig {
|
|
|
118
118
|
collections: CollectionConfig[];
|
|
119
119
|
/** The shared collection registry to register into. */
|
|
120
120
|
collectionRegistry: CollectionRegistryInterface;
|
|
121
|
+
/**
|
|
122
|
+
* How the server is being run — see `RebaseBackendConfig.mode`.
|
|
123
|
+
*
|
|
124
|
+
* In `"baas"` mode `collections` is empty by design: a driver that can
|
|
125
|
+
* describe its own schema should introspect the database and report the
|
|
126
|
+
* collections it found back on `InitializedDriver.collections`. Drivers
|
|
127
|
+
* that cannot introspect may ignore this.
|
|
128
|
+
*/
|
|
129
|
+
mode?: "cms" | "baas";
|
|
130
|
+
/**
|
|
131
|
+
* `baas`-mode options — see `RebaseBackendConfig.baas`. Drivers that
|
|
132
|
+
* introspect should honour `unprotectedTables`.
|
|
133
|
+
*/
|
|
134
|
+
baas?: { unprotectedTables?: "exclude" | "serve" };
|
|
121
135
|
}
|
|
@@ -91,7 +91,7 @@ export interface AfterReadProps<M extends Record<string, unknown> = Record<strin
|
|
|
91
91
|
path: string;
|
|
92
92
|
|
|
93
93
|
/**
|
|
94
|
-
* Fetched row (flat —
|
|
94
|
+
* Fetched row (flat — the table's columns)
|
|
95
95
|
*/
|
|
96
96
|
row: Record<string, unknown>
|
|
97
97
|
|
|
@@ -185,7 +185,7 @@ export interface BeforeDeleteProps<M extends Record<string, unknown> = Record<st
|
|
|
185
185
|
id: string | number;
|
|
186
186
|
|
|
187
187
|
/**
|
|
188
|
-
* Deleted row (flat —
|
|
188
|
+
* Deleted row (flat — the table's columns)
|
|
189
189
|
*/
|
|
190
190
|
row: Record<string, unknown>;
|
|
191
191
|
|
|
@@ -217,7 +217,7 @@ export interface AfterDeleteProps<M extends Record<string, unknown> = Record<str
|
|
|
217
217
|
id: string | number;
|
|
218
218
|
|
|
219
219
|
/**
|
|
220
|
-
* Deleted row (flat —
|
|
220
|
+
* Deleted row (flat — the table's columns)
|
|
221
221
|
*/
|
|
222
222
|
row: Record<string, unknown>;
|
|
223
223
|
|
package/src/types/index.ts
CHANGED
|
@@ -26,6 +26,7 @@ export * from "./entity_views";
|
|
|
26
26
|
export * from "./data_source";
|
|
27
27
|
export * from "./storage_source";
|
|
28
28
|
export * from "./cron";
|
|
29
|
+
export * from "./backup";
|
|
29
30
|
export * from "./component_ref";
|
|
30
31
|
export * from "./auth_adapter";
|
|
31
32
|
export * from "./database_adapter";
|
package/src/types/policy.ts
CHANGED
|
@@ -27,9 +27,28 @@ export type PolicyExpression =
|
|
|
27
27
|
| RolesOverlapPolicyExpression
|
|
28
28
|
| RolesContainPolicyExpression
|
|
29
29
|
| AuthenticatedPolicyExpression
|
|
30
|
+
| ServerContextPolicyExpression
|
|
30
31
|
| ExistsInPolicyExpression
|
|
31
32
|
| RawPolicyExpression;
|
|
32
33
|
|
|
34
|
+
/**
|
|
35
|
+
* The id a request without a logged-in user reports as `auth.uid()`.
|
|
36
|
+
*
|
|
37
|
+
* A user-context request always sets `app.user_id`: blank would read back as
|
|
38
|
+
* `NULL`, and `NULL` is how the trusted server context is recognised, so an
|
|
39
|
+
* anonymous visitor would be promoted to server privileges. The driver
|
|
40
|
+
* therefore substitutes this sentinel at the single chokepoint where the GUC
|
|
41
|
+
* is set.
|
|
42
|
+
*
|
|
43
|
+
* The consequence for policy authors is that **`auth.uid() IS NOT NULL` is a
|
|
44
|
+
* tautology on the user path** — it is true for anonymous visitors too. Use
|
|
45
|
+
* {@link policy.authenticated} (or `auth.uid() <> 'anonymous'`) to mean "signed
|
|
46
|
+
* in", and {@link policy.serverContext} to mean "the trusted server context".
|
|
47
|
+
*
|
|
48
|
+
* @group Models
|
|
49
|
+
*/
|
|
50
|
+
export const ANONYMOUS_USER_ID = "anonymous";
|
|
51
|
+
|
|
33
52
|
/** Always allows. Compiles to `true`. @group Models */
|
|
34
53
|
export interface TruePolicyExpression {
|
|
35
54
|
kind: "true";
|
|
@@ -93,13 +112,42 @@ export interface RolesContainPolicyExpression {
|
|
|
93
112
|
}
|
|
94
113
|
|
|
95
114
|
/**
|
|
96
|
-
* True when
|
|
115
|
+
* True when a signed-in user is making the request. Compiles to
|
|
116
|
+
* `auth.uid() IS NOT NULL AND auth.uid() <> 'anonymous'`.
|
|
117
|
+
*
|
|
118
|
+
* Both halves are load-bearing. `IS NOT NULL` excludes the server context;
|
|
119
|
+
* the {@link ANONYMOUS_USER_ID} comparison excludes anonymous visitors, who
|
|
120
|
+
* *do* carry a non-null `auth.uid()`. Checking only `IS NOT NULL` grants to
|
|
121
|
+
* everyone — see {@link ANONYMOUS_USER_ID}.
|
|
122
|
+
*
|
|
123
|
+
* `policy.not(policy.authenticated())` therefore means "anonymous visitor or
|
|
124
|
+
* the server context". To single out the server context, use
|
|
125
|
+
* {@link ServerContextPolicyExpression}.
|
|
97
126
|
* @group Models
|
|
98
127
|
*/
|
|
99
128
|
export interface AuthenticatedPolicyExpression {
|
|
100
129
|
kind: "authenticated";
|
|
101
130
|
}
|
|
102
131
|
|
|
132
|
+
/**
|
|
133
|
+
* True only in the trusted **server context** — the built-in flows that run
|
|
134
|
+
* without a user (signup, migrations, `dataAsAdmin`) set no user GUC, so
|
|
135
|
+
* `auth.uid()` is `NULL` for them and only for them. Compiles to
|
|
136
|
+
* `auth.uid() IS NULL`.
|
|
137
|
+
*
|
|
138
|
+
* This is what lets the owner connection satisfy a policy even under FORCE RLS.
|
|
139
|
+
* It is deliberately a primitive rather than `not(authenticated())`: the two
|
|
140
|
+
* meant the same thing while `authenticated` ignored {@link ANONYMOUS_USER_ID},
|
|
141
|
+
* and conflating them is what turns a server-only grant into an anonymous one.
|
|
142
|
+
*
|
|
143
|
+
* The JavaScript evaluator always returns `false` for this node — a client is
|
|
144
|
+
* never the server context.
|
|
145
|
+
* @group Models
|
|
146
|
+
*/
|
|
147
|
+
export interface ServerContextPolicyExpression {
|
|
148
|
+
kind: "serverContext";
|
|
149
|
+
}
|
|
150
|
+
|
|
103
151
|
/**
|
|
104
152
|
* Membership / relational access: true when at least one row exists in another
|
|
105
153
|
* collection (a join/membership table) matching `where`. This is what lets you
|
|
@@ -218,6 +266,7 @@ export const policy = {
|
|
|
218
266
|
rolesOverlap: (roles: readonly string[]): RolesOverlapPolicyExpression => ({ kind: "rolesOverlap", roles: roles as string[] }),
|
|
219
267
|
rolesContain: (roles: readonly string[]): RolesContainPolicyExpression => ({ kind: "rolesContain", roles: roles as string[] }),
|
|
220
268
|
authenticated: (): AuthenticatedPolicyExpression => ({ kind: "authenticated" }),
|
|
269
|
+
serverContext: (): ServerContextPolicyExpression => ({ kind: "serverContext" }),
|
|
221
270
|
existsIn: (args: { collection: string; where: PolicyExpression }): ExistsInPolicyExpression =>
|
|
222
271
|
({ kind: "existsIn", collection: args.collection, where: args.where }),
|
|
223
272
|
raw: (sql: string): RawPolicyExpression => ({ kind: "raw", sql }),
|
package/src/types/properties.ts
CHANGED
|
@@ -231,6 +231,20 @@ export interface BaseProperty<CustomProps = unknown> {
|
|
|
231
231
|
*/
|
|
232
232
|
validation?: PropertyValidationSchema;
|
|
233
233
|
|
|
234
|
+
/**
|
|
235
|
+
* Never include this column in an API response.
|
|
236
|
+
*
|
|
237
|
+
* For secrets the server must store and read but no client should ever
|
|
238
|
+
* receive — password hashes, verification tokens. The value is still
|
|
239
|
+
* written and queryable server-side; it is stripped from every row the API
|
|
240
|
+
* serves, for every caller, including admins and service keys.
|
|
241
|
+
*
|
|
242
|
+
* This is a server-side guarantee, unlike `ui.hideFromCollection`, which
|
|
243
|
+
* only stops the admin panel from *rendering* a field and leaves it in the
|
|
244
|
+
* JSON payload.
|
|
245
|
+
*/
|
|
246
|
+
excludeFromApi?: boolean;
|
|
247
|
+
|
|
234
248
|
// NOTE: `defaultValue` is intentionally NOT on BaseProperty.
|
|
235
249
|
// Each concrete property type (StringProperty, NumberProperty, etc.)
|
|
236
250
|
// defines its own typed `defaultValue` for compile-time safety.
|
|
@@ -7,7 +7,7 @@ export type DeepPartial<T> = T extends object
|
|
|
7
7
|
: T;
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* All user-visible strings used internally by @rebasepro/
|
|
10
|
+
* All user-visible strings used internally by @rebasepro/app.
|
|
11
11
|
* Pass a `DeepPartial<RebaseTranslations>` via the `translations` prop
|
|
12
12
|
* on your Rebase entry-point component to override any key, or to add
|
|
13
13
|
* a new locale.
|
|
@@ -549,6 +549,7 @@ export interface RebaseTranslations {
|
|
|
549
549
|
invitation_sent_title?: string;
|
|
550
550
|
temporary_password?: string;
|
|
551
551
|
temporary_password_description?: string;
|
|
552
|
+
temporary_password_email_failed_description?: string;
|
|
552
553
|
copy_password?: string;
|
|
553
554
|
password_copied?: string;
|
|
554
555
|
|
|
@@ -560,6 +561,9 @@ export interface RebaseTranslations {
|
|
|
560
561
|
reset_password?: string;
|
|
561
562
|
reset_password_success?: string;
|
|
562
563
|
reset_password_confirmation?: string;
|
|
564
|
+
reset_password_send_email?: string;
|
|
565
|
+
reset_password_set_manually?: string;
|
|
566
|
+
reset_password_set_manually_description?: string;
|
|
563
567
|
error_resetting_password?: string;
|
|
564
568
|
|
|
565
569
|
/** Permission-denied empty states */
|
|
@@ -10,8 +10,14 @@ export interface UserCreationResult<USER extends User = User> {
|
|
|
10
10
|
/** Whether an invitation email was sent to the user */
|
|
11
11
|
invitationSent: boolean;
|
|
12
12
|
/**
|
|
13
|
-
* Temporary password
|
|
14
|
-
*
|
|
13
|
+
* Temporary password, present when no invitation email could be delivered —
|
|
14
|
+
* either because no email service is configured, or because delivery failed
|
|
15
|
+
* (see `emailDeliveryFailed`). Returned one-time, to be shared manually.
|
|
15
16
|
*/
|
|
16
17
|
temporaryPassword?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Whether an email service was configured but delivery failed, causing the
|
|
20
|
+
* fallback to `temporaryPassword`. Absent when no email service is configured.
|
|
21
|
+
*/
|
|
22
|
+
emailDeliveryFailed?: boolean;
|
|
17
23
|
}
|
package/src/types/websockets.ts
CHANGED
|
@@ -14,10 +14,31 @@ export interface WebSocketMessage {
|
|
|
14
14
|
error?: string;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* The key columns a collection's rows are addressed by.
|
|
19
|
+
*
|
|
20
|
+
* A row is exactly its columns and carries no address, so a subscriber that has
|
|
21
|
+
* to recognise one — to patch it, or to keep its reference across a refetch —
|
|
22
|
+
* derives the address from these. The SDK is usable with no collections
|
|
23
|
+
* declared at all, so the server is the only side that knows them.
|
|
24
|
+
*
|
|
25
|
+
* Undefined when the server cannot resolve them: a table with no primary key
|
|
26
|
+
* and no `id` column has no address, and rows of it cannot be recognised by
|
|
27
|
+
* anyone.
|
|
28
|
+
*/
|
|
29
|
+
export type WirePrimaryKeys = { fieldName: string; type: "string" | "number"; isUUID?: boolean }[];
|
|
30
|
+
|
|
17
31
|
export interface CollectionUpdateMessage extends WebSocketMessage {
|
|
18
32
|
type: "collection_update";
|
|
19
33
|
subscriptionId: string;
|
|
20
34
|
rows: Record<string, unknown>[];
|
|
35
|
+
/**
|
|
36
|
+
* See {@link WirePrimaryKeys}. Sent with the rows themselves — and not only
|
|
37
|
+
* with a patch — because a CDC-originated change sends no patch at all: it
|
|
38
|
+
* invalidates and goes straight to a refetch, and the merge that preserves
|
|
39
|
+
* unchanged rows' references needs an address to match them by.
|
|
40
|
+
*/
|
|
41
|
+
pks?: WirePrimaryKeys;
|
|
21
42
|
}
|
|
22
43
|
|
|
23
44
|
export interface SingleUpdateMessage extends WebSocketMessage {
|
|
@@ -35,9 +56,12 @@ export interface SingleUpdateMessage extends WebSocketMessage {
|
|
|
35
56
|
export interface CollectionPatchMessage extends WebSocketMessage {
|
|
36
57
|
type: "collection_patch";
|
|
37
58
|
subscriptionId: string;
|
|
59
|
+
/** The address of the row this patch refers to — derived, never read off it. */
|
|
38
60
|
id: string;
|
|
39
61
|
/** The updated row, or null if deleted */
|
|
40
62
|
row: Record<string, unknown> | null;
|
|
63
|
+
/** See {@link WirePrimaryKeys}: how the subscriber finds {@link id} in its cache. */
|
|
64
|
+
pks?: WirePrimaryKeys;
|
|
41
65
|
}
|
|
42
66
|
|
|
43
67
|
/**
|
package/src/users/user.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* The canonical representation of an authenticated user in the Rebase ecosystem.
|
|
4
4
|
*
|
|
5
5
|
* Used by {@link AuthController}, collections, callbacks, and both the
|
|
6
|
-
* `@rebasepro/client` and `@rebasepro/
|
|
6
|
+
* `@rebasepro/client` and `@rebasepro/app` packages. All other user types
|
|
7
7
|
* (`RebaseUser`, `UserInfo`) are deprecated aliases of this type.
|
|
8
8
|
*
|
|
9
9
|
* **Backend-managed fields** (`uid`, `email`, `roles`, `metadata`, `createdAt`)
|