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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +13 -13
  2. package/dist/controllers/auth.d.ts +7 -1
  3. package/dist/controllers/client.d.ts +204 -6
  4. package/dist/controllers/collection_registry.d.ts +3 -3
  5. package/dist/controllers/customization_controller.d.ts +1 -1
  6. package/dist/controllers/data.d.ts +309 -35
  7. package/dist/controllers/data_driver.d.ts +92 -37
  8. package/dist/controllers/email.d.ts +2 -2
  9. package/dist/controllers/index.d.ts +1 -1
  10. package/dist/controllers/local_config_persistence.d.ts +4 -4
  11. package/dist/controllers/navigation.d.ts +2 -2
  12. package/dist/controllers/registry.d.ts +21 -8
  13. package/dist/controllers/{side_entity_controller.d.ts → side_panel_controller.d.ts} +7 -7
  14. package/dist/controllers/storage.d.ts +39 -0
  15. package/dist/errors.d.ts +64 -0
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.es.js +160 -15
  18. package/dist/index.es.js.map +1 -1
  19. package/dist/rebase_context.d.ts +8 -4
  20. package/dist/types/auth_adapter.d.ts +29 -4
  21. package/dist/types/backend.d.ts +33 -26
  22. package/dist/types/backup.d.ts +20 -0
  23. package/dist/types/builders.d.ts +2 -2
  24. package/dist/types/collections.d.ts +84 -75
  25. package/dist/types/component_overrides.d.ts +61 -3
  26. package/dist/types/cron.d.ts +11 -2
  27. package/dist/types/data_source.d.ts +15 -2
  28. package/dist/types/database_adapter.d.ts +21 -4
  29. package/dist/types/entities.d.ts +7 -7
  30. package/dist/types/entity_actions.d.ts +7 -7
  31. package/dist/types/entity_callbacks.d.ts +39 -39
  32. package/dist/types/entity_views.d.ts +3 -3
  33. package/dist/types/filter-operators.d.ts +62 -17
  34. package/dist/types/index.d.ts +1 -0
  35. package/dist/types/modify_collections.d.ts +2 -2
  36. package/dist/types/plugins.d.ts +9 -9
  37. package/dist/types/policy.d.ts +111 -11
  38. package/dist/types/properties.d.ts +54 -9
  39. package/dist/types/relations.d.ts +3 -3
  40. package/dist/types/slots.d.ts +14 -14
  41. package/dist/types/translations.d.ts +5 -1
  42. package/dist/types/user_management_delegate.d.ts +8 -2
  43. package/dist/types/websockets.d.ts +46 -13
  44. package/dist/users/user.d.ts +21 -9
  45. package/package.json +5 -6
  46. package/src/controllers/auth.tsx +7 -1
  47. package/src/controllers/client.ts +235 -7
  48. package/src/controllers/collection_registry.ts +3 -3
  49. package/src/controllers/customization_controller.tsx +1 -1
  50. package/src/controllers/data.ts +330 -39
  51. package/src/controllers/data_driver.ts +93 -40
  52. package/src/controllers/email.ts +2 -2
  53. package/src/controllers/index.ts +1 -1
  54. package/src/controllers/local_config_persistence.tsx +4 -4
  55. package/src/controllers/navigation.ts +2 -2
  56. package/src/controllers/registry.ts +22 -8
  57. package/src/controllers/{side_entity_controller.tsx → side_panel_controller.tsx} +7 -7
  58. package/src/controllers/storage.ts +60 -1
  59. package/src/errors.ts +80 -0
  60. package/src/index.ts +1 -0
  61. package/src/rebase_context.tsx +8 -4
  62. package/src/types/auth_adapter.ts +29 -4
  63. package/src/types/backend.ts +44 -36
  64. package/src/types/backup.ts +26 -0
  65. package/src/types/builders.ts +2 -2
  66. package/src/types/collections.ts +101 -91
  67. package/src/types/component_overrides.ts +72 -5
  68. package/src/types/cron.ts +11 -2
  69. package/src/types/data_source.ts +24 -2
  70. package/src/types/database_adapter.ts +19 -4
  71. package/src/types/entities.ts +7 -7
  72. package/src/types/entity_actions.tsx +7 -7
  73. package/src/types/entity_callbacks.ts +42 -42
  74. package/src/types/entity_views.tsx +3 -3
  75. package/src/types/filter-operators.ts +96 -23
  76. package/src/types/index.ts +1 -0
  77. package/src/types/modify_collections.tsx +2 -2
  78. package/src/types/plugins.tsx +9 -9
  79. package/src/types/policy.ts +114 -9
  80. package/src/types/properties.ts +58 -9
  81. package/src/types/relations.ts +3 -3
  82. package/src/types/slots.tsx +14 -14
  83. package/src/types/translations.ts +5 -1
  84. package/src/types/user_management_delegate.ts +8 -2
  85. package/src/types/websockets.ts +43 -14
  86. package/src/users/user.ts +22 -9
  87. package/dist/index.umd.js +0 -458
  88. package/dist/index.umd.js.map +0 -1
