@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.
- package/dist/controllers/auth.d.ts +8 -2
- package/dist/controllers/client.d.ts +13 -0
- package/dist/controllers/collection_registry.d.ts +2 -1
- package/dist/controllers/data_driver.d.ts +36 -1
- package/dist/controllers/navigation.d.ts +18 -6
- package/dist/controllers/registry.d.ts +9 -1
- package/dist/controllers/side_entity_controller.d.ts +7 -0
- package/dist/index.es.js +4 -0
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +4 -0
- package/dist/index.umd.js.map +1 -1
- package/dist/rebase_context.d.ts +17 -0
- package/dist/types/backend_hooks.d.ts +187 -0
- package/dist/types/collections.d.ts +31 -11
- package/dist/types/component_ref.d.ts +47 -0
- package/dist/types/cron.d.ts +1 -1
- package/dist/types/entity_views.d.ts +6 -7
- package/dist/types/formex.d.ts +40 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/plugins.d.ts +6 -3
- package/dist/types/properties.d.ts +72 -88
- package/dist/types/slots.d.ts +20 -10
- package/dist/types/translations.d.ts +6 -0
- package/package.json +1 -9
- package/src/controllers/auth.tsx +5 -2
- package/src/controllers/client.ts +15 -0
- package/src/controllers/collection_registry.ts +2 -1
- package/src/controllers/data_driver.ts +50 -1
- package/src/controllers/navigation.ts +19 -7
- package/src/controllers/registry.ts +10 -1
- package/src/controllers/side_entity_controller.tsx +8 -0
- package/src/rebase_context.tsx +18 -0
- package/src/types/backend_hooks.ts +185 -0
- package/src/types/collections.ts +32 -12
- package/src/types/component_ref.ts +57 -0
- package/src/types/cron.ts +2 -1
- package/src/types/entity_views.tsx +6 -7
- package/src/types/formex.ts +45 -0
- package/src/types/index.ts +3 -0
- package/src/types/plugins.tsx +6 -3
- package/src/types/properties.ts +89 -88
- package/src/types/slots.tsx +20 -10
- package/src/types/translations.ts +7 -0
package/dist/types/slots.d.ts
CHANGED
|
@@ -100,7 +100,8 @@ export interface NavigationSlotProps {
|
|
|
100
100
|
export interface CollectionToolbarProps {
|
|
101
101
|
path: string;
|
|
102
102
|
collection: EntityCollection;
|
|
103
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
},
|
package/src/controllers/auth.tsx
CHANGED
|
@@ -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
|
|
102
|
-
googleLogin
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
176
|
-
*
|
|
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
|
-
|
|
180
|
+
group?: string;
|
|
179
181
|
|
|
180
182
|
/**
|
|
181
|
-
*
|
|
182
|
-
*
|
|
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
|
-
|
|
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
|
/**
|
package/src/rebase_context.tsx
CHANGED
|
@@ -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
|
+
}
|
package/src/types/collections.ts
CHANGED
|
@@ -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
|
|
26
|
-
* The `
|
|
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. `
|
|
188
|
-
* e.g. `
|
|
190
|
+
* e.g. `fixedFilter: { age: [">", 18] }`
|
|
191
|
+
* e.g. `fixedFilter: { related_user: ["==", new EntityReference("sdc43dsw2", "users")] }`
|
|
189
192
|
*/
|
|
190
|
-
readonly
|
|
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. `
|
|
196
|
-
* e.g. `
|
|
198
|
+
* e.g. `defaultFilter: { age: [">", 18] }`
|
|
199
|
+
* e.g. `defaultFilter: { related_user: ["==", new EntityReference("sdc43dsw2", "users")] }`
|
|
197
200
|
*/
|
|
198
|
-
readonly
|
|
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?:
|
|
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
|
-
|
|
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
|
/**
|