@rebasepro/types 0.8.0 → 0.9.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 (78) hide show
  1. package/README.md +10 -10
  2. package/dist/controllers/auth.d.ts +1 -1
  3. package/dist/controllers/client.d.ts +184 -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 +267 -35
  7. package/dist/controllers/data_driver.d.ts +50 -37
  8. package/dist/controllers/index.d.ts +1 -1
  9. package/dist/controllers/local_config_persistence.d.ts +4 -4
  10. package/dist/controllers/navigation.d.ts +2 -2
  11. package/dist/controllers/registry.d.ts +17 -4
  12. package/dist/controllers/{side_entity_controller.d.ts → side_panel_controller.d.ts} +7 -7
  13. package/dist/controllers/storage.d.ts +39 -0
  14. package/dist/errors.d.ts +64 -0
  15. package/dist/index.d.ts +1 -0
  16. package/dist/index.es.js +142 -15
  17. package/dist/index.es.js.map +1 -1
  18. package/dist/index.umd.js +150 -17
  19. package/dist/index.umd.js.map +1 -1
  20. package/dist/rebase_context.d.ts +8 -4
  21. package/dist/types/auth_adapter.d.ts +4 -1
  22. package/dist/types/backend.d.ts +26 -26
  23. package/dist/types/builders.d.ts +2 -2
  24. package/dist/types/collections.d.ts +62 -63
  25. package/dist/types/component_overrides.d.ts +61 -3
  26. package/dist/types/cron.d.ts +10 -1
  27. package/dist/types/data_source.d.ts +15 -2
  28. package/dist/types/database_adapter.d.ts +4 -3
  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/modify_collections.d.ts +2 -2
  35. package/dist/types/plugins.d.ts +9 -9
  36. package/dist/types/policy.d.ts +64 -10
  37. package/dist/types/properties.d.ts +41 -9
  38. package/dist/types/relations.d.ts +3 -3
  39. package/dist/types/slots.d.ts +14 -14
  40. package/dist/types/websockets.d.ts +12 -13
  41. package/dist/users/user.d.ts +21 -9
  42. package/package.json +1 -1
  43. package/src/controllers/auth.tsx +1 -1
  44. package/src/controllers/client.ts +214 -6
  45. package/src/controllers/collection_registry.ts +3 -3
  46. package/src/controllers/customization_controller.tsx +1 -1
  47. package/src/controllers/data.ts +290 -39
  48. package/src/controllers/data_driver.ts +49 -40
  49. package/src/controllers/index.ts +1 -1
  50. package/src/controllers/local_config_persistence.tsx +4 -4
  51. package/src/controllers/navigation.ts +2 -2
  52. package/src/controllers/registry.ts +18 -4
  53. package/src/controllers/{side_entity_controller.tsx → side_panel_controller.tsx} +7 -7
  54. package/src/controllers/storage.ts +60 -1
  55. package/src/errors.ts +80 -0
  56. package/src/index.ts +1 -0
  57. package/src/rebase_context.tsx +8 -4
  58. package/src/types/auth_adapter.ts +4 -1
  59. package/src/types/backend.ts +36 -36
  60. package/src/types/builders.ts +2 -2
  61. package/src/types/collections.ts +78 -79
  62. package/src/types/component_overrides.ts +72 -5
  63. package/src/types/cron.ts +10 -1
  64. package/src/types/data_source.ts +24 -2
  65. package/src/types/database_adapter.ts +4 -3
  66. package/src/types/entities.ts +7 -7
  67. package/src/types/entity_actions.tsx +7 -7
  68. package/src/types/entity_callbacks.ts +42 -42
  69. package/src/types/entity_views.tsx +3 -3
  70. package/src/types/filter-operators.ts +96 -23
  71. package/src/types/modify_collections.tsx +2 -2
  72. package/src/types/plugins.tsx +9 -9
  73. package/src/types/policy.ts +64 -8
  74. package/src/types/properties.ts +44 -9
  75. package/src/types/relations.ts +3 -3
  76. package/src/types/slots.tsx +14 -14
  77. package/src/types/websockets.ts +12 -14
  78. package/src/users/user.ts +22 -9
@@ -1,5 +1,5 @@
1
1
  import { Entity, EntityValues } from "../types/entities";
2
- import { WhereFilterOp, FilterValues } from "../types/filter-operators";
2
+ import { WhereFilterOp, FilterValues, OrderByTuple } from "../types/filter-operators";
3
3
 
