@rebasepro/types 0.9.0 → 0.9.1-canary.0de22e0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/controllers/auth.d.ts +6 -0
- package/dist/controllers/client.d.ts +21 -1
- package/dist/controllers/data.d.ts +44 -2
- package/dist/controllers/data_driver.d.ts +45 -3
- package/dist/controllers/email.d.ts +2 -2
- package/dist/controllers/registry.d.ts +5 -5
- package/dist/index.es.js +19 -1
- package/dist/index.es.js.map +1 -1
- package/dist/types/auth_adapter.d.ts +25 -3
- package/dist/types/backend.d.ts +55 -1
- package/dist/types/backup.d.ts +20 -0
- package/dist/types/collections.d.ts +34 -19
- package/dist/types/cron.d.ts +2 -2
- package/dist/types/database_adapter.d.ts +17 -1
- package/dist/types/entity_callbacks.d.ts +3 -3
- package/dist/types/index.d.ts +1 -0
- package/dist/types/policy.d.ts +48 -2
- package/dist/types/properties.d.ts +13 -0
- package/dist/types/translations.d.ts +5 -1
- package/dist/types/user_management_delegate.d.ts +8 -2
- package/dist/types/websockets.d.ts +77 -0
- package/dist/users/user.d.ts +1 -1
- package/package.json +5 -6
- package/src/controllers/auth.tsx +6 -0
- package/src/controllers/client.ts +22 -2
- package/src/controllers/data.ts +42 -2
- package/src/controllers/data_driver.ts +47 -3
- package/src/controllers/email.ts +2 -2
- package/src/controllers/registry.ts +5 -5
- package/src/types/auth_adapter.ts +25 -3
- package/src/types/backend.ts +59 -1
- package/src/types/backup.ts +26 -0
- package/src/types/collections.ts +35 -19
- package/src/types/cron.ts +2 -2
- package/src/types/database_adapter.ts +15 -1
- package/src/types/entity_callbacks.ts +3 -3
- package/src/types/index.ts +1 -0
- package/src/types/policy.ts +50 -1
- package/src/types/properties.ts +14 -0
- package/src/types/translations.ts +5 -1
- package/src/types/user_management_delegate.ts +8 -2
- package/src/types/websockets.ts +76 -0
- package/src/users/user.ts +1 -1
- package/dist/index.umd.js +0 -591
- package/dist/index.umd.js.map +0 -1
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
|
@@ -12,12 +12,40 @@ export interface WebSocketMessage {
|
|
|
12
12
|
rows?: Record<string, unknown>[];
|
|
13
13
|
row?: Record<string, unknown> | null;
|
|
14
14
|
error?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Channel name, on broadcast and presence frames.
|
|
17
|
+
*
|
|
18
|
+
* These are addressed by channel rather than by `requestId` or
|
|
19
|
+
* `subscriptionId`, so this is the only field that routes them.
|
|
20
|
+
*/
|
|
21
|
+
channel?: string;
|
|
15
22
|
}
|
|
16
23
|
|
|
24
|
+
/**
|
|
25
|
+
* The key columns a collection's rows are addressed by.
|
|
26
|
+
*
|
|
27
|
+
* A row is exactly its columns and carries no address, so a subscriber that has
|
|
28
|
+
* to recognise one — to patch it, or to keep its reference across a refetch —
|
|
29
|
+
* derives the address from these. The SDK is usable with no collections
|
|
30
|
+
* declared at all, so the server is the only side that knows them.
|
|
31
|
+
*
|
|
32
|
+
* Undefined when the server cannot resolve them: a table with no primary key
|
|
33
|
+
* and no `id` column has no address, and rows of it cannot be recognised by
|
|
34
|
+
* anyone.
|
|
35
|
+
*/
|
|
36
|
+
export type WirePrimaryKeys = { fieldName: string; type: "string" | "number"; isUUID?: boolean }[];
|
|
37
|
+
|
|
17
38
|
export interface CollectionUpdateMessage extends WebSocketMessage {
|
|
18
39
|
type: "collection_update";
|
|
19
40
|
subscriptionId: string;
|
|
20
41
|
rows: Record<string, unknown>[];
|
|
42
|
+
/**
|
|
43
|
+
* See {@link WirePrimaryKeys}. Sent with the rows themselves — and not only
|
|
44
|
+
* with a patch — because a CDC-originated change sends no patch at all: it
|
|
45
|
+
* invalidates and goes straight to a refetch, and the merge that preserves
|
|
46
|
+
* unchanged rows' references needs an address to match them by.
|
|
47
|
+
*/
|
|
48
|
+
pks?: WirePrimaryKeys;
|
|
21
49
|
}
|
|
22
50
|
|
|
23
51
|
export interface SingleUpdateMessage extends WebSocketMessage {
|
|
@@ -35,9 +63,57 @@ export interface SingleUpdateMessage extends WebSocketMessage {
|
|
|
35
63
|
export interface CollectionPatchMessage extends WebSocketMessage {
|
|
36
64
|
type: "collection_patch";
|
|
37
65
|
subscriptionId: string;
|
|
66
|
+
/** The address of the row this patch refers to — derived, never read off it. */
|
|
38
67
|
id: string;
|
|
39
68
|
/** The updated row, or null if deleted */
|
|
40
69
|
row: Record<string, unknown> | null;
|
|
70
|
+
/** See {@link WirePrimaryKeys}: how the subscriber finds {@link id} in its cache. */
|
|
71
|
+
pks?: WirePrimaryKeys;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* One retained broadcast, as it travels on the wire.
|
|
76
|
+
*
|
|
77
|
+
* `seq` is per-channel, dense and monotonically increasing — it is the only
|
|
78
|
+
* thing a reconnecting client needs to say where it got to. See
|
|
79
|
+
* {@link ChannelHistoryMessage}.
|
|
80
|
+
*/
|
|
81
|
+
export interface ChannelHistoryEntry {
|
|
82
|
+
seq: number;
|
|
83
|
+
event: string;
|
|
84
|
+
payload: unknown;
|
|
85
|
+
/**
|
|
86
|
+
* The server-side client id of whoever sent it, for information only.
|
|
87
|
+
*
|
|
88
|
+
* Deliberately not used to filter a client's own messages out of a replay:
|
|
89
|
+
* a reconnect assigns a brand-new client id, so the very case replay exists
|
|
90
|
+
* for is the case where this would fail to match. Consumers that cannot
|
|
91
|
+
* tolerate re-applying their own operations must make them idempotent.
|
|
92
|
+
*/
|
|
93
|
+
senderId?: string;
|
|
94
|
+
/** When the server accepted it, ISO-8601. */
|
|
95
|
+
at: string;
|
|
96
|
+
}
|
|
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;
|
|
41
117
|
}
|
|
42
118
|
|
|
43
119
|
/**
|
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`)
|