@rebasepro/types 0.6.1 → 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 (49) hide show
  1. package/dist/controllers/client.d.ts +69 -1
  2. package/dist/controllers/data.d.ts +19 -47
  3. package/dist/controllers/navigation.d.ts +14 -14
  4. package/dist/controllers/registry.d.ts +8 -4
  5. package/dist/index.es.js +164 -10
  6. package/dist/index.es.js.map +1 -1
  7. package/dist/index.umd.js +171 -9
  8. package/dist/index.umd.js.map +1 -1
  9. package/dist/types/api_keys.d.ts +57 -0
  10. package/dist/types/auth_adapter.d.ts +66 -4
  11. package/dist/types/backend.d.ts +5 -0
  12. package/dist/types/collections.d.ts +200 -90
  13. package/dist/types/data_source.d.ts +79 -3
  14. package/dist/types/entity_actions.d.ts +2 -2
  15. package/dist/types/entity_callbacks.d.ts +18 -6
  16. package/dist/types/entity_views.d.ts +1 -1
  17. package/dist/types/filter-operators.d.ts +108 -0
  18. package/dist/types/index.d.ts +4 -2
  19. package/dist/types/plugins.d.ts +1 -1
  20. package/dist/types/policy.d.ts +137 -0
  21. package/dist/types/properties.d.ts +122 -37
  22. package/dist/types/property_config.d.ts +1 -1
  23. package/dist/types/storage_source.d.ts +83 -0
  24. package/dist/types/translations.d.ts +8 -0
  25. package/package.json +1 -1
  26. package/src/controllers/client.ts +69 -1
  27. package/src/controllers/data.ts +20 -59
  28. package/src/controllers/navigation.ts +17 -16
  29. package/src/controllers/registry.ts +10 -4
  30. package/src/types/api_keys.ts +52 -0
  31. package/src/types/auth_adapter.ts +80 -4
  32. package/src/types/backend.ts +6 -1
  33. package/src/types/collections.ts +235 -113
  34. package/src/types/data_source.ts +89 -5
  35. package/src/types/entity_actions.tsx +2 -2
  36. package/src/types/entity_callbacks.ts +18 -7
  37. package/src/types/entity_views.tsx +1 -1
  38. package/src/types/filter-operators.ts +167 -0
  39. package/src/types/index.ts +5 -2
  40. package/src/types/plugins.tsx +1 -1
  41. package/src/types/policy.ts +173 -0
  42. package/src/types/properties.ts +132 -39
  43. package/src/types/property_config.tsx +0 -1
  44. package/src/types/storage_source.ts +90 -0
  45. package/src/types/translations.ts +8 -0
  46. package/dist/types/backend_hooks.d.ts +0 -109
  47. package/dist/types/entity_overrides.d.ts +0 -10
  48. package/src/types/backend_hooks.ts +0 -114
  49. package/src/types/entity_overrides.tsx +0 -11
@@ -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.
@@ -58,34 +60,45 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
58
60
 
59
61
 
60
62
  /**
61
- * Which driver handles this collection.
62
- * Use this to route collections to different backends:
63
- * - `"postgres"` - Route to PostgreSQL backend
64
- * - `"firestore"` - Route to Firestore (client-side)
65
- * - `"mongodb"` - Route to MongoDB backend
66
- * - Custom IDs for your own driver implementations
63
+ * The data source this collection belongs to — the routing key shared by
64
+ * the frontend router and the backend driver registry. It points at a
65
+ * {@link DataSourceDefinition} registered on `<Rebase dataSources>` (front)
66
+ * and `initRebase({ dataSources })` (back).
67
67
  *
68
- * If not specified, the default driver `"(default)"` is used.
68
+ * If not specified, the default data source `"(default)"` is used, which
69
+ * for a standard Rebase app is the server-mediated Postgres backend.
69
70
  *
70
71
  * @example
71
- * // Simple - no driver needed for default
72
+ * // Default data source (server-mediated Postgres)
72
73
  * { slug: "products" }
73
74
  *
74
- * // Firestore collection (client-side real-time)
75
- * { slug: "analytics", driver: "firestore" }
75
+ * // A direct-transport Firestore data source registered as "analytics"
76
+ * { slug: "events", dataSource: "analytics" }
77
+ */
78
+ dataSource?: string;
79
+
80
+ /**
81
+ * The database engine backing this collection (`"postgres"`, `"firestore"`,
82
+ * `"mongodb"`, or a custom id).
76
83
  *
77
- * // Multiple databases within a driver
78
- * { slug: "orders", driver: "postgres", databaseId: "orders_db" }
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}.
89
+ *
90
+ * Prefer setting {@link dataSource} and letting the engine be resolved.
79
91
  */
