@rebasepro/common 0.4.0 → 0.6.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/index.es.js +2064 -2426
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +2282 -2534
- package/dist/index.umd.js.map +1 -1
- package/dist/util/entities.d.ts +5 -1
- package/dist/util/permissions.d.ts +14 -6
- package/package.json +14 -14
- package/src/collections/CollectionRegistry.ts +2 -2
- package/src/collections/default-collections.ts +21 -9
- package/src/data/query_builder.ts +7 -3
- package/src/util/entities.ts +14 -4
- package/src/util/permissions.ts +25 -21
- package/src/util/relations.ts +5 -3
- package/src/util/resolutions.ts +3 -2
package/dist/util/entities.d.ts
CHANGED
|
@@ -29,9 +29,13 @@ export declare function getRelationFrom<M extends Record<string, unknown>>(entit
|
|
|
29
29
|
* Handles EntityRelation class instances, and plain objects
|
|
30
30
|
* with `__type === "relation"` or an `isEntityRelation()` method.
|
|
31
31
|
*
|
|
32
|
+
* When `propertyType` is `"relation"`, also accepts plain objects that
|
|
33
|
+
* have `id` and `path` fields — these are relation-shaped objects from
|
|
34
|
+
* edge cases in the data pipeline (REST fallback, stale cache, custom data source).
|
|
35
|
+
*
|
|
32
36
|
* Returns null if the value cannot be coerced.
|
|
33
37
|
*/
|
|
34
|
-
export declare function normalizeToEntityRelation(value: unknown): EntityRelation | null;
|
|
38
|
+
export declare function normalizeToEntityRelation(value: unknown, propertyType?: string): EntityRelation | null;
|
|
35
39
|
export declare function traverseValuesProperties<M extends Record<string, unknown>>(inputValues: Partial<EntityValues<M>>, properties: Properties, operation: (value: unknown, property: Property) => unknown): EntityValues<M> | undefined;
|
|
36
40
|
export declare function traverseValueProperty(inputValue: unknown, property: Property, operation: (value: unknown, property: Property) => unknown): unknown;
|
|
37
41
|
/**
|
|
@@ -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.6.0",
|
|
5
5
|
"description": "Awesome Firebase/Firestore-based headless open-source CMS",
|
|
6
6
|
"funding": {
|
|
7
7
|
"url": "https://github.com/sponsors/rebaseco"
|
|
@@ -41,27 +41,27 @@
|
|
|
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.6.0",
|
|
45
|
+
"@rebasepro/utils": "0.6.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@jest/globals": "^
|
|
48
|
+
"@jest/globals": "^30.4.1",
|
|
49
49
|
"@testing-library/react": "^16.3.2",
|
|
50
50
|
"@testing-library/user-event": "^14.6.1",
|
|
51
|
-
"@types/jest": "^
|
|
52
|
-
"@types/node": "^
|
|
51
|
+
"@types/jest": "^30.0.0",
|
|
52
|
+
"@types/node": "^25.9.3",
|
|
53
53
|
"@types/object-hash": "^3.0.6",
|
|
54
|
-
"@types/react": "^19.2.
|
|
54
|
+
"@types/react": "^19.2.17",
|
|
55
55
|
"@types/react-dom": "^19.2.3",
|
|
56
56
|
"@types/react-measure": "^2.0.12",
|
|
57
|
-
"@vitejs/plugin-react": "^
|
|
57
|
+
"@vitejs/plugin-react": "^6.0.2",
|
|
58
58
|
"babel-plugin-react-compiler": "beta",
|
|
59
|
-
"cross-env": "^
|
|
60
|
-
"jest": "^
|
|
61
|
-
"ts-jest": "^29.4.
|
|
62
|
-
"tsd": "^0.
|
|
63
|
-
"typescript": "^
|
|
64
|
-
"vite": "^
|
|
59
|
+
"cross-env": "^10.1.0",
|
|
60
|
+
"jest": "^30.4.2",
|
|
61
|
+
"ts-jest": "^29.4.11",
|
|
62
|
+
"tsd": "^0.33.0",
|
|
63
|
+
"typescript": "^6.0.3",
|
|
64
|
+
"vite": "^8.0.16"
|
|
65
65
|
},
|
|
66
66
|
"files": [
|
|
67
67
|
"dist",
|
|
@@ -258,7 +258,7 @@ export class CollectionRegistry {
|
|
|
258
258
|
joinPath: relProp.joinPath ?? relProp.relation?.joinPath,
|
|
259
259
|
onUpdate: relProp.onUpdate ?? relProp.relation?.onUpdate,
|
|
260
260
|
onDelete: relProp.onDelete ?? relProp.relation?.onDelete,
|
|
261
|
-
overrides: relProp.overrides ?? relProp.relation?.overrides
|
|
261
|
+
overrides: relProp.overrides ?? relProp.relation?.overrides
|
|
262
262
|
});
|
|
263
263
|
}
|
|
264
264
|
} else if (property.type === "map" && property.properties) {
|
|
@@ -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
|
}
|
|
@@ -11,6 +11,7 @@ export const defaultUsersCollection: PostgresCollection = {
|
|
|
11
11
|
name: "Users",
|
|
12
12
|
singularName: "User",
|
|
13
13
|
slug: "users",
|
|
14
|
+
auth: true,
|
|
14
15
|
table: "users",
|
|
15
16
|
schema: "rebase",
|
|
16
17
|
icon: "Users",
|
|
@@ -18,8 +19,10 @@ export const defaultUsersCollection: PostgresCollection = {
|
|
|
18
19
|
openEntityMode: "dialog",
|
|
19
20
|
disableDefaultActions: ["copy"],
|
|
20
21
|
securityRules: [
|
|
21
|
-
{ operation: "select",
|
|
22
|
-
|
|
22
|
+
{ operation: "select",
|
|
23
|
+
roles: ["admin"] },
|
|
24
|
+
{ operations: ["insert", "update", "delete"],
|
|
25
|
+
roles: ["admin"] }
|
|
23
26
|
],
|
|
24
27
|
sort: ["createdAt", "desc"],
|
|
25
28
|
properties: {
|
|
@@ -32,7 +35,8 @@ export const defaultUsersCollection: PostgresCollection = {
|
|
|
32
35
|
email: {
|
|
33
36
|
name: "Email",
|
|
34
37
|
type: "string",
|
|
35
|
-
validation: { required: true,
|
|
38
|
+
validation: { required: true,
|
|
39
|
+
unique: true }
|
|
36
40
|
},
|
|
37
41
|
displayName: {
|
|
38
42
|
name: "Name",
|
|
@@ -64,32 +68,39 @@ export const defaultUsersCollection: PostgresCollection = {
|
|
|
64
68
|
name: "Password Hash",
|
|
65
69
|
type: "string",
|
|
66
70
|
columnName: "password_hash",
|
|
67
|
-
ui: { hideFromCollection: true,
|
|
71
|
+
ui: { hideFromCollection: true,
|
|
72
|
+
disabled: { hidden: true } }
|
|
68
73
|
},
|
|
69
74
|
emailVerified: {
|
|
70
75
|
name: "Email Verified",
|
|
71
76
|
type: "boolean",
|
|
72
77
|
columnName: "email_verified",
|
|
73
78
|
defaultValue: false,
|
|
74
|
-
ui: { hideFromCollection: true,
|
|
79
|
+
ui: { hideFromCollection: true,
|
|
80
|
+
disabled: { hidden: true } }
|
|
75
81
|
},
|
|
76
82
|
emailVerificationToken: {
|
|
77
83
|
name: "Email Verification Token",
|
|
78
84
|
type: "string",
|
|
79
85
|
columnName: "email_verification_token",
|
|
80
|
-
ui: { hideFromCollection: true,
|
|
86
|
+
ui: { hideFromCollection: true,
|
|
87
|
+
disabled: { hidden: true } }
|
|
81
88
|
},
|
|
82
89
|
emailVerificationSentAt: {
|
|
83
90
|
name: "Email Verification Sent At",
|
|
84
91
|
type: "date",
|
|
85
92
|
columnName: "email_verification_sent_at",
|
|
86
|
-
ui: { hideFromCollection: true,
|
|
93
|
+
ui: { hideFromCollection: true,
|
|
94
|
+
disabled: { hidden: true } }
|
|
87
95
|
},
|
|
88
96
|
metadata: {
|
|
89
97
|
name: "Metadata",
|
|
90
98
|
type: "map",
|
|
99
|
+
keyValue: true,
|
|
100
|
+
properties: {},
|
|
91
101
|
defaultValue: {},
|
|
92
|
-
ui: { hideFromCollection: true,
|
|
102
|
+
ui: { hideFromCollection: true,
|
|
103
|
+
disabled: { hidden: true } }
|
|
93
104
|
},
|
|
94
105
|
createdAt: {
|
|
95
106
|
name: "Created At",
|
|
@@ -103,7 +114,8 @@ export const defaultUsersCollection: PostgresCollection = {
|
|
|
103
114
|
type: "date",
|
|
104
115
|
columnName: "updated_at",
|
|
105
116
|
autoValue: "on_update",
|
|
106
|
-
ui: { hideFromCollection: true,
|
|
117
|
+
ui: { hideFromCollection: true,
|
|
118
|
+
disabled: { hidden: true } }
|
|
107
119
|
}
|
|
108
120
|
},
|
|
109
121
|
listProperties: ["displayName", "email", "roles", "createdAt"],
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import { FindParams, Entity, FindResponse, CollectionAccessor, QueryBuilderInterface, FilterOperator, LogicalCondition, WhereValue, FilterCondition } from "@rebasepro/types";
|
|
2
2
|
|
|
3
3
|
export function or(...conditions: (FilterCondition | LogicalCondition)[]): LogicalCondition {
|
|
4
|
-
return { type: "or",
|
|
4
|
+
return { type: "or",
|
|
5
|
+
conditions };
|
|
5
6
|
}
|
|
6
7
|
|
|
7
8
|
export function and(...conditions: (FilterCondition | LogicalCondition)[]): LogicalCondition {
|
|
8
|
-
return { type: "and",
|
|
9
|
+
return { type: "and",
|
|
10
|
+
conditions };
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
export function cond(column: string, operator: FilterOperator, value: unknown): FilterCondition {
|
|
12
|
-
return { column,
|
|
14
|
+
return { column,
|
|
15
|
+
operator,
|
|
16
|
+
value };
|
|
13
17
|
}
|
|
14
18
|
|
|
15
19
|
export class QueryBuilder<M extends Record<string, unknown> = Record<string, unknown>> implements QueryBuilderInterface<M> {
|
package/src/util/entities.ts
CHANGED
|
@@ -156,9 +156,13 @@ export function getRelationFrom<M extends Record<string, unknown>>(entity: Entit
|
|
|
156
156
|
* Handles EntityRelation class instances, and plain objects
|
|
157
157
|
* with `__type === "relation"` or an `isEntityRelation()` method.
|
|
158
158
|
*
|
|
159
|
+
* When `propertyType` is `"relation"`, also accepts plain objects that
|
|
160
|
+
* have `id` and `path` fields — these are relation-shaped objects from
|
|
161
|
+
* edge cases in the data pipeline (REST fallback, stale cache, custom data source).
|
|
162
|
+
*
|
|
159
163
|
* Returns null if the value cannot be coerced.
|
|
160
164
|
*/
|
|
161
|
-
export function normalizeToEntityRelation(value: unknown): EntityRelation | null {
|
|
165
|
+
export function normalizeToEntityRelation(value: unknown, propertyType?: string): EntityRelation | null {
|
|
162
166
|
if (value instanceof EntityRelation) return value;
|
|
163
167
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
164
168
|
|
|
@@ -167,7 +171,8 @@ export function normalizeToEntityRelation(value: unknown): EntityRelation | null
|
|
|
167
171
|
obj.__type === "relation" ||
|
|
168
172
|
obj.__type === "reference" ||
|
|
169
173
|
(typeof obj.isEntityRelation === "function" && (obj.isEntityRelation as () => boolean)()) ||
|
|
170
|
-
(typeof obj.isEntityReference === "function" && (obj.isEntityReference as () => boolean)())
|
|
174
|
+
(typeof obj.isEntityReference === "function" && (obj.isEntityReference as () => boolean)()) ||
|
|
175
|
+
(propertyType === "relation" && typeof obj.id !== "undefined" && typeof obj.path === "string");
|
|
171
176
|
|
|
172
177
|
if (!isRelationLike) return null;
|
|
173
178
|
|
|
@@ -263,7 +268,9 @@ export interface RelationRefWithData extends RelationRef {
|
|
|
263
268
|
* Replaces inline `{ id, path, __type: "relation" }` object literals.
|
|
264
269
|
*/
|
|
265
270
|
export function createRelationRef(id: string | number, path: string): RelationRef {
|
|
266
|
-
return { id,
|
|
271
|
+
return { id,
|
|
272
|
+
path,
|
|
273
|
+
__type: "relation" };
|
|
267
274
|
}
|
|
268
275
|
|
|
269
276
|
/**
|
|
@@ -271,5 +278,8 @@ export function createRelationRef(id: string | number, path: string): RelationRe
|
|
|
271
278
|
* Used when entity data has been pre-fetched (e.g., via batch loading or JOINs).
|
|
272
279
|
*/
|
|
273
280
|
export function createRelationRefWithData(id: string | number, path: string, data: Entity): RelationRefWithData {
|
|
274
|
-
return { id,
|
|
281
|
+
return { id,
|
|
282
|
+
path,
|
|
283
|
+
__type: "relation",
|
|
284
|
+
data };
|
|
275
285
|
}
|
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
|
}
|
package/src/util/relations.ts
CHANGED
|
@@ -19,7 +19,8 @@ export function sanitizeRelation(
|
|
|
19
19
|
targetCollection = resolveCollection(rawTarget);
|
|
20
20
|
}
|
|
21
21
|
if (!targetCollection) {
|
|
22
|
-
targetCollection = { slug: rawTarget,
|
|
22
|
+
targetCollection = { slug: rawTarget,
|
|
23
|
+
name: rawTarget } as EntityCollection;
|
|
23
24
|
}
|
|
24
25
|
} else if (typeof rawTarget === "function") {
|
|
25
26
|
const evaluated = rawTarget();
|
|
@@ -28,7 +29,8 @@ export function sanitizeRelation(
|
|
|
28
29
|
targetCollection = resolveCollection(evaluated);
|
|
29
30
|
}
|
|
30
31
|
if (!targetCollection) {
|
|
31
|
-
targetCollection = { slug: evaluated,
|
|
32
|
+
targetCollection = { slug: evaluated,
|
|
33
|
+
name: evaluated } as EntityCollection;
|
|
32
34
|
}
|
|
33
35
|
} else {
|
|
34
36
|
targetCollection = evaluated;
|
|
@@ -295,7 +297,7 @@ export function resolvePropertyRelation({
|
|
|
295
297
|
joinPath: relProp.joinPath,
|
|
296
298
|
onUpdate: relProp.onUpdate,
|
|
297
299
|
onDelete: relProp.onDelete,
|
|
298
|
-
overrides: relProp.overrides
|
|
300
|
+
overrides: relProp.overrides
|
|
299
301
|
} as Relation;
|
|
300
302
|
}
|
|
301
303
|
|
package/src/util/resolutions.ts
CHANGED
|
@@ -354,7 +354,7 @@ export function getSubcollections<M extends Record<string, unknown> = Record<str
|
|
|
354
354
|
if (getDataSourceCapabilities(collection.driver).supportsRelations) {
|
|
355
355
|
const resolvedRelations = resolveCollectionRelations(collection);
|
|
356
356
|
const manyRelations = Object.values(resolvedRelations).filter((r: Relation) => r.cardinality === "many");
|
|
357
|
-
|
|
357
|
+
|
|
358
358
|
return manyRelations.map((r: Relation) => {
|
|
359
359
|
const target = r.target();
|
|
360
360
|
if (!target) return undefined;
|
|
@@ -377,7 +377,8 @@ export function getSubcollections<M extends Record<string, unknown> = Record<str
|
|
|
377
377
|
baseOverrides.singularName = customName;
|
|
378
378
|
}
|
|
379
379
|
|
|
380
|
-
const targetWithOverrides = { ...target,
|
|
380
|
+
const targetWithOverrides = { ...target,
|
|
381
|
+
...baseOverrides };
|
|
381
382
|
return (r.overrides ? mergeDeep(targetWithOverrides, r.overrides) : targetWithOverrides) as EntityCollection<Record<string, unknown>>;
|
|
382
383
|
}).filter((c: EntityCollection<Record<string, unknown>> | undefined): c is EntityCollection<Record<string, unknown>> => Boolean(c));
|
|
383
384
|
}
|