@rebasepro/types 0.8.0 → 0.9.1-canary.09aaf62

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 (88) hide show
  1. package/README.md +13 -13
  2. package/dist/controllers/auth.d.ts +7 -1
  3. package/dist/controllers/client.d.ts +204 -6
  4. package/dist/controllers/collection_registry.d.ts +3 -3
  5. package/dist/controllers/customization_controller.d.ts +1 -1
  6. package/dist/controllers/data.d.ts +309 -35
  7. package/dist/controllers/data_driver.d.ts +92 -37
  8. package/dist/controllers/email.d.ts +2 -2
  9. package/dist/controllers/index.d.ts +1 -1
  10. package/dist/controllers/local_config_persistence.d.ts +4 -4
  11. package/dist/controllers/navigation.d.ts +2 -2
  12. package/dist/controllers/registry.d.ts +21 -8
  13. package/dist/controllers/{side_entity_controller.d.ts → side_panel_controller.d.ts} +7 -7
  14. package/dist/controllers/storage.d.ts +39 -0
  15. package/dist/errors.d.ts +64 -0
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.es.js +160 -15
  18. package/dist/index.es.js.map +1 -1
  19. package/dist/rebase_context.d.ts +8 -4
  20. package/dist/types/auth_adapter.d.ts +29 -4
  21. package/dist/types/backend.d.ts +33 -26
  22. package/dist/types/backup.d.ts +20 -0
  23. package/dist/types/builders.d.ts +2 -2
  24. package/dist/types/collections.d.ts +84 -75
  25. package/dist/types/component_overrides.d.ts +61 -3
  26. package/dist/types/cron.d.ts +11 -2
  27. package/dist/types/data_source.d.ts +15 -2
  28. package/dist/types/database_adapter.d.ts +21 -4
  29. package/dist/types/entities.d.ts +7 -7
  30. package/dist/types/entity_actions.d.ts +7 -7
  31. package/dist/types/entity_callbacks.d.ts +39 -39
  32. package/dist/types/entity_views.d.ts +3 -3
  33. package/dist/types/filter-operators.d.ts +62 -17
  34. package/dist/types/index.d.ts +1 -0
  35. package/dist/types/modify_collections.d.ts +2 -2
  36. package/dist/types/plugins.d.ts +9 -9
  37. package/dist/types/policy.d.ts +111 -11
  38. package/dist/types/properties.d.ts +54 -9
  39. package/dist/types/relations.d.ts +3 -3
  40. package/dist/types/slots.d.ts +14 -14
  41. package/dist/types/translations.d.ts +5 -1
  42. package/dist/types/user_management_delegate.d.ts +8 -2
  43. package/dist/types/websockets.d.ts +46 -13
  44. package/dist/users/user.d.ts +21 -9
  45. package/package.json +5 -6
  46. package/src/controllers/auth.tsx +7 -1
  47. package/src/controllers/client.ts +235 -7
  48. package/src/controllers/collection_registry.ts +3 -3
  49. package/src/controllers/customization_controller.tsx +1 -1
  50. package/src/controllers/data.ts +330 -39
  51. package/src/controllers/data_driver.ts +93 -40
  52. package/src/controllers/email.ts +2 -2
  53. package/src/controllers/index.ts +1 -1
  54. package/src/controllers/local_config_persistence.tsx +4 -4
  55. package/src/controllers/navigation.ts +2 -2
  56. package/src/controllers/registry.ts +22 -8
  57. package/src/controllers/{side_entity_controller.tsx → side_panel_controller.tsx} +7 -7
  58. package/src/controllers/storage.ts +60 -1
  59. package/src/errors.ts +80 -0
  60. package/src/index.ts +1 -0
  61. package/src/rebase_context.tsx +8 -4
  62. package/src/types/auth_adapter.ts +29 -4
  63. package/src/types/backend.ts +44 -36
  64. package/src/types/backup.ts +26 -0
  65. package/src/types/builders.ts +2 -2
  66. package/src/types/collections.ts +101 -91
  67. package/src/types/component_overrides.ts +72 -5
  68. package/src/types/cron.ts +11 -2
  69. package/src/types/data_source.ts +24 -2
  70. package/src/types/database_adapter.ts +19 -4
  71. package/src/types/entities.ts +7 -7
  72. package/src/types/entity_actions.tsx +7 -7
  73. package/src/types/entity_callbacks.ts +42 -42
  74. package/src/types/entity_views.tsx +3 -3
  75. package/src/types/filter-operators.ts +96 -23
  76. package/src/types/index.ts +1 -0
  77. package/src/types/modify_collections.tsx +2 -2
  78. package/src/types/plugins.tsx +9 -9
  79. package/src/types/policy.ts +114 -9
  80. package/src/types/properties.ts +58 -9
  81. package/src/types/relations.ts +3 -3
  82. package/src/types/slots.tsx +14 -14
  83. package/src/types/translations.ts +5 -1
  84. package/src/types/user_management_delegate.ts +8 -2
  85. package/src/types/websockets.ts +43 -14
  86. package/src/users/user.ts +22 -9
  87. package/dist/index.umd.js +0 -458
  88. package/dist/index.umd.js.map +0 -1
