@rebasepro/types 0.0.1-canary.eae7889 → 0.1.0

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 (43) hide show
  1. package/dist/controllers/auth.d.ts +8 -2
  2. package/dist/controllers/client.d.ts +13 -0
  3. package/dist/controllers/collection_registry.d.ts +2 -1
  4. package/dist/controllers/data_driver.d.ts +36 -1
  5. package/dist/controllers/navigation.d.ts +18 -6
  6. package/dist/controllers/registry.d.ts +9 -1
  7. package/dist/controllers/side_entity_controller.d.ts +7 -0
  8. package/dist/index.es.js +4 -0
  9. package/dist/index.es.js.map +1 -1
  10. package/dist/index.umd.js +4 -0
  11. package/dist/index.umd.js.map +1 -1
  12. package/dist/rebase_context.d.ts +17 -0
  13. package/dist/types/backend_hooks.d.ts +187 -0
  14. package/dist/types/collections.d.ts +31 -11
  15. package/dist/types/component_ref.d.ts +47 -0
  16. package/dist/types/cron.d.ts +1 -1
  17. package/dist/types/entity_views.d.ts +6 -7
  18. package/dist/types/formex.d.ts +40 -0
  19. package/dist/types/index.d.ts +3 -0
  20. package/dist/types/plugins.d.ts +6 -3
  21. package/dist/types/properties.d.ts +72 -88
  22. package/dist/types/slots.d.ts +20 -10
  23. package/dist/types/translations.d.ts +6 -0
  24. package/package.json +1 -9
  25. package/src/controllers/auth.tsx +5 -2
  26. package/src/controllers/client.ts +15 -0
  27. package/src/controllers/collection_registry.ts +2 -1
  28. package/src/controllers/data_driver.ts +50 -1
  29. package/src/controllers/navigation.ts +19 -7
  30. package/src/controllers/registry.ts +10 -1
  31. package/src/controllers/side_entity_controller.tsx +8 -0
  32. package/src/rebase_context.tsx +18 -0
  33. package/src/types/backend_hooks.ts +185 -0
  34. package/src/types/collections.ts +32 -12
  35. package/src/types/component_ref.ts +57 -0
  36. package/src/types/cron.ts +2 -1
  37. package/src/types/entity_views.tsx +6 -7
  38. package/src/types/formex.ts +45 -0
  39. package/src/types/index.ts +3 -0
  40. package/src/types/plugins.tsx +6 -3
  41. package/src/types/properties.ts +89 -88
  42. package/src/types/slots.tsx +20 -10
  43. package/src/types/translations.ts +7 -0