80
- driver?: string;
92
+ engine?: string;
81
93
 
82
94
  /**
83
- * Which database within the driver.
95
+ * Which database within the engine.
84
96
  * - For Firestore: The Firestore database ID (e.g., for multi-database projects)
85
97
  * - For PostgreSQL: Schema or database name
86
98
  * - For MongoDB: Database name
87
99
  *
88
- * If not specified, the default database of the driver is used.
100
+ * If not specified, the default database of the engine is used. Resolved
101
+ * from the collection's {@link DataSourceDefinition} when omitted here.
89
102
  */
90
103
  databaseId?: string;
91
104
 
@@ -165,6 +178,26 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
165
178
  */
166
179
  auth?: boolean | AuthCollectionConfig;
167
180
 
181
+ /**
182
+ * Opt out of the framework's default Row Level Security policies for
183
+ * authentication collections.
184
+ *
185
+ * When a collection has `auth` enabled, the schema generator automatically
186
+ * injects an admin-only write policy (INSERT/UPDATE/DELETE require the
187
+ * `admin` role, or the trusted server context) for any write operation that
188
+ * the collection does not already cover with an explicit `securityRules`
189
+ * entry. This makes privileged columns such as `roles` safe by default — a
190
+ * non-admin cannot modify the user row through the data API regardless of
191
+ * which code path issues the write.
192
+ *
193
+ * Defining your own write rule for an operation overrides the default for
194
+ * that operation. Set this flag to `true` to remove the default policies
195
+ * entirely and take full responsibility for the collection's RLS.
196
+ *
197
+ * @default false
198
+ */
199
+ disableDefaultAuthPolicies?: boolean;
200
+
168
201
 
169
202
  /**
170
203
  * Order in which the properties are displayed.
@@ -269,8 +302,8 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
269
302
 
270
303
  /**
271
304
  * Can the elements in this collection be edited inline in the collection
272
- * view. If this flag is set to false but `permissions.edit` is `true`, entities
273
- * 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`).
274
307
  */
275
308
  inlineEditing?: boolean;
276
309
 
@@ -322,9 +355,11 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
322
355
  ownerId?: string;
323
356
 
324
357
  /**
325
- * Overrides for the entity view, like the data source or the storage source.
358
+ * Arbitrary key-value metadata for external consumers.
359
+ * Not interpreted by Rebase — passed through serialization unchanged.
360
+ * Used by domain apps to store custom per-collection config.
326
361
  */
327
- overrides?: EntityOverrides;
362
+ metadata?: Record<string, unknown>;
328
363
 
329
364
  /**
330
365
  * Width of the side dialog (in pixels) when opening an entity in this collection.
@@ -375,7 +410,7 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
375
410
  * Possible values: "table", "cards", "kanban".
376
411
  * Defaults to all three: ["table", "cards", "kanban"].
377
412
  * Note: "kanban" will only be available if the collection has at least
378
- * one string property with enumValues defined, regardless of this setting.
413
+ * one string property with `enum` defined, regardless of this setting.
379
414
  */
380
415
  enabledViews?: ViewMode[];
381
416
 
@@ -464,13 +499,13 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
464
499
  */
465
500
  export interface PostgresCollection<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>
466
501
  extends BaseEntityCollection<M, USER> {
467
- properties: Properties;
502
+ properties: PostgresProperties;
468
503
 
469
504
  /**
470
- * The driver for this collection. For Postgres collections this
505
+ * The database engine for this collection. For Postgres collections this
471
506
  * can be omitted (Postgres is the default) or set to `"postgres"`.
472
507
  */
473
- driver?: "postgres" | undefined;
508
+ engine?: "postgres" | undefined;
474
509
 
475
510
  /**
476
511
  * The PostgreSQL table name for this collection.
@@ -520,9 +555,33 @@ export interface PostgresCollection<M extends Record<string, unknown> = Record<s
520
555
  export interface FirebaseCollection<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>
521
556
  extends BaseEntityCollection<M, USER> {
522
557
  /**
523
- * The driver for this collection. Must be set to `"firestore"`.
558
+ * The database engine for this collection. Must be set to `"firestore"`.
524
559
  */
525
- driver: "firestore";
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
+ * ```
583
+ */
584
+ path?: string;
526
585
 
527
586
  /**
528
587
  * You can add subcollections to your entity in the same way you define the root
@@ -544,9 +603,33 @@ export interface MongoDBCollection<M extends Record<string, unknown> = Record<st
544
603
  extends BaseEntityCollection<M, USER> {
545
604
 
546
605
  /**
547
- * 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`.
548
613
  */
549
- 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;
550
633
  }