@@ -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
  */
@@ -113,6 +161,14 @@ export interface CollectionAccessor<M extends Record<string, unknown> = Record<s
113
161
  */
114
162
  create(data: Partial<EntityValues<M>>, id?: string | number): Promise<Entity<M>>;
115
163
 
164
+ /**
165
+ * Create many records in a single transaction.
166
+ *
167
+ * See {@link SDKCollectionClient.createMany}. Optional: not every driver can
168
+ * write in bulk, and callers should fall back to `create` per record.
169
+ */
170
+ createMany?(data: Partial<EntityValues<M>>[], options?: { upsert?: boolean }): Promise<Entity<M>[]>;
171
+
116
172
  /**
117
173
  * Update an existing record by ID.
118
174
  * @returns The updated entity
@@ -124,11 +180,6 @@ export interface CollectionAccessor<M extends Record<string, unknown> = Record<s
124
180
  */
125
181
  delete(id: string | number): Promise<void>;
126
182
 
127
- /**
128
- * Delete all records in this collection.
129
- */
130
- deleteAll?(): Promise<void>;
131
-
132
183
  /**
133
184
  * Subscribe to a collection for real-time updates.
134
185
  * Optional method, may not be supported by all implementations (like stateless HTTP clients).
@@ -156,28 +207,192 @@ export interface CollectionAccessor<M extends Record<string, unknown> = Record<s
156
207
  include(...relations: string[]): QueryBuilderInterface<M>;
157
208
  }
158
209
 
210
+ // =============================================================================
211
+ // SDK-facing types — flat rows, no Entity wrapper
212
+ // =============================================================================
213
+
159
214
  /**
160
- * The unified data access object.
215
+ * Pagination metadata returned with collection queries.
216
+ * @group Data
217
+ */
218
+ export interface PaginationMeta {
219
+ total: number;
220
+ limit: number;
221
+ offset: number;
222
+ hasMore: boolean;
223
+ }
224
+
225
+ /**
226
+ * Paginated response from a collection query (SDK-facing).
227
+ * Returns flat rows instead of Entity-wrapped objects.
161
228
  *
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).
229
+ * @example
230
+ * const { data, meta } = await rebase.data.posts.find();
231
+ * console.log(data[0].title); // direct access no .values
232
+ * console.log(meta.total);
233
+ *
234
+ * @group Data
235
+ */
236
+ export interface FindResult<M extends Record<string, unknown> = Record<string, unknown>> {
237
+ /** Array of flat rows matching the query */
238
+ data: M[];
239
+ /** Pagination metadata */
240
+ meta: PaginationMeta;
241
+ }
242
+
243
+ /**
244
+ * Fluent Query Builder Interface for the SDK client.
245
+ * Returns `FindResult<M>` (flat rows) instead of `FindResponse<M>` (Entity-wrapped).
246
+ *
247
+ * @group Data
248
+ */
249
+ export interface SDKQueryBuilderInterface<M extends Record<string, unknown> = Record<string, unknown>> {
250
+ where<K extends keyof M & string>(column: K, operator: WhereFilterOp, value: WhereValue<M[K]>): this;
251
+ where(logicalCondition: LogicalCondition): this;
252
+ orderBy(column: keyof M & string, direction?: "asc" | "desc"): this;
253
+ limit(count: number): this;
254
+ offset(count: number): this;
255
+ search(searchString: string): this;
256
+ include(...relations: string[]): this;
257
+ find(): Promise<FindResult<M>>;
258
+ count(): Promise<number>;
259
+ listen(onUpdate: (data: FindResult<M>) => void, onError?: (error: Error) => void): () => void;
260
+ }
261
+
262
+ /**
263
+ * SDK collection client — returns flat rows, no Entity wrapper.
264
+ *
265
+ * This is the public API surface for app developers using
266
+ * `createRebaseClient()`. CMS internals use `CollectionAccessor` instead.
267
+ *
268
+ * Type parameters:
269
+ * - `M` — the **Row** shape returned by reads (`find`, `findById`, `listen`).
270
+ * - `I` — the **Insert** shape accepted by {@link create}. Defaults to
271
+ * `Partial<M>`; the generated SDK supplies a dedicated `Insert` type where
272
+ * required columns are required and auto-generated / read-only columns are
273
+ * omitted, so `create({})` on a table with required fields is a compile error.
274
+ * - `U` — the **Update** shape accepted by {@link update}. Defaults to
275
+ * `Partial<M>`; the generated SDK supplies a dedicated `Update` type.
165
276
  *
166
277
  * @example
167
- * // SDK
168
- * const client = createRebaseClient({ baseUrl: "..." });
169
- * await client.data.products.create({ name: "Camera", price: 299 });
278
+ * const { data: posts } = await rebase.data.posts.find();
279
+ * console.log(posts[0].title); // flat access
280
+ * console.log(posts[0].id); // id at top level
170
281
  *
171
- * // Framework callback
172
- * callbacks: {
173
- * afterSave({ context }) {
174
- * await context.data.logs.create({ action: "saved", timestamp: new Date() });
175
- * }
176
- * }
282
+ * const post = await rebase.data.posts.findById(1);
283
+ * console.log(post?.title); // no .values needed
284
+ *
285
+ * @group Data
286
+ */
287
+ export interface SDKCollectionClient<
288
+ M extends Record<string, unknown> = Record<string, unknown>,
289
+ I = Partial<M>,
290
+ U = Partial<M>
291
+ > {
292
+ /**
293
+ * Find multiple records with optional filtering, pagination, and sorting.
294
+ */
295
+ find(params?: FindParams): Promise<FindResult<M>>;
296
+
297
+ /**
298
+ * Find a single record by its ID.
299
+ */
300
+ findById(id: string | number): Promise<M | undefined>;
301
+
302
+ /**
303
+ * Create a new record.
304
+ * @param data The record data to create (the collection's `Insert` shape).
305
+ * @param id Optional specific id, sent as an `id` column. This is for tables
306
+ * whose key *is* `id`: the value goes in as that column. For a table keyed
307
+ * on anything else (a `sku`, a composite key), there is no `id` column to
308
+ * receive it — put the key in `data` instead, where it belongs among the
309
+ * columns.
310
+ * @returns The created row
311
+ */
312
+ create(data: I, id?: string | number): Promise<M>;
313
+
314
+ /**
315
+ * Write many records in a single request and a single transaction.
316
+ *
317
+ * Built for imports and ETL, where one call per row means one HTTP round
318
+ * trip and one transaction per row. Every record still runs the normal
319
+ * pipeline — callbacks, relations, row-level security — and the batch is
320
+ * all-or-nothing: if any record is rejected, none of them land and the
321
+ * error names the offending index.
322
+ *
323
+ * A record carrying its primary key updates that row; one without inserts.
324
+ * With `{ upsert: true }` each record is written as INSERT ... ON CONFLICT
325
+ * DO UPDATE on the primary key instead, which is what makes a re-runnable
326
+ * import idempotent.
327
+ *
328
+ * Batches are capped server-side (1000 rows by default) because one batch
329
+ * holds its locks for the whole transaction — chunk larger jobs.
330
+ *
331
+ * @returns The written rows, in the order given.
332
+ *
333
+ * @example
334
+ * ```ts
335
+ * for (const chunk of chunks(rows, 1000)) {
336
+ * await client.data.products.createMany(chunk, { upsert: true });
337
+ * }
338
+ * ```
339
+ */
340
+ createMany(data: I[], options?: { upsert?: boolean }): Promise<M[]>;
341
+
342
+ /**
343
+ * Update an existing record by ID.
344
+ * @param data The fields to update (the collection's `Update` shape).
345
+ * @returns The updated row.
346
+ * @throws {RebaseApiError} with status 404 when the record does not exist.
347
+ */
348
+ update(id: string | number, data: U): Promise<M>;
349
+
350
+ /**
351
+ * Delete a record by ID.
352
+ * @throws {RebaseApiError} with status 404 when the record does not exist.
353
+ */
354
+ delete(id: string | number): Promise<void>;
355
+
356
+ /**
357
+ * Subscribe to a collection for real-time updates.
358
+ */
359
+ listen?(params: FindParams | undefined, onUpdate: (response: FindResult<M>) => void, onError?: (error: Error) => void): () => void;
360
+
361
+ /**
362
+ * Subscribe to a single record for real-time updates.
363
+ */
364
+ listenById?(id: string | number, onUpdate: (row: M | undefined) => void, onError?: (error: Error) => void): () => void;
365
+
366
+ /**
367
+ * Count the number of records matching the given filter.
368
+ */
369
+ count?(params?: FindParams): Promise<number>;
370
+
371
+ // Fluent Query Builder
372
+ where<K extends keyof M & string>(column: K, operator: WhereFilterOp, value: WhereValue<M[K]>): SDKQueryBuilderInterface<M>;
373
+ where(logicalCondition: LogicalCondition): SDKQueryBuilderInterface<M>;
374
+ orderBy(column: keyof M & string, direction?: "asc" | "desc"): SDKQueryBuilderInterface<M>;
375
+ limit(count: number): SDKQueryBuilderInterface<M>;
376
+ offset(count: number): SDKQueryBuilderInterface<M>;
377
+ search(searchString: string): SDKQueryBuilderInterface<M>;
378
+ include(...relations: string[]): SDKQueryBuilderInterface<M>;
379
+ }
380
+
381
+ /**
382
+ * The unified data access object for the **admin CMS** (Entity-shaped).
383
+ *
384
+ * Access collections as dynamic properties: `data.products.find(...)`. Each
385
+ * accessor returns `Entity`-wrapped records (`{ id, path, values }`) — the
386
+ * view-model the CMS renders. This is what `useData()` / the admin
387
+ * `RebaseContext.data` are backed by.
388
+ *
389
+ * @internal App developers do **not** use this — they use
390
+ * {@link RebaseSdkData} (flat rows), which is what the SDK client and backend
391
+ * `context.data` expose. This Entity-shaped map backs the admin CMS only.
177
392
  *
178
393
  * @group Data
179
394
  */
