@rebasepro/common 0.0.1-canary.2 → 0.0.1-canary.4d4fb3e
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 +1 -1
- package/dist/collections/CollectionRegistry.d.ts +2 -2
- package/dist/data/buildRebaseData.d.ts +14 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +398 -815
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +418 -835
- package/dist/index.umd.js.map +1 -1
- package/dist/util/builders.d.ts +6 -13
- package/dist/util/collections.d.ts +2 -2
- package/dist/util/entities.d.ts +16 -8
- package/dist/util/index.d.ts +1 -11
- package/dist/util/navigation_from_path.d.ts +10 -5
- package/dist/util/navigation_utils.d.ts +1 -12
- package/dist/util/permissions.d.ts +4 -4
- package/dist/util/references.d.ts +1 -1
- package/dist/util/relations.d.ts +4 -4
- package/dist/util/resolutions.d.ts +10 -12
- package/dist/util/storage.d.ts +4 -4
- package/package.json +11 -16
- package/src/collections/CollectionRegistry.ts +96 -33
- package/src/data/buildRebaseData.ts +221 -0
- package/src/index.ts +1 -0
- package/src/types/json-logic-js.d.ts +8 -0
- package/src/util/builders.ts +8 -18
- package/src/util/callbacks.ts +3 -4
- package/src/util/collections.ts +4 -4
- package/src/util/conditions.ts +4 -4
- package/src/util/entities.ts +37 -12
- package/src/util/index.ts +1 -11
- package/src/util/navigation_from_path.ts +8 -7
- package/src/util/navigation_utils.ts +4 -56
- package/src/util/parent_references_from_path.ts +1 -1
- package/src/util/permissions.test.ts +24 -28
- package/src/util/permissions.ts +14 -14
- package/src/util/references.ts +16 -2
- package/src/util/relations.ts +23 -19
- package/src/util/resolutions.ts +31 -62
- package/src/util/storage.ts +5 -5
- package/dist/util/arrays.d.ts +0 -1
- package/dist/util/dates.d.ts +0 -1
- package/dist/util/entity_actions.d.ts +0 -2
- package/dist/util/fields.d.ts +0 -2
- package/dist/util/flatten_object.d.ts +0 -5
- package/dist/util/hash.d.ts +0 -1
- package/dist/util/names.d.ts +0 -22
- package/dist/util/objects.d.ts +0 -26
- package/dist/util/os.d.ts +0 -2
- package/dist/util/plurals.d.ts +0 -16
- package/dist/util/regexp.d.ts +0 -7
- package/dist/util/strings.d.ts +0 -7
- package/src/util/arrays.ts +0 -3
- package/src/util/dates.ts +0 -1
- package/src/util/entity_actions.ts +0 -28
- package/src/util/fields.ts +0 -28
- package/src/util/flatten_object.ts +0 -45
- package/src/util/hash.ts +0 -11
- package/src/util/names.ts +0 -30
- package/src/util/objects.ts +0 -376
- package/src/util/os.ts +0 -13
- package/src/util/plurals.ts +0 -188
- package/src/util/regexp.ts +0 -32
- package/src/util/strings.ts +0 -84
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DataDriver,
|
|
3
|
+
RebaseData,
|
|
4
|
+
CollectionAccessor,
|
|
5
|
+
FindParams,
|
|
6
|
+
FindResponse,
|
|
7
|
+
Entity,
|
|
8
|
+
EntityValues,
|
|
9
|
+
FilterValues,
|
|
10
|
+
WhereFilterOp
|
|
11
|
+
} from "@rebasepro/types";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Convert PostgREST-style filter object to the internal DataDriver FilterValues format.
|
|
15
|
+
*
|
|
16
|
+
* PostgREST: { status: "eq.published", age: "gte.18" }
|
|
17
|
+
* Internal: { status: ["==", "published"], age: [">=", 18] }
|
|
18
|
+
*/
|
|
19
|
+
function convertWhereToFilter(where?: Record<string, string>): FilterValues<string> | undefined {
|
|
20
|
+
if (!where) return undefined;
|
|
21
|
+
|
|
22
|
+
const operatorMap: Record<string, WhereFilterOp> = {
|
|
23
|
+
"eq": "==",
|
|
24
|
+
"neq": "!=",
|
|
25
|
+
"gt": ">",
|
|
26
|
+
"gte": ">=",
|
|
27
|
+
"lt": "<",
|
|
28
|
+
"lte": "<=",
|
|
29
|
+
"in": "in",
|
|
30
|
+
"nin": "not-in",
|
|
31
|
+
"cs": "array-contains",
|
|
32
|
+
"csa": "array-contains-any",
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const filter: FilterValues<string> = {};
|
|
36
|
+
|
|
37
|
+
for (const [field, rawValue] of Object.entries(where)) {
|
|
38
|
+
const dotIndex = rawValue.indexOf(".");
|
|
39
|
+
if (dotIndex === -1) continue;
|
|
40
|
+
|
|
41
|
+
const op = rawValue.substring(0, dotIndex);
|
|
42
|
+
let value: unknown = rawValue.substring(dotIndex + 1);
|
|
43
|
+
|
|
44
|
+
// Parse list values like "(admin,editor)"
|
|
45
|
+
if (typeof value === "string" && value.startsWith("(") && value.endsWith(")")) {
|
|
46
|
+
value = value.slice(1, -1).split(",").map((v: string) => v.trim());
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Try to parse numbers
|
|
50
|
+
if (typeof value === "string" && !isNaN(Number(value)) && value.trim() !== "") {
|
|
51
|
+
value = Number(value);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const mappedOp = operatorMap[op];
|
|
55
|
+
if (mappedOp) {
|
|
56
|
+
filter[field] = [mappedOp, value];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return Object.keys(filter).length > 0 ? filter : undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Parse an orderBy string like "created_at:desc" into [field, direction].
|
|
65
|
+
*/
|
|
66
|
+
function parseOrderBy(orderBy?: string): [string, "asc" | "desc"] | undefined {
|
|
67
|
+
if (!orderBy) return undefined;
|
|
68
|
+
const parts = orderBy.split(":");
|
|
69
|
+
const field = parts[0];
|
|
70
|
+
const direction = (parts[1] as "asc" | "desc") || "asc";
|
|
71
|
+
return [field, direction];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Create a CollectionAccessor that delegates to a DataDriver for a given collection slug.
|
|
76
|
+
*/
|
|
77
|
+
function createDriverAccessor<M extends Record<string, unknown> = Record<string, unknown>>(
|
|
78
|
+
driver: DataDriver,
|
|
79
|
+
slug: string
|
|
80
|
+
): CollectionAccessor<M> {
|
|
81
|
+
return {
|
|
82
|
+
async find(params?: FindParams): Promise<FindResponse<M>> {
|
|
83
|
+
const orderParsed = parseOrderBy(params?.orderBy);
|
|
84
|
+
const entities = await driver.fetchCollection<M>({
|
|
85
|
+
path: slug,
|
|
86
|
+
limit: params?.limit,
|
|
87
|
+
startAfter: params?.offset,
|
|
88
|
+
filter: convertWhereToFilter(params?.where),
|
|
89
|
+
orderBy: orderParsed?.[0],
|
|
90
|
+
order: orderParsed?.[1],
|
|
91
|
+
searchString: params?.searchString,
|
|
92
|
+
});
|
|
93
|
+
const limit = params?.limit ?? 20;
|
|
94
|
+
const offset = params?.offset ?? 0;
|
|
95
|
+
return {
|
|
96
|
+
data: entities,
|
|
97
|
+
meta: {
|
|
98
|
+
total: entities.length,
|
|
99
|
+
limit,
|
|
100
|
+
offset,
|
|
101
|
+
hasMore: entities.length >= limit
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
async findById(id: string | number): Promise<Entity<M> | undefined> {
|
|
107
|
+
return driver.fetchEntity<M>({ path: slug, entityId: id });
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
async create(data: Partial<EntityValues<M>>, id?: string | number): Promise<Entity<M>> {
|
|
111
|
+
return driver.saveEntity<M>({
|
|
112
|
+
path: slug,
|
|
113
|
+
values: data,
|
|
114
|
+
entityId: id,
|
|
115
|
+
status: "new"
|
|
116
|
+
});
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
async update(id: string | number, data: Partial<EntityValues<M>>): Promise<Entity<M>> {
|
|
120
|
+
return driver.saveEntity<M>({
|
|
121
|
+
path: slug,
|
|
122
|
+
values: data,
|
|
123
|
+
entityId: id,
|
|
124
|
+
status: "existing"
|
|
125
|
+
});
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
async delete(id: string | number): Promise<void> {
|
|
129
|
+
return driver.deleteEntity({
|
|
130
|
+
entity: { id, path: slug, values: {} as Record<string, unknown> }
|
|
131
|
+
});
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
count: driver.countEntities
|
|
135
|
+
? async (params?: FindParams): Promise<number> => {
|
|
136
|
+
return driver.countEntities!({
|
|
137
|
+
path: slug,
|
|
138
|
+
filter: convertWhereToFilter(params?.where),
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
: undefined,
|
|
142
|
+
|
|
143
|
+
listen: driver.listenCollection
|
|
144
|
+
? (params: FindParams | undefined, onUpdate: (response: FindResponse<M>) => void, onError?: (error: Error) => void) => {
|
|
145
|
+
const orderParsed = parseOrderBy(params?.orderBy);
|
|
146
|
+
const limit = params?.limit ?? 20;
|
|
147
|
+
const offset = params?.offset ?? 0;
|
|
148
|
+
return driver.listenCollection!<M>({
|
|
149
|
+
path: slug,
|
|
150
|
+
limit: params?.limit,
|
|
151
|
+
startAfter: params?.offset,
|
|
152
|
+
filter: convertWhereToFilter(params?.where),
|
|
153
|
+
orderBy: orderParsed?.[0],
|
|
154
|
+
order: orderParsed?.[1],
|
|
155
|
+
searchString: params?.searchString,
|
|
156
|
+
onUpdate: (entities) => {
|
|
157
|
+
onUpdate({
|
|
158
|
+
data: entities,
|
|
159
|
+
meta: {
|
|
160
|
+
total: entities.length,
|
|
161
|
+
limit,
|
|
162
|
+
offset,
|
|
163
|
+
hasMore: entities.length >= limit
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
},
|
|
167
|
+
onError
|
|
168
|
+
});
|
|
169
|
+
} : undefined,
|
|
170
|
+
|
|
171
|
+
listenById: driver.listenEntity
|
|
172
|
+
? (id: string | number, onUpdate: (entity: Entity<M> | undefined) => void, onError?: (error: Error) => void) => {
|
|
173
|
+
return driver.listenEntity!<M>({
|
|
174
|
+
path: slug,
|
|
175
|
+
entityId: id,
|
|
176
|
+
onUpdate: (entity) => onUpdate(entity ?? undefined),
|
|
177
|
+
onError
|
|
178
|
+
});
|
|
179
|
+
} : undefined
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Build a `RebaseData` object from a `DataDriver` using JavaScript Proxy.
|
|
185
|
+
*
|
|
186
|
+
* This is the key bridge: any property access like `data.products` returns
|
|
187
|
+
* a `CollectionAccessor` backed by the underlying DataDriver, without
|
|
188
|
+
* needing per-collection code generation.
|
|
189
|
+
*
|
|
190
|
+
* @example
|
|
191
|
+
* const data = buildRebaseData(driver);
|
|
192
|
+
* await data.products.create({ name: "Camera", price: 299 });
|
|
193
|
+
* const { data: items } = await data.products.find({ where: { status: "eq.published" } });
|
|
194
|
+
*/
|
|
195
|
+
export function buildRebaseData(driver: DataDriver): RebaseData {
|
|
196
|
+
const cache = new Map<string, CollectionAccessor>();
|
|
197
|
+
|
|
198
|
+
function getAccessor(slug: string): CollectionAccessor {
|
|
199
|
+
let accessor = cache.get(slug);
|
|
200
|
+
if (!accessor) {
|
|
201
|
+
accessor = createDriverAccessor(driver, slug);
|
|
202
|
+
cache.set(slug, accessor);
|
|
203
|
+
}
|
|
204
|
+
return accessor;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const target = {
|
|
208
|
+
collection: getAccessor
|
|
209
|
+
} as RebaseData;
|
|
210
|
+
|
|
211
|
+
return new Proxy(target, {
|
|
212
|
+
get(_target, prop: string | symbol) {
|
|
213
|
+
if (prop === "collection") return getAccessor;
|
|
214
|
+
// Ignore Symbol properties (e.g. Symbol.toPrimitive, Symbol.iterator)
|
|
215
|
+
if (typeof prop === "symbol") return undefined;
|
|
216
|
+
// Ignore internal JS properties
|
|
217
|
+
if (prop === "then" || prop === "toJSON" || prop === "$$typeof") return undefined;
|
|
218
|
+
return getAccessor(prop);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
}
|
package/src/index.ts
CHANGED
package/src/util/builders.ts
CHANGED
|
@@ -11,12 +11,12 @@ import {
|
|
|
11
11
|
MapProperty,
|
|
12
12
|
NumberProperty, Properties,
|
|
13
13
|
Property,
|
|
14
|
-
PropertyConfig,
|
|
15
14
|
ReferenceProperty,
|
|
16
15
|
StringProperty,
|
|
17
16
|
User
|
|
18
17
|
} from "@rebasepro/types";
|
|
19
18
|
|
|
19
|
+
|
|
20
20
|
/**
|
|
21
21
|
* Identity function we use to defeat the type system of Typescript and build
|
|
22
22
|
* collection views with all its properties
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
* @group Builder
|
|
25
25
|
*/
|
|
26
26
|
export function buildCollection<
|
|
27
|
-
M extends Record<string,
|
|
27
|
+
M extends Record<string, unknown> = Record<string, unknown>,
|
|
28
28
|
USER extends User = User>
|
|
29
29
|
(
|
|
30
30
|
collection: EntityCollection<M, USER>
|
|
@@ -50,7 +50,7 @@ export function buildProperty<T, P extends Property = Property>(
|
|
|
50
50
|
P extends ArrayProperty ? ArrayProperty :
|
|
51
51
|
P extends MapProperty ? MapProperty : never {
|
|
52
52
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
53
|
-
return property as
|
|
53
|
+
return property as unknown as ReturnType<typeof buildProperty<T, P>>;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
/**
|
|
@@ -59,7 +59,7 @@ export function buildProperty<T, P extends Property = Property>(
|
|
|
59
59
|
* @param properties
|
|
60
60
|
* @group Builder
|
|
61
61
|
*/
|
|
62
|
-
export function buildProperties<M extends Record<string,
|
|
62
|
+
export function buildProperties<M extends Record<string, unknown>>(
|
|
63
63
|
properties: Properties
|
|
64
64
|
): Properties {
|
|
65
65
|
return properties;
|
|
@@ -71,7 +71,7 @@ export function buildProperties<M extends Record<string, any>>(
|
|
|
71
71
|
* @param propertiesOrBuilder
|
|
72
72
|
* @group Builder
|
|
73
73
|
*/
|
|
74
|
-
export function buildPropertiesOrBuilder<M extends Record<string,
|
|
74
|
+
export function buildPropertiesOrBuilder<M extends Record<string, unknown>>(
|
|
75
75
|
propertiesOrBuilder: Properties
|
|
76
76
|
): Properties {
|
|
77
77
|
return propertiesOrBuilder;
|
|
@@ -107,7 +107,7 @@ export function buildEnumValueConfig(
|
|
|
107
107
|
* @param callbacks
|
|
108
108
|
* @group Builder
|
|
109
109
|
*/
|
|
110
|
-
export function buildEntityCallbacks<M extends Record<string,
|
|
110
|
+
export function buildEntityCallbacks<M extends Record<string, unknown> = Record<string, unknown>>(
|
|
111
111
|
callbacks: EntityCallbacks<M>
|
|
112
112
|
): EntityCallbacks<M> {
|
|
113
113
|
return callbacks;
|
|
@@ -119,20 +119,10 @@ export function buildEntityCallbacks<M extends Record<string, any> = any>(
|
|
|
119
119
|
* @param additionalFieldDelegate
|
|
120
120
|
* @group Builder
|
|
121
121
|
*/
|
|
122
|
-
export function buildAdditionalFieldDelegate<M extends Record<string,
|
|
122
|
+
export function buildAdditionalFieldDelegate<M extends Record<string, unknown>, USER extends User = User>(
|
|
123
123
|
additionalFieldDelegate: AdditionalFieldDelegate<M, USER>
|
|
124
124
|
): AdditionalFieldDelegate<M, USER> {
|
|
125
125
|
return additionalFieldDelegate;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
* Identity function we use to defeat the type system of Typescript and build
|
|
130
|
-
* additional field delegates views with all its properties
|
|
131
|
-
* @param propertyConfig
|
|
132
|
-
* @group Builder
|
|
133
|
-
*/
|
|
134
|
-
export function buildFieldConfig(
|
|
135
|
-
propertyConfig: PropertyConfig
|
|
136
|
-
): PropertyConfig {
|
|
137
|
-
return propertyConfig;
|
|
138
|
-
}
|
|
128
|
+
|
package/src/util/callbacks.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { EntityCallbacks, Properties } from "@rebasepro/types";
|
|
2
|
-
import { mergeDeep } from "./objects";
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Helper function to recursively check if there are any callbacks in the properties.
|
|
@@ -33,13 +32,13 @@ async function processProperties(
|
|
|
33
32
|
): Promise<Record<string, unknown>> {
|
|
34
33
|
if (!values || typeof values !== "object") return values;
|
|
35
34
|
|
|
36
|
-
|
|
35
|
+
const result = { ...values };
|
|
37
36
|
|
|
38
37
|
for (const [key, property] of Object.entries(properties)) {
|
|
39
38
|
if (result[key] === undefined) continue;
|
|
40
39
|
|
|
41
40
|
let currentValue = result[key];
|
|
42
|
-
|
|
41
|
+
const previousValue = previousValues?.[key];
|
|
43
42
|
|
|
44
43
|
// 1. Array Property
|
|
45
44
|
if (property.type === "array" && Array.isArray(currentValue)) {
|
|
@@ -66,7 +65,7 @@ async function processProperties(
|
|
|
66
65
|
...(propsContext as Record<string, unknown>),
|
|
67
66
|
value: currentValue,
|
|
68
67
|
previousValue
|
|
69
|
-
} as
|
|
68
|
+
} as never));
|
|
70
69
|
if (cbRes !== undefined) {
|
|
71
70
|
currentValue = cbRes;
|
|
72
71
|
}
|
package/src/util/collections.ts
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "@rebasepro/types";
|
|
8
8
|
import { isPropertyBuilder } from "./entities";
|
|
9
9
|
|
|
10
|
-
export function sortProperties<M extends Record<string,
|
|
10
|
+
export function sortProperties<M extends Record<string, unknown>>(properties: Properties, propertiesOrder?: string[]): Properties {
|
|
11
11
|
try {
|
|
12
12
|
const propertiesKeys = Object.keys(properties);
|
|
13
13
|
// If no propertiesOrder, just use the original keys order
|
|
@@ -54,7 +54,7 @@ export function sortProperties<M extends Record<string, any>>(properties: Proper
|
|
|
54
54
|
return ({ [key]: property });
|
|
55
55
|
}
|
|
56
56
|
})
|
|
57
|
-
.reduce((a:
|
|
57
|
+
.reduce((a: Properties, b: Properties) => ({ ...a, ...b }), {}) as Properties;
|
|
58
58
|
|
|
59
59
|
// Append any properties that were NOT in propertiesOrder (so they don't disappear!)
|
|
60
60
|
const missingProperties = propertiesKeys
|
|
@@ -72,7 +72,7 @@ export function sortProperties<M extends Record<string, any>>(properties: Proper
|
|
|
72
72
|
return ({ [key]: property });
|
|
73
73
|
}
|
|
74
74
|
})
|
|
75
|
-
.reduce((a:
|
|
75
|
+
.reduce((a: Properties, b: Properties) => ({ ...a, ...b }), {}) as Properties;
|
|
76
76
|
|
|
77
77
|
return { ...orderedResult, ...missingProperties };
|
|
78
78
|
} catch (e) {
|
|
@@ -110,7 +110,7 @@ export function getLocalChangesBackup(collection: EntityCollection) {
|
|
|
110
110
|
* Fallbacks to `["id"]` if no properties are marked as `isId: true`.
|
|
111
111
|
* @param collection
|
|
112
112
|
*/
|
|
113
|
-
export function getPrimaryKeys<M extends Record<string,
|
|
113
|
+
export function getPrimaryKeys<M extends Record<string, unknown>>(collection: EntityCollection<M>): Extract<keyof M, string>[] {
|
|
114
114
|
const properties = collection.properties;
|
|
115
115
|
if (!properties) {
|
|
116
116
|
return ["id"] as Extract<keyof M, string>[];
|
package/src/util/conditions.ts
CHANGED
|
@@ -152,7 +152,7 @@ export function buildConditionContext(params: {
|
|
|
152
152
|
email: user?.email ?? null,
|
|
153
153
|
displayName: user?.displayName ?? null,
|
|
154
154
|
photoURL: user?.photoURL ?? null,
|
|
155
|
-
roles: user?.roles ?? []
|
|
155
|
+
roles: (user?.roles ?? []).map((r: unknown) => typeof r === "string" ? r : (r as { id: string }).id)
|
|
156
156
|
},
|
|
157
157
|
now: Date.now()
|
|
158
158
|
};
|
|
@@ -233,9 +233,9 @@ export function applyPropertyConditions(
|
|
|
233
233
|
// ENUM CONDITIONS
|
|
234
234
|
// ═══════════════════════════════════════════════════════════════════════
|
|
235
235
|
|
|
236
|
-
if ("
|
|
237
|
-
(result as Record<string, unknown>).
|
|
238
|
-
result.
|
|
236
|
+
if ("enum" in result && result.enum && (conditions.enumConditions || conditions.allowedEnumValues || conditions.excludedEnumValues)) {
|
|
237
|
+
(result as Record<string, unknown>).enum = applyEnumConditions(
|
|
238
|
+
result.enum as EnumValueConfig[],
|
|
239
239
|
conditions,
|
|
240
240
|
context
|
|
241
241
|
);
|
package/src/util/entities.ts
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
Property,
|
|
10
10
|
} from "@rebasepro/types";
|
|
11
11
|
import { DEFAULT_ONE_OF_TYPE, DEFAULT_ONE_OF_VALUE } from "./common";
|
|
12
|
-
import { mergeDeep } from "
|
|
12
|
+
import { mergeDeep } from "@rebasepro/utils";
|
|
13
13
|
|
|
14
14
|
export function isReadOnly(property: Property): boolean {
|
|
15
15
|
if (property.readOnly)
|
|
@@ -19,7 +19,7 @@ export function isReadOnly(property: Property): boolean {
|
|
|
19
19
|
return true;
|
|
20
20
|
}
|
|
21
21
|
if (property.type === "reference") {
|
|
22
|
-
return !property.path && !property.Field;
|
|
22
|
+
return !property.path && !("Field" in property && property.Field);
|
|
23
23
|
}
|
|
24
24
|
return false;
|
|
25
25
|
}
|
|
@@ -32,7 +32,7 @@ export function isPropertyBuilder(property?: Property) {
|
|
|
32
32
|
return typeof property?.dynamicProps === "function";
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
export function getDefaultValuesFor<M extends Record<string,
|
|
35
|
+
export function getDefaultValuesFor<M extends Record<string, unknown>>(properties: Properties): Partial<EntityValues<M>> {
|
|
36
36
|
if (!properties) return {};
|
|
37
37
|
return Object.entries(properties)
|
|
38
38
|
.map(([key, property]) => {
|
|
@@ -77,9 +77,9 @@ export function getDefaultValueFortype(type: DataType) {
|
|
|
77
77
|
|
|
78
78
|
/**
|
|
79
79
|
* Update the automatic values in an entity before save
|
|
80
|
-
* @group
|
|
80
|
+
* @group Driver
|
|
81
81
|
*/
|
|
82
|
-
export function updateDateAutoValues<M extends Record<string,
|
|
82
|
+
export function updateDateAutoValues<M extends Record<string, unknown>>({
|
|
83
83
|
inputValues,
|
|
84
84
|
properties,
|
|
85
85
|
status,
|
|
@@ -115,9 +115,9 @@ export function updateDateAutoValues<M extends Record<string, any>>({
|
|
|
115
115
|
* Add missing required fields, expected in the collection, to the values of an entity
|
|
116
116
|
* @param values
|
|
117
117
|
* @param properties
|
|
118
|
-
* @group
|
|
118
|
+
* @group Driver
|
|
119
119
|
*/
|
|
120
|
-
export function sanitizeData<M extends Record<string,
|
|
120
|
+
export function sanitizeData<M extends Record<string, unknown>>
|
|
121
121
|
(
|
|
122
122
|
values: EntityValues<M>,
|
|
123
123
|
properties: Properties
|
|
@@ -131,22 +131,47 @@ export function sanitizeData<M extends Record<string, any>>
|
|
|
131
131
|
return result;
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
export function getReferenceFrom<M extends Record<string,
|
|
134
|
+
export function getReferenceFrom<M extends Record<string, unknown>>(entity: Entity<M>): EntityReference {
|
|
135
135
|
if (typeof entity.id !== "string")
|
|
136
136
|
throw new Error("Only string IDs are supported in references");
|
|
137
137
|
return new EntityReference({
|
|
138
138
|
id: entity.id,
|
|
139
139
|
path: entity.path,
|
|
140
|
-
|
|
140
|
+
driver: entity.driver,
|
|
141
141
|
databaseId: entity.databaseId
|
|
142
142
|
});
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
export function getRelationFrom<M extends Record<string,
|
|
146
|
-
return new EntityRelation(entity.id, entity.path);
|
|
145
|
+
export function getRelationFrom<M extends Record<string, unknown>>(entity: Entity<M>): EntityRelation {
|
|
146
|
+
return new EntityRelation(entity.id, entity.path, entity);
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
|
|
149
|
+
/**
|
|
150
|
+
* Normalize a value into a proper EntityRelation instance.
|
|
151
|
+
* Handles EntityRelation class instances, and plain objects
|
|
152
|
+
* with `__type === "relation"` or an `isEntityRelation()` method.
|
|
153
|
+
*
|
|
154
|
+
* Returns null if the value cannot be coerced.
|
|
155
|
+
*/
|
|
156
|
+
export function normalizeToEntityRelation(value: unknown): EntityRelation | null {
|
|
157
|
+
if (value instanceof EntityRelation) return value;
|
|
158
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
159
|
+
|
|
160
|
+
const obj = value as Record<string, unknown>;
|
|
161
|
+
const isRelationLike =
|
|
162
|
+
obj.__type === "relation" ||
|
|
163
|
+
(typeof obj.isEntityRelation === "function" && (obj.isEntityRelation as () => boolean)());
|
|
164
|
+
|
|
165
|
+
if (!isRelationLike) return null;
|
|
166
|
+
|
|
167
|
+
return new EntityRelation(
|
|
168
|
+
obj.id as string | number,
|
|
169
|
+
obj.path as string,
|
|
170
|
+
obj.data as Entity | undefined
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function traverseValuesProperties<M extends Record<string, unknown>>(
|
|
150
175
|
inputValues: Partial<EntityValues<M>>,
|
|
151
176
|
properties: Properties,
|
|
152
177
|
operation: (value: unknown, property: Property) => unknown
|
package/src/util/index.ts
CHANGED
|
@@ -1,26 +1,16 @@
|
|
|
1
1
|
export * from "./collections";
|
|
2
2
|
export * from "./common";
|
|
3
3
|
export * from "./entities";
|
|
4
|
-
export * from "./strings";
|
|
5
|
-
export * from "./dates";
|
|
6
4
|
export * from "./enums";
|
|
7
|
-
export * from "./objects";
|
|
8
5
|
export * from "./paths";
|
|
9
|
-
export * from "./regexp";
|
|
10
|
-
export * from "./navigation_utils";
|
|
11
|
-
export * from "./entity_actions";
|
|
12
|
-
export * from "./fields";
|
|
13
6
|
export * from "./resolutions";
|
|
14
7
|
export * from "./permissions";
|
|
15
|
-
export * from "./plurals";
|
|
16
8
|
export * from "./references";
|
|
17
|
-
export * from "./flatten_object";
|
|
18
9
|
export * from "./navigation_from_path";
|
|
19
10
|
export * from "./parent_references_from_path";
|
|
20
11
|
export * from "./builders";
|
|
21
12
|
export * from "./storage";
|
|
22
|
-
export * from "./arrays";
|
|
23
13
|
export * from "./callbacks";
|
|
24
|
-
export * from "./hash";
|
|
25
14
|
export * from "./relations";
|
|
26
15
|
export * from "./conditions";
|
|
16
|
+
export * from "./navigation_utils";
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { EntityCollection
|
|
1
|
+
import { EntityCollection } from "@rebasepro/types";
|
|
2
|
+
type EntityCustomView<M extends Record<string, unknown> = Record<string, unknown>> = { key: string; [key: string]: unknown };
|
|
2
3
|
import { getCollectionPathsCombinations, removeInitialAndTrailingSlashes } from "./navigation_utils";
|
|
3
4
|
import { getSubcollections } from "./resolutions";
|
|
4
5
|
|
|
5
|
-
export type NavigationViewInternal<M extends Record<string,
|
|
6
|
+
export type NavigationViewInternal<M extends Record<string, unknown> = Record<string, unknown>> =
|
|
6
7
|
| NavigationViewEntityInternal<M>
|
|
7
8
|
| NavigationViewCollectionInternal<M>
|
|
8
9
|
| NavigationViewEntityCustomInternal<M>;
|
|
9
10
|
|
|
10
|
-
export interface NavigationViewEntityInternal<M extends Record<string,
|
|
11
|
+
export interface NavigationViewEntityInternal<M extends Record<string, unknown>> {
|
|
11
12
|
type: "entity";
|
|
12
13
|
entityId: string | number;
|
|
13
14
|
slug: string;
|
|
@@ -15,7 +16,7 @@ export interface NavigationViewEntityInternal<M extends Record<string, any>> {
|
|
|
15
16
|
parentCollection: EntityCollection<M>;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
export interface NavigationViewCollectionInternal<M extends Record<string,
|
|
19
|
+
export interface NavigationViewCollectionInternal<M extends Record<string, unknown>> {
|
|
19
20
|
type: "collection";
|
|
20
21
|
id: string;
|
|
21
22
|
slug: string;
|
|
@@ -23,7 +24,7 @@ export interface NavigationViewCollectionInternal<M extends Record<string, any>>
|
|
|
23
24
|
collection: EntityCollection<M>;
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
export interface NavigationViewEntityCustomInternal<M extends Record<string,
|
|
27
|
+
export interface NavigationViewEntityCustomInternal<M extends Record<string, unknown>> {
|
|
27
28
|
type: "custom_view";
|
|
28
29
|
slug: string;
|
|
29
30
|
path: string;
|
|
@@ -84,8 +85,8 @@ export function getNavigationEntriesFromPath(props: {
|
|
|
84
85
|
const entityViews = collection.entityViews;
|
|
85
86
|
const customView = entityViews && entityViews
|
|
86
87
|
.map((entry) => resolveEntityView(entry, props.contextEntityViews))
|
|
87
|
-
.filter(
|
|
88
|
-
.find((entry) => entry
|
|
88
|
+
.filter((v): v is EntityCustomView => v != null)
|
|
89
|
+
.find((entry) => entry.key === newPath);
|
|
89
90
|
const subcollections = getSubcollections(collection);
|
|
90
91
|
if (customView) {
|
|
91
92
|
result.push({
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { EntityCollection
|
|
1
|
+
import { EntityCollection } from "@rebasepro/types";
|
|
2
|
+
|
|
2
3
|
import { getSubcollections } from "./resolutions";
|
|
3
4
|
|
|
4
5
|
export function removeInitialAndTrailingSlashes(s: string): string {
|
|
@@ -66,7 +67,7 @@ export function resolveCollectionPathIds(path: string, allCollections: EntityCol
|
|
|
66
67
|
match: matchString
|
|
67
68
|
} = potentialMatches[0];
|
|
68
69
|
|
|
69
|
-
resolvedPathParts.push(foundCollection.
|
|
70
|
+
resolvedPathParts.push(foundCollection.slug); // Use the defined path
|
|
70
71
|
remainingPath = removeInitialSlash(remainingPath.substring(matchString.length));
|
|
71
72
|
|
|
72
73
|
// Check if we are at the end of the path
|
|
@@ -141,7 +142,7 @@ export function getCollectionBySlugWithin(slugOrPath: string, collections: Entit
|
|
|
141
142
|
|
|
142
143
|
if (subpathCombination === slugOrPath) {
|
|
143
144
|
result = navigationEntry;
|
|
144
|
-
} else if (navigationEntry.
|
|
145
|
+
} else if (getSubcollections(navigationEntry).length > 0) {
|
|
145
146
|
const newPath = slugOrPath.replace(subpathCombination, "").split("/").slice(2).join("/");
|
|
146
147
|
if (newPath.length > 0)
|
|
147
148
|
result = getCollectionBySlugWithin(newPath, getSubcollections(navigationEntry));
|
|
@@ -166,57 +167,4 @@ export function getCollectionPathsCombinations(subpaths: string[]): string[] {
|
|
|
166
167
|
result.push(entries.slice(0, i).join("/"));
|
|
167
168
|
}
|
|
168
169
|
return result;
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
export function navigateToEntity({
|
|
173
|
-
openEntityMode,
|
|
174
|
-
collection,
|
|
175
|
-
entityId,
|
|
176
|
-
copy,
|
|
177
|
-
path,
|
|
178
|
-
selectedTab,
|
|
179
|
-
sideEntityController,
|
|
180
|
-
onClose,
|
|
181
|
-
navigation
|
|
182
|
-
}:
|
|
183
|
-
|
|
184
|
-
{
|
|
185
|
-
openEntityMode: "side_panel" | "full_screen";
|
|
186
|
-
collection?: EntityCollection;
|
|
187
|
-
entityId?: string | number;
|
|
188
|
-
selectedTab?: string;
|
|
189
|
-
copy?: boolean;
|
|
190
|
-
path: string;
|
|
191
|
-
sideEntityController: SideEntityController;
|
|
192
|
-
onClose?: () => void;
|
|
193
|
-
navigation: CMSUrlController
|
|
194
|
-
}) {
|
|
195
|
-
|
|
196
|
-
if (openEntityMode === "side_panel") {
|
|
197
|
-
|
|
198
|
-
sideEntityController.open({
|
|
199
|
-
entityId,
|
|
200
|
-
path: path,
|
|
201
|
-
copy,
|
|
202
|
-
selectedTab,
|
|
203
|
-
collection,
|
|
204
|
-
updateUrl: true,
|
|
205
|
-
onClose
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
} else {
|
|
209
|
-
let to = navigation.buildUrlCollectionPath(entityId ? `${path ?? path}/${entityId}` : path ?? path);
|
|
210
|
-
if (entityId && selectedTab) {
|
|
211
|
-
to += `/${selectedTab}`;
|
|
212
|
-
}
|
|
213
|
-
if (!entityId) {
|
|
214
|
-
to += "#new";
|
|
215
|
-
}
|
|
216
|
-
if (copy) {
|
|
217
|
-
to += "#copy";
|
|
218
|
-
}
|
|
219
|
-
navigation.navigate(to);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
170
|
}
|
|
@@ -40,7 +40,7 @@ export function getParentReferencesFromPath(props: {
|
|
|
40
40
|
if (!collection) {
|
|
41
41
|
throw Error("collection not found resolving path: " + collection);
|
|
42
42
|
}
|
|
43
|
-
if (collection.
|
|
43
|
+
if (getSubcollections(collection).length > 0) {
|
|
44
44
|
result.push(...getParentReferencesFromPath({
|
|
45
45
|
path: newPath,
|
|
46
46
|
collections: getSubcollections(collection),
|