551
634
 
552
635
  /**
@@ -562,33 +645,15 @@ export type EntityCollection<M extends Record<string, unknown> = Record<string,
562
645
  | FirebaseCollection<M, USER>
563
646
  | MongoDBCollection<M, USER>;
564
647
 
565
- // ── Capability intersection types ─────────────────────────────────────
566
- // Use these after a `getDataSourceCapabilities()` guard to safely access
567
- // driver-specific fields without coupling to a concrete driver type.
568
-
569
- /**
570
- * An EntityCollection that supports SQL-style relations (e.g. Postgres).
571
- * @deprecated Use `EntityCollection` directly — `table`, `relations`, and `securityRules` are now on `BaseEntityCollection`.
572
- */
573
- export type CollectionWithRelations<M extends Record<string, unknown> = Record<string, unknown>> =
574
- EntityCollection<M> & { table?: string; relations?: Relation[]; securityRules?: SecurityRule[] };
575
-
576
- /** An EntityCollection that supports subcollections (e.g. Firestore). */
577
- export type CollectionWithSubcollections<M extends Record<string, unknown> = Record<string, unknown>> =
578
- EntityCollection<M> & { subcollections?: () => EntityCollection<Record<string, unknown>>[] };
579
-
580
-
581
- // ── Type guards ───────────────────────────────────────────────────────
582
-
583
648
  /**
584
649
  * Type guard for PostgreSQL collections.
585
- * 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).
586
651
  * @group Models
587
652
  */
588
653
  export function isPostgresCollection<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>(
589
654
  collection: EntityCollection<M, USER>
590
655
  ): collection is PostgresCollection<M, USER> {
591
- return !collection.driver || collection.driver === "postgres";
656
+ return !collection.engine || collection.engine === "postgres";
592
657
  }
593
658
 
594
659
  /**
@@ -598,7 +663,7 @@ export function isPostgresCollection<M extends Record<string, unknown> = Record<
598
663
  export function isFirebaseCollection<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>(
599
664
  collection: EntityCollection<M, USER>
600
665
  ): collection is FirebaseCollection<M, USER> {
601
- return collection.driver === "firestore";
666
+ return collection.engine === "firestore";
602
667
  }
603
668
 
604
669
  /**
@@ -608,7 +673,40 @@ export function isFirebaseCollection<M extends Record<string, unknown> = Record<
608
673
  export function isMongoDBCollection<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User>(
609
674
  collection: EntityCollection<M, USER>
610
675
  ): collection is MongoDBCollection<M, USER> {
611
- 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;
612
710
  }
613
711
 
614
712
 
@@ -619,9 +717,9 @@ export function isMongoDBCollection<M extends Record<string, unknown> = Record<s
619
717
  export interface KanbanConfig<M extends Record<string, unknown> = Record<string, unknown>> {
620
718
  /**
621
719
  * Property key to use for Kanban board columns.
622
- * Must reference a string property with enumValues defined.
720
+ * Must reference a string property with `enum` values defined.
623
721
  * Entities will be grouped into columns based on this property's value.
624
- * The column order is determined by the order of enumValues in the property.
722
+ * The column order is determined by the order of `enum` values in the property.
625
723
  */
626
724
  columnProperty: Extract<keyof M, string> | (string & {});
627
725
  }
@@ -720,57 +818,8 @@ export interface SelectionController<M extends Record<string, unknown> = Record<
720
818
  toggleEntitySelection(entity: Entity<M>, newSelectedState?: boolean): void;
721
819
  }
722
820
 
723
- /**
724
- * Filter conditions in a `Query.where()` clause are specified using the
725
- * strings `<`, `<=`, `==`, `>=`, `>`, `array-contains`, `in`, and `array-contains-any`.
726
- * @group Models
727
- */
728
- export type WhereFilterOp =
729
- | "<"
730
- | "<="
731
- | "=="
732
- | "!="
733
- | ">="
734
- | ">"
735
- | "array-contains"
736
- | "in"
737
- | "not-in"
738
- | "array-contains-any";
739
-
740
- /**
741
- * Used to define filters applied in collections
742
- *
743
- * e.g. `{ age: [">=", 18] }`
744
- *
745
- * @group Models
746
- */
747
- export type FilterValues<Key extends string> =
748
- Partial<Record<Key, [WhereFilterOp, unknown] | [WhereFilterOp, unknown][]>>;
749
-
750
- /**
751
- * A pre-defined filter preset for quick access in the collection toolbar.
752
- * Users can select a preset to instantly apply a set of filters and
753
- * optionally a sort order.
754
- *
755
- * @group Models
756
- */
757
- export interface FilterPreset<Key extends string = string> {
758
- /**
759
- * Display label shown in the preset menu.
760
- * If omitted, a summary is auto-generated from the filter keys.
761
- */
762
- label?: string;
763
-
764
- /**
765
- * The filter values to apply when this preset is selected.
766
- */
767
- filterValues: FilterValues<Key>;
768
-
769
- /**
770
- * Optional sort override to apply alongside the filter values.
771
- */
772
- sort?: [Key, "asc" | "desc"];
773
- }
821
+ // Canonical filter types — re-exported from the single source-of-truth.
822
+ export type { WhereFilterOp, FilterValues, WireFilterValues, FilterPreset } from "./filter-operators";
774
823
 
