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