@@ -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 | 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";
@@ -29,12 +46,12 @@ export interface FalsePolicyExpression {
29
46
  /** Logical AND — every operand must pass. @group Models */
30
47
  export interface AndPolicyExpression {
31
48
  kind: "and";
32
- operands: PolicyExpression[];
49
+ operands: readonly PolicyExpression[];
33
50
  }
34
51
  /** Logical OR — at least one operand must pass. @group Models */
35
52
  export interface OrPolicyExpression {
36
53
  kind: "or";
37
- operands: PolicyExpression[];
54
+ operands: readonly PolicyExpression[];
38
55
  }
39
56
  /** Logical negation. @group Models */
40
57
  export interface NotPolicyExpression {
@@ -60,7 +77,7 @@ export interface ComparePolicyExpression {
60
77
  */
61
78
  export interface RolesOverlapPolicyExpression {
62
79
  kind: "rolesOverlap";
63
- roles: string[];
80
+ roles: readonly string[];
64
81
  }
65
82
  /**
66
83
  * True when the user holds *all* of the given application roles.
@@ -69,15 +86,81 @@ export interface RolesOverlapPolicyExpression {
69
86
  */
70
87
  export interface RolesContainPolicyExpression {
71
88
  kind: "rolesContain";
72
- roles: string[];
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
+ }
126
+ /**
127
+ * Membership / relational access: true when at least one row exists in another
128
+ * collection (a join/membership table) matching `where`. This is what lets you
129
+ * scope reads to "rows whose team the caller belongs to" without an N+1
130
+ * per-row lookup — it compiles to a single correlated `EXISTS` subquery.
131
+ *
132
+ * Inside `where`, {@link FieldPolicyOperand} (`policy.field`) references a column
133
+ * of the joined collection, while {@link OuterFieldPolicyOperand}
134
+ * (`policy.outerField`) references a column of the row being checked (the outer
135
+ * table under RLS). Combine with {@link AuthUidPolicyOperand} to correlate to
136
+ * the caller.
137
+ *
138
+ * @example
139
+ * ```ts
140
+ * // documents visible only to members of the document's team:
141
+ * policy.existsIn({
142
+ * collection: "team_members",
143
+ * where: policy.and(
144
+ * policy.compare(policy.field("team_id"), "eq", policy.outerField("team_id")),
145
+ * policy.compare(policy.field("user_id"), "eq", policy.authUid()),
146
+ * ),
147
+ * })
148
+ * // → EXISTS (SELECT 1 FROM team_members _ex0
149
+ * // WHERE _ex0.team_id = documents.team_id AND _ex0.user_id = auth.uid())
150
+ * ```
151
+ *
152
+ * Postgres-authoritative: like {@link RawPolicyExpression}, the JavaScript
153
+ * evaluator treats it as *unknown* (it cannot run a subquery client-side), so
154
+ * enforcement is always the database's.
155
+ * @group Models
156
+ */
157
+ export interface ExistsInPolicyExpression {
158
+ kind: "existsIn";
159
+ /** Slug of the collection to search (the join / membership table). */
160
+ collection: string;
161
+ /** Condition evaluated against the joined collection's rows. */
162
+ where: PolicyExpression;
163
+ }
81
164
  /**
82
165
  * A raw PostgreSQL boolean expression — the full-power escape hatch.
83
166
  *
@@ -94,13 +177,24 @@ export interface RawPolicyExpression {
94
177
  * An operand referenced by a {@link ComparePolicyExpression}.
95
178
  * @group Models
96
179
  */
97
- export type PolicyOperand = FieldPolicyOperand | LiteralPolicyOperand | AuthUidPolicyOperand | AuthRolesPolicyOperand;
180
+ export type PolicyOperand = FieldPolicyOperand | OuterFieldPolicyOperand | LiteralPolicyOperand | AuthUidPolicyOperand | AuthRolesPolicyOperand;
98
181
  /** A column value on the row being evaluated. @group Models */
99
182
  export interface FieldPolicyOperand {
100
183
  kind: "field";
101
184
  /** The property/column name (resolved to its DB column when compiled). */
102
185
  name: string;
103
186
  }
187
+ /**
188
+ * A column value on the *outer* row when used inside {@link ExistsInPolicyExpression}
189
+ * — i.e. the row the RLS policy is being evaluated for, referenced from within the
190
+ * subquery. Outside an `existsIn` it is equivalent to {@link FieldPolicyOperand}.
191
+ * @group Models
192
+ */
193
+ export interface OuterFieldPolicyOperand {
194
+ kind: "outerField";
195
+ /** The property/column name on the outer collection. */
196
+ name: string;
197
+ }
104
198
  /** A constant value. @group Models */
105
199
  export interface LiteralPolicyOperand {
106
200
  kind: "literal";
@@ -122,15 +216,21 @@ export interface AuthRolesPolicyOperand {
122
216
  export declare const policy: {
123
217
  true: () => TruePolicyExpression;
124
218
  false: () => FalsePolicyExpression;
125
- and: (...operands: PolicyExpression[]) => AndPolicyExpression;
126
- or: (...operands: PolicyExpression[]) => OrPolicyExpression;
219
+ and: (...operands: readonly PolicyExpression[]) => AndPolicyExpression;
220
+ or: (...operands: readonly PolicyExpression[]) => OrPolicyExpression;
127
221
  not: (operand: PolicyExpression) => NotPolicyExpression;
128
222
  compare: (left: PolicyOperand, op: PolicyCompareOperator, right: PolicyOperand) => ComparePolicyExpression;
129
- rolesOverlap: (roles: string[]) => RolesOverlapPolicyExpression;
130
- rolesContain: (roles: string[]) => RolesContainPolicyExpression;
223
+ rolesOverlap: (roles: readonly string[]) => RolesOverlapPolicyExpression;
224
+ rolesContain: (roles: readonly string[]) => RolesContainPolicyExpression;
131
225
  authenticated: () => AuthenticatedPolicyExpression;
226
+ serverContext: () => ServerContextPolicyExpression;
227
+ existsIn: (args: {
228
+ collection: string;
229
+ where: PolicyExpression;
230
+ }) => ExistsInPolicyExpression;
132
231
  raw: (sql: string) => RawPolicyExpression;
133
232
  field: (name: string) => FieldPolicyOperand;
233
+ outerField: (name: string) => OuterFieldPolicyOperand;
134
234
  literal: (value: string | number | boolean | null) => LiteralPolicyOperand;
135
235
  authUid: () => AuthUidPolicyOperand;
136
236
  authRoles: () => AuthRolesPolicyOperand;
@@ -1,10 +1,10 @@
1
1
  import type { ComponentRef } from "./component_ref";
2
2
  import type { Entity, EntityReference, EntityRelation, EntityValues, GeoPoint, Vector } from "./entities";
3
3
  import type { JoinStep, OnAction, Relation } from "./relations";
4
- import type { EntityCollection, FilterValues } from "./collections";
4
+ import type { CollectionConfig, FilterValues, WhereFilterOp } from "./collections";
5
5
  import type { ColorKey, ColorScheme } from "./chips";
6
6
  import type { AuthController } from "../controllers/auth";
7
- import type { EntityAfterReadProps, EntityBeforeSaveProps } from "./entity_callbacks";
7
+ import type { AfterReadProps, BeforeSaveProps } from "./entity_callbacks";
8
8
  import type { User } from "../users";
9
9
  /**
10
10
  * Callbacks/Hooks for individual property fields
@@ -14,7 +14,7 @@ export type PropertyCallbacks<T = unknown, M extends Record<string, unknown> = R
14
14
  /**
15
15
  * Callback used after fetching data, to transform the value before rendering
16
16
  */
17
- afterRead?(props: Omit<EntityAfterReadProps<M, USER>, "entity"> & {
17
+ afterRead?(props: Omit<AfterReadProps<M, USER>, "entity"> & {
18
18
  value: T;
19
19
  entity: Entity<M> | undefined;
20
20
  }): Promise<T> | T;
@@ -22,7 +22,7 @@ export type PropertyCallbacks<T = unknown, M extends Record<string, unknown> = R
22
22
  * Callback used before saving, after validation.
23
23
  * You can modify the value before it's saved.
24
24
  */
25
- beforeSave?(props: Omit<EntityBeforeSaveProps<M, USER>, "values"> & {
25
+ beforeSave?(props: Omit<BeforeSaveProps<M, USER>, "values"> & {
26
26
  value: T;
27
27
  previousValue: T | undefined;
28
28
  values: Partial<M>;
@@ -116,6 +116,31 @@ export interface BaseUIConfig<CustomProps = unknown> {
116
116
  customProps?: CustomProps;
117
117
  Field?: ComponentRef<any>;
118
118
  Preview?: ComponentRef<any>;
119
+ /**
120
+ * Narrow the filter operators offered for this property in collection
121
+ * filter UIs (table header filters and the Filters dialog).
122
+ *
123
+ * The final offered set is the **intersection** of the engine's
124
+ * capabilities, the property-type defaults, and this list — you can only
125
+ * *restrict*, never enable an operator the underlying engine cannot run.
126
+ *
127
+ * Pass an empty array to disable filtering on this property entirely.
128
+ *
129
+ * @example
130
+ * // Email column: exact match, contains, and null check only
131
+ * ui: { filterOperators: ["==", "ilike", "is-null"] }
132
+ */
133
+ filterOperators?: readonly WhereFilterOp[];
134
+ /**
135
+ * Replace the filter field rendered for this property in collection
136
+ * filter UIs. The component receives `FilterFieldBindingProps`
137
+ * (property, resolved `operators`, `value`, `setValue`, …).
138
+ *
139
+ * Takes precedence over the collection-level
140
+ * `components["Collection.FilterField"]` override and the built-in
141
+ * per-type filter fields.
142
+ */
143
+ Filter?: ComponentRef<any>;
119
144
  }
120
145
  export interface BaseProperty<CustomProps = unknown> {
121
146
  ui?: BaseUIConfig<CustomProps>;
@@ -150,6 +175,19 @@ export interface BaseProperty<CustomProps = unknown> {
150
175
  * Rules for validating this property
151
176
  */
152
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;
153
191
  /**
154
192
  * Use this to define dynamic properties that change based on certain conditions
155
193
  * or on the entity's values. For example, you can make a field read-only if
@@ -414,7 +452,7 @@ export interface ReferenceUIConfig extends BaseUIConfig {
414
452
  previewProperties?: string[];
415
453
  }
416
454
  /**
417
- * A pointer to an entity, stored **as a value** on the row (id + path, and
455
+ * A pointer to a entity, stored **as a value** on the row (id + path, and
418
456
  * optionally a `driver`/`databaseId` for cross-datasource pointers).
419
457
  *
420
458
  * This is the native primitive of **document databases** — it maps 1:1 to a
@@ -433,7 +471,7 @@ export interface ReferenceProperty extends BaseProperty {
433
471
  ui?: ReferenceUIConfig;
434
472
  type: "reference";
435
473
  /**
436
- * Default value for new entities. Must be an EntityReference.
474
+ * Default value for new entities. Must be a EntityReference.
437
475
  */
438
476
  defaultValue?: EntityReference;
439
477
  /**
@@ -494,7 +532,7 @@ export interface RelationProperty extends BaseProperty {
494
532
  ui?: RelationUIConfig;
495
533
  type: "relation";
496
534
  /**
497
- * Default value for new entities. Must be an EntityRelation or array of EntityRelation.
535
+ * Default value for new entities. Must be a EntityRelation or array of EntityRelation.
498
536
  */
499
537
  defaultValue?: EntityRelation | EntityRelation[];
500
538
  /**
@@ -509,7 +547,7 @@ export interface RelationProperty extends BaseProperty {
509
547
  * When set, the framework treats this property as a self-contained relation
510
548
  * definition and no separate `relations[]` entry is needed.
511
549
  */
512
- target?: string | (() => EntityCollection | string);
550
+ target?: string | (() => CollectionConfig | string);
513
551
  /**
514
552
  * Whether this property references one or many records.
515
553
  * Defaults to `"one"`.
@@ -567,7 +605,7 @@ export interface RelationProperty extends BaseProperty {
567
605
  /**
568
606
  * Overrides applied to the target collection when rendered as a subcollection tab.
569
607
  */
570
- overrides?: Partial<EntityCollection>;
608
+ overrides?: Partial<CollectionConfig>;
571
609
  /**
572
610
  * Optional name for this relation. Defaults to the property key at runtime.
573
611
  * Only needed when the relation name should differ from the property key,
@@ -879,6 +917,13 @@ export type StorageConfig = {
879
917
  * When omitted, the default storage source is used.
880
918
  */
881
919
  storageSource?: string;
920
+ /**
921
+ * Store files for this property as **public**: they are placed under the
922
+ * public prefix and served via stable, token-less, permanent, CDN-cacheable
923
+ * URLs (safe to persist and hotlink). Use for public assets like avatars or
924
+ * storefront images. Defaults to `false` (private, short-lived signed URLs).
925
+ */
926
+ public?: boolean;
882
927
  /**
883
928
  * File MIME types that can be uploaded to this reference. Don't specify for
884
929
  * all.
@@ -1,4 +1,4 @@
1
- import type { EntityCollection } from "./collections";
1
+ import type { CollectionConfig } from "./collections";
2
2
  /**
3
3
  * @group Models
4
4
  */
@@ -17,7 +17,7 @@ export interface Relation {
17
17
  /**
18
18
  * The final collection you want to retrieve records from.
19
19
  */
20
- target: (() => EntityCollection) | any;
20
+ target: (() => CollectionConfig) | any;
21
21
  /**
22
22
  * The nature of the relationship, determining if one or many records are returned.
23
23
  */
@@ -270,7 +270,7 @@ export interface Relation {
270
270
  * Action to perform on delete.
271
271
  */
272
272
  onDelete?: OnAction;
273
- overrides?: Partial<EntityCollection>;
273
+ overrides?: Partial<CollectionConfig>;
274
274
  validation?: {
275
275
  required?: boolean;
276
276
  };
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import type { CollectionActionsProps, EntityTableController, SelectionController, EntityCollection } from "./collections";
2
+ import type { CollectionActionsProps, EntityTableController, SelectionController, CollectionConfig } from "./collections";
3
3
  import type { Entity } from "./entities";
4
4
  import type { PluginFormActionProps, PluginGenericProps, PluginHomePageActionsProps, PluginHomePageAdditionalCardsProps } from "./plugins";
5
5
  import type { Property } from "./properties";
@@ -99,7 +99,7 @@ export interface NavigationSlotProps {
99
99
  */
100
100
  export interface CollectionToolbarProps {
101
101
  path: string;
102
- collection: EntityCollection;
102
+ collection: CollectionConfig;
103
103
  parentCollectionSlugs: string[];
104
104
  parentEntityIds: string[];
105
105
  tableController: EntityTableController;
@@ -111,7 +111,7 @@ export interface CollectionToolbarProps {
111
111
  */
112
112
  export interface CollectionEmptyStateProps {
113
113
  path: string;
114
- collection: EntityCollection;
114
+ collection: CollectionConfig;
115
115
  parentCollectionSlugs: string[];
116
116
  parentEntityIds: string[];
117
117
  canCreate: boolean;
@@ -128,7 +128,7 @@ export interface CollectionHeaderActionProps {
128
128
  parentCollectionSlugs: string[];
129
129
  parentEntityIds: string[];
130
130
  onHover: boolean;
131
- collection: EntityCollection;
131
+ collection: CollectionConfig;
132
132
  tableController: EntityTableController;
133
133
  }
134
134
  /**
@@ -139,7 +139,7 @@ export interface CollectionAddColumnProps {
139
139
  path: string;
140
140
  parentCollectionSlugs: string[];
141
141
  parentEntityIds: string[];
142
- collection: EntityCollection;
142
+ collection: CollectionConfig;
143
143
  tableController: EntityTableController;
144
144
  }
145
145
  /**
@@ -148,7 +148,7 @@ export interface CollectionAddColumnProps {
148
148
  */
149
149
  export interface CollectionErrorProps {
150
150
  path: string;
151
- collection: EntityCollection;
151
+ collection: CollectionConfig;
152
152
  parentCollectionSlugs?: string[];
153
153
  parentEntityIds?: string[];
154
154
  error: Error;
@@ -158,7 +158,7 @@ export interface CollectionErrorProps {
158
158
  * @group Plugins
159
159
  */
160
160
  export interface KanbanSetupProps {
161
- collection: EntityCollection;
161
+ collection: CollectionConfig;
162
162
  fullPath: string;
163
163
  parentCollectionSlugs: string[];
164
164
  parentEntityIds: string[];
@@ -168,7 +168,7 @@ export interface KanbanSetupProps {
168
168
  * @group Plugins
169
169
  */
170
170
  export interface KanbanAddColumnProps {
171
- collection: EntityCollection;
171
+ collection: CollectionConfig;
172
172
  fullPath: string;
173
173
  parentCollectionSlugs: string[];
174
174
  parentEntityIds: string[];
@@ -176,14 +176,14 @@ export interface KanbanAddColumnProps {
176
176
  }
177
177
  /**
178
178
  * Props for `entity.row.actions` slot.
179
- * Rendered for each row in an entity collection table.
179
+ * Rendered for each row in a entity collection table.
180
180
  * @group Plugins
181
181
  */
182
182
  export interface EntityRowActionsProps {
183
183
  entity: Entity;
184
184
  entityId: string;
185
185
  path: string;
186
- collection: EntityCollection;
186
+ collection: CollectionConfig;
187
187
  parentCollectionSlugs: string[];
188
188
  parentEntityIds: string[];
189
189
  selectionController: SelectionController;
@@ -199,7 +199,7 @@ export interface EntityFieldSlotProps {
199
199
  property: Property;
200
200
  path: string;
201
201
  entityId?: string | number;
202
- collection: EntityCollection;
202
+ collection: CollectionConfig;
203
203
  context: RebaseContext;
204
204
  }
205
205
  /**
@@ -209,7 +209,7 @@ export interface EntityFieldSlotProps {
209
209
  */
210
210
  export interface CollectionFilterPanelProps {
211
211
  path: string;
212
- collection: EntityCollection;
212
+ collection: CollectionConfig;
213
213
  parentCollectionSlugs: string[];
214
214
  parentEntityIds: string[];
215
215
  tableController: EntityTableController;
@@ -246,7 +246,7 @@ export interface ShellToolbarProps {
246
246
  */
247
247
  export interface CollectionInsightsSlotProps {
248
248
  path: string;
249
- collection: EntityCollection;
249
+ collection: CollectionConfig;
250
250
  parentCollectionSlugs: string[];
251
251
  parentEntityIds: string[];
252
252
  }
@@ -257,6 +257,6 @@ export interface CollectionInsightsSlotProps {
257
257
  */
258
258
  export interface HomeCardInsightSlotProps {
259
259
  slug: string;
260
- collection: EntityCollection;
260
+ collection: CollectionConfig;
261
261
  context: RebaseContext;
262
262
  }
@@ -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
  }
@@ -1,4 +1,3 @@
1
- import { Entity } from "./entities";
2
1
  export interface WebSocketErrorPayload {
3
2
  error?: string | {
4
3
  message: string;
@@ -12,32 +11,66 @@ export interface WebSocketMessage {
12
11
  payload?: unknown;
13
12
  subscriptionId?: string;
14
13
  requestId?: string;
15
- entities?: Entity<Record<string, unknown>>[];
16
- entity?: Entity<Record<string, unknown>> | null;
14
+ rows?: Record<string, unknown>[];
15
+ row?: Record<string, unknown> | null;
17
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;
18
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
+ }[];
19
42
  export interface CollectionUpdateMessage extends WebSocketMessage {
20
43
  type: "collection_update";
21
44
  subscriptionId: string;
22
- entities: Entity<Record<string, unknown>>[];
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;
23
53
  }
24
- export interface EntityUpdateMessage extends WebSocketMessage {
25
- type: "entity_update";
54
+ export interface SingleUpdateMessage extends WebSocketMessage {
55
+ type: "single_update";
26
56
  subscriptionId: string;
27
- entity: Entity<Record<string, unknown>> | null;
57
+ row: Record<string, unknown> | null;
28
58
  }
29
59
  /**
30
60
  * Lightweight patch message sent to collection subscribers when a single
31
- * entity is created, updated, or deleted. The client can merge this into
61
+ * row is created, updated, or deleted. The client can merge this into
32
62
  * its cached collection data for near-instant cross-tab updates without
33
63
  * waiting for a full collection refetch.
34
64
  */
35
- export interface CollectionEntityPatchMessage extends WebSocketMessage {
36
- type: "collection_entity_patch";
65
+ export interface CollectionPatchMessage extends WebSocketMessage {
66
+ type: "collection_patch";
37
67
  subscriptionId: string;
38
- entityId: string;
39
- /** The updated entity, or null if deleted */
40
- entity: Entity<Record<string, unknown>> | null;
68
+ /** The address of the row this patch refers to — derived, never read off it. */
69
+ id: string;
70
+ /** The updated row, or null if deleted */
71
+ row: Record<string, unknown> | null;
72
+ /** See {@link WirePrimaryKeys}: how the subscriber finds {@link id} in its cache. */
73
+ pks?: WirePrimaryKeys;
41
74
  }
42
75
  /**
43
76
  * Column metadata returned by table introspection.
@@ -1,10 +1,17 @@
1
1
  /**
2
- * This interface represents a user.
3
- * It has some of the same fields as a Firebase User.
4
- * Note that in the default implementation, we simply take the Firebase user
5
- * and use it as a Rebase user, so that means that even if they are not mapped
6
- * in this interface, it contains all the methods of the former, such as `delete`,
7
- * `getIdToken`, etc.
2
+ * The canonical representation of an authenticated user in the Rebase ecosystem.
3
+ *
4
+ * Used by {@link AuthController}, collections, callbacks, and both the
5
+ * `@rebasepro/client` and `@rebasepro/app` packages. All other user types
6
+ * (`RebaseUser`, `UserInfo`) are deprecated aliases of this type.
7
+ *
8
+ * **Backend-managed fields** (`uid`, `email`, `roles`, `metadata`, `createdAt`)
9
+ * are populated by the server. **Client-visible fields** (`displayName`,
10
+ * `photoURL`, `providerId`, `isAnonymous`, `emailVerified`) may be set during
11
+ * authentication or profile updates.
12
+ *
13
+ * @see AdminUser — the admin-API DTO, which adds audit fields (`createdAt`,
14
+ * `updatedAt`) and required `roles`.
8
15
  *
9
16
  * @group Models
10
17
  */
@@ -26,15 +33,20 @@ export type User = {
26
33
  */
27
34
  readonly photoURL: string | null;
28
35
  /**
29
- * The provider used to authenticate the user.
36
+ * The provider used to authenticate the user (e.g. `"password"`,
37
+ * `"google"`, `"github"`).
30
38
  */
31
39
  readonly providerId: string;
32
40
  /**
33
- *
41
+ * Whether the user is anonymous (created via anonymous sign-in).
34
42
  */
35
43
  readonly isAnonymous: boolean;
36
44
  /**
37
- * Role IDs assigned to this user (e.g. ["admin", "editor"]).
45
+ * Whether the user's email address has been verified.
46
+ */
47
+ readonly emailVerified?: boolean;
48
+ /**
49
+ * Role IDs assigned to this user (e.g. `["admin", "editor"]`).
38
50
  */
39
51
  roles?: string[];
40
52
  /**