@rebasepro/common 0.3.0 → 0.5.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/README.md +73 -220
- package/dist/collections/default-collections.d.ts +5 -8
- package/dist/data/query_builder.d.ts +6 -2
- package/dist/index.es.js +153 -78
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +153 -78
- package/dist/index.umd.js.map +1 -1
- package/dist/util/permissions.d.ts +14 -6
- package/package.json +3 -3
- package/src/collections/CollectionRegistry.ts +1 -1
- package/src/collections/default-collections.ts +62 -30
- package/src/data/buildRebaseData.ts +28 -8
- package/src/data/query_builder.ts +37 -27
- package/src/util/permissions.ts +25 -21
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { Entity, EntityCollection, User } from "@rebasepro/types";
|
|
2
|
+
/**
|
|
3
|
+
* Minimal auth context for permission checking.
|
|
4
|
+
* Only requires the user object — avoids forcing callers to construct
|
|
5
|
+
* a full AuthController just to check permissions.
|
|
6
|
+
*/
|
|
7
|
+
export interface AuthContext<USER extends User = User> {
|
|
8
|
+
user: USER | null;
|
|
9
|
+
}
|
|
10
|
+
export declare function checkOperation<M extends Record<string, unknown>, USER extends User>(collection: EntityCollection<M>, authContext: AuthContext<USER>, entity: Entity<M> | null, targetOperation: "select" | "insert" | "update" | "delete"): boolean;
|
|
11
|
+
export declare function canReadCollection<M extends Record<string, unknown>, USER extends User>(collection: EntityCollection<M>, authContext: AuthContext<USER>): boolean;
|
|
12
|
+
export declare function canEditEntity<M extends Record<string, unknown>, USER extends User>(collection: EntityCollection<M>, authContext: AuthContext<USER>, path: string, entity: Entity<M> | null): boolean;
|
|
13
|
+
export declare function canCreateEntity<M extends Record<string, unknown>, USER extends User>(collection: EntityCollection<M>, authContext: AuthContext<USER>, path: string, entity: Entity<M> | null): boolean;
|
|
14
|
+
export declare function canDeleteEntity<M extends Record<string, unknown>, USER extends User>(collection: EntityCollection<M>, authContext: AuthContext<USER>, path: string, entity: Entity<M> | null): boolean;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/common",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.5.0",
|
|
5
5
|
"description": "Awesome Firebase/Firestore-based headless open-source CMS",
|
|
6
6
|
"funding": {
|
|
7
7
|
"url": "https://github.com/sponsors/rebaseco"
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"fast-equals": "6.0.0",
|
|
43
43
|
"json-logic-js": "^2.0.5",
|
|
44
|
-
"@rebasepro/types": "0.
|
|
45
|
-
"@rebasepro/utils": "0.
|
|
44
|
+
"@rebasepro/types": "0.5.0",
|
|
45
|
+
"@rebasepro/utils": "0.5.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@jest/globals": "^29.7.0",
|
|
@@ -305,7 +305,7 @@ export class CollectionRegistry {
|
|
|
305
305
|
const relation = relations.find(r => r.relationName === name);
|
|
306
306
|
if (relation) {
|
|
307
307
|
// we attach the resolved relation to the property
|
|
308
|
-
|
|
308
|
+
relationProperty.relation = relation;
|
|
309
309
|
} else {
|
|
310
310
|
console.warn(`Could not find relation for property '${key}' with relationName: ${name}`);
|
|
311
311
|
}
|
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
import { PostgresCollection } from "@rebasepro/types";
|
|
1
|
+
import type { PostgresCollection } from "@rebasepro/types";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Default users collection
|
|
4
|
+
* Default users collection.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* (Map keyed by slug, last-write-wins) so that developer-defined
|
|
10
|
-
* collections with the same slug override this default — no hardcoded
|
|
11
|
-
* string checks required.
|
|
6
|
+
* Prepended to the developer's collections array by the admin and server.
|
|
7
|
+
* Slug-based dedup (Map keyed by slug, last-write-wins) lets developers
|
|
8
|
+
* override by defining their own collection with `slug: "users"`.
|
|
12
9
|
*/
|
|
13
10
|
export const defaultUsersCollection: PostgresCollection = {
|
|
14
11
|
name: "Users",
|
|
@@ -18,62 +15,97 @@ export const defaultUsersCollection: PostgresCollection = {
|
|
|
18
15
|
schema: "rebase",
|
|
19
16
|
icon: "Users",
|
|
20
17
|
group: "Settings",
|
|
18
|
+
openEntityMode: "dialog",
|
|
19
|
+
disableDefaultActions: ["copy"],
|
|
20
|
+
securityRules: [
|
|
21
|
+
{ operation: "select", roles: ["admin"] },
|
|
22
|
+
{ operations: ["insert", "update", "delete"], roles: ["admin"] }
|
|
23
|
+
],
|
|
24
|
+
sort: ["createdAt", "desc"],
|
|
21
25
|
properties: {
|
|
22
26
|
id: {
|
|
23
27
|
name: "ID",
|
|
24
28
|
type: "string",
|
|
25
|
-
isId: "uuid"
|
|
29
|
+
isId: "uuid",
|
|
30
|
+
ui: { readOnly: true }
|
|
26
31
|
},
|
|
27
32
|
email: {
|
|
28
33
|
name: "Email",
|
|
29
34
|
type: "string",
|
|
30
35
|
validation: { required: true, unique: true }
|
|
31
36
|
},
|
|
32
|
-
|
|
33
|
-
name: "
|
|
37
|
+
displayName: {
|
|
38
|
+
name: "Name",
|
|
34
39
|
type: "string",
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
display_name: {
|
|
38
|
-
name: "Display Name",
|
|
39
|
-
type: "string"
|
|
40
|
+
columnName: "display_name",
|
|
41
|
+
validation: { required: true }
|
|
40
42
|
},
|
|
41
|
-
|
|
43
|
+
photoURL: {
|
|
42
44
|
name: "Photo URL",
|
|
43
|
-
type: "string"
|
|
45
|
+
type: "string",
|
|
46
|
+
columnName: "photo_url",
|
|
47
|
+
url: "image"
|
|
48
|
+
},
|
|
49
|
+
roles: {
|
|
50
|
+
name: "Roles",
|
|
51
|
+
type: "array",
|
|
52
|
+
columnType: "text[]",
|
|
53
|
+
of: {
|
|
54
|
+
name: "Role",
|
|
55
|
+
type: "string",
|
|
56
|
+
enum: {
|
|
57
|
+
admin: "Admin",
|
|
58
|
+
editor: "Editor",
|
|
59
|
+
viewer: "Viewer"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
passwordHash: {
|
|
64
|
+
name: "Password Hash",
|
|
65
|
+
type: "string",
|
|
66
|
+
columnName: "password_hash",
|
|
67
|
+
ui: { hideFromCollection: true, disabled: { hidden: true } }
|
|
44
68
|
},
|
|
45
|
-
|
|
69
|
+
emailVerified: {
|
|
46
70
|
name: "Email Verified",
|
|
47
71
|
type: "boolean",
|
|
48
|
-
|
|
72
|
+
columnName: "email_verified",
|
|
73
|
+
defaultValue: false,
|
|
74
|
+
ui: { hideFromCollection: true, disabled: { hidden: true } }
|
|
49
75
|
},
|
|
50
|
-
|
|
76
|
+
emailVerificationToken: {
|
|
51
77
|
name: "Email Verification Token",
|
|
52
78
|
type: "string",
|
|
53
|
-
|
|
79
|
+
columnName: "email_verification_token",
|
|
80
|
+
ui: { hideFromCollection: true, disabled: { hidden: true } }
|
|
54
81
|
},
|
|
55
|
-
|
|
82
|
+
emailVerificationSentAt: {
|
|
56
83
|
name: "Email Verification Sent At",
|
|
57
84
|
type: "date",
|
|
58
|
-
|
|
85
|
+
columnName: "email_verification_sent_at",
|
|
86
|
+
ui: { hideFromCollection: true, disabled: { hidden: true } }
|
|
59
87
|
},
|
|
60
88
|
metadata: {
|
|
61
89
|
name: "Metadata",
|
|
62
90
|
type: "map",
|
|
63
91
|
defaultValue: {},
|
|
64
|
-
ui: { hideFromCollection: true }
|
|
92
|
+
ui: { hideFromCollection: true, disabled: { hidden: true } }
|
|
65
93
|
},
|
|
66
|
-
|
|
94
|
+
createdAt: {
|
|
67
95
|
name: "Created At",
|
|
68
96
|
type: "date",
|
|
97
|
+
columnName: "created_at",
|
|
69
98
|
autoValue: "on_create",
|
|
70
|
-
ui: { readOnly: true
|
|
99
|
+
ui: { readOnly: true }
|
|
71
100
|
},
|
|
72
|
-
|
|
101
|
+
updatedAt: {
|
|
73
102
|
name: "Updated At",
|
|
74
103
|
type: "date",
|
|
104
|
+
columnName: "updated_at",
|
|
75
105
|
autoValue: "on_update",
|
|
76
|
-
ui: {
|
|
106
|
+
ui: { hideFromCollection: true, disabled: { hidden: true } }
|
|
77
107
|
}
|
|
78
|
-
}
|
|
108
|
+
},
|
|
109
|
+
listProperties: ["displayName", "email", "roles", "createdAt"],
|
|
110
|
+
propertiesOrder: ["id", "email", "displayName", "roles", "createdAt"]
|
|
79
111
|
};
|
|
@@ -8,7 +8,10 @@ import {
|
|
|
8
8
|
EntityValues,
|
|
9
9
|
FilterValues,
|
|
10
10
|
WhereFilterOp,
|
|
11
|
-
WhereFieldValue
|
|
11
|
+
WhereFieldValue,
|
|
12
|
+
WhereFilterOpShort,
|
|
13
|
+
LogicalCondition,
|
|
14
|
+
WhereValue
|
|
12
15
|
} from "@rebasepro/types";
|
|
13
16
|
import { toSnakeCase } from "@rebasepro/utils";
|
|
14
17
|
import { QueryBuilder } from "./query_builder";
|
|
@@ -69,11 +72,18 @@ function convertWhereToFilter(where?: Record<string, WhereFieldValue>): FilterVa
|
|
|
69
72
|
continue;
|
|
70
73
|
}
|
|
71
74
|
|
|
72
|
-
// Handle tuple
|
|
73
|
-
if (Array.isArray(rawValue)
|
|
74
|
-
const [
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
// Handle tuple or array of tuples
|
|
76
|
+
if (Array.isArray(rawValue)) {
|
|
77
|
+
const conditions: [WhereFilterOpShort, unknown][] = Array.isArray(rawValue[0])
|
|
78
|
+
? (rawValue as [WhereFilterOpShort, unknown][])
|
|
79
|
+
: [rawValue as [WhereFilterOpShort, unknown]];
|
|
80
|
+
|
|
81
|
+
const mappedConditions: [WhereFilterOp, unknown][] = conditions.map(([rawOp, val]) => {
|
|
82
|
+
const mappedOp = operatorMap[rawOp] ?? "==";
|
|
83
|
+
return [mappedOp, val];
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
filter[field] = Array.isArray(rawValue[0]) ? mappedConditions : mappedConditions[0];
|
|
77
87
|
continue;
|
|
78
88
|
}
|
|
79
89
|
|
|
@@ -190,6 +200,12 @@ values: {} as Record<string, unknown> }
|
|
|
190
200
|
});
|
|
191
201
|
},
|
|
192
202
|
|
|
203
|
+
deleteAll: driver.deleteAll
|
|
204
|
+
? async (): Promise<void> => {
|
|
205
|
+
return driver.deleteAll!(slug);
|
|
206
|
+
}
|
|
207
|
+
: undefined,
|
|
208
|
+
|
|
193
209
|
count: driver.countEntities
|
|
194
210
|
? async (params?: FindParams): Promise<number> => {
|
|
195
211
|
return driver.countEntities!({
|
|
@@ -238,8 +254,12 @@ values: {} as Record<string, unknown> }
|
|
|
238
254
|
} : undefined,
|
|
239
255
|
|
|
240
256
|
// Fluent Query Builder
|
|
241
|
-
where(
|
|
242
|
-
|
|
257
|
+
where(columnOrCondition: string | LogicalCondition, operator?: WhereFilterOpShort, value?: unknown) {
|
|
258
|
+
const builder = new QueryBuilder<M>(accessor);
|
|
259
|
+
if (typeof columnOrCondition === "object") {
|
|
260
|
+
return builder.where(columnOrCondition);
|
|
261
|
+
}
|
|
262
|
+
return builder.where(columnOrCondition as keyof M & string, operator!, value as WhereValue<M[keyof M & string]>);
|
|
243
263
|
},
|
|
244
264
|
orderBy(column: keyof M & string, ascending?: "asc" | "desc") {
|
|
245
265
|
return new QueryBuilder<M>(accessor).orderBy(column, ascending);
|
|
@@ -1,21 +1,15 @@
|
|
|
1
|
-
import { FindParams, Entity, FindResponse, CollectionAccessor, QueryBuilderInterface, FilterOperator } from "@rebasepro/types";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
case "<=": return "lte";
|
|
14
|
-
case "array-contains": return "cs";
|
|
15
|
-
case "array-contains-any": return "csa";
|
|
16
|
-
case "not-in": return "nin";
|
|
17
|
-
default: return op;
|
|
18
|
-
}
|
|
1
|
+
import { FindParams, Entity, FindResponse, CollectionAccessor, QueryBuilderInterface, FilterOperator, LogicalCondition, WhereValue, FilterCondition } from "@rebasepro/types";
|
|
2
|
+
|
|
3
|
+
export function or(...conditions: (FilterCondition | LogicalCondition)[]): LogicalCondition {
|
|
4
|
+
return { type: "or", conditions };
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function and(...conditions: (FilterCondition | LogicalCondition)[]): LogicalCondition {
|
|
8
|
+
return { type: "and", conditions };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function cond(column: string, operator: FilterOperator, value: unknown): FilterCondition {
|
|
12
|
+
return { column, operator, value };
|
|
19
13
|
}
|
|
20
14
|
|
|
21
15
|
export class QueryBuilder<M extends Record<string, unknown> = Record<string, unknown>> implements QueryBuilderInterface<M> {
|
|
@@ -28,22 +22,38 @@ export class QueryBuilder<M extends Record<string, unknown> = Record<string, unk
|
|
|
28
22
|
* @example
|
|
29
23
|
* client.collection('users').where('age', '>=', 18).find()
|
|
30
24
|
*/
|
|
31
|
-
where
|
|
25
|
+
where<K extends keyof M & string>(column: K, operator: FilterOperator, value: WhereValue<M[K]>): this;
|
|
26
|
+
where(logicalCondition: LogicalCondition): this;
|
|
27
|
+
where(columnOrCondition: string | LogicalCondition, operator?: FilterOperator, value?: unknown): this {
|
|
28
|
+
// Handle LogicalCondition signature
|
|
29
|
+
if (typeof columnOrCondition === "object" && columnOrCondition !== null && "type" in columnOrCondition) {
|
|
30
|
+
this.params.logical = columnOrCondition as LogicalCondition;
|
|
31
|
+
return this;
|
|
32
|
+
}
|
|
33
|
+
|
|
32
34
|
if (!this.params.where) {
|
|
33
35
|
this.params.where = {};
|
|
34
36
|
}
|
|
35
37
|
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
+
const column = columnOrCondition as string;
|
|
39
|
+
const condition: [FilterOperator, unknown] = [operator!, value];
|
|
40
|
+
const existing = this.params.where[column];
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
if (existing === undefined) {
|
|
43
|
+
this.params.where[column] = condition;
|
|
44
|
+
} else if (Array.isArray(existing) && existing.length > 0 && Array.isArray(existing[0])) {
|
|
45
|
+
(this.params.where[column] as [FilterOperator, unknown][]).push(condition);
|
|
46
|
+
} else {
|
|
47
|
+
// Convert existing single tuple/value into array of tuples
|
|
48
|
+
let firstCondition: [FilterOperator, unknown];
|
|
49
|
+
if (Array.isArray(existing) && existing.length === 2 && typeof existing[0] === "string") {
|
|
50
|
+
firstCondition = existing as [FilterOperator, unknown];
|
|
51
|
+
} else {
|
|
52
|
+
firstCondition = ["==", existing];
|
|
53
|
+
}
|
|
54
|
+
this.params.where[column] = [firstCondition, condition];
|
|
44
55
|
}
|
|
45
56
|
|
|
46
|
-
this.params.where[column] = mappedOp === "eq" ? String(formattedValue) : `${mappedOp}.${formattedValue}`;
|
|
47
57
|
return this;
|
|
48
58
|
}
|
|
49
59
|
|
package/src/util/permissions.ts
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CollectionWithRelations, Entity, EntityCollection, getDataSourceCapabilities, SecurityRule, User } from "@rebasepro/types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Minimal auth context for permission checking.
|
|
5
|
+
* Only requires the user object — avoids forcing callers to construct
|
|
6
|
+
* a full AuthController just to check permissions.
|
|
7
|
+
*/
|
|
8
|
+
export interface AuthContext<USER extends User = User> {
|
|
9
|
+
user: USER | null;
|
|
10
|
+
}
|
|
2
11
|
|
|
3
|
-
function evaluateAST<USER extends User, M extends Record<string, unknown>>(sqlString: string, auth:
|
|
12
|
+
function evaluateAST<USER extends User, M extends Record<string, unknown>>(sqlString: string, auth: AuthContext<USER>, entity: Entity<M> | null): boolean {
|
|
4
13
|
// This is a client-side SQL evaluator used *only* for optimistic UI updates.
|
|
5
14
|
// It parses basic AND / OR statements to evaluate RLS without backend roundtrips.
|
|
6
15
|
if (!entity) return true;
|
|
@@ -111,7 +120,7 @@ function evaluateAST<USER extends User, M extends Record<string, unknown>>(sqlSt
|
|
|
111
120
|
return true; // Optimistic fallback for anything else
|
|
112
121
|
}
|
|
113
122
|
|
|
114
|
-
function evaluateRule<USER extends User, M extends Record<string, unknown>>(rule: SecurityRule, auth:
|
|
123
|
+
function evaluateRule<USER extends User, M extends Record<string, unknown>>(rule: SecurityRule, auth: AuthContext<USER>, entity: Entity<M> | null): boolean {
|
|
115
124
|
|
|
116
125
|
if (rule.access === "public") return true;
|
|
117
126
|
|
|
@@ -137,15 +146,12 @@ function evaluateRule<USER extends User, M extends Record<string, unknown>>(rule
|
|
|
137
146
|
|
|
138
147
|
export function checkOperation<M extends Record<string, unknown>, USER extends User>(
|
|
139
148
|
collection: EntityCollection<M>,
|
|
140
|
-
|
|
149
|
+
authContext: AuthContext<USER>,
|
|
141
150
|
entity: Entity<M> | null,
|
|
142
151
|
targetOperation: "select" | "insert" | "update" | "delete"
|
|
143
152
|
): boolean {
|
|
144
153
|
const securityRules = getDataSourceCapabilities(collection.driver).supportsRLS ? (collection as CollectionWithRelations).securityRules : undefined;
|
|
145
154
|
if (!securityRules || securityRules.length === 0) {
|
|
146
|
-
// According to our plan: Postgres RLS implicitly denies if enabled without rules.
|
|
147
|
-
// But for Rebase we default to true if securityRules is undefined,
|
|
148
|
-
// so as not to break everything without rules. Let's assume true for now.
|
|
149
155
|
return true;
|
|
150
156
|
}
|
|
151
157
|
|
|
@@ -158,15 +164,13 @@ export function checkOperation<M extends Record<string, unknown>, USER extends U
|
|
|
158
164
|
|
|
159
165
|
if (applicableRules.length === 0) return false;
|
|
160
166
|
|
|
161
|
-
|
|
162
|
-
const userRoleIds = authController.user?.roles ?? [];
|
|
167
|
+
const userRoleIds = authContext.user?.roles ?? [];
|
|
163
168
|
const userRoles = [...userRoleIds, "public"];
|
|
164
169
|
const roleApplicableRules = applicableRules.filter((rule: SecurityRule) => {
|
|
165
|
-
if (!rule.roles || rule.roles.length === 0) return true;
|
|
170
|
+
if (!rule.roles || rule.roles.length === 0) return true;
|
|
166
171
|
return rule.roles.some((r: string) => userRoles.includes(r));
|
|
167
172
|
});
|
|
168
173
|
|
|
169
|
-
// If no rules apply to this user's roles, the operation is implicitly denied.
|
|
170
174
|
if (roleApplicableRules.length === 0) return false;
|
|
171
175
|
|
|
172
176
|
let grantedByPermissive = false;
|
|
@@ -174,11 +178,11 @@ export function checkOperation<M extends Record<string, unknown>, USER extends U
|
|
|
174
178
|
|
|
175
179
|
for (const rule of roleApplicableRules) {
|
|
176
180
|
const mode = rule.mode || "permissive";
|
|
177
|
-
const passed = evaluateRule(rule,
|
|
181
|
+
const passed = evaluateRule(rule, authContext, entity);
|
|
178
182
|
|
|
179
183
|
if (mode === "restrictive" && !passed) {
|
|
180
184
|
deniedByRestrictive = true;
|
|
181
|
-
break;
|
|
185
|
+
break;
|
|
182
186
|
}
|
|
183
187
|
|
|
184
188
|
if (mode === "permissive" && passed) {
|
|
@@ -199,37 +203,37 @@ export function checkOperation<M extends Record<string, unknown>, USER extends U
|
|
|
199
203
|
export function canReadCollection<M extends Record<string, unknown>, USER extends User>
|
|
200
204
|
(
|
|
201
205
|
collection: EntityCollection<M>,
|
|
202
|
-
|
|
206
|
+
authContext: AuthContext<USER>
|
|
203
207
|
): boolean {
|
|
204
|
-
return checkOperation(collection,
|
|
208
|
+
return checkOperation(collection, authContext, null, "select");
|
|
205
209
|
}
|
|
206
210
|
|
|
207
211
|
export function canEditEntity<M extends Record<string, unknown>, USER extends User>
|
|
208
212
|
(
|
|
209
213
|
collection: EntityCollection<M>,
|
|
210
|
-
|
|
214
|
+
authContext: AuthContext<USER>,
|
|
211
215
|
path: string,
|
|
212
216
|
entity: Entity<M> | null
|
|
213
217
|
): boolean {
|
|
214
|
-
return checkOperation(collection,
|
|
218
|
+
return checkOperation(collection, authContext, entity, "update");
|
|
215
219
|
}
|
|
216
220
|
|
|
217
221
|
export function canCreateEntity<M extends Record<string, unknown>, USER extends User>
|
|
218
222
|
(
|
|
219
223
|
collection: EntityCollection<M>,
|
|
220
|
-
|
|
224
|
+
authContext: AuthContext<USER>,
|
|
221
225
|
path: string,
|
|
222
226
|
entity: Entity<M> | null
|
|
223
227
|
): boolean {
|
|
224
|
-
return checkOperation(collection,
|
|
228
|
+
return checkOperation(collection, authContext, entity, "insert");
|
|
225
229
|
}
|
|
226
230
|
|
|
227
231
|
export function canDeleteEntity<M extends Record<string, unknown>, USER extends User>
|
|
228
232
|
(
|
|
229
233
|
collection: EntityCollection<M>,
|
|
230
|
-
|
|
234
|
+
authContext: AuthContext<USER>,
|
|
231
235
|
path: string,
|
|
232
236
|
entity: Entity<M> | null
|
|
233
237
|
): boolean {
|
|
234
|
-
return checkOperation(collection,
|
|
238
|
+
return checkOperation(collection, authContext, entity, "delete");
|
|
235
239
|
}
|