@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,27 +1,29 @@
1
1
  import React, { Dispatch, SetStateAction } from "react";
2
2
  import type { Entity, EntityStatus, EntityValues } from "./entities";
3
- import type { EntityCallbacks } from "./entity_callbacks";
3
+ import type { CollectionCallbacks } from "./entity_callbacks";
4
4
 
5
- import type { EnumValues, Properties } from "./properties";
5
+ import type { EnumValues, Properties, PostgresProperties, FirebaseProperties, MongoProperties } from "./properties";
6
6
  import type { ExportConfig } from "./export_import";
7
- import type { EntityOverrides } from "./entity_overrides";
7
+
8
8
  import type { User } from "../users";
9
9
  import type { RebaseContext } from "../rebase_context";
10
10
  import type { Relation } from "./relations";
11
11
  import type { EntityCustomView, FormViewConfig } from "./entity_views";
12
12
  import type { EntityAction } from "./entity_actions";
13
+ import type { PolicyExpression } from "./policy";
13
14
  import type { ComponentRef } from "./component_ref";
14
15
  import type { CollectionComponentOverrideMap } from "./component_overrides";
16
+ import type { WhereFilterOp, FilterValues, FilterPreset, OrderByTuple } from "./filter-operators";
15
17
 
16
18
  /**
17
19
  * Base interface containing all driver-agnostic collection properties.
18
- * Use {@link PostgresCollection} or {@link FirebaseCollection} for
19
- * driver-specific type safety, or {@link EntityCollection} when you
20
+ * Use {@link PostgresCollectionConfig} or {@link FirebaseCollectionConfig} for
21
+ * driver-specific type safety, or {@link CollectionConfig} when you
20
22
  * need to handle any collection regardless of backend.
21
23
  *
22
24
  * @group Models
23
25
  */
