@rebasepro/types 0.7.0 → 0.8.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 (37) hide show
  1. package/dist/controllers/client.d.ts +53 -1
  2. package/dist/controllers/data.d.ts +19 -47
  3. package/dist/controllers/registry.d.ts +0 -2
  4. package/dist/index.es.js +157 -10
  5. package/dist/index.es.js.map +1 -1
  6. package/dist/index.umd.js +163 -9
  7. package/dist/index.umd.js.map +1 -1
  8. package/dist/types/auth_adapter.d.ts +5 -5
  9. package/dist/types/backend.d.ts +4 -0
  10. package/dist/types/collections.d.ts +158 -82
  11. package/dist/types/data_source.d.ts +3 -3
  12. package/dist/types/entity_callbacks.d.ts +18 -6
  13. package/dist/types/filter-operators.d.ts +108 -0
  14. package/dist/types/index.d.ts +3 -2
  15. package/dist/types/policy.d.ts +137 -0
  16. package/dist/types/properties.d.ts +115 -37
  17. package/dist/types/property_config.d.ts +1 -1
  18. package/dist/types/storage_source.d.ts +83 -0
  19. package/package.json +1 -1
  20. package/src/controllers/client.ts +61 -1
  21. package/src/controllers/data.ts +20 -59
  22. package/src/controllers/registry.ts +0 -2
  23. package/src/types/auth_adapter.ts +5 -5
  24. package/src/types/backend.ts +5 -0
  25. package/src/types/collections.ts +191 -106
  26. package/src/types/data_source.ts +5 -5
  27. package/src/types/entity_callbacks.ts +18 -7
  28. package/src/types/filter-operators.ts +167 -0
  29. package/src/types/index.ts +3 -2
  30. package/src/types/policy.ts +173 -0
  31. package/src/types/properties.ts +123 -38
  32. package/src/types/property_config.tsx +0 -1
  33. package/src/types/storage_source.ts +90 -0
  34. package/dist/types/backend_hooks.d.ts +0 -109
  35. package/dist/types/entity_overrides.d.ts +0 -10
  36. package/src/types/backend_hooks.ts +0 -114
  37. package/src/types/entity_overrides.tsx +0 -11
@@ -1,29 +1,5 @@
1
1
  import { Entity, EntityValues } from "../types/entities";
2
-
3
- /**
4
- * Parameters for querying a collection.
5
- * Uses PostgREST-style filter syntax for consistency between
6
- * the SDK (HTTP) and framework (in-process) contexts.
7
- *
8
- * @group Data
9
- */
10
- /**
11
- * A where-clause value for a single field.
12
- *
13
- * Supports three syntaxes:
14
- * 1. **Equality shorthand**: raw JS values — `null`, `"active"`, `42`, `true`
15
- * 2. **Tuple syntax**: `[operator, value]` — `[">", 18]`, `["in", ["a","b"]]`
16
- * 3. **PostgREST string**: `"eq.published"`, `"gte.18"`, `"in.(a,b)"`
17
- *
18
- * @group Data
19
- */
20
- export type WhereFieldValue =
21
- | string
22
- | number
23
- | boolean
24
- | null
25
- | [WhereFilterOpShort, any]
26
- | [WhereFilterOpShort, any][];
2
+ import { WhereFilterOp, FilterValues } from "../types/filter-operators";
27
3
 
28
4
  export type WhereValue<T> = T | T[] | null;
29
5
 