775
824
 
776
825
  /**
@@ -927,13 +976,26 @@ export type SecurityOperation = "select" | "insert" | "update" | "delete" | "all
927
976
  * operation. Permissive rules are OR'd together (any one passing is enough).
928
977
  * Restrictive rules are AND'd (all must pass). This mirrors Supabase behavior.
929
978
  *
930
- * **Mutual exclusivity:** `ownerField`, `access`, and raw SQL (`using`/`withCheck`)
931
- * cannot be combined. The type system enforces this — attempting to set
932
- * 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).
933
990
  *
934
991
  * @group Models
935
992
  */
936
- export type SecurityRule = OwnerSecurityRule | PublicSecurityRule | RawSQLSecurityRule | RolesOnlySecurityRule;
993
+ export type SecurityRule =
994
+ | OwnerSecurityRule
995
+ | PublicSecurityRule
996
+ | StructuredSecurityRule
997
+ | RawSQLSecurityRule
998
+ | RolesOnlySecurityRule;
937
999
 
938
1000
  /**
939
1001
  * Shared fields for all SecurityRule variants.
@@ -1003,11 +1065,16 @@ export interface SecurityRuleBase {
1003
1065
  * application roles managed by Rebase, stored in the `rebase.user_roles`
1004
1066
  * table, and injected into each transaction via `auth.roles()`.
1005
1067
  *
1006
- * Generates a condition like:
1007
- * `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>']`
1008
1071
  *
1009
- * Can be combined with `ownerField`, `access`, or raw `using`/`withCheck`.
1010
- * 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.
1011
1078
  *
1012
1079
  * @example
1013
1080
  * // Only admins can delete
@@ -1062,6 +1129,8 @@ export interface OwnerSecurityRule extends SecurityRuleBase {
1062
1129
  access?: never;
1063
1130
  using?: never;
1064
1131
  withCheck?: never;
1132
+ condition?: never;
1133
+ check?: never;
1065
1134
  }
1066
1135
 
1067
1136
  /**
@@ -1086,12 +1155,61 @@ export interface PublicSecurityRule extends SecurityRuleBase {
1086
1155
  ownerField?: never;
1087
1156
  using?: never;
1088
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;
1089
1201
  }
1090
1202
 
1091
1203
  /**
1092
1204
  * Security rule using raw SQL expressions for full PostgreSQL RLS power.
1093
1205
  *
1094
- * 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`.
1095
1213
  *
1096
1214
  * You can reference columns via `{column_name}` which will be resolved to
1097
1215
  * `table.column_name` in the generated Drizzle code.
@@ -1129,6 +1247,8 @@ export interface RawSQLSecurityRule extends SecurityRuleBase {
1129
1247
 
1130
1248
  ownerField?: never;
1131
1249
  access?: never;
1250
+ condition?: never;
1251
+ check?: never;
1132
1252
  }
1133
1253
 
1134
1254
  /**
@@ -1149,6 +1269,8 @@ export interface RolesOnlySecurityRule extends SecurityRuleBase {
1149
1269
  access?: never;
1150
1270
  using?: never;
1151
1271
  withCheck?: never;
1272
+ condition?: never;
1273
+ check?: never;
1152
1274
  }
1153
1275
 
1154
1276
  /**
@@ -53,6 +53,90 @@ export interface DataSourceCapabilities {
53
53
  */
54
54
  export type DataSourceFeatures = Omit<DataSourceCapabilities, "key" | "label">;
55
55
 