24
- export interface BaseEntityCollection<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User> {
26
+ export interface BaseCollectionConfig<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User> {
25
27
 
26
28
  /**
27
29
  * You can set an alias that will be used internally instead of the collection name.
@@ -54,14 +56,14 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
54
56
  *
55
57
  * Custom drivers can set this directly to expose child collections to the UI.
56
58
  */
57
- childCollections?: () => EntityCollection<Record<string, unknown>>[];
59
+ childCollections?: () => CollectionConfig<Record<string, unknown>>[];
58
60
 
59
61
 
60
62
  /**
61
63
  * The data source this collection belongs to — the routing key shared by
62
64
  * the frontend router and the backend driver registry. It points at a
63
65
  * {@link DataSourceDefinition} registered on `<Rebase dataSources>` (front)
64
- * and `initRebase({ dataSources })` (back).
66
+ * and `initializeRebaseBackend({ dataSources })` (back).
65
67
  *
66
68
  * If not specified, the default data source `"(default)"` is used, which
67
69
  * for a standard Rebase app is the server-mediated Postgres backend.
@@ -76,17 +78,18 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
76
78
  dataSource?: string;
77
79
 
78
80
  /**
79
- * The engine backing this collection (`"postgres"`, `"firestore"`,
80
- * `"mongodb"`, or a custom id). Drives editor capabilities (relations vs
81
- * subcollections, RLS, column types).
81
+ * The database engine backing this collection (`"postgres"`, `"firestore"`,
82
+ * `"mongodb"`, or a custom id).
82
83
  *
83
- * @deprecated Prefer {@link dataSource} for routing. `driver` is retained
84
- * as an engine hint and for backward compatibility: when `dataSource` is
85
- * omitted it also acts as the data-source key.
84
+ * On concrete collection types ({@link PostgresCollectionConfig},
85
+ * {@link FirebaseCollectionConfig}, {@link MongoDBCollectionConfig}) this is a literal
86
+ * discriminant. On the base type it is optional and gets stamped
87
+ * automatically during collection normalization from the registered
88
+ * {@link DataSourceDefinition}.
86
89
  *
87
- * If not specified, `"postgres"` is assumed.
90
+ * Prefer setting {@link dataSource} and letting the engine be resolved.
88
91
  */
89
- driver?: string;
92
+ engine?: string;
90
93
 
91
94
  /**
92
95
  * Which database within the engine.
@@ -100,7 +103,7 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
100
103
  databaseId?: string;
101
104
 
102
105
  /**
103
- * Set of properties that compose an entity
106
+ * Set of properties that compose a entity
104
107
  */
105
108
  properties: Properties;
106
109
 
@@ -142,13 +145,13 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
142
145
  readonly titleProperty?: Extract<keyof M, string> | (string & {});
143
146
 
144
147
  /**
145
- * When editing an entity, you can choose to open the entity in a side dialog
148
+ * When editing a entity, you can choose to open the entity in a side dialog
146
149
  * or in a full screen dialog. Defaults to `full_screen`.
147
150
  */
148
151
  openEntityMode?: "side_panel" | "full_screen" | "split" | "dialog";
149
152
 
150
153
  /**
151
- * Controls what happens when a user clicks on an entity in the collection view.
154
+ * Controls what happens when a user clicks on a entity in the collection view.
152
155
  * - `"edit"` (default): Opens the entity in the edit form.
153
156
  * - `"view"`: Opens a read-only detail view with an "Edit" button.
154
157
  */
@@ -201,19 +204,18 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
201
204
  * If you are specifying your collection as code, the order is the same as the
202
205
  * one you define in `properties`. Additional columns are added at the
203
206
  * end of the list, if the order is not specified.
207
+ *
204
208
  * You can use this prop to hide some properties from the table view.
205
209
  * Note that if you set this prop, other ways to hide fields, like
206
210
  * `hidden` in the property definition, will be ignored.
207
211
  * `propertiesOrder` has precedence over `hidden`.
208
- * - For properties use the property key.
209
- * - For additional fields use the field key.
210
- * - If you have subcollections, you get a column for each subcollection,
211
- * with the path (or alias) as the subcollection, prefixed with
212
- * `subcollection:`. e.g. `subcollection:orders`.
213
- * You can use this prop to hide some properties from the table view.
214
- * Note that if you set this prop, other ways to hide fields, like
215
- * `hidden` in the property definition,will be ignored.
216
- * `propertiesOrder` has precedence over `hidden`.
212
+ *
213
+ * Supported entry formats:
214
+ * - For properties, use the property key.
215
+ * - For additional fields, use the field key.
216
+ * - Child collections (Firestore subcollections, or Postgres relations
217
+ * with `many` cardinality) each get a column with id
218
+ * `subcollection:<slug>`, e.g. `subcollection:orders`.
217
219
  */
218
220
  propertiesOrder?: (Extract<keyof M, string> | (string & {}) | string | `subcollection:${string}`)[];
219
221
 
@@ -230,11 +232,11 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
230
232
  selectionEnabled?: boolean;
231
233
 
232
234
  /**
233
- * This interface defines all the callbacks that can be used when an entity
235
+ * This interface defines all the callbacks that can be used when a entity
234
236
  * is being created, updated or deleted.
235
237
  * Useful for adding your own logic or blocking the execution of the operation.
236
238
  */
237
- readonly callbacks?: EntityCallbacks<M, USER>;
239
+ readonly callbacks?: CollectionCallbacks<M, USER>;
238
240
 
239
241
  /**
240
242
  * Pass your own selection controller if you want to control selected
@@ -284,7 +286,7 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
284
286
  * applied in the collection.
285
287
  * e.g. `sort: ["order", "asc"]`
286
288
  */
287
- readonly sort?: [Extract<keyof M, string> | (string & {}), "asc" | "desc"];
289
+ readonly sort?: OrderByTuple<Extract<keyof M, string> | (string & {})>;
288
290
 
289
291
  /**
290
292
  * You can add additional fields to the collection view by implementing
@@ -299,8 +301,8 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
299
301
 
300
302
  /**
301
303
  * Can the elements in this collection be edited inline in the collection
302
- * view. If this flag is set to false but `permissions.edit` is `true`, entities
303
- * can still be edited in the side panel
304
+ * view. Even when inline editing is disabled, entities can still be
305
+ * edited in the side panel (subject to `securityRules`).
304
306
  */
305
307
  inlineEditing?: boolean;
306
308
 
@@ -315,7 +317,7 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
315
317
 
316
318
  /**
317
319
  * If you want to open custom views or subcollections by default when opening the edit
318
- * view of an entity, you can specify the path to the view here.
320
+ * view of a entity, you can specify the path to the view here.
319
321
  * The path is relative to the current collection. For example if you have a collection
320
322
  * that has a custom view as well as a subcollection that refers to another entity, you can
321
323
  * either specify the path to the custom view or the path to the subcollection.
@@ -359,12 +361,7 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
359
361
  metadata?: Record<string, unknown>;
360
362
 
361
363
  /**
362
- * Overrides for the entity view, like the data source or the storage source.
363
- */
364
- overrides?: EntityOverrides;
365
-
366
- /**
367
- * Width of the side dialog (in pixels) when opening an entity in this collection.
364
+ * Width of the side dialog (in pixels) when opening a entity in this collection.
368
365
  */
369
366
  sideDialogWidth?: number | string;
370
367
 
@@ -389,9 +386,9 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
389
386
  /**
390
387
  * Should local changes be backed up in local storage, to prevent data loss on
391
388
  * accidental navigations.
392
- * - `manual_apply`: When the user navigates back to an entity with local changes,
389
+ * - `manual_apply`: When the user navigates back to a entity with local changes,
393
390
  * they will be prompted to restore the changes.
394
- * - `auto_apply`: When the user navigates back to an entity with local changes,
391
+ * - `auto_apply`: When the user navigates back to a entity with local changes,
395
392
  * the changes will be automatically applied.
396
393
  * - `false`: Local changes will not be backed up.
397
394
  * Defaults to `manual_apply`.
@@ -460,7 +457,7 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
460
457
  * Security rules for this collection (Row Level Security).
461
458
  * When defined, the backend enforces access control policies.
462
459
  */
463
- securityRules?: SecurityRule[];
460
+ securityRules?: readonly SecurityRule[];
464
461
 
465
462
  /**
466
463
  * Collection-scoped component overrides. These take precedence over
@@ -473,7 +470,7 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
473
470
  *
474
471
  * @example
475
472
  * ```tsx
476
- * const productsCollection: PostgresCollection = {
473
+ * const productsCollection: PostgresCollectionConfig = {
477
474
  * name: "Products",
478
475
  * slug: "products",
479
476
  * table: "products",
@@ -494,20 +491,20 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
494
491
  * A collection backed by PostgreSQL (or any SQL database).
495
492
  * Adds support for SQL-style relations (JOINs) and Row Level Security.
496
493
  *
497
- * Use this type instead of {@link EntityCollection} when you want
494
+ * Use this type instead of {@link CollectionConfig} when you want
498
495
  * compile-time safety that only SQL-relevant fields appear.
499
496
  *
500
497
  * @group Models
501
498
  */
502
- export interface PostgresCollection<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>
503
- extends BaseEntityCollection<M, USER> {
504
- properties: Properties;
499
+ export interface PostgresCollectionConfig<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>
500
+ extends BaseCollectionConfig<M, USER> {
501
+ properties: PostgresProperties;
505
502
 
506
503
  /**
507
- * The driver for this collection. For Postgres collections this
504
+ * The database engine for this collection. For Postgres collections this
508
505
  * can be omitted (Postgres is the default) or set to `"postgres"`.
509
506
  */
510
- driver?: "postgres" | undefined;
507
+ engine?: "postgres" | undefined;
511
508
 
512
509
  /**
513
510
  * The PostgreSQL table name for this collection.
@@ -528,7 +525,7 @@ export interface PostgresCollection<M extends Record<string, unknown> = Record<s
528
525
  relations?: Relation[];
529
526
 
530
527
  /**
531
- * Security rules for this collection (Supabase-style Row Level Security).
528
+ * Security rules for this collection (PostgreSQL Row Level Security).
532
529
  * When defined, the schema generator will enable RLS on the table and
533
530
  * create the corresponding PostgreSQL policies.
534
531
  *
@@ -542,110 +539,173 @@ export interface PostgresCollection<M extends Record<string, unknown> = Record<s
542
539
  * - `auth.roles()` — comma-separated app role IDs
543
540
  * - `auth.jwt()` — full JWT claims as JSONB
544
541
  */
545
- securityRules?: SecurityRule[];
542
+ securityRules?: readonly SecurityRule[];
546
543
  }
547
544
 
548
545
  /**
549
546
  * A collection backed by Firebase / Firestore.
550
547
  * Adds support for subcollections (nested document collections).
551
548
  *
552
- * Use this type instead of {@link EntityCollection} when you want
549
+ * Use this type instead of {@link CollectionConfig} when you want
553
550
  * compile-time safety that only Firestore-relevant fields appear.
554
551
  *
555
552
  * @group Models
556
553
  */
557
- export interface FirebaseCollection<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>
558
- extends BaseEntityCollection<M, USER> {
554
+ export interface FirebaseCollectionConfig<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>
555
+ extends BaseCollectionConfig<M, USER> {
556
+ /**
557
+ * The database engine for this collection. Must be set to `"firestore"`.
558
+ */
559
+ engine: "firestore";
560
+
561
+ /**
562
+ * Set of properties that compose a entity.
563
+ * Firestore collections support `reference` properties but not `relation`.
564
+ */
565
+ properties: FirebaseProperties;
566
+
559
567
  /**
560
- * The driver for this collection. Must be set to `"firestore"`.
568
+ * The Firestore collection path to query. Defaults to `slug` if not set.
569
+ * Use this when the Firestore path differs from the slug
570
+ * (e.g., when a PostgreSQL collection already uses the same slug).
571
+ *
572
+ * @example
573
+ * ```typescript
574
+ * const fsCustomer: FirebaseCollectionConfig = {
575
+ * slug: "fs_customer", // URL: /c/fs_customer
576
+ * path: "customer", // Firestore path: customer
577
+ * name: "Customers (Firestore)",
578
+ * engine: "firestore",
579
+ * properties: { ... }
580
+ * };
581
+ * ```
561
582
  */
562
- driver: "firestore";
583
+ path?: string;
563
584
 
564
585
  /**
565
586
  * You can add subcollections to your entity in the same way you define the root
566
587
  * collections. The collections added here will be displayed when opening
567
- * the side dialog of an entity.
588
+ * the side dialog of a entity.
568
589
  */
569
- subcollections?: () => EntityCollection<Record<string, unknown>>[];
590
+ subcollections?: () => CollectionConfig<Record<string, unknown>>[];
570
591
  }
571
592
 
572
593
  /**
573
594
  * A collection backed by MongoDB.
574
595
  *
575
- * Use this type instead of {@link EntityCollection} when you want
596
+ * Use this type instead of {@link CollectionConfig} when you want
576
597
  * compile-time safety that only MongoDB-relevant fields appear.
577
598
  *
578
599
  * @group Models
579
600
  */
580
- export interface MongoDBCollection<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>
581
- extends BaseEntityCollection<M, USER> {
601
+ export interface MongoDBCollectionConfig<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>
602
+ extends BaseCollectionConfig<M, USER> {
603
+
604
+ /**
605
+ * The database engine for this collection. Must be set to `"mongodb"`.
606
+ */
607
+ engine: "mongodb";
582
608
 
583
609
  /**
584
- * The driver for this collection. Must be set to `"mongodb"`.
610
+ * Set of properties that compose a entity.
611
+ * MongoDB collections support `reference` properties but not `relation`.
585
612
  */
586
- driver: "mongodb";
613
+ properties: MongoProperties;
614
+
615
+ /**
616
+ * The MongoDB collection name to use. Defaults to `slug` if not set.
617
+ * Use this when the MongoDB collection name differs from the slug
618
+ * (e.g., when a PostgreSQL collection already uses the same slug).
619
+ *
620
+ * @example
621
+ * ```typescript
622
+ * const mongoCustomer: MongoDBCollectionConfig = {
623
+ * slug: "mongo_customer", // URL: /c/mongo_customer
624
+ * path: "customer", // MongoDB collection: customer
625
+ * name: "Customers (MongoDB)",
626
+ * engine: "mongodb",
627
+ * properties: { ... }
628
+ * };
629
+ * ```
630
+ */
631
+ path?: string;
587
632
  }
588
633
 
589
634
  /**
590
635
  * A collection backed by any data source.
591
- * This is a discriminated union — use {@link PostgresCollection},
592
- * {@link FirebaseCollection}, or {@link MongoDBCollection} for
636
+ * This is a discriminated union — use {@link PostgresCollectionConfig},
637
+ * {@link FirebaseCollectionConfig}, or {@link MongoDBCollectionConfig} for
593
638
  * driver-specific type safety.
594
639
  *
595
640
  * @group Models
596
641
  */
597
- export type EntityCollection<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User> =
598
- | PostgresCollection<M, USER>
599
- | FirebaseCollection<M, USER>
600
- | MongoDBCollection<M, USER>;
601
-
602
- // ── Capability intersection types ─────────────────────────────────────
603
- // Use these after a `getDataSourceCapabilities()` guard to safely access
604
- // driver-specific fields without coupling to a concrete driver type.
605
-
606
- /**
607
- * An EntityCollection that supports SQL-style relations (e.g. Postgres).
608
- * @deprecated Use `EntityCollection` directly — `table`, `relations`, and `securityRules` are now on `BaseEntityCollection`.
609
- */
610
- export type CollectionWithRelations<M extends Record<string, unknown> = Record<string, unknown>> =
611
- EntityCollection<M> & { table?: string; relations?: Relation[]; securityRules?: SecurityRule[] };
612
-
613
- /** An EntityCollection that supports subcollections (e.g. Firestore). */
614
- export type CollectionWithSubcollections<M extends Record<string, unknown> = Record<string, unknown>> =
615
- EntityCollection<M> & { subcollections?: () => EntityCollection<Record<string, unknown>>[] };
616
-
617
-
618
- // ── Type guards ───────────────────────────────────────────────────────
642
+ export type CollectionConfig<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User> =
643
+ | PostgresCollectionConfig<M, USER>
644
+ | FirebaseCollectionConfig<M, USER>
645
+ | MongoDBCollectionConfig<M, USER>;
619
646
 
620
647
  /**
621
648
  * Type guard for PostgreSQL collections.
622
- * Returns true if the collection uses the Postgres driver (or the default driver).
649
+ * Returns true if the collection uses the Postgres engine (or the default engine).
623
650
  * @group Models
624
651
  */
625
- export function isPostgresCollection<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>(
626
- collection: EntityCollection<M, USER>
627
- ): collection is PostgresCollection<M, USER> {
628
- return !collection.driver || collection.driver === "postgres";
652
+ export function isPostgresCollectionConfig<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>(
653
+ collection: CollectionConfig<M, USER>
654
+ ): collection is PostgresCollectionConfig<M, USER> {
655
+ return !collection.engine || collection.engine === "postgres";
629
656
  }
630
657
 
631
658
  /**
632
659
  * Type guard for Firebase / Firestore collections.
633
660
  * @group Models
634
661
  */
635
- export function isFirebaseCollection<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>(
636
- collection: EntityCollection<M, USER>
637
- ): collection is FirebaseCollection<M, USER> {
638
- return collection.driver === "firestore";
662
+ export function isFirebaseCollectionConfig<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>(
663
+ collection: CollectionConfig<M, USER>
664
+ ): collection is FirebaseCollectionConfig<M, USER> {
665
+ return collection.engine === "firestore";
639
666
  }
640
667
 
641
668
  /**
642
669
  * Type guard for MongoDB collections.
643
670
  * @group Models
644
671
  */
645
- export function isMongoDBCollection<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>(
646
- collection: EntityCollection<M, USER>
647
- ): collection is MongoDBCollection<M, USER> {
648
- return collection.driver === "mongodb";
672
+ export function isMongoDBCollectionConfig<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>(
673
+ collection: CollectionConfig<M, USER>
674
+ ): collection is MongoDBCollectionConfig<M, USER> {
675
+ return collection.engine === "mongodb";
676
+ }
677
+
678
+ /**
679
+ * Returns the data path for a collection.
680
+ * For Firestore or MongoDB collections with a `path`, returns that value;
681
+ * otherwise falls back to `slug`.
682
+ */
683
+ export function getCollectionDataPath<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>(
684
+ collection: CollectionConfig<M, USER>
685
+ ): string {
686
+ if (isFirebaseCollectionConfig(collection) && collection.path) {
687
+ return collection.path;
688
+ }
689
+ if (isMongoDBCollectionConfig(collection) && collection.path) {
690
+ return collection.path;
691
+ }
692
+ return collection.slug;
693
+ }
694
+
695
+ /**
696
+ * Reads a collection's driver-declared subcollections thunk (the `subcollections`
697
+ * field) independent of engine identity, so engine-agnostic code doesn't have to
698
+ * type-guard against a specific driver. Returns `undefined` when the collection
699
+ * declares none.
700
+ *
701
+ * Pair with `getDataSourceCapabilities(engine).supportsSubcollections` to decide
702
+ * whether the engine honours subcollections at all before reading them.
703
+ * @group Models
704
+ */
705
+ export function getDeclaredSubcollections<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>(
706
+ collection: CollectionConfig<M, USER>
707
+ ): (() => CollectionConfig<Record<string, unknown>>[]) | undefined {
708
+ return (collection as FirebaseCollectionConfig<M, USER>).subcollections;
649
709
  }
650
710
 
651
711
 
@@ -680,7 +740,7 @@ export type ViewMode = "list" | "table" | "cards" | "kanban";
680
740
  *
681
741
  * @group Models
682
742
  */
683
- export interface CollectionActionsProps<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User, EC extends EntityCollection<M> = EntityCollection<M>> {
743
+ export interface CollectionActionsProps<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User, EC extends CollectionConfig<M> = CollectionConfig<M>> {
684
744
  /**
685
745
  * Full collection path of this entity. This is the full path, like
686
746
  * `users/1234/addresses`
@@ -746,7 +806,7 @@ export interface CollectionActionsProps<M extends Record<string, unknown> = Reco
746
806
 
747
807
  /**
748
808
  * Use this controller to retrieve the selected entities or modify them in
749
- * an {@link EntityCollection}
809
+ * an {@link CollectionConfig}
750
810
  * @group Models
751
811
  */
752
812
  export interface SelectionController<M extends Record<string, unknown> = Record<string, unknown>> {
@@ -757,57 +817,8 @@ export interface SelectionController<M extends Record<string, unknown> = Record<
757
817
  toggleEntitySelection(entity: Entity<M>, newSelectedState?: boolean): void;
758
818
  }
759
819
 
760
- /**
761
- * Filter conditions in a `Query.where()` clause are specified using the
762
- * strings `<`, `<=`, `==`, `>=`, `>`, `array-contains`, `in`, and `array-contains-any`.
763
- * @group Models
764
- */
765
- export type WhereFilterOp =
766
- | "<"
767
- | "<="
768
- | "=="
769
- | "!="
770
- | ">="
771
- | ">"
772
- | "array-contains"
773
- | "in"
774
- | "not-in"
775
- | "array-contains-any";
776
-
777
- /**
778
- * Used to define filters applied in collections
779
- *
780
- * e.g. `{ age: [">=", 18] }`
781
- *
782
- * @group Models
783
- */
784
- export type FilterValues<Key extends string> =
785
- Partial<Record<Key, [WhereFilterOp, unknown] | [WhereFilterOp, unknown][]>>;
786
-
787
- /**
788
- * A pre-defined filter preset for quick access in the collection toolbar.
789
- * Users can select a preset to instantly apply a set of filters and
790
- * optionally a sort order.
791
- *
792
- * @group Models
793
- */
794
- export interface FilterPreset<Key extends string = string> {
795
- /**
796
- * Display label shown in the preset menu.
797
- * If omitted, a summary is auto-generated from the filter keys.
798
- */
799
- label?: string;
800
-
801
- /**
802
- * The filter values to apply when this preset is selected.
803
- */
804
- filterValues: FilterValues<Key>;
805
-
806
- /**
807
- * Optional sort override to apply alongside the filter values.
808
- */
809
- sort?: [Key, "asc" | "desc"];
810
- }
820
+ // Canonical filter types — re-exported from the single source-of-truth.
821
+ export type { WhereFilterOp, FilterValues, WireFilterValues, FilterPreset } from "./filter-operators";
811
822
 
812
823
 
813
824
  /**
@@ -882,16 +893,16 @@ export interface AdditionalFieldDelegate<M extends Record<string, unknown> = Rec
882
893
  }
883
894
 
884
895
 
885
- export type InferCollectionType<S extends EntityCollection> = S extends EntityCollection<infer M> ? M : never;
896
+ export type InferCollectionConfigType<S extends CollectionConfig> = S extends CollectionConfig<infer M> ? M : never;
886
897
 
887
898
  /**
888
- * Used in the {@link EntityCollection#defaultSelectedView} to define the default
899
+ * Used in the {@link CollectionConfig#defaultSelectedView} to define the default
889
900
  * @group Models
890
901
  */
891
902
  export type DefaultSelectedViewBuilder = (params: DefaultSelectedViewParams) => string | undefined;
892
903
 
893
904
  /**
894
- * Used in the {@link EntityCollection#defaultSelectedView} to define the default
905
+ * Used in the {@link CollectionConfig#defaultSelectedView} to define the default
895
906
  * @group Models
896
907
  */
897
908
  export type DefaultSelectedViewParams = {
@@ -949,7 +960,7 @@ export type SecurityOperation = "select" | "insert" | "update" | "delete" | "all
949
960
  /**
950
961
  * Flexible Row Level Security rule for a collection.
951
962
  *
952
- * Inspired by Supabase's approach to PostgreSQL RLS. Rules can range from
963
+ * Built on PostgreSQL Row Level Security. Rules can range from
953
964
  * simple convenience shortcuts to fully custom SQL expressions, giving you the
954
965
  * full power of PostgreSQL Row Level Security.
955
966
  *
@@ -962,15 +973,28 @@ export type SecurityOperation = "select" | "insert" | "update" | "delete" | "all
962
973
  *
963
974
  * **How rules combine:** PostgreSQL evaluates all matching policies for an
964
975
  * operation. Permissive rules are OR'd together (any one passing is enough).
965
- * Restrictive rules are AND'd (all must pass). This mirrors Supabase behavior.
976
+ * Restrictive rules are AND'd (all must pass). This is standard PostgreSQL RLS behavior.
966
977
  *
967
- * **Mutual exclusivity:** `ownerField`, `access`, and raw SQL (`using`/`withCheck`)
968
- * cannot be combined. The type system enforces this — attempting to set
969
- * conflicting fields will produce a compile-time error.
978
+ * **Mutual exclusivity:** `ownerField`, `access`, structured `condition`, and
979
+ * raw SQL (`using`/`withCheck`) cannot be combined. The type system enforces
980
+ * this — attempting to set conflicting fields will produce a compile-time
981
+ * error.
982
+ *
983
+ * **Which form to reach for:** prefer the structured {@link StructuredSecurityRule}
984
+ * (`condition`/`check`) or the shortcuts (`ownerField`, `access`, `roles`). These
985
+ * are engine-agnostic and evaluated identically by the database and the admin UI,
986
+ * so the UI never shows an action the database will reject. Raw SQL
987
+ * ({@link RawSQLSecurityRule}) keeps full PostgreSQL power but is Postgres-only
988
+ * and server-authoritative (the UI cannot evaluate arbitrary SQL locally).
970
989
  *
971
990
  * @group Models
972
991
  */
973
- export type SecurityRule = OwnerSecurityRule | PublicSecurityRule | RawSQLSecurityRule | RolesOnlySecurityRule;
992
+ export type SecurityRule =
993
+ | OwnerSecurityRule
994
+ | PublicSecurityRule
995
+ | StructuredSecurityRule
996
+ | RawSQLSecurityRule
997
+ | RolesOnlySecurityRule;
974
998
 
975
999
  /**
976
1000
  * Shared fields for all SecurityRule variants.
@@ -1016,7 +1040,7 @@ export interface SecurityRuleBase {
1016
1040
  * // Equivalent to operation: "all"
1017
1041
  * { operations: ["all"], ownerField: "user_id" }
1018
1042
  */
1019
- operations?: SecurityOperation[];
1043
+ operations?: readonly SecurityOperation[];
1020
1044
 
1021
1045
  /**
1022
1046
  * Whether this policy is `"permissive"` (default) or `"restrictive"`.
@@ -1026,7 +1050,7 @@ export interface SecurityRuleBase {
1026
1050
  * - **restrictive**: Restrictive policies are AND'd with all permissive
1027
1051
  * policies — they act as additional gates that *must* also pass.
1028
1052
  *
1029
- * This is the same model as PostgreSQL / Supabase.
1053
+ * This is the standard PostgreSQL RLS model.
1030
1054
  *
1031
1055
  * @default "permissive"
1032
1056
  */
@@ -1040,11 +1064,16 @@ export interface SecurityRuleBase {
1040
1064
  * application roles managed by Rebase, stored in the `rebase.user_roles`
1041
1065
  * table, and injected into each transaction via `auth.roles()`.
1042
1066
  *
1043
- * Generates a condition like:
1044
- * `auth.roles() ~ '<role1>|<role2>'`
1067
+ * Generates a safe array-overlap condition — the user passes if they hold
1068
+ * *any* of the listed roles:
1069
+ * `string_to_array(auth.roles(), ',') && ARRAY['<role1>', '<role2>']`
1070
+ *
1071
+ * (Note: this is a true set intersection, NOT a regex/substring match, so
1072
+ * a role named `admin` never matches `nonadmin` or `superadmin`.)
1045
1073
  *
1046
- * Can be combined with `ownerField`, `access`, or raw `using`/`withCheck`.
1047
- * When combined, the role check is AND'd with the other condition.
1074
+ * Can be combined with `ownerField`, `access`, `condition`, or raw
1075
+ * `using`/`withCheck`. When combined, the role check is AND'd with the
1076
+ * other condition.
1048
1077
  *
1049
1078
  * @example
1050
1079
  * // Only admins can delete
@@ -1054,7 +1083,7 @@ export interface SecurityRuleBase {
1054
1083
  * // Admins have unfiltered read access to all rows
1055
1084
  * { operation: "select", roles: ["admin"], using: "true" }
1056
1085
  */
1057
- roles?: string[];
1086
+ roles?: readonly string[];
1058
1087
 
1059
1088
  // ── Advanced: native PostgreSQL role targeting ───────────────────────
1060
1089
 
@@ -1079,7 +1108,7 @@ export interface SecurityRuleBase {
1079
1108
  * // Only apply this policy when connected as `app_role`
1080
1109
  * { operation: "select", access: "public", pgRoles: ["app_role"] }
1081
1110
  */
1082
- pgRoles?: string[];
1111
+ pgRoles?: readonly string[];
1083
1112
  }
1084
1113
 
1085
1114
  /**
@@ -1099,6 +1128,8 @@ export interface OwnerSecurityRule extends SecurityRuleBase {
1099
1128
  access?: never;
1100
1129
  using?: never;
1101
1130
  withCheck?: never;
1131
+ condition?: never;
1132
+ check?: never;
1102
1133
  }
1103
1134
 
1104
1135
  /**
@@ -1123,12 +1154,61 @@ export interface PublicSecurityRule extends SecurityRuleBase {
1123
1154
  ownerField?: never;
1124
1155
  using?: never;
1125
1156
  withCheck?: never;
1157
+ condition?: never;
1158
+ check?: never;
1159
+ }
1160
+
1161
+ /**
1162
+ * Security rule expressed as a structured, engine-agnostic
1163
+ * {@link PolicyExpression}. This is the **recommended** way to write a
1164
+ * non-trivial condition: it compiles to PostgreSQL `USING`/`WITH CHECK` SQL
1165
+ * *and* is evaluated identically by the admin UI, so the UI can never show an
1166
+ * action the database will reject.
1167
+ *
1168
+ * Cannot be combined with `ownerField`, `access`, or raw `using`/`withCheck`.
1169
+ *
1170
+ * @example
1171
+ * // Owner, or any user holding the `moderator` role
1172
+ * {
1173
+ * operation: "update",
1174
+ * condition: policy.or(
1175
+ * policy.compare(policy.field("user_id"), "eq", policy.authUid()),
1176
+ * policy.rolesOverlap(["moderator"])
1177
+ * )
1178
+ * }
1179
+ *
1180
+ * @group Models
1181
+ */
1182
+ export interface StructuredSecurityRule extends SecurityRuleBase {
1183
+ /**
1184
+ * Structured condition for the `USING` clause — which *existing* rows are
1185
+ * visible / can be modified / deleted (SELECT, UPDATE, DELETE).
1186
+ */
1187
+ condition: PolicyExpression;
1188
+
1189
+ /**
1190
+ * Structured condition for the `WITH CHECK` clause — which *new/updated*
1191
+ * row values are allowed (INSERT, UPDATE). Defaults to `condition` when
1192
+ * omitted, mirroring PostgreSQL's own behavior.
1193
+ */
1194
+ check?: PolicyExpression;
1195
+
1196
+ ownerField?: never;
1197
+ access?: never;
1198
+ using?: never;
1199
+ withCheck?: never;
1126
1200
  }
1127
1201
 
1128
1202
  /**
1129
1203
  * Security rule using raw SQL expressions for full PostgreSQL RLS power.
1130
1204
  *
1131
- * Cannot be combined with `ownerField` or `access`.
1205
+ * **Postgres-only and server-authoritative.** Arbitrary SQL cannot be
1206
+ * evaluated by the admin UI, so a rule using this form is treated as *unknown*
1207
+ * client-side (never silently allowed) and its effect on visible actions is
1208
+ * reflected from the server. For conditions that should also drive the UI
1209
+ * precisely, prefer the structured {@link StructuredSecurityRule}.
1210
+ *
1211
+ * Cannot be combined with `ownerField`, `access`, or structured `condition`.
1132
1212
  *
1133
1213
  * You can reference columns via `{column_name}` which will be resolved to
1134
1214
  * `table.column_name` in the generated Drizzle code.
@@ -1166,6 +1246,8 @@ export interface RawSQLSecurityRule extends SecurityRuleBase {
1166
1246
 
1167
1247
  ownerField?: never;
1168
1248
  access?: never;
1249
+ condition?: never;
1250
+ check?: never;
1169
1251
  }
1170
1252
 
1171
1253
  /**
@@ -1186,6 +1268,8 @@ export interface RolesOnlySecurityRule extends SecurityRuleBase {
1186
1268
  access?: never;
1187
1269
  using?: never;
1188
1270
  withCheck?: never;
1271
+ condition?: never;
1272
+ check?: never;
1189
1273
  }
1190
1274
 
1191
1275
  /**