@@ -100,7 +100,8 @@ export interface NavigationSlotProps {
100
100
  export interface CollectionToolbarProps {
101
101
  path: string;
102
102
  collection: EntityCollection;
103
- parentCollectionIds: string[];
103
+ parentCollectionSlugs: string[];
104
+ parentEntityIds: string[];
104
105
  tableController: EntityTableController;
105
106
  selectionController: SelectionController;
106
107
  }
@@ -111,7 +112,8 @@ export interface CollectionToolbarProps {
111
112
  export interface CollectionEmptyStateProps {
112
113
  path: string;
113
114
  collection: EntityCollection;
114
- parentCollectionIds: string[];
115
+ parentCollectionSlugs: string[];
116
+ parentEntityIds: string[];
115
117
  canCreate: boolean;
116
118
  onNewClick?: () => void;
117
119
  }
@@ -123,7 +125,8 @@ export interface CollectionHeaderActionProps {
123
125
  property: Property;
124
126
  propertyKey: string;
125
127
  path: string;
126
- parentCollectionIds: string[];
128
+ parentCollectionSlugs: string[];
129
+ parentEntityIds: string[];
127
130
  onHover: boolean;
128
131
  collection: EntityCollection;
129
132
  tableController: EntityTableController;
@@ -134,7 +137,8 @@ export interface CollectionHeaderActionProps {
134
137
  */
135
138
  export interface CollectionAddColumnProps {
136
139
  path: string;
137
- parentCollectionIds: string[];
140
+ parentCollectionSlugs: string[];
141
+ parentEntityIds: string[];
138
142
  collection: EntityCollection;
139
143
  tableController: EntityTableController;
140
144
  }
@@ -145,7 +149,8 @@ export interface CollectionAddColumnProps {
145
149
  export interface CollectionErrorProps {
146
150
  path: string;
147
151
  collection: EntityCollection;
148
- parentCollectionIds?: string[];
152
+ parentCollectionSlugs?: string[];
153
+ parentEntityIds?: string[];
149
154
  error: Error;
150
155
  }
151
156
  /**
@@ -155,7 +160,8 @@ export interface CollectionErrorProps {
155
160
  export interface KanbanSetupProps {
156
161
  collection: EntityCollection;
157
162
  fullPath: string;
158
- parentCollectionIds: string[];
163
+ parentCollectionSlugs: string[];
164
+ parentEntityIds: string[];
159
165
  }
160
166
  /**
161
167
  * Props for the `kanban.add-column` slot.
@@ -164,7 +170,8 @@ export interface KanbanSetupProps {
164
170
  export interface KanbanAddColumnProps {
165
171
  collection: EntityCollection;
166
172
  fullPath: string;
167
- parentCollectionIds: string[];
173
+ parentCollectionSlugs: string[];
174
+ parentEntityIds: string[];
168
175
  columnProperty: string;
169
176
  }
170
177
  /**
@@ -177,7 +184,8 @@ export interface EntityRowActionsProps {
177
184
  entityId: string;
178
185
  path: string;
179
186
  collection: EntityCollection;
180
- parentCollectionIds: string[];
187
+ parentCollectionSlugs: string[];
188
+ parentEntityIds: string[];
181
189
  selectionController: SelectionController;
182
190
  context: RebaseContext;
183
191
  }
@@ -202,7 +210,8 @@ export interface EntityFieldSlotProps {
202
210
  export interface CollectionFilterPanelProps {
203
211
  path: string;
204
212
  collection: EntityCollection;
205
- parentCollectionIds: string[];
213
+ parentCollectionSlugs: string[];
214
+ parentEntityIds: string[];
206
215
  tableController: EntityTableController;
207
216
  context: RebaseContext;
208
217
  }
@@ -238,7 +247,8 @@ export interface ShellToolbarProps {
238
247
  export interface CollectionInsightsSlotProps {
239
248
  path: string;
240
249
  collection: EntityCollection;
241
- parentCollectionIds: string[];
250
+ parentCollectionSlugs: string[];
251
+ parentEntityIds: string[];
242
252
  }
243
253
  /**
244
254
  * Props for `home.card.insight` slot.
@@ -51,6 +51,8 @@ export interface RebaseTranslations {
51
51
  all_entries_loaded: string;
52
52
  create_your_first_entry: string;
53
53
  no_results_filter_sort: string;
54
+ /** Shown when a text search yields no results. Supports `{{search}}` interpolation. */
55
+ no_results_search?: string;
54
56
  add: string;
55
57
  remove: string;
56
58
  copy_id: string;
@@ -461,6 +463,10 @@ export interface RebaseTranslations {
461
463
  reset_password_success?: string;
462
464
  reset_password_confirmation?: string;
463
465
  error_resetting_password?: string;
466
+ /** Permission-denied empty states */
467
+ no_permission_to_view_users?: string;
468
+ no_permission_to_view_roles?: string;
469
+ no_permission_description?: string;
464
470
  /** Editor table-bubble */
465
471
  add_row_before: string;
466
472
  add_row_after: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rebasepro/types",
3
3
  "type": "module",
4
- "version": "0.0.1-canary.eae7889",
4
+ "version": "0.1.0",
5
5
  "description": "Rebase type definitions — shared interfaces and controller types",
6
6
  "funding": {
7
7
  "url": "https://github.com/sponsors/rebaseco"
@@ -39,9 +39,6 @@
39
39
  "./package.json": "./package.json"
40
40
  },
41
41
  "devDependencies": {
42
- "@jest/globals": "^29.7.0",
43
- "@testing-library/react": "^16.3.0",
44
- "@testing-library/user-event": "^14.6.1",
45
42
  "@types/jest": "^29.5.14",
46
43
  "@types/node": "^20.17.14",
47
44
  "@types/object-hash": "^3.0.6",
@@ -49,14 +46,9 @@
49
46
  "@types/react-dom": "^19.0.3",
50
47
  "@types/react-measure": "^2.0.12",
51
48
  "@vitejs/plugin-react": "^4.3.4",
52
- "babel-plugin-react-compiler": "beta",
53
- "cross-env": "^7.0.3",
54
49
  "eslint-plugin-react-compiler": "beta",
55
50
  "jest": "^29.7.0",
56
- "npm-run-all": "^4.1.5",
57
51
  "ts-jest": "^29.3.1",
58
- "ts-node": "^10.9.2",
59
- "tsd": "^0.31.2",
60
52
  "typescript": "^5.8.3",
61
53
  "vite": "^5.4.17"
62
54
  },
@@ -98,8 +98,11 @@ export type AuthController<USER extends User = User, ExtraData = unknown> = {
98
98
  export interface AuthControllerExtended<USER extends User = User, ExtraData = unknown> extends AuthController<USER, ExtraData> {
99
99
  /** Login with email and password */
100
100
  emailPasswordLogin?(email: string, password: string): Promise<void>;
101
- /** Login with a Google ID token or trigger Google popup */
102
- googleLogin?(idToken: string): Promise<void>;
101
+ /** Login with a Google token or authorization code */
102
+ googleLogin?: {
103
+ (token: string, tokenType?: "idToken" | "accessToken"): Promise<void>;
104
+ (payload: { code: string; redirectUri: string }): Promise<void>;
105
+ };
103
106
  /** Register a new user */
104
107
  register?(email: string, password: string, displayName?: string): Promise<void>;
105
108
  /** Skip login (for anonymous access if enabled) */
@@ -130,4 +130,19 @@ export interface RebaseClient<DB = unknown> {
130
130
 
131
131
  /** Admin API for user and role management */
132
132
  admin?: AdminAPI;
133
+
134
+ /**
135
+ * The base HTTP URL of the backend server.
136
+ * Exposed by the SDK client (`@rebasepro/client`) and used to auto-derive
137
+ * the `ApiConfigProvider` URL.
138
+ */
139
+ baseUrl?: string;
140
+
141
+ /**
142
+ * WebSocket client for realtime subscriptions and admin capabilities.
143
+ * Exposed by the SDK client (`@rebasepro/client`). The shape is intentionally
144
+ * left as `unknown` in the base interface — callers should narrow via feature
145
+ * detection (e.g. `typeof ws.executeSql === "function"`).
146
+ */
147
+ ws?: unknown;
133
148
  }
@@ -46,7 +46,8 @@ export type CollectionRegistryController<
46
46
  * Retrieve all the related parent collection ids for a given path
47
47
  * @param path
48
48
  */
49
- getParentCollectionIds: (path: string) => string[];
49
+ getParentCollectionSlugs: (path: string) => string[];
50
+ getParentEntityIds: (path: string) => string[];
50
51
 
51
52
  /**
52
53
  * Resolve paths from a list of ids
@@ -179,7 +179,8 @@ export interface DataDriver {
179
179
  path: string,
180
180
  databaseId?: string,
181
181
  collection: EntityCollection,
182
- parentCollectionIds?: string[]
182
+ parentCollectionSlugs?: string[];
183
+ parentEntityIds?: string[];
183
184
  }) => Promise<boolean>;
184
185
 
185
186
  /**
@@ -187,6 +188,15 @@ export interface DataDriver {
187
188
  */
188
189
  needsInitTextSearch?: boolean;
189
190
 
191
+ // ── REST fetch capabilities ─────────────────────────────────────────
192
+
193
+ /**
194
+ * Optional REST-optimised fetch service. When present, the REST API
195
+ * generator uses these methods instead of the generic `fetchEntity` /
196
+ * `fetchCollection` pipeline, enabling include-aware eager-loading.
197
+ */
198
+ restFetchService?: RestFetchService;
199
+
190
200
  // ── Admin capabilities ─────────────────────────────────────────────
191
201
  //
192
202
  // Admin operations are now modelled as capability-specific interfaces
@@ -205,3 +215,42 @@ export interface DataDriver {
205
215
  admin?: import("../types/backend").DatabaseAdmin;
206
216
 
207
217
  }
218
+
219
+ /**
220
+ * REST-optimised fetch service exposed by drivers that support
221
+ * eager-loading of relations via `include`.
222
+ *
223
+ * The methods return flattened rows (`{ id, ...columns }`) rather
224
+ * than the `Entity<M>` wrapper used by the generic DataDriver API.
225
+ *
226
+ * @group DataDriver
227
+ */
228
+ export interface RestFetchService {
229
+ /**
230
+ * Fetch a collection of flattened entities with optional relation includes.
231
+ */
232
+ fetchCollectionForRest(
233
+ collectionPath: string,
234
+ options?: {
235
+ filter?: FilterValues<string>;
236
+ orderBy?: string;
237
+ order?: "desc" | "asc";
238
+ limit?: number;
239
+ offset?: number;
240
+ startAfter?: Record<string, unknown>;
241
+ searchString?: string;
242
+ databaseId?: string;
243
+ },
244
+ include?: string[]
245
+ ): Promise<Record<string, unknown>[]>;
246
+
247
+ /**
248
+ * Fetch a single flattened entity with optional relation includes.
249
+ */
250
+ fetchEntityForRest(
251
+ collectionPath: string,
252
+ entityId: string | number,
253
+ include?: string[],
254
+ databaseId?: string
255
+ ): Promise<Record<string, unknown> | null>;
256
+ }
@@ -172,17 +172,18 @@ export interface AppView {
172
172
  hideFromNavigation?: boolean;
173
173
 
174
174
  /**
175
- * Component to be rendered. This can be any React component, and can use
176
- * any of the provided hooks
175
+ * Navigation group for this view.
176
+ * Views sharing the same group name will be visually grouped
177
+ * together in the drawer and home page. If not set, the view
178
+ * falls into the default "Views" group.
177
179
  */
178
- view: React.ReactNode;
180
+ group?: string;
179
181
 
180
182
  /**
181
- * Optional field used to group top level navigation entries under a
182
- * navigation view.
183
- * This prop is ignored for admin views.
183
+ * Component to be rendered. This can be any React component, and can use
184
+ * any of the provided hooks
184
185
  */
185
- group?: string;
186
+ view: React.ReactNode;
186
187
 
187
188
  /**
188
189
  * If true, a wildcard route (slug/*) is automatically registered
@@ -227,6 +228,17 @@ export interface NavigationGroupMapping {
227
228
  * List of collection ids or view paths that belong to this group.
228
229
  */
229
230
  entries: string[];
231
+ /**
232
+ * Configure which groups start collapsed.
233
+ * Set to `true` to collapse in both drawer and home page,
234
+ * or use an object to control each independently.
235
+ *
236
+ * @defaultValue false (expanded)
237
+ */
238
+ collapsedByDefault?: boolean | {
239
+ drawer?: boolean;
240
+ home?: boolean;
241
+ };
230
242
  }
231
243
 
232
244
  export interface NavigationEntry {
@@ -3,7 +3,7 @@ import type { EntityCollection } from "../types/collections";
3
3
  import type { EntityCollectionsBuilder } from "../types/builders";
4
4
  import type { EntityCustomView } from "../types/entity_views";
5
5
  import type { EntityAction } from "../types/entity_actions";
6
- import type { AppView } from "./navigation";
6
+ import type { AppView, NavigationGroupMapping } from "./navigation";
7
7
 
8
8
  /**
9
9
  * Options to enable the built-in collection editor.
@@ -28,6 +28,15 @@ export interface RebaseCMSConfig<EC extends EntityCollection = any> {
28
28
  entityActions?: EntityAction[];
29
29
  plugins?: any[];
30
30
 
31
+ /**
32
+ * Centralized configuration for how collections and views are grouped
33
+ * in the navigation sidebar and home page.
34
+ * Each mapping defines a named group and the collection/view slugs
35
+ * that belong to it. The array order determines group display order.
36
+ * Entry order within each group determines card order.
37
+ */
38
+ navigationGroupMappings?: NavigationGroupMapping[];
39
+
31
40
  /**
32
41
  * Enable the built-in visual collection/schema editor.
33
42
  * Pass `true` for zero-config, or an options object for fine-grained control.
@@ -73,6 +73,14 @@ export interface EntitySidePanelProps<M extends Record<string, unknown> = Record
73
73
  * Allow the user to open the entity fullscreen
74
74
  */
75
75
  allowFullScreen?: boolean;
76
+
77
+ /**
78
+ * Pre-populate the form with these values when creating a new entity.
79
+ * Only applied when `entityId` is not set (i.e. the form is in "new" mode).
80
+ * Useful for actions that fetch data from an external source (e.g. a URL)
81
+ * and want to pre-fill the document before the user saves.
82
+ */
83
+ defaultValues?: Partial<M>;
76
84
  }
77
85
 
78
86
  /**
@@ -3,6 +3,7 @@ import type { AuthController } from "./controllers/auth";
3
3
  import type { StorageSource } from "./controllers/storage";
4
4
  import type { UserConfigurationPersistence } from "./controllers/local_config_persistence";
5
5
  import type { DatabaseAdmin } from "./types/backend";
6
+ import type { RebaseClient } from "./controllers/client";
6
7
 
7
8
  import type { RebaseData } from "./controllers/data";
8
9
  import type { User } from "./users";
@@ -16,6 +17,23 @@ import type { UserManagementDelegate } from "./types/user_management_delegate";
16
17
  */
17
18
  export type RebaseCallContext<USER extends User = User> = {
18
19
 
20
+ /**
21
+ * The Rebase client instance.
22
+ * Available in all entity callbacks (beforeSave, afterSave, afterRead,
23
+ * beforeDelete, afterDelete) and in CollectionActionsProps via context.
24
+ * Use it to call backend functions, access data, storage, etc.
25
+ *
26
+ * @example
27
+ * // In a beforeSave callback:
28
+ * const result = await context.client.functions.invoke('my-function', { ... });
29
+ *
30
+ * @example
31
+ * // In a CollectionAction component:
32
+ * const { client } = props.context;
33
+ * const result = await client.functions.invoke('extract-job', { url });
34
+ */
35
+ client: RebaseClient<any>;
36
+
19
37
  /**
20
38
  * Unified data access — `context.data.products.create(...)`.
21
39
  * Access any collection as a dynamic property.
@@ -0,0 +1,185 @@
1
+ import type { AdminUser, AdminRole } from "../controllers/client";
2
+
3
+ /**
4
+ * Context passed to every backend hook.
5
+ * Provides information about the request that triggered the hook.
6
+ * @group Backend Hooks
7
+ */
8
+ export interface BackendHookContext {
9
+ /** The currently authenticated user making the request (if any) */
10
+ requestUser?: { userId: string; roles: string[] };
11
+ /** The HTTP method of the request */
12
+ method: "GET" | "POST" | "PUT" | "DELETE";
13
+ }
14
+
15
+ /**
16
+ * Hooks for intercepting Admin User data at the API boundary.
17
+ *
18
+ * These hooks run on the server after the database operation completes
19
+ * but before the response is sent to the client.
20
+ *
21
+ * @group Backend Hooks
22
+ */
23
+ export interface UserHooks {
24
+ /**
25
+ * Transform a user record after it's read from the database,
26
+ * before it's returned to the client.
27
+ *
28
+ * Return the modified user, or `null` to filter it out entirely
29
+ * (the user won't appear in listings or individual fetches).
30
+ */
31
+ afterRead?(user: AdminUser, context: BackendHookContext): AdminUser | null | Promise<AdminUser | null>;
32
+
33
+ /**
34
+ * Transform user data before it's written to the database.
35
+ * Runs on POST (create) and PUT (update).
36
+ *
37
+ * Return the (possibly modified) data to proceed with the save.
38
+ * Throw an error to abort the operation.
39
+ */
40
+ beforeSave?(data: { email?: string; displayName?: string; roles?: string[] }, context: BackendHookContext): { email?: string; displayName?: string; roles?: string[] } | Promise<{ email?: string; displayName?: string; roles?: string[] }>;
41
+
42
+ /**
43
+ * Called after a user is successfully created or updated.
44
+ * Useful for side-effects like sending notifications.
45
+ */
46
+ afterSave?(user: AdminUser, context: BackendHookContext): void | Promise<void>;
47
+
48
+ /**
49
+ * Called before a user is deleted. Throw to prevent deletion.
50
+ */
51
+ beforeDelete?(userId: string, context: BackendHookContext): void | Promise<void>;
52
+
53
+ /**
54
+ * Called after a user is successfully deleted.
55
+ */
56
+ afterDelete?(userId: string, context: BackendHookContext): void | Promise<void>;
57
+ }
58
+
59
+ /**
60
+ * Hooks for intercepting Admin Role data at the API boundary.
61
+ * @group Backend Hooks
62
+ */
63
+ export interface RoleHooks {
64
+ /**
65
+ * Transform a role record after it's read from the database,
66
+ * before it's returned to the client.
67
+ *
68
+ * Return the modified role, or `null` to filter it out entirely.
69
+ */
70
+ afterRead?(role: AdminRole, context: BackendHookContext): AdminRole | null | Promise<AdminRole | null>;
71
+ }
72
+
73
+ /**
74
+ * Hooks for intercepting collection entity data at the REST API boundary.
75
+ *
76
+ * These run **after** per-collection `EntityCallbacks` (which execute inside
77
+ * the DataDriver) and provide a single cross-cutting interception point for
78
+ * ALL collections flowing through the REST API.
79
+ *
80
+ * Every callback receives the collection `slug` so you can target specific
81
+ * collections or apply logic globally.
82
+ *
83
+ * @group Backend Hooks
84
+ */
85
+ export interface DataHooks {
86
+ /**
87
+ * Transform an entity after it's read from the database,
88
+ * before it's returned to the client.
89
+ *
90
+ * Runs for both list (GET /:slug) and single (GET /:slug/:id) fetches.
91
+ * Return the modified entity, or `null` to filter it out.
92
+ *
93
+ * @param slug - The collection slug (e.g. "orders", "products")
94
+ * @param entity - The flattened entity object (id + values merged)
95
+ * @param context - Request context (authenticated user, HTTP method)
96
+ */
97
+ afterRead?(slug: string, entity: Record<string, unknown>, context: BackendHookContext): Record<string, unknown> | null | Promise<Record<string, unknown> | null>;
98
+
99
+ /**
100
+ * Transform entity values before they are written to the database.
101
+ * Runs on POST (create) and PUT (update).
102
+ *
103
+ * Return the (possibly modified) values. Throw to abort the save.
104
+ *
105
+ * @param slug - The collection slug
106
+ * @param values - The raw request body values
107
+ * @param entityId - The entity ID (only present on updates)
108
+ * @param context - Request context
109
+ */
110
+ beforeSave?(slug: string, values: Record<string, unknown>, entityId: string | undefined, context: BackendHookContext): Record<string, unknown> | Promise<Record<string, unknown>>;
111
+
112
+ /**
113
+ * Called after an entity is successfully saved (created or updated).
114
+ * Useful for side-effects like syncing to external systems.
115
+ *
116
+ * @param slug - The collection slug
117
+ * @param entity - The saved entity (flattened)
118
+ * @param context - Request context
119
+ */
120
+ afterSave?(slug: string, entity: Record<string, unknown>, context: BackendHookContext): void | Promise<void>;
121
+
122
+ /**
123
+ * Called before an entity is deleted. Throw to prevent deletion.
124
+ *
125
+ * @param slug - The collection slug
126
+ * @param entityId - The entity ID being deleted
127
+ * @param context - Request context
128
+ */
129
+ beforeDelete?(slug: string, entityId: string, context: BackendHookContext): void | Promise<void>;
130
+
131
+ /**
132
+ * Called after an entity is successfully deleted.
133
+ *
134
+ * @param slug - The collection slug
135
+ * @param entityId - The deleted entity ID
136
+ * @param context - Request context
137
+ */
138
+ afterDelete?(slug: string, entityId: string, context: BackendHookContext): void | Promise<void>;
139
+ }
140
+
141
+ /**
142
+ * Backend-level hooks for intercepting data at the API boundary.
143
+ *
144
+ * These hooks run server-side after database operations complete and before
145
+ * API responses are sent.
146
+ *
147
+ * - `users` / `roles` — intercept admin user and role management endpoints
148
+ * - `data` — intercept ALL collection entity data flowing through the REST API
149
+ *
150
+ * `data` hooks complement per-collection `EntityCallbacks`. Entity callbacks
151
+ * run inside the DataDriver (close to the DB); data hooks run at the HTTP
152
+ * boundary (close to the client). Use data hooks for cross-cutting concerns
153
+ * like audit logging, response enrichment, or field masking.
154
+ *
155
+ * @example
156
+ * ```typescript
157
+ * const hooks: BackendHooks = {
158
+ * data: {
159
+ * afterRead(slug, entity, ctx) {
160
+ * // Mask PII for non-admin users across all collections
161
+ * if (!ctx.requestUser?.roles.includes("admin") && entity.email) {
162
+ * return { ...entity, email: "***" };
163
+ * }
164
+ * return entity;
165
+ * }
166
+ * },
167
+ * users: {
168
+ * afterRead(user, ctx) {
169
+ * if (user.email.endsWith("@system.internal")) return null;
170
+ * return user;
171
+ * }
172
+ * }
173
+ * };
174
+ * ```
175
+ *
176
+ * @group Backend Hooks
177
+ */
178
+ export interface BackendHooks {
179
+ /** Hooks for intercepting user management data */
180
+ users?: UserHooks;
181
+ /** Hooks for intercepting role management data */
182
+ roles?: RoleHooks;
183
+ /** Hooks for intercepting ALL collection entity data via the REST API */
184
+ data?: DataHooks;
185
+ }
@@ -10,6 +10,7 @@ import type { RebaseContext } from "../rebase_context";
10
10
  import type { Relation } from "./relations";
11
11
  import type { EntityCustomView } from "./entity_views";
12
12
  import type { EntityAction } from "./entity_actions";
13
+ import type { ComponentRef } from "./component_ref";
13
14
 
14
15
  /**
15
16
  * Base interface containing all driver-agnostic collection properties.
@@ -22,9 +23,8 @@ import type { EntityAction } from "./entity_actions";
22
23
  export interface BaseEntityCollection<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User> {
23
24
 
24
25
  /**
25
- * You can set an alias that will be used internally instead of the `path`.
26
- * The `alias` value will be used to determine the URL of the collection,
27
- * while `path` will still be used in the driver.
26
+ * You can set an alias that will be used internally instead of the collection name.
27
+ * The `slug` value will be used to determine the URL of the collection.
28
28
  * Note that you can use this value in reference properties too.
29
29
  */
30
30
  slug: string;
@@ -100,6 +100,9 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
100
100
 
101
101
  /**
102
102
  * Navigation group for this collection.
103
+ * Collections sharing the same group name will be visually grouped
104
+ * together in the drawer and home page. If not set, the collection
105
+ * falls into the default "Views" group.
103
106
  */
104
107
  group?: string;
105
108
 
@@ -184,18 +187,18 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
184
187
  /**
185
188
  * Force a filter in this view. If applied, the rest of the filters will
186
189
  * be disabled. Filters applied with this prop cannot be changed.
187
- * e.g. `forceFilter: { age: [">", 18] }`
188
- * e.g. `forceFilter: { related_user: ["==", new EntityReference("sdc43dsw2", "users")] }`
190
+ * e.g. `fixedFilter: { age: [">", 18] }`
191
+ * e.g. `fixedFilter: { related_user: ["==", new EntityReference("sdc43dsw2", "users")] }`
189
192
  */
190
- readonly forceFilter?: FilterValues<Extract<keyof M, string> | (string & {})>;
193
+ readonly fixedFilter?: FilterValues<Extract<keyof M, string> | (string & {})>;
191
194
 
192
195
  /**
193
196
  * Initial filters applied to the collection this collection is related to.
194
197
  * Defaults to none. Filters applied with this prop can be changed.
195
- * e.g. `filter: { age: [">", 18] }`
196
- * e.g. `filter: { related_user: ["==", new EntityReference("sdc43dsw2", "users")] }`
198
+ * e.g. `defaultFilter: { age: [">", 18] }`
199
+ * e.g. `defaultFilter: { related_user: ["==", new EntityReference("sdc43dsw2", "users")] }`
197
200
  */
198
- readonly filter?: FilterValues<Extract<keyof M, string> | (string & {})>; // setting FilterValues<M> can break defining collections by code
201
+ readonly defaultFilter?: FilterValues<Extract<keyof M, string> | (string & {})>; // setting FilterValues<M> can break defining collections by code
199
202
 
200
203
  /**
201
204
  * Default sort applied to this collection.
@@ -352,7 +355,7 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
352
355
  /**
353
356
  * Builder for the collection actions rendered in the toolbar
354
357
  */
355
- Actions?: React.ComponentType<any>[];
358
+ Actions?: ComponentRef<CollectionActionsProps>[];
356
359
 
357
360
 
358
361
  }
@@ -370,6 +373,7 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
370
373
  */
371
374
  export interface PostgresCollection<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>
372
375
  extends BaseEntityCollection<M, USER> {
376
+ properties: Properties;
373
377
 
374
378
  /**
375
379
  * The driver for this collection. For Postgres collections this
@@ -417,7 +421,6 @@ export interface PostgresCollection<M extends Record<string, unknown> = Record<s
417
421
  */
418
422
  export interface FirebaseCollection<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>
419
423
  extends BaseEntityCollection<M, USER> {
420
-
421
424
  /**
422
425
  * The driver for this collection. Must be set to `"firestore"`.
423
426
  */
@@ -554,7 +557,8 @@ export interface CollectionActionsProps<M extends Record<string, unknown> = Reco
554
557
  /**
555
558
  * Array of the parent path segments like `['users']`
556
559
  */
557
- parentCollectionIds: string[];
560
+ parentCollectionSlugs: string[];
561
+ parentEntityIds: string[];
558
562
 
559
563
  /**
560
564
  * The collection configuration
@@ -584,6 +588,22 @@ export interface CollectionActionsProps<M extends Record<string, unknown> = Reco
584
588
  */
585
589
  collectionEntitiesCount?: number;
586
590
 
591
+ /**
592
+ * Programmatically open the new-document form for this collection,
593
+ * optionally pre-populating it with initial field values.
594
+ * The form opens in the same mode configured for the collection
595
+ * (side panel, full screen, or split).
596
+ *
597
+ * This is the primary hook for workflows that need to create a document
598
+ * from external data — e.g. fetching content from a URL, importing from
599
+ * a third-party API, or duplicating from another system.
600
+ *
601
+ * @example
602
+ * // Inside a custom CollectionAction component:
603
+ * openNewDocument({ title: "Fetched title", body: "..." });
604
+ */
605
+ openNewDocument: (defaultValues?: Record<string, unknown>) => void;
606
+
587
607
  }
588
608
 
589
609
  /**