@@ -34,17 +10,15 @@ export interface LogicalCondition {
34
10
 
35
11
  export interface FilterCondition {
36
12
  column: string;
37
- operator: FilterOperator;
13
+ operator: WhereFilterOp;
38
14
  value: unknown;
39
15
  }
40
16
 
41
- /** Short operator strings accepted in the tuple syntax. */
42
- export type WhereFilterOpShort =
43
- | "==" | "!=" | ">" | ">=" | "<" | "<="
44
- | "eq" | "neq" | "gt" | "gte" | "lt" | "lte"
45
- | "in" | "nin" | "not-in"
46
- | "array-contains" | "array-contains-any" | "cs" | "csa";
47
-
17
+ /**
18
+ * Parameters for querying a collection.
19
+ *
20
+ * @group Data
21
+ */
48
22
  export interface FindParams {
49
23
  /** Maximum number of items to return (default: 20) */
50
24
  limit?: number;
@@ -53,30 +27,17 @@ export interface FindParams {
53
27
  /** Page number (1-indexed), alternative to offset */
54
28
  page?: number;
55
29
  /**
56
- * Filter object. Supports multiple syntaxes per field:
57
- *
58
- * **Equality shorthand** raw JS values (null, string, number, boolean):
59
- * ```ts
60
- * { company_profile_id: null }
61
- * { status: "active" }
62
- * { age: 18 }
63
- * ```
30
+ * Filter conditions keyed by field name.
31
+ * Each value is a `[WhereFilterOp, value]` tuple or an array of tuples
32
+ * for multiple conditions on the same field.
64
33
  *
65
- * **Tuple syntax** — `[operator, value]`:
66
- * ```ts
34
+ * @example
35
+ * { status: ["==", "active"] }
67
36
  * { age: [">=", 18] }
68
37
  * { role: ["in", ["admin", "editor"]] }
69
- * { deleted_at: ["!=", null] }
70
- * ```
71
- *
72
- * **PostgREST string syntax** (original format):
73
- * ```ts
74
- * { status: "eq.published" }
75
- * { age: "gte.18" }
76
- * { role: "in.(admin,editor)" }
77
- * ```
38
+ * { age: [[">=", 18], ["<", 65]] }
78
39
  */
79
- where?: Record<string, WhereFieldValue>;
40
+ where?: FilterValues<string>;
80
41
  /** Logical grouping conditions (AND/OR) */
81
42
  logical?: LogicalCondition;
82
43
  /**
@@ -106,16 +67,16 @@ export interface FindResponse<M extends Record<string, unknown> = Record<string,
106
67
  };
107
68
  }
108
69
 
109
- export type FilterOperator = WhereFilterOpShort;
70
+
110
71
 
111
72
  /**
112
73
  * Fluent Query Builder Interface supported on both client and server accessors.
113
74
  * @group Data
114
75
  */
115
76
  export interface QueryBuilderInterface<M extends Record<string, unknown> = Record<string, unknown>> {
116
- where<K extends keyof M & string>(column: K, operator: FilterOperator, value: WhereValue<M[K]>): this;
77
+ where<K extends keyof M & string>(column: K, operator: WhereFilterOp, value: WhereValue<M[K]>): this;
117
78
  where(logicalCondition: LogicalCondition): this;
118
- orderBy(column: keyof M & string, ascending?: "asc" | "desc"): this;
79
+ orderBy(column: keyof M & string, direction?: "asc" | "desc"): this;
119
80
  limit(count: number): this;
120
81
  offset(count: number): this;
121
82
  search(searchString: string): this;
@@ -186,9 +147,9 @@ export interface CollectionAccessor<M extends Record<string, unknown> = Record<s
186
147
  count?(params?: FindParams): Promise<number>;
187
148
 
188
149
  // Fluent Query Builder
189
- where<K extends keyof M & string>(column: K, operator: FilterOperator, value: WhereValue<M[K]>): QueryBuilderInterface<M>;
150
+ where<K extends keyof M & string>(column: K, operator: WhereFilterOp, value: WhereValue<M[K]>): QueryBuilderInterface<M>;
190
151
  where(logicalCondition: LogicalCondition): QueryBuilderInterface<M>;
191
- orderBy(column: keyof M & string, ascending?: "asc" | "desc"): QueryBuilderInterface<M>;
152
+ orderBy(column: keyof M & string, direction?: "asc" | "desc"): QueryBuilderInterface<M>;
192
153
  limit(count: number): QueryBuilderInterface<M>;
193
154
  offset(count: number): QueryBuilderInterface<M>;
194
155
  search(searchString: string): QueryBuilderInterface<M>;
@@ -226,7 +187,7 @@ export interface RebaseData {
226
187
  * const accessor = data.collection("products");
227
188
  * await accessor.find({ limit: 10 });
228
189
  */
229
- collection(slug: string): CollectionAccessor;
190
+ collection<M extends Record<string, unknown> = Record<string, unknown>>(slug: string): CollectionAccessor<M>;
230
191
 
231
192
  /**
232
193
  * Dynamic collection accessor.
@@ -4,7 +4,6 @@ import type { EntityCollectionsBuilder, AppViewsBuilder } from "../types/builder
4
4
  import type { EntityCustomView } from "../types/entity_views";
5
5
  import type { EntityAction } from "../types/entity_actions";
6
6
  import type { AppView, NavigationGroupMapping } from "./navigation";
7
- import type { RebasePlugin } from "../types/plugins";
8
7
 
9
8
  /**
10
9
  * Options to enable the built-in collection editor.
@@ -35,7 +34,6 @@ export interface RebaseCMSConfig<EC extends EntityCollection = EntityCollection>
35
34
  homePage?: ReactNode;
36
35
  entityViews?: EntityCustomView[];
37
36
  entityActions?: EntityAction[];
38
- plugins?: RebasePlugin[];
39
37
 
40
38
  /**
41
39
  * Centralized configuration for how collections and views are grouped
@@ -4,8 +4,8 @@
4
4
  * Pluggable authentication abstraction for Rebase.
5
5
  *
6
6
  * An `AuthAdapter` decouples authentication from the database layer,
7
- * allowing users to bring their own auth system (Clerk, Auth0, Firebase Auth,
8
- * custom JWT, etc.) while keeping the Rebase admin frontend fully functional.
7
+ * allowing users to bring their own auth system (Clerk, Auth0, or other
8
+ * external providers) while keeping the Rebase admin frontend fully functional.
9
9
  *
10
10
  * @example Built-in auth (default — zero config change)
11
11
  * ```ts
@@ -277,7 +277,7 @@ export interface TransformAuthResponseContext {
277
277
  * 4. Advertise its capabilities so the frontend can adapt
278
278
  *
279
279
  * The built-in Rebase auth implements this interface internally.
280
- * External providers (Clerk, Auth0, Firebase Auth) provide their own adapters.
280
+ * External providers (Clerk, Auth0, or others) provide their own adapters.
281
281
  * Users with custom auth can use `createCustomAuthAdapter()` for a minimal setup.
282
282
  *
283
283
  * @group Auth
@@ -286,7 +286,7 @@ export interface AuthAdapter {
286
286
  /**
287
287
  * Unique identifier for this auth adapter.
288
288
  *
289
- * @example "rebase-builtin", "clerk", "auth0", "firebase", "custom"
289
+ * @example "rebase-builtin", "clerk", "auth0", "external-provider", "custom"
290
290
  */
291
291
  readonly id: string;
292
292
 
@@ -435,7 +435,7 @@ export interface AuthAdapter {
435
435
  * response and returns a (potentially enriched) response.
436
436
  *
437
437
  * Use cases:
438
- * - Inject tokens from external auth systems (Firebase Custom Tokens, etc.)
438
+ * - Inject tokens from external auth systems (custom provider tokens, etc.)
439
439
  * - Add project-specific metadata to the response
440
440
  * - Enrich the user object with data from external sources
441
441
  *
@@ -325,6 +325,11 @@ export interface CollectionRegistryInterface {
325
325
  * Get all registered collections
326
326
  */
327
327
  getCollections(): EntityCollection[];
328
+
329
+ /**
330
+ * Get the currently registered global callbacks, if any.
331
+ */
332
+ getGlobalCallbacks(): any | undefined;
328
333
  }
329
334
 
330
335
  // =============================================================================
@@ -2,16 +2,18 @@ import React, { Dispatch, SetStateAction } from "react";
2
2
  import type { Entity, EntityStatus, EntityValues } from "./entities";
3
3
  import type { EntityCallbacks } 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 } from "./filter-operators";
15
17
 
16
18
  /**
17
19
  * Base interface containing all driver-agnostic collection properties.
@@ -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 PostgresCollection},
85
+ * {@link FirebaseCollection}, {@link MongoDBCollection}) 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.
@@ -299,8 +302,8 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
299
302
 
300
303
  /**
301
304
  * 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
305
+ * view. Even when inline editing is disabled, entities can still be
306
+ * edited in the side panel (subject to `securityRules`).
304
307
  */
305
308
  inlineEditing?: boolean;
306
309
 
@@ -358,11 +361,6 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
358
361
  */
359
362
  metadata?: Record<string, unknown>;
360
363
 
361
- /**
362
- * Overrides for the entity view, like the data source or the storage source.
363
- */
364
- overrides?: EntityOverrides;
365
-
366
364
  /**
367
365
  * Width of the side dialog (in pixels) when opening an entity in this collection.
368
366
  */
@@ -501,13 +499,13 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
501
499
  */
502
500
  export interface PostgresCollection<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>
503
501
  extends BaseEntityCollection<M, USER> {
504
- properties: Properties;
502
+ properties: PostgresProperties;
505
503
 
506
504
  /**
507
- * The driver for this collection. For Postgres collections this
505
+ * The database engine for this collection. For Postgres collections this
508
506
  * can be omitted (Postgres is the default) or set to `"postgres"`.
509
507
  */
510
- driver?: "postgres" | undefined;
508
+ engine?: "postgres" | undefined;
511
509
 
512
510
  /**
513
511
  * The PostgreSQL table name for this collection.
@@ -557,9 +555,33 @@ export interface PostgresCollection<M extends Record<string, unknown> = Record<s
557
555
  export interface FirebaseCollection<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>
558
556
  extends BaseEntityCollection<M, USER> {
559
557
  /**
560
- * The driver for this collection. Must be set to `"firestore"`.
558
+ * The database engine for this collection. Must be set to `"firestore"`.
559
+ */
560
+ engine: "firestore";
561
+
562
+ /**
563
+ * Set of properties that compose an entity.
564
+ * Firestore collections support `reference` properties but not `relation`.
565
+ */
566
+ properties: FirebaseProperties;
567
+
568
+ /**
569
+ * The Firestore collection path to query. Defaults to `slug` if not set.
570
+ * Use this when the Firestore path differs from the slug
571
+ * (e.g., when a PostgreSQL collection already uses the same slug).
572
+ *
573
+ * @example
574
+ * ```typescript
575
+ * const fsCustomer: FirebaseCollection = {
576
+ * slug: "fs_customer", // URL: /c/fs_customer
577
+ * path: "customer", // Firestore path: customer
578
+ * name: "Customers (Firestore)",
579
+ * engine: "firestore",
580
+ * properties: { ... }
581
+ * };
582
+ * ```
561
583
  */
562
- driver: "firestore";
584
+ path?: string;
563
585
 
564
586
  /**
565
587
  * You can add subcollections to your entity in the same way you define the root
@@ -581,9 +603,33 @@ export interface MongoDBCollection<M extends Record<string, unknown> = Record<st
581
603
  extends BaseEntityCollection<M, USER> {
582
604
 
583
605
  /**
584
- * The driver for this collection. Must be set to `"mongodb"`.
606
+ * The database engine for this collection. Must be set to `"mongodb"`.
607
+ */
608
+ engine: "mongodb";
609
+
610
+ /**
611
+ * Set of properties that compose an entity.
612
+ * MongoDB collections support `reference` properties but not `relation`.
585
613
  */
586
- driver: "mongodb";
614
+ properties: MongoProperties;
615
+
616
+ /**
617
+ * The MongoDB collection name to use. Defaults to `slug` if not set.
618
+ * Use this when the MongoDB collection name differs from the slug
619
+ * (e.g., when a PostgreSQL collection already uses the same slug).
620
+ *
621
+ * @example
622
+ * ```typescript
623
+ * const mongoCustomer: MongoDBCollection = {
624
+ * slug: "mongo_customer", // URL: /c/mongo_customer
625
+ * path: "customer", // MongoDB collection: customer
626
+ * name: "Customers (MongoDB)",
627
+ * engine: "mongodb",
628
+ * properties: { ... }
629
+ * };
630
+ * ```
631
+ */
632
+ path?: string;
587
633
  }
588
634
 
589
635
  /**
@@ -599,33 +645,15 @@ export type EntityCollection<M extends Record<string, unknown> = Record<string,
599
645
  | FirebaseCollection<M, USER>
600
646
  | MongoDBCollection<M, USER>;
601
647
 
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 ───────────────────────────────────────────────────────
619
-
620
648
  /**
621
649
  * Type guard for PostgreSQL collections.
622
- * Returns true if the collection uses the Postgres driver (or the default driver).
650
+ * Returns true if the collection uses the Postgres engine (or the default engine).
623
651
  * @group Models
624
652
  */
625
653
  export function isPostgresCollection<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>(
626
654
  collection: EntityCollection<M, USER>
627
655
  ): collection is PostgresCollection<M, USER> {
628
- return !collection.driver || collection.driver === "postgres";
656
+ return !collection.engine || collection.engine === "postgres";
629
657
  }
630
658
 
631
659
  /**
@@ -635,7 +663,7 @@ export function isPostgresCollection<M extends Record<string, unknown> = Record<
635
663
  export function isFirebaseCollection<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>(
636
664
  collection: EntityCollection<M, USER>
637
665
  ): collection is FirebaseCollection<M, USER> {
638
- return collection.driver === "firestore";
666
+ return collection.engine === "firestore";
639
667
  }
640
668
 
641
669
  /**
@@ -645,7 +673,40 @@ export function isFirebaseCollection<M extends Record<string, unknown> = Record<
645
673
  export function isMongoDBCollection<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>(
646
674
  collection: EntityCollection<M, USER>
647
675
  ): collection is MongoDBCollection<M, USER> {
648
- return collection.driver === "mongodb";
676
+ return collection.engine === "mongodb";
677
+ }
678
+
679
+ /**
680
+ * Returns the data path for a collection.
681
+ * For Firestore or MongoDB collections with a `path`, returns that value;
682
+ * otherwise falls back to `slug`.
683
+ */
684
+ export function getCollectionDataPath<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>(
685
+ collection: EntityCollection<M, USER>
686
+ ): string {
687
+ if (isFirebaseCollection(collection) && collection.path) {
688
+ return collection.path;
689
+ }
690
+ if (isMongoDBCollection(collection) && collection.path) {
691
+ return collection.path;
692
+ }
693
+ return collection.slug;
694
+ }
695
+
696
+ /**
697
+ * Reads a collection's driver-declared subcollections thunk (the `subcollections`
698
+ * field) independent of engine identity, so engine-agnostic code doesn't have to
699
+ * type-guard against a specific driver. Returns `undefined` when the collection
700
+ * declares none.
701
+ *
702
+ * Pair with `getDataSourceCapabilities(engine).supportsSubcollections` to decide
703
+ * whether the engine honours subcollections at all before reading them.
704
+ * @group Models
705
+ */
706
+ export function getDeclaredSubcollections<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>(
707
+ collection: EntityCollection<M, USER>
708
+ ): (() => EntityCollection<Record<string, unknown>>[]) | undefined {
709
+ return (collection as FirebaseCollection<M, USER>).subcollections;
649
710
  }
650
711
 
651
712
 
@@ -757,57 +818,8 @@ export interface SelectionController<M extends Record<string, unknown> = Record<
757
818
  toggleEntitySelection(entity: Entity<M>, newSelectedState?: boolean): void;
758
819
  }
759
820
 
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
- }
821
+ // Canonical filter types — re-exported from the single source-of-truth.
822
+ export type { WhereFilterOp, FilterValues, WireFilterValues, FilterPreset } from "./filter-operators";
811
823
 
812
824
 
813
825
  /**
@@ -964,13 +976,26 @@ export type SecurityOperation = "select" | "insert" | "update" | "delete" | "all
964
976
  * operation. Permissive rules are OR'd together (any one passing is enough).
965
977
  * Restrictive rules are AND'd (all must pass). This mirrors Supabase behavior.
966
978
  *
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.
979
+ * **Mutual exclusivity:** `ownerField`, `access`, structured `condition`, and
980
+ * raw SQL (`using`/`withCheck`) cannot be combined. The type system enforces
981
+ * this — attempting to set conflicting fields will produce a compile-time
982
+ * error.
983
+ *
984
+ * **Which form to reach for:** prefer the structured {@link StructuredSecurityRule}
985
+ * (`condition`/`check`) or the shortcuts (`ownerField`, `access`, `roles`). These
986
+ * are engine-agnostic and evaluated identically by the database and the admin UI,
987
+ * so the UI never shows an action the database will reject. Raw SQL
988
+ * ({@link RawSQLSecurityRule}) keeps full PostgreSQL power but is Postgres-only
989
+ * and server-authoritative (the UI cannot evaluate arbitrary SQL locally).
970
990
  *
971
991
  * @group Models
972
992
  */
973
- export type SecurityRule = OwnerSecurityRule | PublicSecurityRule | RawSQLSecurityRule | RolesOnlySecurityRule;
993
+ export type SecurityRule =
994
+ | OwnerSecurityRule
995
+ | PublicSecurityRule
996
+ | StructuredSecurityRule
997
+ | RawSQLSecurityRule
998
+ | RolesOnlySecurityRule;
974
999
 
975
1000
  /**
976
1001
  * Shared fields for all SecurityRule variants.
@@ -1040,11 +1065,16 @@ export interface SecurityRuleBase {
1040
1065
  * application roles managed by Rebase, stored in the `rebase.user_roles`
1041
1066
  * table, and injected into each transaction via `auth.roles()`.
1042
1067
  *
1043
- * Generates a condition like:
1044
- * `auth.roles() ~ '<role1>|<role2>'`
1068
+ * Generates a safe array-overlap condition — the user passes if they hold
1069
+ * *any* of the listed roles:
1070
+ * `string_to_array(auth.roles(), ',') && ARRAY['<role1>', '<role2>']`
1045
1071
  *
1046
- * Can be combined with `ownerField`, `access`, or raw `using`/`withCheck`.
1047
- * When combined, the role check is AND'd with the other condition.
1072
+ * (Note: this is a true set intersection, NOT a regex/substring match, so
1073
+ * a role named `admin` never matches `nonadmin` or `superadmin`.)
1074
+ *
1075
+ * Can be combined with `ownerField`, `access`, `condition`, or raw
1076
+ * `using`/`withCheck`. When combined, the role check is AND'd with the
1077
+ * other condition.
1048
1078
  *
1049
1079
  * @example
1050
1080
  * // Only admins can delete
@@ -1099,6 +1129,8 @@ export interface OwnerSecurityRule extends SecurityRuleBase {
1099
1129
  access?: never;
1100
1130
  using?: never;
1101
1131
  withCheck?: never;
1132
+ condition?: never;
1133
+ check?: never;
1102
1134
  }
1103
1135
 
1104
1136
  /**
@@ -1123,12 +1155,61 @@ export interface PublicSecurityRule extends SecurityRuleBase {
1123
1155
  ownerField?: never;
1124
1156
  using?: never;
1125
1157
  withCheck?: never;
1158
+ condition?: never;
1159
+ check?: never;
1160
+ }
1161
+
1162
+ /**
1163
+ * Security rule expressed as a structured, engine-agnostic
1164
+ * {@link PolicyExpression}. This is the **recommended** way to write a
1165
+ * non-trivial condition: it compiles to PostgreSQL `USING`/`WITH CHECK` SQL
1166
+ * *and* is evaluated identically by the admin UI, so the UI can never show an
1167
+ * action the database will reject.
1168
+ *
1169
+ * Cannot be combined with `ownerField`, `access`, or raw `using`/`withCheck`.
1170
+ *
1171
+ * @example
1172
+ * // Owner, or any user holding the `moderator` role
1173
+ * {
1174
+ * operation: "update",
1175
+ * condition: policy.or(
1176
+ * policy.compare(policy.field("user_id"), "eq", policy.authUid()),
1177
+ * policy.rolesOverlap(["moderator"])
1178
+ * )
1179
+ * }
1180
+ *
1181
+ * @group Models
1182
+ */
1183
+ export interface StructuredSecurityRule extends SecurityRuleBase {
1184
+ /**
1185
+ * Structured condition for the `USING` clause — which *existing* rows are
1186
+ * visible / can be modified / deleted (SELECT, UPDATE, DELETE).
1187
+ */
1188
+ condition: PolicyExpression;
1189
+
1190
+ /**
1191
+ * Structured condition for the `WITH CHECK` clause — which *new/updated*
1192
+ * row values are allowed (INSERT, UPDATE). Defaults to `condition` when
1193
+ * omitted, mirroring PostgreSQL's own behavior.
1194
+ */
1195
+ check?: PolicyExpression;
1196
+
1197
+ ownerField?: never;
1198
+ access?: never;
1199
+ using?: never;
1200
+ withCheck?: never;
1126
1201
  }
1127
1202
 
1128
1203
  /**
1129
1204
  * Security rule using raw SQL expressions for full PostgreSQL RLS power.
1130
1205
  *
1131
- * Cannot be combined with `ownerField` or `access`.
1206
+ * **Postgres-only and server-authoritative.** Arbitrary SQL cannot be
1207
+ * evaluated by the admin UI, so a rule using this form is treated as *unknown*
1208
+ * client-side (never silently allowed) and its effect on visible actions is
1209
+ * reflected from the server. For conditions that should also drive the UI
1210
+ * precisely, prefer the structured {@link StructuredSecurityRule}.
1211
+ *
1212
+ * Cannot be combined with `ownerField`, `access`, or structured `condition`.
1132
1213
  *
1133
1214
  * You can reference columns via `{column_name}` which will be resolved to
1134
1215
  * `table.column_name` in the generated Drizzle code.
@@ -1166,6 +1247,8 @@ export interface RawSQLSecurityRule extends SecurityRuleBase {
1166
1247
 
1167
1248
  ownerField?: never;
1168
1249
  access?: never;
1250
+ condition?: never;
1251
+ check?: never;
1169
1252
  }
1170
1253
 
1171
1254
  /**
@@ -1186,6 +1269,8 @@ export interface RolesOnlySecurityRule extends SecurityRuleBase {
1186
1269
  access?: never;
1187
1270
  using?: never;
1188
1271
  withCheck?: never;
1272
+ condition?: never;
1273
+ check?: never;
1189
1274
  }
1190
1275
 
1191
1276
  /**
@@ -211,13 +211,13 @@ const CAPABILITIES_REGISTRY: Record<string, DataSourceCapabilities> = {
211
211
  };
212
212
 
213
213
  /**
214
- * Look up capabilities for a given driver key.
215
- * If `driver` is undefined or not found, returns `DEFAULT_CAPABILITIES`.
214
+ * Look up capabilities for a given engine key.
215
+ * If `engine` is undefined or not found, returns `DEFAULT_CAPABILITIES`.
216
216
  * @group Models
217
217
  */
218
- export function getDataSourceCapabilities(driver?: string): DataSourceCapabilities {
219
- if (!driver) return POSTGRES_CAPABILITIES; // postgres is the default driver
220
- return CAPABILITIES_REGISTRY[driver] ?? DEFAULT_CAPABILITIES;
218
+ export function getDataSourceCapabilities(engine?: string): DataSourceCapabilities {
219
+ if (!engine) return POSTGRES_CAPABILITIES; // postgres is the default engine
220
+ return CAPABILITIES_REGISTRY[engine] ?? DEFAULT_CAPABILITIES;
221
221
  }
222
222
 
223
223
  /**