@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.
Files changed (46) hide show
  1. package/README.md +4 -4
  2. package/dist/controllers/auth.d.ts +6 -0
  3. package/dist/controllers/client.d.ts +21 -1
  4. package/dist/controllers/data.d.ts +44 -2
  5. package/dist/controllers/data_driver.d.ts +45 -3
  6. package/dist/controllers/email.d.ts +2 -2
  7. package/dist/controllers/registry.d.ts +5 -5
  8. package/dist/index.es.js +19 -1
  9. package/dist/index.es.js.map +1 -1
  10. package/dist/types/auth_adapter.d.ts +25 -3
  11. package/dist/types/backend.d.ts +55 -1
  12. package/dist/types/backup.d.ts +20 -0
  13. package/dist/types/collections.d.ts +34 -19
  14. package/dist/types/cron.d.ts +2 -2
  15. package/dist/types/database_adapter.d.ts +17 -1
  16. package/dist/types/entity_callbacks.d.ts +3 -3
  17. package/dist/types/index.d.ts +1 -0
  18. package/dist/types/policy.d.ts +48 -2
  19. package/dist/types/properties.d.ts +13 -0
  20. package/dist/types/translations.d.ts +5 -1
  21. package/dist/types/user_management_delegate.d.ts +8 -2
  22. package/dist/types/websockets.d.ts +77 -0
  23. package/dist/users/user.d.ts +1 -1
  24. package/package.json +5 -6
  25. package/src/controllers/auth.tsx +6 -0
  26. package/src/controllers/client.ts +22 -2
  27. package/src/controllers/data.ts +42 -2
  28. package/src/controllers/data_driver.ts +47 -3
  29. package/src/controllers/email.ts +2 -2
  30. package/src/controllers/registry.ts +5 -5
  31. package/src/types/auth_adapter.ts +25 -3
  32. package/src/types/backend.ts +59 -1
  33. package/src/types/backup.ts +26 -0
  34. package/src/types/collections.ts +35 -19
  35. package/src/types/cron.ts +2 -2
  36. package/src/types/database_adapter.ts +15 -1
  37. package/src/types/entity_callbacks.ts +3 -3
  38. package/src/types/index.ts +1 -0
  39. package/src/types/policy.ts +50 -1
  40. package/src/types/properties.ts +14 -0
  41. package/src/types/translations.ts +5 -1
  42. package/src/types/user_management_delegate.ts +8 -2
  43. package/src/types/websockets.ts +76 -0
  44. package/src/users/user.ts +1 -1
  45. package/dist/index.umd.js +0 -591
  46. 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-core";
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
- /** Supports password reset flow. */
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-core's middleware.
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()`
@@ -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 for
158
- * authentication collections.
157
+ * Opt out of the framework's default Row Level Security policies.
159
158
  *
160
- * When a collection has `auth` enabled, the schema generator automatically
161
- * injects an admin-only write policy (INSERT/UPDATE/DELETE require the
162
- * `admin` role, or the trusted server context) for any write operation that
163
- * the collection does not already cover with an explicit `securityRules`
164
- * entry. This makes privileged columns such as `roles` safe by default a
165
- * non-admin cannot modify the user row through the data API regardless of
166
- * which code path issues the write.
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
- * Defining your own write rule for an operation overrides the default for
169
- * that operation. Set this flag to `true` to remove the default policies
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
- disableDefaultAuthPolicies?: boolean;
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. They are
903
- * application roles managed by Rebase, stored in the `rebase.user_roles`
904
- * table, and injected into each transaction via `auth.roles()`.
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 `roles`
934
- * (admin, editor, viewer, etc.) — those are enforced in the USING/WITH
935
- * CHECK clauses via `auth.roles()`. This field controls the PostgreSQL
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.
@@ -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-core, client,
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-core`) and as
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-postgresql";
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 — `{ id, ...columns }`)
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 — `{ id, ...columns }`)
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 — `{ id, ...columns }`)
178
+ * Deleted row (flat — the table's columns)
179
179
  */
180
180
  row: Record<string, unknown>;
181
181
  /**
@@ -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";
@@ -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 there is an authenticated user (`auth.uid() IS NOT NULL`).
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/core.
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 (only present when email service is not configured).
13
- * This is returned one-time and should be shown to the admin to share manually.
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.
@@ -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/auth` packages. All other user types
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.0",
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.umd.js",
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": ">=14"
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"
@@ -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;