56
+ /**
57
+ * The default data-source key, used when a collection does not name a
58
+ * `dataSource`. Shared by the frontend router and the backend driver
59
+ * registry so both agree on "the default database".
60
+ * @group Models
61
+ */
62
+ export const DEFAULT_DATA_SOURCE_KEY = "(default)";
63
+
64
+ /**
65
+ * How the *frontend* reaches a data source.
66
+ *
67
+ * - `"server"` — through the Rebase backend (the `RebaseClient`). The backend
68
+ * holds the actual database adapter and routes by data-source key. This is
69
+ * the default and covers Postgres, MongoDB, and any other server-mediated
70
+ * engine.
71
+ * - `"direct"` — straight from the client to the external backend via its own
72
+ * SDK driver (e.g. Firestore). The Rebase backend is not in the data path.
73
+ * - `"custom"` — a developer-supplied {@link DataDriver}, transport unspecified.
74
+ *
75
+ * @group Models
76
+ */
77
+ export type DataSourceTransport = "server" | "direct" | "custom";
78
+
79
+ /**
80
+ * Declarative definition of a data source — a named place data lives.
81
+ *
82
+ * Declared once and shared front and back: the frontend uses it to decide
83
+ * transport (client vs direct driver), the backend uses the same `key` to
84
+ * resolve a database adapter, and the editor derives capabilities from
85
+ * `engine`. Collections reference a definition by its `key` via
86
+ * `collection.dataSource`.
87
+ *
88
+ * @group Models
89
+ */
90
+ export interface DataSourceDefinition {
91
+ /**
92
+ * Unique identifier for this data source. Collections point at it via
93
+ * `dataSource`. Defaults to {@link DEFAULT_DATA_SOURCE_KEY}.
94
+ */
95
+ key: string;
96
+
97
+ /**
98
+ * The engine backing this data source (e.g. `"postgres"`, `"mongodb"`,
99
+ * `"firestore"`, or a custom id). Determines the
100
+ * {@link DataSourceCapabilities} surfaced in the editor.
101
+ */
102
+ engine: string;
103
+
104
+ /**
105
+ * How the frontend reaches this source. Defaults to `"server"`.
106
+ */
107
+ transport: DataSourceTransport;
108
+
109
+ /**
110
+ * The physical database/schema/Firestore-database within the engine.
111
+ * Threaded to drivers/adapters as the existing `databaseId` runtime
112
+ * parameter. Defaults to the engine's own default.
113
+ */
114
+ databaseId?: string;
115
+
116
+ /** Human-readable label for the UI. */
117
+ label?: string;
118
+ }
119
+
120
+ /**
121
+ * The resolved data source for a collection: the single source of truth that
122
+ * the frontend router, backend registry, and editor all derive from.
123
+ * Produced by `resolveDataSource(collection, registry)`.
124
+ *
125
+ * @group Models
126
+ */
127
+ export interface ResolvedDataSource {
128
+ /** Data-source key (routing key, shared front + back). */
129
+ key: string;
130
+ /** Engine backing the source (drives capabilities). */
131
+ engine: string;
132
+ /** Frontend transport. */
133
+ transport: DataSourceTransport;
134
+ /** Within-engine instance, if any (the `databaseId` runtime param). */
135
+ databaseId?: string;
136
+ /** Capabilities derived from {@link engine}. */
137
+ capabilities: DataSourceCapabilities;
138
+ }
139
+
56
140
  // ── Built-in driver capabilities ─────────────────────────────────────
57
141
 
58
142
  /** @group Models */
@@ -127,13 +211,13 @@ const CAPABILITIES_REGISTRY: Record<string, DataSourceCapabilities> = {
127
211
  };
128
212
 
129
213
  /**
130
- * Look up capabilities for a given driver key.
131
- * 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`.
132
216
  * @group Models
133
217
  */
134
- export function getDataSourceCapabilities(driver?: string): DataSourceCapabilities {
135
- if (!driver) return POSTGRES_CAPABILITIES; // postgres is the default driver
136
- 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;
137
221
  }
138
222
 
139
223
  /**
@@ -69,7 +69,7 @@ export interface EntityAction<M extends Record<string, unknown> = Record<string,
69
69
 
70
70
  export type EntityActionClickProps<M extends Record<string, unknown>, USER extends User = User> = {
71
71
  entity?: Entity<M>;
72
- context: RebaseContext<USER>;
72
+ context?: RebaseContext<USER>;
73
73
 
74
74
  path?: string;
75
75
  collection?: EntityCollection<M>;
@@ -93,7 +93,7 @@ export type EntityActionClickProps<M extends Record<string, unknown>, USER exten
93
93
  /**
94
94
  * If the action is rendered in the form, is it open in a side panel or full screen?
95
95
  */
96
- openEntityMode: "side_panel" | "full_screen" | "split" | "dialog";
96
+ openEntityMode?: "side_panel" | "full_screen" | "split" | "dialog";
97
97
 
98
98
  /**
99
99
  * Optional selection controller, present if the action is being called from a collection view