180
- export interface RebaseData {
395
+ export type RebaseData<DB = unknown> = {
181
396
  /**
182
397
  * Get a collection accessor by slug.
183
398
  * Alternative to dynamic property access for cases where
@@ -188,13 +403,89 @@ export interface RebaseData {
188
403
  * await accessor.find({ limit: 10 });
189
404
  */
190
405
  collection<M extends Record<string, unknown> = Record<string, unknown>>(slug: string): CollectionAccessor<M>;
406
+ } & (
407
+ DB extends Record<string, unknown>
408
+ ? { [K in keyof DB]: CollectionAccessor<DB[K] extends { Row: infer R extends Record<string, unknown> } ? R : Record<string, unknown>> }
409
+ : {
410
+ /**
411
+ * Dynamic collection accessor.
412
+ * Access any collection by its slug as a property.
413
+ *
414
+ * @example
415
+ * data.products.find({ where: { status: ["==", "published"] } })
416
+ */
417
+ [collectionSlug: string]: CollectionAccessor | ((slug: string) => CollectionAccessor);
418
+ }
419
+ );
191
420
 
421
+ /**
422
+ * The unified data access object for the **SDK** — flat rows, no Entity wrapper.
423
+ *
424
+ * This is the symmetric developer-facing data API, identical in shape on both
425
+ * sides of the stack:
426
+ * - The frontend SDK client (`client.data.products.find()`)
427
+ * - Backend framework callbacks & scripts (`context.data.products.find()`)
428
+ *
429
+ * Every accessor returns flat rows (the table's columns) via
430
+ * {@link SDKCollectionClient} — access fields directly (`row.title`), never
431
+ * `row.values.title`. The admin CMS uses {@link RebaseData} (Entity) instead.
432
+ *
433
+ * @example
434
+ * // Frontend SDK
435
+ * const { data: posts } = await client.data.posts.find();
436
+ * console.log(posts[0].title); // flat — no .values
437
+ *
438
+ * // Backend callback — identical shape
439
+ * callbacks: {
440
+ * beforeSave: async ({ context }) => {
441
+ * const product = await context.data.products.findById(id);
442
+ * console.log(product?.price); // flat — no .values
443
+ * }
444
+ * }
445
+ *
446
+ * @group Data
447
+ */
448
+ /**
449
+ * Extract the `Row` shape from a generated `Database[slug]` entry, falling
450
+ * back to an open record when the entry is untyped.
451
+ * @group Data
452
+ */
453
+ export type RowOf<T> = T extends { Row: infer R extends Record<string, unknown> } ? R : Record<string, unknown>;
454
+
455
+ /**
456
+ * Extract the `Insert` shape from a generated `Database[slug]` entry (the
457
+ * input accepted by `create`), falling back to `Partial<Row>`.
458
+ * @group Data
459
+ */
460
+ export type InsertOf<T> = T extends { Insert: infer I extends Record<string, unknown> } ? I : Partial<RowOf<T>>;
461
+
462
+ /**
463
+ * Extract the `Update` shape from a generated `Database[slug]` entry (the
464
+ * input accepted by `update`), falling back to `Partial<Row>`.
465
+ * @group Data
466
+ */
467
+ export type UpdateOf<T> = T extends { Update: infer U extends Record<string, unknown> } ? U : Partial<RowOf<T>>;
468
+
469
+ export type RebaseSdkData<DB = unknown> = {
192
470
  /**
193
- * Dynamic collection accessor.
194
- * Access any collection by its slug as a property.
471
+ * Get a flat collection accessor by slug.
195
472
  *
196
473
  * @example
197
- * data.products.find({ where: { status: "eq.published" } })
474
+ * const accessor = data.collection("products");
475
+ * await accessor.find({ limit: 10 });
198
476
  */
199
- [collectionSlug: string]: CollectionAccessor | ((slug: string) => CollectionAccessor);
200
- }
477
+ collection<M extends Record<string, unknown> = Record<string, unknown>>(slug: string): SDKCollectionClient<M>;
478
+ } & (
479
+ DB extends Record<string, unknown>
480
+ ? { [K in keyof DB]: SDKCollectionClient<RowOf<DB[K]>, InsertOf<DB[K]>, UpdateOf<DB[K]>> }
481
+ : {
482
+ /**
483
+ * Dynamic flat collection accessor.
484
+ * Access any collection by its slug as a property.
485
+ *
486
+ * @example
487
+ * data.products.find({ where: { status: ["==", "published"] } })
488
+ */
489
+ [collectionSlug: string]: SDKCollectionClient | ((slug: string) => SDKCollectionClient);
490
+ }
491
+ );