@rebasepro/common 0.7.0 → 0.9.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 +4 -4
- package/dist/collections/CollectionRegistry.d.ts +30 -15
- package/dist/collections/default-collections.d.ts +255 -2
- package/dist/data/buildRebaseData.d.ts +30 -2
- package/dist/data/buildRoutedRebaseData.d.ts +14 -9
- package/dist/data/filter-dialect.d.ts +75 -0
- package/dist/data/query_builder.d.ts +4 -4
- package/dist/data/resolveDataSource.d.ts +8 -8
- package/dist/data/sort-dialect.d.ts +41 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.es.js +1125 -299
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +1138 -303
- package/dist/index.umd.js.map +1 -1
- package/dist/util/builders.d.ts +52 -42
- package/dist/util/callbacks.d.ts +8 -3
- package/dist/util/collections.d.ts +4 -4
- package/dist/util/entities.d.ts +2 -2
- package/dist/util/filter-operator-resolution.d.ts +32 -0
- package/dist/util/index.d.ts +2 -0
- package/dist/util/navigation_from_path.d.ts +4 -4
- package/dist/util/navigation_utils.d.ts +3 -3
- package/dist/util/parent_references_from_path.d.ts +2 -2
- package/dist/util/permissions.d.ts +30 -6
- package/dist/util/policy/evaluatePolicy.d.ts +31 -0
- package/dist/util/policy/index.d.ts +3 -0
- package/dist/util/policy/policyToPostgres.d.ts +22 -0
- package/dist/util/policy/securityRuleToConditions.d.ts +24 -0
- package/dist/util/policy/sqlToPolicy.d.ts +20 -0
- package/dist/util/references.d.ts +2 -2
- package/dist/util/relations.d.ts +5 -5
- package/dist/util/resolutions.d.ts +2 -2
- package/dist/util/storage.d.ts +26 -1
- package/package.json +13 -13
- package/src/collections/CollectionRegistry.ts +92 -61
- package/src/collections/default-collections.ts +4 -4
- package/src/data/buildRebaseData.ts +336 -172
- package/src/data/buildRoutedRebaseData.ts +22 -16
- package/src/data/filter-dialect.ts +403 -0
- package/src/data/query_builder.ts +19 -10
- package/src/data/resolveDataSource.ts +10 -10
- package/src/data/sort-dialect.ts +56 -0
- package/src/index.ts +2 -0
- package/src/util/builders.ts +87 -84
- package/src/util/callbacks.ts +15 -8
- package/src/util/collections.ts +4 -4
- package/src/util/entities.ts +4 -4
- package/src/util/filter-operator-resolution.ts +81 -0
- package/src/util/index.ts +2 -0
- package/src/util/navigation_from_path.ts +4 -4
- package/src/util/navigation_utils.ts +8 -8
- package/src/util/parent_references_from_path.ts +3 -3
- package/src/util/permissions.test.ts +7 -5
- package/src/util/permissions.ts +90 -163
- package/src/util/policy/evaluatePolicy.ts +152 -0
- package/src/util/policy/index.ts +3 -0
- package/src/util/policy/policyToPostgres.ts +165 -0
- package/src/util/policy/securityRuleToConditions.ts +67 -0
- package/src/util/policy/sqlToPolicy.ts +88 -0
- package/src/util/references.ts +3 -3
- package/src/util/relations.ts +19 -20
- package/src/util/resolutions.ts +11 -11
- package/src/util/storage.ts +34 -1
package/src/util/builders.ts
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
|
-
AdditionalFieldDelegate,
|
|
3
2
|
ArrayProperty,
|
|
4
3
|
BooleanProperty,
|
|
5
4
|
DateProperty,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
EnumValues,
|
|
5
|
+
CollectionConfig,
|
|
6
|
+
FirebaseCollectionConfig,
|
|
7
|
+
FirebaseProperties,
|
|
10
8
|
GeopointProperty,
|
|
9
|
+
InferEntityType,
|
|
11
10
|
MapProperty,
|
|
12
|
-
|
|
11
|
+
MongoDBCollectionConfig,
|
|
12
|
+
MongoProperties,
|
|
13
|
+
NumberProperty,
|
|
14
|
+
PostgresCollectionConfig,
|
|
15
|
+
PostgresProperties,
|
|
13
16
|
Property,
|
|
14
17
|
ReferenceProperty,
|
|
15
18
|
StringProperty,
|
|
@@ -18,112 +21,112 @@ import {
|
|
|
18
21
|
|
|
19
22
|
|
|
20
23
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
+
* @deprecated Use {@link defineCollection} instead — it infers property
|
|
25
|
+
* types automatically (autocomplete on `titleProperty`, `sort`,
|
|
26
|
+
* `propertiesOrder`, callbacks) without manual generics.
|
|
27
|
+
* `buildCollection` is kept for FireCMS migration compatibility and will
|
|
28
|
+
* be removed before 1.0.
|
|
29
|
+
*
|
|
24
30
|
* @group Builder
|
|
25
31
|
*/
|
|
26
32
|
export function buildCollection<
|
|
27
33
|
M extends Record<string, unknown> = Record<string, unknown>,
|
|
28
34
|
USER extends User = User>
|
|
29
35
|
(
|
|
30
|
-
collection:
|
|
31
|
-
):
|
|
36
|
+
collection: CollectionConfig<M, USER>
|
|
37
|
+
): CollectionConfig<M, USER> {
|
|
32
38
|
return collection;
|
|
33
39
|
}
|
|
34
40
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
*/
|
|
41
|
-
export function buildProperty<T, P extends Property = Property>(
|
|
42
|
-
property: P
|
|
43
|
-
):
|
|
44
|
-
P extends StringProperty ? StringProperty :
|
|
45
|
-
P extends NumberProperty ? NumberProperty :
|
|
46
|
-
P extends BooleanProperty ? BooleanProperty :
|
|
47
|
-
P extends DateProperty ? DateProperty :
|
|
48
|
-
P extends GeopointProperty ? GeopointProperty :
|
|
49
|
-
P extends ReferenceProperty ? ReferenceProperty :
|
|
50
|
-
P extends ArrayProperty ? ArrayProperty :
|
|
51
|
-
P extends MapProperty ? MapProperty : never {
|
|
52
|
-
|
|
53
|
-
// SAFETY: Identity function — P is a subtype of the conditional return type by definition
|
|
54
|
-
return property as unknown as ReturnType<typeof buildProperty<T, P>>;
|
|
55
|
-
}
|
|
41
|
+
// ── defineCollection ─────────────────────────────────────────────────────
|
|
42
|
+
// A smarter builder that uses `const` type-parameter inference (TS 5.0+)
|
|
43
|
+
// to capture literal property types automatically. This gives you
|
|
44
|
+
// autocomplete on `titleProperty`, `sort`, `propertiesOrder`, `fixedFilter`,
|
|
45
|
+
// callbacks, etc. — without writing `as const` or passing manual generics.
|
|
56
46
|
|
|
57
47
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
48
|
+
* Define a PostgreSQL-backed collection with full type inference.
|
|
49
|
+
*
|
|
50
|
+
* The `const P` generic captures literal property types from your
|
|
51
|
+
* `properties` object, which enables autocomplete on `titleProperty`,
|
|
52
|
+
* `sort`, `propertiesOrder`, `fixedFilter`, and entity callbacks.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts
|
|
56
|
+
* const products = defineCollection({
|
|
57
|
+
* name: "Products",
|
|
58
|
+
* slug: "products",
|
|
59
|
+
* table: "products",
|
|
60
|
+
* properties: {
|
|
61
|
+
* name: { name: "Name", type: "string", validation: { required: true } },
|
|
62
|
+
* price: { name: "Price", type: "number" },
|
|
63
|
+
* },
|
|
64
|
+
* titleProperty: "name", // ✅ autocomplete: "name" | "price"
|
|
65
|
+
* sort: ["price", "asc"], // ✅ autocomplete on first element
|
|
66
|
+
* });
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
61
69
|
* @group Builder
|
|
62
70
|
*/
|
|
63
|
-
export function
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
71
|
+
export function defineCollection<
|
|
72
|
+
const P extends PostgresProperties,
|
|
73
|
+
USER extends User = User
|
|
74
|
+
>(
|
|
75
|
+
collection: Omit<PostgresCollectionConfig<InferEntityType<P>, USER>, "properties"> & { properties: P }
|
|
76
|
+
): PostgresCollectionConfig<InferEntityType<P>, USER> & { properties: P };
|
|
68
77
|
|
|
69
78
|
/**
|
|
70
|
-
*
|
|
71
|
-
* the properties keys.
|
|
72
|
-
* @param propertiesOrBuilder
|
|
79
|
+
* Define a Firestore-backed collection with full type inference.
|
|
73
80
|
* @group Builder
|
|
74
81
|
*/
|
|
75
|
-
export function
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
82
|
+
export function defineCollection<
|
|
83
|
+
const P extends FirebaseProperties,
|
|
84
|
+
USER extends User = User
|
|
85
|
+
>(
|
|
86
|
+
collection: Omit<FirebaseCollectionConfig<InferEntityType<P>, USER>, "properties"> & { properties: P }
|
|
87
|
+
): FirebaseCollectionConfig<InferEntityType<P>, USER> & { properties: P };
|
|
80
88
|
|
|
81
89
|
/**
|
|
82
|
-
*
|
|
83
|
-
* the properties keys.
|
|
84
|
-
* @param enumValues
|
|
90
|
+
* Define a MongoDB-backed collection with full type inference.
|
|
85
91
|
* @group Builder
|
|
86
92
|
*/
|
|
87
|
-
export function
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
93
|
+
export function defineCollection<
|
|
94
|
+
const P extends MongoProperties,
|
|
95
|
+
USER extends User = User
|
|
96
|
+
>(
|
|
97
|
+
collection: Omit<MongoDBCollectionConfig<InferEntityType<P>, USER>, "properties"> & { properties: P }
|
|
98
|
+
): MongoDBCollectionConfig<InferEntityType<P>, USER> & { properties: P };
|
|
92
99
|
|
|
93
100
|
/**
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* @param enumValueConfig
|
|
97
|
-
* @group Builder
|
|
101
|
+
* Implementation — delegates to the correct overload at the type level.
|
|
102
|
+
* At runtime this is a plain identity function.
|
|
98
103
|
*/
|
|
99
|
-
export function
|
|
100
|
-
|
|
101
|
-
):
|
|
102
|
-
return
|
|
104
|
+
export function defineCollection(
|
|
105
|
+
collection: CollectionConfig
|
|
106
|
+
): CollectionConfig {
|
|
107
|
+
return collection;
|
|
103
108
|
}
|
|
104
109
|
|
|
105
110
|
/**
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
111
|
+
* @deprecated Use plain typed property objects with {@link defineCollection}
|
|
112
|
+
* instead — `defineCollection` infers property types automatically, making
|
|
113
|
+
* this wrapper unnecessary. `buildProperty` is kept for FireCMS migration
|
|
114
|
+
* compatibility and will be removed before 1.0.
|
|
115
|
+
*
|
|
109
116
|
* @group Builder
|
|
110
117
|
*/
|
|
111
|
-
export function
|
|
112
|
-
|
|
113
|
-
):
|
|
114
|
-
|
|
115
|
-
|
|
118
|
+
export function buildProperty<T, P extends Property = Property>(
|
|
119
|
+
property: P
|
|
120
|
+
):
|
|
121
|
+
P extends StringProperty ? StringProperty :
|
|
122
|
+
P extends NumberProperty ? NumberProperty :
|
|
123
|
+
P extends BooleanProperty ? BooleanProperty :
|
|
124
|
+
P extends DateProperty ? DateProperty :
|
|
125
|
+
P extends GeopointProperty ? GeopointProperty :
|
|
126
|
+
P extends ReferenceProperty ? ReferenceProperty :
|
|
127
|
+
P extends ArrayProperty ? ArrayProperty :
|
|
128
|
+
P extends MapProperty ? MapProperty : never {
|
|
116
129
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
* additional field delegates views with all its properties
|
|
120
|
-
* @param additionalFieldDelegate
|
|
121
|
-
* @group Builder
|
|
122
|
-
*/
|
|
123
|
-
export function buildAdditionalFieldDelegate<M extends Record<string, unknown>, USER extends User = User>(
|
|
124
|
-
additionalFieldDelegate: AdditionalFieldDelegate<M, USER>
|
|
125
|
-
): AdditionalFieldDelegate<M, USER> {
|
|
126
|
-
return additionalFieldDelegate;
|
|
130
|
+
// SAFETY: Identity function — P is a subtype of the conditional return type by definition
|
|
131
|
+
return property as unknown as ReturnType<typeof buildProperty<T, P>>;
|
|
127
132
|
}
|
|
128
|
-
|
|
129
|
-
|
package/src/util/callbacks.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CollectionCallbacks, Properties, RebaseCallContext } from "@rebasepro/types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Context passed to entity lifecycle callbacks.
|
|
5
|
+
* @group Models
|
|
6
|
+
*/
|
|
7
|
+
export type EntityCallbackContext = RebaseCallContext;
|
|
8
|
+
|
|
2
9
|
|
|
3
10
|
/**
|
|
4
11
|
* Helper function to recursively check if there are any callbacks in the properties.
|
|
@@ -78,24 +85,24 @@ async function processProperties(
|
|
|
78
85
|
|
|
79
86
|
/**
|
|
80
87
|
* Helper function to extract field-level PropertyCallbacks from a properties schema
|
|
81
|
-
* and wrap them into an
|
|
88
|
+
* and wrap them into an CollectionCallbacks object recursively.
|
|
82
89
|
*/
|
|
83
|
-
export const buildPropertyCallbacks = (properties: Properties):
|
|
90
|
+
export const buildPropertyCallbacks = (properties: Properties): CollectionCallbacks | undefined => {
|
|
84
91
|
if (!properties) return undefined;
|
|
85
92
|
|
|
86
|
-
const propertyCallbacks:
|
|
93
|
+
const propertyCallbacks: CollectionCallbacks = {};
|
|
87
94
|
|
|
88
95
|
if (hasPropertyCallbacks(properties, "afterRead")) {
|
|
89
96
|
propertyCallbacks.afterRead = async (props) => {
|
|
97
|
+
const row = props.row;
|
|
90
98
|
const processedValues = await processProperties(
|
|
91
99
|
properties,
|
|
92
|
-
|
|
93
|
-
|
|
100
|
+
row,
|
|
101
|
+
row,
|
|
94
102
|
props as unknown,
|
|
95
103
|
"afterRead"
|
|
96
104
|
);
|
|
97
|
-
return { ...props.
|
|
98
|
-
values: processedValues };
|
|
105
|
+
return { ...props.row, ...processedValues };
|
|
99
106
|
};
|
|
100
107
|
}
|
|
101
108
|
|
package/src/util/collections.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DefaultSelectedViewBuilder,
|
|
3
3
|
DefaultSelectedViewParams,
|
|
4
|
-
|
|
4
|
+
CollectionConfig,
|
|
5
5
|
Properties,
|
|
6
6
|
Property
|
|
7
7
|
} from "@rebasepro/types";
|
|
@@ -99,7 +99,7 @@ export function resolveDefaultSelectedView(
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
|
|
102
|
-
export function getLocalChangesBackup(collection:
|
|
102
|
+
export function getLocalChangesBackup(collection: CollectionConfig) {
|
|
103
103
|
if (!collection.localChangesBackup) {
|
|
104
104
|
return "manual_apply";
|
|
105
105
|
}
|
|
@@ -108,12 +108,12 @@ export function getLocalChangesBackup(collection: EntityCollection) {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
/**
|
|
111
|
-
* Returns the primary keys for
|
|
111
|
+
* Returns the primary keys for a entity collection by inspecting the properties
|
|
112
112
|
* and finding any properties with `isId`.
|
|
113
113
|
* Fallbacks to `["id"]` if no properties are marked as `isId: true`.
|
|
114
114
|
* @param collection
|
|
115
115
|
*/
|
|
116
|
-
export function getPrimaryKeys<M extends Record<string, unknown>>(collection:
|
|
116
|
+
export function getPrimaryKeys<M extends Record<string, unknown>>(collection: CollectionConfig<M>): Extract<keyof M, string>[] {
|
|
117
117
|
const properties = collection.properties;
|
|
118
118
|
if (!properties) {
|
|
119
119
|
return ["id"] as Extract<keyof M, string>[];
|
package/src/util/entities.ts
CHANGED
|
@@ -81,7 +81,7 @@ export function getDefaultValueFortype(type: DataType): unknown {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
/**
|
|
84
|
-
* Update the automatic values in
|
|
84
|
+
* Update the automatic values in a entity before save
|
|
85
85
|
* @group Driver
|
|
86
86
|
*/
|
|
87
87
|
export function updateDateAutoValues<M extends Record<string, unknown>>({
|
|
@@ -117,7 +117,7 @@ export function updateDateAutoValues<M extends Record<string, unknown>>({
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
/**
|
|
120
|
-
* Add missing required fields, expected in the collection, to the values of
|
|
120
|
+
* Add missing required fields, expected in the collection, to the values of a entity
|
|
121
121
|
* @param values
|
|
122
122
|
* @param properties
|
|
123
123
|
* @group Driver
|
|
@@ -148,7 +148,7 @@ export function getReferenceFrom<M extends Record<string, unknown>>(entity: Enti
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
export function getRelationFrom<M extends Record<string, unknown>>(entity: Entity<M>): EntityRelation {
|
|
151
|
-
return new EntityRelation(entity.id, entity.path, entity);
|
|
151
|
+
return new EntityRelation(entity.id, entity.path, entity as unknown as Record<string, unknown>);
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
/**
|
|
@@ -179,7 +179,7 @@ export function normalizeToEntityRelation(value: unknown, propertyType?: string)
|
|
|
179
179
|
return new EntityRelation(
|
|
180
180
|
obj.id as string | number,
|
|
181
181
|
obj.path as string,
|
|
182
|
-
obj.data as
|
|
182
|
+
obj.data as Record<string, unknown> | undefined
|
|
183
183
|
);
|
|
184
184
|
}
|
|
185
185
|
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ALL_WHERE_FILTER_OPS,
|
|
3
|
+
DataType,
|
|
4
|
+
getDataSourceCapabilities,
|
|
5
|
+
Property,
|
|
6
|
+
WhereFilterOp
|
|
7
|
+
} from "@rebasepro/types";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Default operators offered per property type, before engine capabilities and
|
|
11
|
+
* per-property narrowing are applied. These mirror what the built-in filter
|
|
12
|
+
* fields can render.
|
|
13
|
+
*/
|
|
14
|
+
const COMPARISON_OPS: readonly WhereFilterOp[] = ["==", "!=", ">", ">=", "<", "<="];
|
|
15
|
+
const NULL_CHECK_OPS: readonly WhereFilterOp[] = ["is-null", "is-not-null"];
|
|
16
|
+
const MEMBERSHIP_OPS: readonly WhereFilterOp[] = ["in", "not-in"];
|
|
17
|
+
const PATTERN_OPS: readonly WhereFilterOp[] = ["like", "ilike", "not-like", "not-ilike"];
|
|
18
|
+
|
|
19
|
+
const DEFAULT_OPS_BY_TYPE: Partial<Record<DataType, readonly WhereFilterOp[]>> = {
|
|
20
|
+
string: [...COMPARISON_OPS, ...MEMBERSHIP_OPS, ...PATTERN_OPS, ...NULL_CHECK_OPS],
|
|
21
|
+
number: [...COMPARISON_OPS, ...MEMBERSHIP_OPS, ...NULL_CHECK_OPS],
|
|
22
|
+
date: [...COMPARISON_OPS, ...NULL_CHECK_OPS],
|
|
23
|
+
boolean: ["==", "!=", ...NULL_CHECK_OPS],
|
|
24
|
+
reference: ["==", "!=", ...MEMBERSHIP_OPS, ...NULL_CHECK_OPS],
|
|
25
|
+
relation: ["==", "!=", ...MEMBERSHIP_OPS, ...NULL_CHECK_OPS]
|
|
26
|
+
// geopoint, map, vector, binary, array (as a container): not filterable
|
|
27
|
+
// through the generic filter UI.
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** Operators offered when the property is an *array of* a filterable type. */
|
|
31
|
+
const ARRAY_OPS: readonly WhereFilterOp[] = ["array-contains", "array-contains-any"];
|
|
32
|
+
|
|
33
|
+
export interface ResolveFilterOperatorsParams {
|
|
34
|
+
/**
|
|
35
|
+
* The property to filter on. For array properties, pass the **item**
|
|
36
|
+
* property (`property.of`) together with `isArray: true` — the same
|
|
37
|
+
* convention the filter field dispatchers use.
|
|
38
|
+
*/
|
|
39
|
+
property: Property;
|
|
40
|
+
/** True when filtering an array of `property`. */
|
|
41
|
+
isArray?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* The engine backing the collection (`collection.engine`, e.g.
|
|
44
|
+
* `"postgres"`, `"firestore"`). Falls back to the default engine's
|
|
45
|
+
* capabilities when omitted.
|
|
46
|
+
*/
|
|
47
|
+
engine?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Resolve which filter operators the UI should offer for a property.
|
|
52
|
+
*
|
|
53
|
+
* The result is the **intersection** of three sets:
|
|
54
|
+
* 1. what the engine can execute — {@link DataSourceCapabilities.filterOperators}
|
|
55
|
+
* (e.g. Firestore cannot run the LIKE family);
|
|
56
|
+
* 2. what makes sense for the property type (e.g. no `>` on booleans);
|
|
57
|
+
* 3. the developer's optional narrowing — `property.ui.filterOperators`.
|
|
58
|
+
*
|
|
59
|
+
* Returns an empty array when the property is not filterable (either by
|
|
60
|
+
* type, or because the developer disabled it with `filterOperators: []`).
|
|
61
|
+
*
|
|
62
|
+
* @group Models
|
|
63
|
+
*/
|
|
64
|
+
export function resolveFilterOperators({
|
|
65
|
+
property,
|
|
66
|
+
isArray,
|
|
67
|
+
engine
|
|
68
|
+
}: ResolveFilterOperatorsParams): WhereFilterOp[] {
|
|
69
|
+
const typeDefaults: readonly WhereFilterOp[] = isArray
|
|
70
|
+
? ARRAY_OPS
|
|
71
|
+
: DEFAULT_OPS_BY_TYPE[property.type] ?? [];
|
|
72
|
+
if (typeDefaults.length === 0) return [];
|
|
73
|
+
|
|
74
|
+
const engineOps = new Set(getDataSourceCapabilities(engine).filterOperators ?? ALL_WHERE_FILTER_OPS);
|
|
75
|
+
|
|
76
|
+
const narrowing = property.ui?.filterOperators;
|
|
77
|
+
const narrowingSet = narrowing !== undefined ? new Set(narrowing) : undefined;
|
|
78
|
+
|
|
79
|
+
return typeDefaults.filter(op =>
|
|
80
|
+
engineOps.has(op) && (narrowingSet === undefined || narrowingSet.has(op)));
|
|
81
|
+
}
|
package/src/util/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ export * from "./entities";
|
|
|
4
4
|
export * from "./enums";
|
|
5
5
|
export * from "./paths";
|
|
6
6
|
export * from "./resolutions";
|
|
7
|
+
export * from "./policy";
|
|
7
8
|
export * from "./permissions";
|
|
8
9
|
export * from "./references";
|
|
9
10
|
export * from "./navigation_from_path";
|
|
@@ -14,3 +15,4 @@ export * from "./callbacks";
|
|
|
14
15
|
export * from "./relations";
|
|
15
16
|
export * from "./conditions";
|
|
16
17
|
export * from "./navigation_utils";
|
|
18
|
+
export * from "./filter-operator-resolution";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CollectionConfig } from "@rebasepro/types";
|
|
2
2
|
type EntityCustomView<M extends Record<string, unknown> = Record<string, unknown>> = { key: string; [key: string]: unknown };
|
|
3
3
|
import { getCollectionPathsCombinations, removeInitialAndTrailingSlashes } from "./navigation_utils";
|
|
4
4
|
import { getSubcollections } from "./resolutions";
|
|
@@ -13,7 +13,7 @@ export interface NavigationViewEntityInternal<M extends Record<string, unknown>>
|
|
|
13
13
|
entityId: string | number;
|
|
14
14
|
slug: string;
|
|
15
15
|
path: string;
|
|
16
|
-
parentCollection:
|
|
16
|
+
parentCollection: CollectionConfig<M>;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export interface NavigationViewCollectionInternal<M extends Record<string, unknown>> {
|
|
@@ -21,7 +21,7 @@ export interface NavigationViewCollectionInternal<M extends Record<string, unkno
|
|
|
21
21
|
id: string;
|
|
22
22
|
slug: string;
|
|
23
23
|
path: string;
|
|
24
|
-
collection:
|
|
24
|
+
collection: CollectionConfig<M>;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export interface NavigationViewEntityCustomInternal<M extends Record<string, unknown>> {
|
|
@@ -34,7 +34,7 @@ export interface NavigationViewEntityCustomInternal<M extends Record<string, unk
|
|
|
34
34
|
|
|
35
35
|
export function getNavigationEntriesFromPath(props: {
|
|
36
36
|
path: string,
|
|
37
|
-
collections:
|
|
37
|
+
collections: CollectionConfig[] | undefined,
|
|
38
38
|
currentFullPath?: string,
|
|
39
39
|
contextEntityViews?: EntityCustomView[]
|
|
40
40
|
}): NavigationViewInternal[] {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CollectionConfig } from "@rebasepro/types";
|
|
2
2
|
|
|
3
3
|
import { getSubcollections } from "./resolutions";
|
|
4
4
|
|
|
@@ -33,13 +33,13 @@ export function getLastSegment(path: string) {
|
|
|
33
33
|
return cleanPath;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
export function resolveCollectionPathIds(path: string, allCollections:
|
|
36
|
+
export function resolveCollectionPathIds(path: string, allCollections: CollectionConfig[]): string {
|
|
37
37
|
let remainingPath = removeInitialAndTrailingSlashes(path);
|
|
38
38
|
if (!remainingPath) {
|
|
39
39
|
return "";
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
let currentCollections:
|
|
42
|
+
let currentCollections: CollectionConfig[] | undefined = allCollections;
|
|
43
43
|
const resolvedPathParts: string[] = [];
|
|
44
44
|
|
|
45
45
|
while (remainingPath.length > 0) {
|
|
@@ -53,7 +53,7 @@ export function resolveCollectionPathIds(path: string, allCollections: EntityCol
|
|
|
53
53
|
|
|
54
54
|
let foundMatch = false;
|
|
55
55
|
// Sort potential matches by length descending to prioritize longer matches (e.g., "a/b" over "a")
|
|
56
|
-
const potentialMatches: { col:
|
|
56
|
+
const potentialMatches: { col: CollectionConfig; match: string; }[] = currentCollections
|
|
57
57
|
.flatMap(col => [{
|
|
58
58
|
col,
|
|
59
59
|
match: col.slug
|
|
@@ -76,7 +76,7 @@ export function resolveCollectionPathIds(path: string, allCollections: EntityCol
|
|
|
76
76
|
break; // Path ends with a collection segment
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
// The next segment must be
|
|
79
|
+
// The next segment must be a entity ID
|
|
80
80
|
const idSeparatorIndex = remainingPath.indexOf("/");
|
|
81
81
|
let entityId: string | number;
|
|
82
82
|
if (idSeparatorIndex > -1) {
|
|
@@ -87,7 +87,7 @@ export function resolveCollectionPathIds(path: string, allCollections: EntityCol
|
|
|
87
87
|
// but handle it defensively: assume the rest is the ID
|
|
88
88
|
entityId = remainingPath;
|
|
89
89
|
remainingPath = "";
|
|
90
|
-
console.warn(`resolveCollectionPathIds: Path seems to end with
|
|
90
|
+
console.warn(`resolveCollectionPathIds: Path seems to end with a entity ID "${entityId}" instead of a collection segment in original path "${path}". This might indicate an invalid input path.`);
|
|
91
91
|
// Even if it ends here, we still need to push the ID
|
|
92
92
|
}
|
|
93
93
|
|
|
@@ -123,7 +123,7 @@ export function resolveCollectionPathIds(path: string, allCollections: EntityCol
|
|
|
123
123
|
* @param slugOrPath
|
|
124
124
|
* @param collections
|
|
125
125
|
*/
|
|
126
|
-
export function getCollectionBySlugWithin(slugOrPath: string, collections:
|
|
126
|
+
export function getCollectionBySlugWithin(slugOrPath: string, collections: CollectionConfig[]): CollectionConfig | undefined {
|
|
127
127
|
|
|
128
128
|
const subpaths = removeInitialAndTrailingSlashes(slugOrPath).split("/");
|
|
129
129
|
if (subpaths.length % 2 === 0) {
|
|
@@ -131,7 +131,7 @@ export function getCollectionBySlugWithin(slugOrPath: string, collections: Entit
|
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
const subpathCombinations = getCollectionPathsCombinations(subpaths);
|
|
134
|
-
let result:
|
|
134
|
+
let result: CollectionConfig | undefined;
|
|
135
135
|
for (let i = 0; i < subpathCombinations.length; i++) {
|
|
136
136
|
const subpathCombination = subpathCombinations[i];
|
|
137
137
|
const navigationEntry = collections && collections
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CollectionConfig, EntityReference } from "@rebasepro/types";
|
|
2
2
|
import { getCollectionPathsCombinations, removeInitialAndTrailingSlashes } from "./navigation_utils";
|
|
3
3
|
import { getSubcollections } from "./resolutions";
|
|
4
4
|
|
|
5
5
|
export function getParentReferencesFromPath(props: {
|
|
6
6
|
path: string,
|
|
7
|
-
collections:
|
|
7
|
+
collections: CollectionConfig[] | undefined,
|
|
8
8
|
currentFullPath?: string,
|
|
9
9
|
}): EntityReference[] {
|
|
10
10
|
|
|
@@ -21,7 +21,7 @@ export function getParentReferencesFromPath(props: {
|
|
|
21
21
|
for (let i = 0; i < subpathCombinations.length; i++) {
|
|
22
22
|
const subpathCombination = subpathCombinations[i];
|
|
23
23
|
|
|
24
|
-
const collection:
|
|
24
|
+
const collection: CollectionConfig | undefined = collections && collections.find((entry) => entry.slug === subpathCombination);
|
|
25
25
|
|
|
26
26
|
// If we find a collection, we add the reference and continue
|
|
27
27
|
if (collection) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { canCreateEntity, canEditEntity, canDeleteEntity, canReadCollection } from "./permissions";
|
|
2
|
-
import {
|
|
2
|
+
import { CollectionConfig, AuthController, Entity, User, SecurityRule } from "@rebasepro/types";
|
|
3
3
|
|
|
4
4
|
describe("Permissions Evaluator", () => {
|
|
5
5
|
|
|
@@ -46,7 +46,7 @@ describe("Permissions Evaluator", () => {
|
|
|
46
46
|
user: null
|
|
47
47
|
};
|
|
48
48
|
|
|
49
|
-
const createMockCollection = (rules?: SecurityRule[]):
|
|
49
|
+
const createMockCollection = (rules?: SecurityRule[]): CollectionConfig => ({
|
|
50
50
|
slug: "test",
|
|
51
51
|
name: "Test",
|
|
52
52
|
table: "test",
|
|
@@ -162,9 +162,10 @@ roles: ["author"] }
|
|
|
162
162
|
expect(canReadCollection(collection, mockAuthController)).toBe(true);
|
|
163
163
|
});
|
|
164
164
|
|
|
165
|
-
test("11. Empty roles array []
|
|
165
|
+
test("11. Empty roles array [] adds no role restriction (public rule stays public)", () => {
|
|
166
166
|
const collection = createMockCollection([
|
|
167
167
|
{ operation: "insert",
|
|
168
|
+
access: "public",
|
|
168
169
|
roles: [] }
|
|
169
170
|
]);
|
|
170
171
|
expect(canCreateEntity(collection, mockAuthController, "test", null)).toBe(true);
|
|
@@ -172,9 +173,10 @@ roles: [] }
|
|
|
172
173
|
expect(canCreateEntity(collection, adminAuthController, "test", null)).toBe(true);
|
|
173
174
|
});
|
|
174
175
|
|
|
175
|
-
test("12. Undefined roles on rule grants access to everyone
|
|
176
|
+
test("12. Undefined roles on a public rule grants access to everyone", () => {
|
|
176
177
|
const collection = createMockCollection([
|
|
177
|
-
{ operation: "insert"
|
|
178
|
+
{ operation: "insert",
|
|
179
|
+
access: "public" }
|
|
178
180
|
]);
|
|
179
181
|
expect(canCreateEntity(collection, mockAuthController, "test", null)).toBe(true);
|
|
180
182
|
expect(canCreateEntity(collection, unauthenticatedController, "test", null)).toBe(true);
|