@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.
- package/dist/controllers/client.d.ts +53 -1
- package/dist/controllers/data.d.ts +19 -47
- package/dist/controllers/registry.d.ts +0 -2
- package/dist/index.es.js +157 -10
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +163 -9
- package/dist/index.umd.js.map +1 -1
- package/dist/types/auth_adapter.d.ts +5 -5
- package/dist/types/backend.d.ts +4 -0
- package/dist/types/collections.d.ts +158 -82
- package/dist/types/data_source.d.ts +3 -3
- package/dist/types/entity_callbacks.d.ts +18 -6
- package/dist/types/filter-operators.d.ts +108 -0
- package/dist/types/index.d.ts +3 -2
- package/dist/types/policy.d.ts +137 -0
- package/dist/types/properties.d.ts +115 -37
- package/dist/types/property_config.d.ts +1 -1
- package/dist/types/storage_source.d.ts +83 -0
- package/package.json +1 -1
- package/src/controllers/client.ts +61 -1
- package/src/controllers/data.ts +20 -59
- package/src/controllers/registry.ts +0 -2
- package/src/types/auth_adapter.ts +5 -5
- package/src/types/backend.ts +5 -0
- package/src/types/collections.ts +191 -106
- package/src/types/data_source.ts +5 -5
- package/src/types/entity_callbacks.ts +18 -7
- package/src/types/filter-operators.ts +167 -0
- package/src/types/index.ts +3 -2
- package/src/types/policy.ts +173 -0
- package/src/types/properties.ts +123 -38
- package/src/types/property_config.tsx +0 -1
- package/src/types/storage_source.ts +90 -0
- package/dist/types/backend_hooks.d.ts +0 -109
- package/dist/types/entity_overrides.d.ts +0 -10
- package/src/types/backend_hooks.ts +0 -114
- package/src/types/entity_overrides.tsx +0 -11
package/src/controllers/data.ts
CHANGED
|
@@ -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:
|
|
13
|
+
operator: WhereFilterOp;
|
|
38
14
|
value: unknown;
|
|
39
15
|
}
|
|
40
16
|
|
|
41
|
-
/**
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
|
57
|
-
*
|
|
58
|
-
*
|
|
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
|
-
*
|
|
66
|
-
*
|
|
34
|
+
* @example
|
|
35
|
+
* { status: ["==", "active"] }
|
|
67
36
|
* { age: [">=", 18] }
|
|
68
37
|
* { role: ["in", ["admin", "editor"]] }
|
|
69
|
-
* {
|
|
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?:
|
|
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
|
-
|
|
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:
|
|
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,
|
|
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:
|
|
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,
|
|
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,
|
|
8
|
-
*
|
|
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,
|
|
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", "
|
|
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 (
|
|
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
|
*
|
package/src/types/backend.ts
CHANGED
|
@@ -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
|
// =============================================================================
|
package/src/types/collections.ts
CHANGED
|
@@ -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
|
-
|
|
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).
|
|
81
|
-
* subcollections, RLS, column types).
|
|
81
|
+
* The database engine backing this collection (`"postgres"`, `"firestore"`,
|
|
82
|
+
* `"mongodb"`, or a custom id).
|
|
82
83
|
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
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
|
-
*
|
|
90
|
+
* Prefer setting {@link dataSource} and letting the engine be resolved.
|
|
88
91
|
*/
|
|
89
|
-
|
|
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.
|
|
303
|
-
*
|
|
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:
|
|
502
|
+
properties: PostgresProperties;
|
|
505
503
|
|
|
506
504
|
/**
|
|
507
|
-
* The
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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`,
|
|
968
|
-
* cannot be combined. The type system enforces
|
|
969
|
-
* conflicting fields will produce a compile-time
|
|
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 =
|
|
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
|
|
1044
|
-
*
|
|
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
|
-
*
|
|
1047
|
-
*
|
|
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
|
-
*
|
|
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
|
/**
|
package/src/types/data_source.ts
CHANGED
|
@@ -211,13 +211,13 @@ const CAPABILITIES_REGISTRY: Record<string, DataSourceCapabilities> = {
|
|
|
211
211
|
};
|
|
212
212
|
|
|
213
213
|
/**
|
|
214
|
-
* Look up capabilities for a given
|
|
215
|
-
* If `
|
|
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(
|
|
219
|
-
if (!
|
|
220
|
-
return CAPABILITIES_REGISTRY[
|
|
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
|
/**
|