@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
|
@@ -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
|
@@ -551,6 +551,13 @@ export interface InitializedDriver {
|
|
|
551
551
|
realtimeProvider?: RealtimeProvider;
|
|
552
552
|
/** A collection registry to register schema / tables into. */
|
|
553
553
|
collectionRegistry?: CollectionRegistryInterface;
|
|
554
|
+
/**
|
|
555
|
+
* Collections the driver derived from the live database schema.
|
|
556
|
+
*
|
|
557
|
+
* Set by drivers that introspect in `baas` mode; the server serves these
|
|
558
|
+
* instead of collections loaded from config files.
|
|
559
|
+
*/
|
|
560
|
+
collections?: import("./collections").CollectionConfig[];
|
|
554
561
|
/** The underlying database connection (for lifecycle management). */
|
|
555
562
|
connection?: DatabaseConnection;
|
|
556
563
|
/**
|
|
@@ -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.
|
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
|
}
|
|
@@ -15,10 +15,34 @@ export interface WebSocketMessage {
|
|
|
15
15
|
row?: Record<string, unknown> | null;
|
|
16
16
|
error?: string;
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* The key columns a collection's rows are addressed by.
|
|
20
|
+
*
|
|
21
|
+
* A row is exactly its columns and carries no address, so a subscriber that has
|
|
22
|
+
* to recognise one — to patch it, or to keep its reference across a refetch —
|
|
23
|
+
* derives the address from these. The SDK is usable with no collections
|
|
24
|
+
* declared at all, so the server is the only side that knows them.
|
|
25
|
+
*
|
|
26
|
+
* Undefined when the server cannot resolve them: a table with no primary key
|
|
27
|
+
* and no `id` column has no address, and rows of it cannot be recognised by
|
|
28
|
+
* anyone.
|
|
29
|
+
*/
|
|
30
|
+
export type WirePrimaryKeys = {
|
|
31
|
+
fieldName: string;
|
|
32
|
+
type: "string" | "number";
|
|
33
|
+
isUUID?: boolean;
|
|
34
|
+
}[];
|
|
18
35
|
export interface CollectionUpdateMessage extends WebSocketMessage {
|
|
19
36
|
type: "collection_update";
|
|
20
37
|
subscriptionId: string;
|
|
21
38
|
rows: Record<string, unknown>[];
|
|
39
|
+
/**
|
|
40
|
+
* See {@link WirePrimaryKeys}. Sent with the rows themselves — and not only
|
|
41
|
+
* with a patch — because a CDC-originated change sends no patch at all: it
|
|
42
|
+
* invalidates and goes straight to a refetch, and the merge that preserves
|
|
43
|
+
* unchanged rows' references needs an address to match them by.
|
|
44
|
+
*/
|
|
45
|
+
pks?: WirePrimaryKeys;
|
|
22
46
|
}
|
|
23
47
|
export interface SingleUpdateMessage extends WebSocketMessage {
|
|
24
48
|
type: "single_update";
|
|
@@ -34,9 +58,12 @@ export interface SingleUpdateMessage extends WebSocketMessage {
|
|
|
34
58
|
export interface CollectionPatchMessage extends WebSocketMessage {
|
|
35
59
|
type: "collection_patch";
|
|
36
60
|
subscriptionId: string;
|
|
61
|
+
/** The address of the row this patch refers to — derived, never read off it. */
|
|
37
62
|
id: string;
|
|
38
63
|
/** The updated row, or null if deleted */
|
|
39
64
|
row: Record<string, unknown> | null;
|
|
65
|
+
/** See {@link WirePrimaryKeys}: how the subscriber finds {@link id} in its cache. */
|
|
66
|
+
pks?: WirePrimaryKeys;
|
|
40
67
|
}
|
|
41
68
|
/**
|
|
42
69
|
* 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.0fce67c",
|
|
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;
|
|
@@ -3,6 +3,7 @@ import type { RebaseSdkData } from "./data";
|
|
|
3
3
|
import type { EmailService } from "./email";
|
|
4
4
|
import type { StorageSource } from "./storage";
|
|
5
5
|
import type { CronJobStatus, CronJobLogEntry } from "../types/cron";
|
|
6
|
+
import type { BackupInfo, BackupDestinationKind } from "../types/backup";
|
|
6
7
|
import type { ApiKeysAPI } from "../types/api_keys";
|
|
7
8
|
import type { StorageSourceDefinition } from "../types/storage_source";
|
|
8
9
|
|
|
@@ -142,7 +143,7 @@ export interface AdminAPI {
|
|
|
142
143
|
createUser(data: { email: string; displayName?: string; password?: string; roles?: string[]; metadata?: Record<string, any> }): Promise<{ user: AdminUser }>;
|
|
143
144
|
updateUser(userId: string, data: { email?: string; displayName?: string; password?: string; roles?: string[]; metadata?: Record<string, any> }): Promise<{ user: AdminUser }>;
|
|
144
145
|
deleteUser(userId: string): Promise<{ success: boolean }>;
|
|
145
|
-
resetPassword(userId: string, options?: { password?: string }): Promise<{ user: AdminUser; temporaryPassword?: string; invitationSent?: boolean }>;
|
|
146
|
+
resetPassword(userId: string, options?: { password?: string }): Promise<{ user: AdminUser; temporaryPassword?: string; invitationSent?: boolean; emailDeliveryFailed?: boolean }>;
|
|
146
147
|
listRoles(): Promise<{ roles: Array<{ id: string; name: string }> }>;
|
|
147
148
|
bootstrap(): Promise<{ success: boolean; message: string; user: { uid: string; roles: string[] } }>;
|
|
148
149
|
}
|
|
@@ -161,6 +162,19 @@ export interface CronAPI {
|
|
|
161
162
|
toggleJob(jobId: string, enabled: boolean): Promise<{ job: CronJobStatus }>;
|
|
162
163
|
}
|
|
163
164
|
|
|
165
|
+
// ─── Backups API ─────────────────────────────────────────────────────────────
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Client-side database-backup management interface.
|
|
169
|
+
* @group Backups
|
|
170
|
+
*/
|
|
171
|
+
export interface BackupsAPI {
|
|
172
|
+
/** List available backups at the configured destination, newest first. */
|
|
173
|
+
list(): Promise<{ backups: BackupInfo[]; destinationKind: BackupDestinationKind; configured: boolean }>;
|
|
174
|
+
/** Fetch a backup's bytes for download (authenticated). */
|
|
175
|
+
download(key: string): Promise<Blob>;
|
|
176
|
+
}
|
|
177
|
+
|
|
164
178
|
// ─── Functions API ───────────────────────────────────────────────────────────
|
|
165
179
|
|
|
166
180
|
/**
|
|
@@ -294,6 +308,9 @@ export interface RebaseClient<DB = unknown> {
|
|
|
294
308
|
/** Cron job management API */
|
|
295
309
|
cron?: CronAPI;
|
|
296
310
|
|
|
311
|
+
/** Database backup management API */
|
|
312
|
+
backups?: BackupsAPI;
|
|
313
|
+
|
|
297
314
|
/** Custom backend functions API */
|
|
298
315
|
functions?: FunctionsAPI;
|
|
299
316
|
|
|
@@ -333,7 +350,7 @@ export interface RebaseClient<DB = unknown> {
|
|
|
333
350
|
|
|
334
351
|
/**
|
|
335
352
|
* The server-side Rebase surface — the shape of the `rebase` singleton exported
|
|
336
|
-
* from `@rebasepro/server
|
|
353
|
+
* from `@rebasepro/server`.
|
|
337
354
|
*
|
|
338
355
|
* Narrows {@link RebaseClient} to the guarantees that always hold on the server:
|
|
339
356
|
* the admin-scoped {@link dataAsAdmin} accessor, raw {@link sql}, and the
|
|
@@ -409,6 +426,9 @@ export interface RebaseBrowserClient<DB = unknown> {
|
|
|
409
426
|
/** Cron job management API */
|
|
410
427
|
cron?: CronAPI;
|
|
411
428
|
|
|
429
|
+
/** Database backup management API */
|
|
430
|
+
backups?: BackupsAPI;
|
|
431
|
+
|
|
412
432
|
/** Custom backend functions API */
|
|
413
433
|
functions?: FunctionsAPI;
|
|
414
434
|
|
package/src/controllers/data.ts
CHANGED
|
@@ -161,6 +161,14 @@ export interface CollectionAccessor<M extends Record<string, unknown> = Record<s
|
|
|
161
161
|
*/
|
|
162
162
|
create(data: Partial<EntityValues<M>>, id?: string | number): Promise<Entity<M>>;
|
|
163
163
|
|
|
164
|
+
/**
|
|
165
|
+
* Create many records in a single transaction.
|
|
166
|
+
*
|
|
167
|
+
* See {@link SDKCollectionClient.createMany}. Optional: not every driver can
|
|
168
|
+
* write in bulk, and callers should fall back to `create` per record.
|
|
169
|
+
*/
|
|
170
|
+
createMany?(data: Partial<EntityValues<M>>[], options?: { upsert?: boolean }): Promise<Entity<M>[]>;
|
|
171
|
+
|
|
164
172
|
/**
|
|
165
173
|
* Update an existing record by ID.
|
|
166
174
|
* @returns The updated entity
|
|
@@ -294,11 +302,43 @@ export interface SDKCollectionClient<
|
|
|
294
302
|
/**
|
|
295
303
|
* Create a new record.
|
|
296
304
|
* @param data The record data to create (the collection's `Insert` shape).
|
|
297
|
-
* @param id Optional specific
|
|
305
|
+
* @param id Optional specific id, sent as an `id` column. This is for tables
|
|
306
|
+
* whose key *is* `id`: the value goes in as that column. For a table keyed
|
|
307
|
+
* on anything else (a `sku`, a composite key), there is no `id` column to
|
|
308
|
+
* receive it — put the key in `data` instead, where it belongs among the
|
|
309
|
+
* columns.
|
|
298
310
|
* @returns The created row
|
|
299
311
|
*/
|
|
300
312
|
create(data: I, id?: string | number): Promise<M>;
|
|
301
313
|
|
|
314
|
+
/**
|
|
315
|
+
* Write many records in a single request and a single transaction.
|
|
316
|
+
*
|
|
317
|
+
* Built for imports and ETL, where one call per row means one HTTP round
|
|
318
|
+
* trip and one transaction per row. Every record still runs the normal
|
|
319
|
+
* pipeline — callbacks, relations, row-level security — and the batch is
|
|
320
|
+
* all-or-nothing: if any record is rejected, none of them land and the
|
|
321
|
+
* error names the offending index.
|
|
322
|
+
*
|
|
323
|
+
* A record carrying its primary key updates that row; one without inserts.
|
|
324
|
+
* With `{ upsert: true }` each record is written as INSERT ... ON CONFLICT
|
|
325
|
+
* DO UPDATE on the primary key instead, which is what makes a re-runnable
|
|
326
|
+
* import idempotent.
|
|
327
|
+
*
|
|
328
|
+
* Batches are capped server-side (1000 rows by default) because one batch
|
|
329
|
+
* holds its locks for the whole transaction — chunk larger jobs.
|
|
330
|
+
*
|
|
331
|
+
* @returns The written rows, in the order given.
|
|
332
|
+
*
|
|
333
|
+
* @example
|
|
334
|
+
* ```ts
|
|
335
|
+
* for (const chunk of chunks(rows, 1000)) {
|
|
336
|
+
* await client.data.products.createMany(chunk, { upsert: true });
|
|
337
|
+
* }
|
|
338
|
+
* ```
|
|
339
|
+
*/
|
|
340
|
+
createMany(data: I[], options?: { upsert?: boolean }): Promise<M[]>;
|
|
341
|
+
|
|
302
342
|
/**
|
|
303
343
|
* Update an existing record by ID.
|
|
304
344
|
* @param data The fields to update (the collection's `Update` shape).
|
|
@@ -386,7 +426,7 @@ export type RebaseData<DB = unknown> = {
|
|
|
386
426
|
* - The frontend SDK client (`client.data.products.find()`)
|
|
387
427
|
* - Backend framework callbacks & scripts (`context.data.products.find()`)
|
|
388
428
|
*
|
|
389
|
-
* Every accessor returns flat rows (
|
|
429
|
+
* Every accessor returns flat rows (the table's columns) via
|
|
390
430
|
* {@link SDKCollectionClient} — access fields directly (`row.title`), never
|
|
391
431
|
* `row.values.title`. The admin CMS uses {@link RebaseData} (Entity) instead.
|
|
392
432
|
*
|