4
4
  export type WhereValue<T> = T | T[] | null;
5
5
 
@@ -17,19 +17,51 @@ export interface FilterCondition {
17
17
  /**
18
18
  * Parameters for querying a collection.
19
19
  *
20
+ * ## How the filter parameters combine
21
+ *
22
+ * `where`, `logical`, and `searchString` are **independent** and, when more
23
+ * than one is present, are combined with **AND** — every clause must match.
24
+ * Concretely the backend builds:
25
+ *
26
+ * ```text
27
+ * (where filters, AND-ed together)
28
+ * AND (logical group)
29
+ * AND (searchString matches, OR-ed across searchable columns)
30
+ * ```
31
+ *
32
+ * So `where` does **not** conflict with or override `logical` — they stack.
33
+ * If you need `where` fields OR-ed with each other, move them into `logical`
34
+ * instead. There is no way to OR `where` against `logical`; express anything
35
+ * that isn't a plain AND of the three groups inside a single `logical` tree.
36
+ *
37
+ * ## Pagination precedence
38
+ *
39
+ * `limit`/`offset` and `page` describe the same window two ways. If **both
40
+ * `offset` and `page` are provided, `page` wins** — the backend computes
41
+ * `offset = (page - 1) * (limit ?? 20)` and ignores the explicit `offset`.
42
+ * Pick one style per query.
43
+ *
20
44
  * @group Data
21
45
  */
22
46
  export interface FindParams {
23
- /** Maximum number of items to return (default: 20) */
47
+ /** Maximum number of items to return (default: 20). */
24
48
  limit?: number;
25
- /** Number of items to skip */
49
+ /**
50
+ * Number of items to skip. Ignored when {@link FindParams.page} is also
51
+ * set — `page` takes precedence.
52
+ */
26
53
  offset?: number;
27
- /** Page number (1-indexed), alternative to offset */
54
+ /**
55
+ * Page number (1-indexed), alternative to {@link FindParams.offset}.
56
+ * When set, overrides `offset` as `(page - 1) * (limit ?? 20)`.
57
+ */
28
58
  page?: number;
29
59
  /**
30
60
  * Filter conditions keyed by field name.
31
61
  * Each value is a `[WhereFilterOp, value]` tuple or an array of tuples
32
- * for multiple conditions on the same field.
62
+ * for multiple conditions on the same field. Multiple fields, and multiple
63
+ * tuples on one field, are **AND-ed**; also AND-ed with `logical` and
64
+ * `searchString` when present (see the interface docs).
33
65
  *
34
66
  * @example
35
67
  * { status: ["==", "active"] }
@@ -38,16 +70,24 @@ export interface FindParams {
38
70
  * { age: [[">=", 18], ["<", 65]] }
39
71
  */
40
72
  where?: FilterValues<string>;
41
- /** Logical grouping conditions (AND/OR) */
73
+ /**
74
+ * Logical grouping conditions (AND/OR). Use this for anything `where`
75
+ * can't express — notably OR-ing conditions. AND-ed with `where` and
76
+ * `searchString` when present (see the interface docs).
77
+ */
42
78
  logical?: LogicalCondition;
43
79
  /**
44
- * Sort order. Format: "field:direction".
45
- * @example "created_at:desc", "name:asc"
80
+ * Sort order as a `[field, direction]` tuple.
81
+ * @example orderBy: ["created_at", "desc"]
46
82
  */
47
- orderBy?: string;
83
+ orderBy?: OrderByTuple;
48
84
  /** Relations to include in the response */
49
85
  include?: string[];
50
- /** Full-text search string */
86
+ /**
87
+ * Full-text search string. Matched (OR-ed) across the collection's
88
+ * searchable columns, then AND-ed with `where`/`logical`. This is the
89
+ * value behind the query builder's `.search()` method.
90
+ */
51
91
  searchString?: string;
52
92
  }
53
93
 
@@ -70,7 +110,13 @@ export interface FindResponse<M extends Record<string, unknown> = Record<string,
70
110
 
71
111
 
72
112
  /**
73
- * Fluent Query Builder Interface supported on both client and server accessors.
113
+ * Fluent query builder for the **admin CMS** resolves to `FindResponse<M>`
114
+ * (Snapshot-wrapped rows).
115
+ *
116
+ * @internal App developers should use {@link SDKQueryBuilderInterface}
117
+ * (flat rows, returned by `client.data.*` / `context.data.*`). This
118
+ * Snapshot-flavored variant backs the admin CMS internals only.
119
+ *
74
120
  * @group Data
75
121
  */
76
122
  export interface QueryBuilderInterface<M extends Record<string, unknown> = Record<string, unknown>> {
@@ -86,11 +132,13 @@ export interface QueryBuilderInterface<M extends Record<string, unknown> = Recor
86
132
  }
87
133
 
88
134
  /**
89
- * A single collection's CRUD accessor.
135
+ * A single collection's CRUD accessor for the **admin CMS** — every method
136
+ * resolves to `Snapshot`-wrapped rows (`FindResponse<M>` / `Snapshot<M>`).
90
137
  *
91
- * This is the unified API surface used in both:
92
- * - The generated SDK (`client.data.products.create(...)`)
93
- * - Framework callbacks (`context.data.products.create(...)`)
138
+ * @internal App developers do **not** use this. The public, symmetric surface
139
+ * is {@link SDKCollectionClient} (flat rows), exposed as `client.data.products`
140
+ * in the SDK and `context.data.products` in framework callbacks. This
141
+ * Snapshot-flavored accessor backs the admin CMS view-model only.
94
142
  *
95
143
  * @group Data
96
144
  */
@@ -124,11 +172,6 @@ export interface CollectionAccessor<M extends Record<string, unknown> = Record<s
124
172
  */
125
173
  delete(id: string | number): Promise<void>;
126
174
 
127
- /**
128
- * Delete all records in this collection.
129
- */
130
- deleteAll?(): Promise<void>;
131
-
132
175
  /**
133
176
  * Subscribe to a collection for real-time updates.
134
177
  * Optional method, may not be supported by all implementations (like stateless HTTP clients).
@@ -156,28 +199,160 @@ export interface CollectionAccessor<M extends Record<string, unknown> = Record<s
156
199
  include(...relations: string[]): QueryBuilderInterface<M>;
157
200
  }
158
201
 
202
+ // =============================================================================
203
+ // SDK-facing types — flat rows, no Entity wrapper
204
+ // =============================================================================
205
+
159
206
  /**
160
- * The unified data access object.
207
+ * Pagination metadata returned with collection queries.
208
+ * @group Data
209
+ */
210
+ export interface PaginationMeta {
211
+ total: number;
212
+ limit: number;
213
+ offset: number;
214
+ hasMore: boolean;
215
+ }
216
+
217
+ /**
218
+ * Paginated response from a collection query (SDK-facing).
219
+ * Returns flat rows instead of Entity-wrapped objects.
161
220
  *
162
- * Access collections as dynamic properties: `data.products.find(...)`.
163
- * In the SDK this is backed by HTTP transport (typed, generated per-project).
164
- * In the framework this is backed by a Proxy + in-process database driver (dynamic).
221
+ * @example
222
+ * const { data, meta } = await rebase.data.posts.find();
223
+ * console.log(data[0].title); // direct access no .values
224
+ * console.log(meta.total);
225
+ *
226
+ * @group Data
227
+ */
228
+ export interface FindResult<M extends Record<string, unknown> = Record<string, unknown>> {
229
+ /** Array of flat rows matching the query */
230
+ data: M[];
231
+ /** Pagination metadata */
232
+ meta: PaginationMeta;
233
+ }
234
+
235
+ /**
236
+ * Fluent Query Builder Interface for the SDK client.
237
+ * Returns `FindResult<M>` (flat rows) instead of `FindResponse<M>` (Entity-wrapped).
238
+ *
239
+ * @group Data
240
+ */
241
+ export interface SDKQueryBuilderInterface<M extends Record<string, unknown> = Record<string, unknown>> {
242
+ where<K extends keyof M & string>(column: K, operator: WhereFilterOp, value: WhereValue<M[K]>): this;
243
+ where(logicalCondition: LogicalCondition): this;
244
+ orderBy(column: keyof M & string, direction?: "asc" | "desc"): this;
245
+ limit(count: number): this;
246
+ offset(count: number): this;
247
+ search(searchString: string): this;
248
+ include(...relations: string[]): this;
249
+ find(): Promise<FindResult<M>>;
250
+ count(): Promise<number>;
251
+ listen(onUpdate: (data: FindResult<M>) => void, onError?: (error: Error) => void): () => void;
252
+ }
253
+
254
+ /**
255
+ * SDK collection client — returns flat rows, no Entity wrapper.
256
+ *
257
+ * This is the public API surface for app developers using
258
+ * `createRebaseClient()`. CMS internals use `CollectionAccessor` instead.
259
+ *
260
+ * Type parameters:
261
+ * - `M` — the **Row** shape returned by reads (`find`, `findById`, `listen`).
262
+ * - `I` — the **Insert** shape accepted by {@link create}. Defaults to
263
+ * `Partial<M>`; the generated SDK supplies a dedicated `Insert` type where
264
+ * required columns are required and auto-generated / read-only columns are
265
+ * omitted, so `create({})` on a table with required fields is a compile error.
266
+ * - `U` — the **Update** shape accepted by {@link update}. Defaults to
267
+ * `Partial<M>`; the generated SDK supplies a dedicated `Update` type.
165
268
  *
166
269
  * @example
167
- * // SDK
168
- * const client = createRebaseClient({ baseUrl: "..." });
169
- * await client.data.products.create({ name: "Camera", price: 299 });
270
+ * const { data: posts } = await rebase.data.posts.find();
271
+ * console.log(posts[0].title); // flat access
272
+ * console.log(posts[0].id); // id at top level
170
273
  *
171
- * // Framework callback
172
- * callbacks: {
173
- * afterSave({ context }) {
174
- * await context.data.logs.create({ action: "saved", timestamp: new Date() });
175
- * }
176
- * }
274
+ * const post = await rebase.data.posts.findById(1);
275
+ * console.log(post?.title); // no .values needed
276
+ *
277
+ * @group Data
278
+ */
279
+ export interface SDKCollectionClient<
280
+ M extends Record<string, unknown> = Record<string, unknown>,
281
+ I = Partial<M>,
282
+ U = Partial<M>
283
+ > {
284
+ /**
285
+ * Find multiple records with optional filtering, pagination, and sorting.
286
+ */
287
+ find(params?: FindParams): Promise<FindResult<M>>;
288
+
289
+ /**
290
+ * Find a single record by its ID.
291
+ */
292
+ findById(id: string | number): Promise<M | undefined>;
293
+
294
+ /**
295
+ * Create a new record.
296
+ * @param data The record data to create (the collection's `Insert` shape).
297
+ * @param id Optional specific ID to use for the new record.
298
+ * @returns The created row
299
+ */
300
+ create(data: I, id?: string | number): Promise<M>;
301
+
302
+ /**
303
+ * Update an existing record by ID.
304
+ * @param data The fields to update (the collection's `Update` shape).
305
+ * @returns The updated row.
306
+ * @throws {RebaseApiError} with status 404 when the record does not exist.
307
+ */
308
+ update(id: string | number, data: U): Promise<M>;
309
+
310
+ /**
311
+ * Delete a record by ID.
312
+ * @throws {RebaseApiError} with status 404 when the record does not exist.
313
+ */
314
+ delete(id: string | number): Promise<void>;
315
+
316
+ /**
317
+ * Subscribe to a collection for real-time updates.
318
+ */
319
+ listen?(params: FindParams | undefined, onUpdate: (response: FindResult<M>) => void, onError?: (error: Error) => void): () => void;
320
+
321
+ /**
322
+ * Subscribe to a single record for real-time updates.
323
+ */
324
+ listenById?(id: string | number, onUpdate: (row: M | undefined) => void, onError?: (error: Error) => void): () => void;
325
+
326
+ /**
327
+ * Count the number of records matching the given filter.
328
+ */
329
+ count?(params?: FindParams): Promise<number>;
330
+
331
+ // Fluent Query Builder
332
+ where<K extends keyof M & string>(column: K, operator: WhereFilterOp, value: WhereValue<M[K]>): SDKQueryBuilderInterface<M>;
333
+ where(logicalCondition: LogicalCondition): SDKQueryBuilderInterface<M>;
334
+ orderBy(column: keyof M & string, direction?: "asc" | "desc"): SDKQueryBuilderInterface<M>;
335
+ limit(count: number): SDKQueryBuilderInterface<M>;
336
+ offset(count: number): SDKQueryBuilderInterface<M>;
337
+ search(searchString: string): SDKQueryBuilderInterface<M>;
338
+ include(...relations: string[]): SDKQueryBuilderInterface<M>;
339
+ }
340
+
341
+ /**
342
+ * The unified data access object for the **admin CMS** (Entity-shaped).
343
+ *
344
+ * Access collections as dynamic properties: `data.products.find(...)`. Each
345
+ * accessor returns `Entity`-wrapped records (`{ id, path, values }`) — the
346
+ * view-model the CMS renders. This is what `useData()` / the admin
347
+ * `RebaseContext.data` are backed by.
348
+ *
349
+ * @internal App developers do **not** use this — they use
350
+ * {@link RebaseSdkData} (flat rows), which is what the SDK client and backend
351
+ * `context.data` expose. This Entity-shaped map backs the admin CMS only.
177
352
  *
178
353
  * @group Data
179
354
  */
180
- export interface RebaseData {
355
+ export type RebaseData<DB = unknown> = {
181
356
  /**
182
357
  * Get a collection accessor by slug.
183
358
  * Alternative to dynamic property access for cases where
@@ -188,13 +363,89 @@ export interface RebaseData {
188
363
  * await accessor.find({ limit: 10 });
189
364
  */
190
365
  collection<M extends Record<string, unknown> = Record<string, unknown>>(slug: string): CollectionAccessor<M>;
366
+ } & (
367
+ DB extends Record<string, unknown>
368
+ ? { [K in keyof DB]: CollectionAccessor<DB[K] extends { Row: infer R extends Record<string, unknown> } ? R : Record<string, unknown>> }
369
+ : {
370
+ /**
371
+ * Dynamic collection accessor.
372
+ * Access any collection by its slug as a property.
373
+ *
374
+ * @example
375
+ * data.products.find({ where: { status: ["==", "published"] } })
376
+ */
377
+ [collectionSlug: string]: CollectionAccessor | ((slug: string) => CollectionAccessor);
378
+ }
379
+ );
380
+
381
+ /**
382
+ * The unified data access object for the **SDK** — flat rows, no Entity wrapper.
383
+ *
384
+ * This is the symmetric developer-facing data API, identical in shape on both
385
+ * sides of the stack:
386
+ * - The frontend SDK client (`client.data.products.find()`)
387
+ * - Backend framework callbacks & scripts (`context.data.products.find()`)
388
+ *
389
+ * Every accessor returns flat rows (`{ id, ...columns }`) via
390
+ * {@link SDKCollectionClient} — access fields directly (`row.title`), never
391
+ * `row.values.title`. The admin CMS uses {@link RebaseData} (Entity) instead.
392
+ *
393
+ * @example
394
+ * // Frontend SDK
395
+ * const { data: posts } = await client.data.posts.find();
396
+ * console.log(posts[0].title); // flat — no .values
397
+ *
398
+ * // Backend callback — identical shape
399
+ * callbacks: {
400
+ * beforeSave: async ({ context }) => {
401
+ * const product = await context.data.products.findById(id);
402
+ * console.log(product?.price); // flat — no .values
403
+ * }
404
+ * }
405
+ *
406
+ * @group Data
407
+ */
408
+ /**
409
+ * Extract the `Row` shape from a generated `Database[slug]` entry, falling
410
+ * back to an open record when the entry is untyped.
411
+ * @group Data
412
+ */
413
+ export type RowOf<T> = T extends { Row: infer R extends Record<string, unknown> } ? R : Record<string, unknown>;
414
+
415
+ /**
416
+ * Extract the `Insert` shape from a generated `Database[slug]` entry (the
417
+ * input accepted by `create`), falling back to `Partial<Row>`.
418
+ * @group Data
419
+ */
420
+ export type InsertOf<T> = T extends { Insert: infer I extends Record<string, unknown> } ? I : Partial<RowOf<T>>;
191
421
 
422
+ /**
423
+ * Extract the `Update` shape from a generated `Database[slug]` entry (the
424
+ * input accepted by `update`), falling back to `Partial<Row>`.
425
+ * @group Data
426
+ */
427
+ export type UpdateOf<T> = T extends { Update: infer U extends Record<string, unknown> } ? U : Partial<RowOf<T>>;
428
+
429
+ export type RebaseSdkData<DB = unknown> = {
192
430
  /**
193
- * Dynamic collection accessor.
194
- * Access any collection by its slug as a property.
431
+ * Get a flat collection accessor by slug.
195
432
  *
196
433
  * @example
197
- * data.products.find({ where: { status: "eq.published" } })
434
+ * const accessor = data.collection("products");
435
+ * await accessor.find({ limit: 10 });
198
436
  */
199
- [collectionSlug: string]: CollectionAccessor | ((slug: string) => CollectionAccessor);
200
- }
437
+ collection<M extends Record<string, unknown> = Record<string, unknown>>(slug: string): SDKCollectionClient<M>;
438
+ } & (
439
+ DB extends Record<string, unknown>
440
+ ? { [K in keyof DB]: SDKCollectionClient<RowOf<DB[K]>, InsertOf<DB[K]>, UpdateOf<DB[K]>> }
441
+ : {
442
+ /**
443
+ * Dynamic flat collection accessor.
444
+ * Access any collection by its slug as a property.
445
+ *
446
+ * @example
447
+ * data.products.find({ where: { status: ["==", "published"] } })
448
+ */
449
+ [collectionSlug: string]: SDKCollectionClient | ((slug: string) => SDKCollectionClient);
450
+ }
451
+ );
@@ -1,26 +1,26 @@
1
1
  import type { CollectionRegistryController } from "./collection_registry";
2
- import type { Entity, EntityStatus, EntityValues } from "../types/entities";
3
- import type { EntityCollection, FilterValues } from "../types/collections";
2
+ import type { EntityStatus, EntityValues } from "../types/entities";
3
+ import type { CollectionConfig, FilterValues } from "../types/collections";
4
4
  import type { RebaseContext } from "../rebase_context";
5
5
 
6
6
 
7
7
  /**
8
8
  * @internal
9
9
  */
10
- export interface FetchEntityProps<M extends Record<string, unknown> = Record<string, unknown>> {
10
+ export interface FetchOneProps<M extends Record<string, unknown> = Record<string, unknown>> {
11
11
  path: string;
12
- entityId: string | number;
12
+ id: string | number;
13
13
  databaseId?: string;
14
- collection?: EntityCollection<M>
14
+ collection?: CollectionConfig<M>
15
15
  }
16
16
 
17
17
  /**
18
18
  * @internal
19
19
  */
20
- export type ListenEntityProps<M extends Record<string, unknown> = Record<string, unknown>> =
21
- FetchEntityProps<M>
20
+ export type ListenOneProps<M extends Record<string, unknown> = Record<string, unknown>> =
21
+ FetchOneProps<M>
22
22
  & {
23
- onUpdate: (entity: Entity<M> | null) => void,
23
+ onUpdate: (row: Record<string, unknown> | null) => void,
24
24
  onError?: (error: Error) => void,
25
25
  }
26
26
 
@@ -45,7 +45,7 @@ export interface VectorSearchParams {
45
45
  */
46
46
  export interface FetchCollectionProps<M extends Record<string, unknown> = Record<string, unknown>> {
47
47
  path: string;
48
- collection?: EntityCollection<M>;
48
+ collection?: CollectionConfig<M>;
49
49
  filter?: FilterValues<Extract<keyof M, string>>,
50
50
  limit?: number;
51
51
  offset?: number;
@@ -63,42 +63,50 @@ export interface FetchCollectionProps<M extends Record<string, unknown> = Record
63
63
  export type ListenCollectionProps<M extends Record<string, unknown> = Record<string, unknown>> =
64
64
  FetchCollectionProps<M> &
65
65
  {
66
- onUpdate: (entities: Entity<M>[]) => void;
66
+ onUpdate: (rows: Record<string, unknown>[]) => void;
67
67
  onError?: (error: Error) => void;
68
68
  };
69
69
 
70
70
  /**
71
71
  * @internal
72
72
  */
73
- export interface SaveEntityProps<M extends Record<string, unknown> = Record<string, unknown>> {
73
+ export interface SaveProps<M extends Record<string, unknown> = Record<string, unknown>> {
74
74
  path: string;
75
75
  values: Partial<EntityValues<M>>;
76
- entityId?: string | number; // can be empty for new entities
76
+ id?: string | number; // can be empty for new entities
77
77
  previousValues?: Partial<EntityValues<M>>;
78
- collection?: EntityCollection<M>;
78
+ collection?: CollectionConfig<M>;
79
79
  status: EntityStatus;
80
80
  }
81
81
 
82
82
  /**
83
83
  * @internal
84
84
  */
85
- export interface DeleteEntityProps<M extends Record<string, unknown> = Record<string, unknown>> {
86
- entity: Entity<M>;
87
- collection?: EntityCollection<M>;
85
+ export interface DeleteProps<M extends Record<string, unknown> = Record<string, unknown>> {
86
+ row: { id: string | number; path: string; values?: Partial<EntityValues<M>> };
87
+ collection?: CollectionConfig<M>;
88
88
  }
89
89
 
90
90
  export type FilterCombinationValidProps = {
91
91
  path: string;
92
92
  databaseId?: string;
93
- collection: EntityCollection;
93
+ collection: CollectionConfig;
94
94
  filterValues: FilterValues<string>;
95
95
  sortBy?: [string, "asc" | "desc"];
96
96
  };
97
97
 
98
98
  /**
99
- * Internal driver interface for communicating with the data layer.
100
- * This is NOT the public API — use `RebaseData` / `context.data` instead.
101
- * @internal
99
+ * The integration SPI for plugging a data backend into Rebase.
100
+ *
101
+ * Implement this interface to connect a custom backend (or use a built-in
102
+ * driver such as the Firestore one) and register it on
103
+ * `<Rebase dataSources>`. Rebase wraps drivers via `buildRebaseData` and
104
+ * routes collections to them by their `dataSource` key.
105
+ *
106
+ * For *consuming* data in application code, use `RebaseData` /
107
+ * `context.data` instead — this interface is only for providing it.
108
+ *
109
+ * @group Datasource
102
110
  */
103
111
  export interface DataDriver {
104
112
 
@@ -115,9 +123,9 @@ export interface DataDriver {
115
123
  /**
116
124
  * Fetch data from a collection
117
125
  * @param props
118
- * @return Promise of entities
126
+ * @return Promise of flat rows
119
127
  */
120
- fetchCollection<M extends Record<string, unknown> = Record<string, unknown>>(props: FetchCollectionProps<M>): Promise<Entity<M>[]>;
128
+ fetchCollection<M extends Record<string, unknown> = Record<string, unknown>>(props: FetchCollectionProps<M>): Promise<Record<string, unknown>[]>;
121
129
 
122
130
  /**
123
131
  * Listen to a collection in a given path. If you don't implement this method
@@ -128,30 +136,30 @@ export interface DataDriver {
128
136
  listenCollection?<M extends Record<string, unknown> = Record<string, unknown>>(props: ListenCollectionProps<M>): () => void;
129
137
 
130
138
  /**
131
- * Retrieve an entity given a path and a collection
139
+ * Retrieve a single row given a path and a collection
132
140
  * @param props
133
141
  */
134
- fetchEntity<M extends Record<string, unknown> = Record<string, unknown>>(props: FetchEntityProps<M>): Promise<Entity<M> | undefined>;
142
+ fetchOne<M extends Record<string, unknown> = Record<string, unknown>>(props: FetchOneProps<M>): Promise<Record<string, unknown> | undefined>;
135
143
 
136
144
  /**
137
- * Get realtime updates on one entity.
145
+ * Get realtime updates on one row.
138
146
  * @param props
139
147
  * @return Function to cancel subscription
140
148
  */
141
- listenEntity?<M extends Record<string, unknown> = Record<string, unknown>>(props: ListenEntityProps<M>): () => void;
149
+ listenOne?<M extends Record<string, unknown> = Record<string, unknown>>(props: ListenOneProps<M>): () => void;
142
150
 
143
151
  /**
144
- * Save entity to the specified path
152
+ * Save a row to the specified path
145
153
  * @param props
146
154
  */
147
- saveEntity<M extends Record<string, unknown> = Record<string, unknown>>(props: SaveEntityProps<M>): Promise<Entity<M>>;
155
+ save<M extends Record<string, unknown> = Record<string, unknown>>(props: SaveProps<M>): Promise<Record<string, unknown>>;
148
156
 
149
157
  /**
150
- * Delete an entity
158
+ * Delete a entity
151
159
  * @param props
152
160
  * @return was the whole deletion flow successful
153
161
  */
154
- deleteEntity<M extends Record<string, unknown> = Record<string, unknown>>(props: DeleteEntityProps<M>): Promise<void>;
162
+ delete<M extends Record<string, unknown> = Record<string, unknown>>(props: DeleteProps<M>): Promise<void>;
155
163
 
156
164
  /**
157
165
  * Delete all entities from a collection.
@@ -164,7 +172,7 @@ export interface DataDriver {
164
172
  * @param path Collection path
165
173
  * @param name of the property
166
174
  * @param value
167
- * @param entityId
175
+ * @param id
168
176
  * @param collection
169
177
  * @return `true` if there are no other fields besides the given entity
170
178
  */
@@ -172,14 +180,14 @@ export interface DataDriver {
172
180
  path: string,
173
181
  name: string,
174
182
  value: unknown,
175
- entityId?: string | number,
176
- collection?: EntityCollection
183
+ id?: string | number,
184
+ collection?: CollectionConfig
177
185
  ): Promise<boolean>;
178
186
 
179
187
  /**
180
188
  * Count the number of entities in a collection
181
189
  */
182
- countEntities?<M extends Record<string, unknown> = Record<string, unknown>>(props: FetchCollectionProps<M>): Promise<number>;
190
+ count?<M extends Record<string, unknown> = Record<string, unknown>>(props: FetchCollectionProps<M>): Promise<number>;
183
191
 
184
192
  /**
185
193
  * Check if the given filter combination is valid
@@ -202,7 +210,7 @@ export interface DataDriver {
202
210
  context: RebaseContext,
203
211
  path: string,
204
212
  databaseId?: string,
205
- collection: EntityCollection,
213
+ collection: CollectionConfig,
206
214
  parentCollectionSlugs?: string[];
207
215
  parentEntityIds?: string[];
208
216
  }) => Promise<boolean>;
@@ -216,7 +224,7 @@ export interface DataDriver {
216
224
 
217
225
  /**
218
226
  * Optional REST-optimised fetch service. When present, the REST API
219
- * generator uses these methods instead of the generic `fetchEntity` /
227
+ * generator uses these methods instead of the generic `fetchOne` /
220
228
  * `fetchCollection` pipeline, enabling include-aware eager-loading.
221
229
  */
222
230
  restFetchService?: RestFetchService;
@@ -244,8 +252,9 @@ export interface DataDriver {
244
252
  * REST-optimised fetch service exposed by drivers that support
245
253
  * eager-loading of relations via `include`.
246
254
  *
247
- * The methods return flattened rows (`{ id, ...columns }`) rather
248
- * than the `Entity<M>` wrapper used by the generic DataDriver API.
255
+ * The methods return flattened rows (`{ id, ...columns }`) with
256
+ * included relations inlined as plain nested rows — the shape served
257
+ * to app developers through the REST API / SDK client.
249
258
  *
250
259
  * @group DataDriver
251
260
  */
@@ -272,9 +281,9 @@ export interface RestFetchService {
272
281
  /**
273
282
  * Fetch a single flattened entity with optional relation includes.
274
283
  */
275
- fetchEntityForRest(
284
+ fetchOneForRest(
276
285
  collectionPath: string,
277
- entityId: string | number,
286
+ id: string | number,
278
287
  include?: string[],
279
288
  databaseId?: string
280
289
  ): Promise<Record<string, unknown> | null>;
@@ -14,7 +14,7 @@ export * from "./storage";
14
14
  export * from "./email";
15
15
  export * from "./client";
16
16
  export * from "./customization_controller";
17
- export * from "./side_entity_controller";
17
+ export * from "./side_panel_controller";
18
18
  export * from "./side_dialogs_controller";
19
19
  export * from "./dialogs_controller";
20
20
  export * from "./snackbar";
@@ -1,9 +1,9 @@
1
- import type { EntityCollection } from "../types/collections";
1
+ import type { CollectionConfig } from "../types/collections";
2
2
 
3
3
  /**
4
4
  * @group Models
5
5
  */
6
- export type PartialEntityCollection<M extends Record<string, unknown> = Record<string, unknown>> = Partial<EntityCollection<M>>;
6
+ export type PartialCollectionConfig<M extends Record<string, unknown> = Record<string, unknown>> = Partial<CollectionConfig<M>>;
7
7
 
8
8
  /**
9
9
  * This interface is in charge of defining the controller that persists
@@ -11,8 +11,8 @@ export type PartialEntityCollection<M extends Record<string, unknown> = Record<s
11
11
  * a data source, such as local storage or Firestore.
12
12
  */
13
13
  export interface UserConfigurationPersistence {
14
- onCollectionModified: <M extends Record<string, unknown> = Record<string, unknown>>(path: string, partialCollection: PartialEntityCollection<M>) => void;
15
- getCollectionConfig: <M extends Record<string, unknown> = Record<string, unknown>>(path: string) => PartialEntityCollection<M>;
14
+ onCollectionModified: <M extends Record<string, unknown> = Record<string, unknown>>(path: string, partialCollection: PartialCollectionConfig<M>) => void;
15
+ getCollectionConfig: <M extends Record<string, unknown> = Record<string, unknown>>(path: string) => PartialCollectionConfig<M>;
16
16
  recentlyVisitedPaths: string[];
17
17
  setRecentlyVisitedPaths: (paths: string[]) => void;
18
18
  favouritePaths: string[];