@rebasepro/common 0.0.1-canary.0 → 0.0.1-canary.0e2f892
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 +3 -3
- package/dist/collections/CollectionRegistry.d.ts +10 -2
- package/dist/data/buildRebaseData.d.ts +14 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +635 -853
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +653 -871
- 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 +38 -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 +10 -0
- package/dist/util/resolutions.d.ts +11 -13
- package/dist/util/storage.d.ts +4 -4
- package/package.json +106 -113
- package/src/collections/CollectionRegistry.ts +220 -48
- package/src/data/buildRebaseData.ts +285 -0
- package/src/index.ts +1 -0
- package/src/types/json-logic-js.d.ts +8 -0
- package/src/util/builders.ts +9 -19
- package/src/util/callbacks.ts +6 -6
- package/src/util/collections.ts +11 -8
- package/src/util/conditions.ts +6 -6
- package/src/util/entities.ts +74 -15
- package/src/util/index.ts +1 -11
- package/src/util/navigation_from_path.ts +9 -8
- package/src/util/navigation_utils.ts +4 -56
- package/src/util/parent_references_from_path.ts +4 -3
- package/src/util/permissions.test.ts +233 -115
- package/src/util/permissions.ts +26 -26
- package/src/util/references.ts +16 -2
- package/src/util/relations.ts +109 -29
- package/src/util/resolutions.ts +83 -83
- 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
package/dist/index.es.js
CHANGED
|
@@ -1,299 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import { EntityReference, EntityRelation, getDataSourceCapabilities } from "@rebasepro/types";
|
|
2
|
+
import { mergeDeep, getIn as getIn$1, isDefaultFieldConfigId, randomString, toSnakeCase, generateForeignKeyName, removeFunctions } from "@rebasepro/utils";
|
|
3
3
|
import jsonLogic from "json-logic-js";
|
|
4
4
|
import { deepEqual } from "fast-equals";
|
|
5
5
|
import cloneDeep from "lodash/cloneDeep.js";
|
|
6
6
|
const DEFAULT_ONE_OF_TYPE = "type";
|
|
7
7
|
const DEFAULT_ONE_OF_VALUE = "value";
|
|
8
|
-
const isEmptyArray = (value) => Array.isArray(value) && value.length === 0;
|
|
9
|
-
const isFunction = (obj) => typeof obj === "function";
|
|
10
|
-
const isInteger = (obj) => String(Math.floor(Number(obj))) === String(obj);
|
|
11
|
-
const isNaN$1 = (obj) => obj !== obj;
|
|
12
|
-
function getIn$1(obj, key, def, p = 0) {
|
|
13
|
-
const path = toPath(key);
|
|
14
|
-
while (obj && p < path.length) {
|
|
15
|
-
obj = obj[path[p++]];
|
|
16
|
-
}
|
|
17
|
-
if (p !== path.length && !obj) {
|
|
18
|
-
return def;
|
|
19
|
-
}
|
|
20
|
-
return obj === void 0 ? def : obj;
|
|
21
|
-
}
|
|
22
|
-
function setIn(obj, path, value) {
|
|
23
|
-
const res = clone(obj);
|
|
24
|
-
let resVal = res;
|
|
25
|
-
let i = 0;
|
|
26
|
-
const pathArray = toPath(path);
|
|
27
|
-
for (; i < pathArray.length - 1; i++) {
|
|
28
|
-
const currentPath = pathArray[i];
|
|
29
|
-
const currentObj = getIn$1(obj, pathArray.slice(0, i + 1));
|
|
30
|
-
if (currentObj && (isObject(currentObj) || Array.isArray(currentObj))) {
|
|
31
|
-
resVal = resVal[currentPath] = clone(currentObj);
|
|
32
|
-
} else {
|
|
33
|
-
const nextPath = pathArray[i + 1];
|
|
34
|
-
resVal = resVal[currentPath] = isInteger(nextPath) && Number(nextPath) >= 0 ? [] : {};
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
if ((i === 0 ? obj : resVal)[pathArray[i]] === value) {
|
|
38
|
-
return obj;
|
|
39
|
-
}
|
|
40
|
-
if (value === void 0) {
|
|
41
|
-
delete resVal[pathArray[i]];
|
|
42
|
-
} else {
|
|
43
|
-
resVal[pathArray[i]] = value;
|
|
44
|
-
}
|
|
45
|
-
if (i === 0 && value === void 0) {
|
|
46
|
-
delete res[pathArray[i]];
|
|
47
|
-
}
|
|
48
|
-
return res;
|
|
49
|
-
}
|
|
50
|
-
function clone(value) {
|
|
51
|
-
if (Array.isArray(value)) {
|
|
52
|
-
return [...value];
|
|
53
|
-
} else if (typeof value === "object" && value !== null) {
|
|
54
|
-
return {
|
|
55
|
-
...value
|
|
56
|
-
};
|
|
57
|
-
} else {
|
|
58
|
-
return value;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
function toPath(value) {
|
|
62
|
-
if (Array.isArray(value)) return value;
|
|
63
|
-
return value.replace(/\[(\d+)]/g, ".$1").replace(/^\./, "").replace(/\.$/, "").split(".");
|
|
64
|
-
}
|
|
65
|
-
const pick = (obj, ...args) => ({
|
|
66
|
-
...args.reduce((res, key) => ({
|
|
67
|
-
...res,
|
|
68
|
-
[key]: obj[key]
|
|
69
|
-
}), {})
|
|
70
|
-
});
|
|
71
|
-
function isObject(item) {
|
|
72
|
-
return !!item && typeof item === "object" && !Array.isArray(item);
|
|
73
|
-
}
|
|
74
|
-
function isPlainObject(obj) {
|
|
75
|
-
if (typeof obj !== "object" || obj === null || Array.isArray(obj)) {
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
const proto = Object.getPrototypeOf(obj);
|
|
79
|
-
return proto === Object.prototype;
|
|
80
|
-
}
|
|
81
|
-
function mergeDeep(target, source, ignoreUndefined = false) {
|
|
82
|
-
if (!isObject(target)) {
|
|
83
|
-
return target;
|
|
84
|
-
}
|
|
85
|
-
const output = {
|
|
86
|
-
...target
|
|
87
|
-
};
|
|
88
|
-
if (!isObject(source)) {
|
|
89
|
-
return output;
|
|
90
|
-
}
|
|
91
|
-
for (const key in source) {
|
|
92
|
-
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
93
|
-
const sourceValue = source[key];
|
|
94
|
-
const outputValue = output[key];
|
|
95
|
-
if (ignoreUndefined && sourceValue === void 0) {
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
if (sourceValue instanceof Date) {
|
|
99
|
-
output[key] = new Date(sourceValue.getTime());
|
|
100
|
-
} else if (Array.isArray(sourceValue)) {
|
|
101
|
-
if (Array.isArray(outputValue)) {
|
|
102
|
-
const newArray = [];
|
|
103
|
-
const maxLength = Math.max(outputValue.length, sourceValue.length);
|
|
104
|
-
for (let i = 0; i < maxLength; i++) {
|
|
105
|
-
const sourceItem = sourceValue[i];
|
|
106
|
-
const targetItem = outputValue[i];
|
|
107
|
-
if (i >= sourceValue.length) {
|
|
108
|
-
newArray[i] = targetItem;
|
|
109
|
-
} else if (i >= outputValue.length) {
|
|
110
|
-
newArray[i] = sourceItem;
|
|
111
|
-
} else if (sourceItem === null) {
|
|
112
|
-
newArray[i] = targetItem;
|
|
113
|
-
} else if (isPlainObject(sourceItem) && isPlainObject(targetItem)) {
|
|
114
|
-
newArray[i] = mergeDeep(targetItem, sourceItem, ignoreUndefined);
|
|
115
|
-
} else {
|
|
116
|
-
newArray[i] = sourceItem;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
output[key] = newArray;
|
|
120
|
-
} else {
|
|
121
|
-
output[key] = [...sourceValue];
|
|
122
|
-
}
|
|
123
|
-
} else if (isPlainObject(sourceValue)) {
|
|
124
|
-
if (isPlainObject(outputValue)) {
|
|
125
|
-
output[key] = mergeDeep(outputValue, sourceValue, ignoreUndefined);
|
|
126
|
-
} else {
|
|
127
|
-
output[key] = sourceValue;
|
|
128
|
-
}
|
|
129
|
-
} else if (isObject(sourceValue)) {
|
|
130
|
-
output[key] = sourceValue;
|
|
131
|
-
} else {
|
|
132
|
-
output[key] = sourceValue;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
return output;
|
|
137
|
-
}
|
|
138
|
-
function getValueInPath(o, path) {
|
|
139
|
-
if (!o) return void 0;
|
|
140
|
-
if (typeof o === "object") {
|
|
141
|
-
if (path in o) {
|
|
142
|
-
return o[path];
|
|
143
|
-
}
|
|
144
|
-
if (path.includes(".") || path.includes("[")) {
|
|
145
|
-
let pathSegments = path.split(/[.[]/);
|
|
146
|
-
if (path.includes("[")) {
|
|
147
|
-
pathSegments = pathSegments.map((segment) => segment.replace("]", ""));
|
|
148
|
-
}
|
|
149
|
-
const firstSegment = pathSegments[0];
|
|
150
|
-
const isArrayAndIndexExists = Array.isArray(o[firstSegment]) && !isNaN$1(parseInt(pathSegments[1]));
|
|
151
|
-
const nextObject = isArrayAndIndexExists ? o[firstSegment][parseInt(pathSegments[1])] : o[firstSegment];
|
|
152
|
-
const nextPath = pathSegments.slice(isArrayAndIndexExists ? 2 : 1).join(".");
|
|
153
|
-
if (nextPath === "") return nextObject;
|
|
154
|
-
return getValueInPath(nextObject, nextPath);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
return void 0;
|
|
158
|
-
}
|
|
159
|
-
function removeInPath(o, path) {
|
|
160
|
-
let currentObject = {
|
|
161
|
-
...o
|
|
162
|
-
};
|
|
163
|
-
const parts = path.split(".");
|
|
164
|
-
const last = parts.pop();
|
|
165
|
-
for (const part of parts) {
|
|
166
|
-
currentObject = currentObject[part];
|
|
167
|
-
}
|
|
168
|
-
if (last) delete currentObject[last];
|
|
169
|
-
return currentObject;
|
|
170
|
-
}
|
|
171
|
-
function removeFunctions(o) {
|
|
172
|
-
if (o === void 0) return void 0;
|
|
173
|
-
if (o === null) return null;
|
|
174
|
-
if (typeof o === "object") {
|
|
175
|
-
if (Array.isArray(o)) {
|
|
176
|
-
return o.map((v) => removeFunctions(v));
|
|
177
|
-
}
|
|
178
|
-
if (!isPlainObject(o)) {
|
|
179
|
-
return o;
|
|
180
|
-
}
|
|
181
|
-
return Object.entries(o).filter(([_, value]) => typeof value !== "function").map(([key, value]) => {
|
|
182
|
-
if (Array.isArray(value)) {
|
|
183
|
-
return {
|
|
184
|
-
[key]: value.map((v) => removeFunctions(v))
|
|
185
|
-
};
|
|
186
|
-
} else if (typeof value === "object") {
|
|
187
|
-
return {
|
|
188
|
-
[key]: removeFunctions(value)
|
|
189
|
-
};
|
|
190
|
-
} else return {
|
|
191
|
-
[key]: value
|
|
192
|
-
};
|
|
193
|
-
}).reduce((a, b) => ({
|
|
194
|
-
...a,
|
|
195
|
-
...b
|
|
196
|
-
}), {});
|
|
197
|
-
}
|
|
198
|
-
return o;
|
|
199
|
-
}
|
|
200
|
-
function getHashValue(v) {
|
|
201
|
-
if (!v) return null;
|
|
202
|
-
if (typeof v === "object" && v !== null) {
|
|
203
|
-
if ("id" in v) return String(v.id);
|
|
204
|
-
else if (v instanceof Date) return v.toLocaleString();
|
|
205
|
-
else if (v instanceof GeoPoint) return hash(v);
|
|
206
|
-
}
|
|
207
|
-
return hash(v, {
|
|
208
|
-
ignoreUnknown: true
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
function removeUndefined(value, removeEmptyStrings) {
|
|
212
|
-
if (typeof value === "function") {
|
|
213
|
-
return value;
|
|
214
|
-
}
|
|
215
|
-
if (Array.isArray(value)) {
|
|
216
|
-
return value.map((v) => removeUndefined(v, removeEmptyStrings));
|
|
217
|
-
}
|
|
218
|
-
if (typeof value === "object") {
|
|
219
|
-
if (value === null) return value;
|
|
220
|
-
if (!isPlainObject(value)) {
|
|
221
|
-
return value;
|
|
222
|
-
}
|
|
223
|
-
const res = {};
|
|
224
|
-
Object.keys(value).forEach((key) => {
|
|
225
|
-
if (!isEmptyObject(value)) {
|
|
226
|
-
const childRes = removeUndefined(value[key], removeEmptyStrings);
|
|
227
|
-
const isString = typeof childRes === "string";
|
|
228
|
-
const shouldKeepIfString = !removeEmptyStrings || removeEmptyStrings && !isString || removeEmptyStrings && isString && childRes !== "";
|
|
229
|
-
if (childRes !== void 0 && !isEmptyObject(childRes) && shouldKeepIfString) res[key] = childRes;
|
|
230
|
-
}
|
|
231
|
-
});
|
|
232
|
-
return res;
|
|
233
|
-
}
|
|
234
|
-
return value;
|
|
235
|
-
}
|
|
236
|
-
function removeNulls(value) {
|
|
237
|
-
if (typeof value === "function") {
|
|
238
|
-
return value;
|
|
239
|
-
}
|
|
240
|
-
if (Array.isArray(value)) {
|
|
241
|
-
return value.map((v) => removeNulls(v));
|
|
242
|
-
}
|
|
243
|
-
if (typeof value === "object") {
|
|
244
|
-
if (value === null) return value;
|
|
245
|
-
if (!isPlainObject(value)) {
|
|
246
|
-
return value;
|
|
247
|
-
}
|
|
248
|
-
const res = {};
|
|
249
|
-
const obj = value;
|
|
250
|
-
Object.keys(obj).forEach((key) => {
|
|
251
|
-
if (obj[key] !== null) res[key] = removeNulls(obj[key]);
|
|
252
|
-
});
|
|
253
|
-
return res;
|
|
254
|
-
}
|
|
255
|
-
return value;
|
|
256
|
-
}
|
|
257
|
-
function isEmptyObject(obj) {
|
|
258
|
-
return obj && Object.getPrototypeOf(obj) === Object.prototype && Object.keys(obj).length === 0;
|
|
259
|
-
}
|
|
260
|
-
function removePropsIfExisting(source, comparison) {
|
|
261
|
-
const isObject2 = (val) => typeof val === "object" && val !== null;
|
|
262
|
-
const isArray = (val) => Array.isArray(val);
|
|
263
|
-
if (!isObject2(source) || !isObject2(comparison)) {
|
|
264
|
-
return source;
|
|
265
|
-
}
|
|
266
|
-
const res = isArray(source) ? [...source] : {
|
|
267
|
-
...source
|
|
268
|
-
};
|
|
269
|
-
if (isArray(res)) {
|
|
270
|
-
for (let i = res.length - 1; i >= 0; i--) {
|
|
271
|
-
if (res[i] === comparison[i]) {
|
|
272
|
-
res.splice(i, 1);
|
|
273
|
-
} else if (isObject2(res[i]) && isObject2(comparison[i])) {
|
|
274
|
-
res[i] = removePropsIfExisting(res[i], comparison[i]);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
} else {
|
|
278
|
-
Object.keys(comparison).forEach((key) => {
|
|
279
|
-
if (key in res) {
|
|
280
|
-
if (isObject2(res[key]) && isObject2(comparison[key])) {
|
|
281
|
-
res[key] = removePropsIfExisting(res[key], comparison[key]);
|
|
282
|
-
} else if (res[key] === comparison[key]) {
|
|
283
|
-
delete res[key];
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
});
|
|
287
|
-
}
|
|
288
|
-
return res;
|
|
289
|
-
}
|
|
290
8
|
function isReadOnly(property) {
|
|
291
9
|
if (property.readOnly) return true;
|
|
292
10
|
if (property.type === "date") {
|
|
293
11
|
if (property.autoValue) return true;
|
|
294
12
|
}
|
|
295
13
|
if (property.type === "reference") {
|
|
296
|
-
return !property.path && !property.Field;
|
|
14
|
+
return !property.path && !("Field" in property && property.Field);
|
|
297
15
|
}
|
|
298
16
|
return false;
|
|
299
17
|
}
|
|
@@ -379,12 +97,20 @@ function getReferenceFrom(entity) {
|
|
|
379
97
|
return new EntityReference({
|
|
380
98
|
id: entity.id,
|
|
381
99
|
path: entity.path,
|
|
382
|
-
|
|
100
|
+
driver: entity.driver,
|
|
383
101
|
databaseId: entity.databaseId
|
|
384
102
|
});
|
|
385
103
|
}
|
|
386
104
|
function getRelationFrom(entity) {
|
|
387
|
-
return new EntityRelation(entity.id, entity.path);
|
|
105
|
+
return new EntityRelation(entity.id, entity.path, entity);
|
|
106
|
+
}
|
|
107
|
+
function normalizeToEntityRelation(value) {
|
|
108
|
+
if (value instanceof EntityRelation) return value;
|
|
109
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
110
|
+
const obj = value;
|
|
111
|
+
const isRelationLike = obj.__type === "relation" || obj.__type === "reference" || typeof obj.isEntityRelation === "function" && obj.isEntityRelation() || typeof obj.isEntityReference === "function" && obj.isEntityReference();
|
|
112
|
+
if (!isRelationLike) return null;
|
|
113
|
+
return new EntityRelation(obj.id, obj.path, obj.data);
|
|
388
114
|
}
|
|
389
115
|
function traverseValuesProperties(inputValues, properties, operation) {
|
|
390
116
|
const safeInputValues = inputValues ?? {};
|
|
@@ -440,6 +166,21 @@ function traverseValueProperty(inputValue, property, operation) {
|
|
|
440
166
|
}
|
|
441
167
|
return value;
|
|
442
168
|
}
|
|
169
|
+
function createRelationRef(id, path) {
|
|
170
|
+
return {
|
|
171
|
+
id,
|
|
172
|
+
path,
|
|
173
|
+
__type: "relation"
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function createRelationRefWithData(id, path, data) {
|
|
177
|
+
return {
|
|
178
|
+
id,
|
|
179
|
+
path,
|
|
180
|
+
__type: "relation",
|
|
181
|
+
data
|
|
182
|
+
};
|
|
183
|
+
}
|
|
443
184
|
function sortProperties(properties, propertiesOrder) {
|
|
444
185
|
try {
|
|
445
186
|
const propertiesKeys = Object.keys(properties);
|
|
@@ -538,58 +279,6 @@ function getPrimaryKeys(collection) {
|
|
|
538
279
|
}
|
|
539
280
|
return ["id"];
|
|
540
281
|
}
|
|
541
|
-
const kebabCaseRegex = /[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g;
|
|
542
|
-
const toKebabCase = (str) => {
|
|
543
|
-
const regExpMatchArray = str.match(kebabCaseRegex);
|
|
544
|
-
if (!regExpMatchArray) return "";
|
|
545
|
-
return regExpMatchArray.map((x) => x.toLowerCase()).join("-");
|
|
546
|
-
};
|
|
547
|
-
const snakeCaseRegex = /[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g;
|
|
548
|
-
const toSnakeCase = (str) => {
|
|
549
|
-
const regExpMatchArray = str.match(snakeCaseRegex);
|
|
550
|
-
if (!regExpMatchArray) return "";
|
|
551
|
-
return regExpMatchArray.map((x) => x.toLowerCase()).join("_");
|
|
552
|
-
};
|
|
553
|
-
function randomString(strLength = 5) {
|
|
554
|
-
return Math.random().toString(36).slice(2, 2 + strLength);
|
|
555
|
-
}
|
|
556
|
-
function randomColor() {
|
|
557
|
-
return Math.floor(Math.random() * 16777215).toString(16);
|
|
558
|
-
}
|
|
559
|
-
function slugify(text, separator = "_", lowercase = true) {
|
|
560
|
-
if (!text) return "";
|
|
561
|
-
const from = "ãàáäâẽèéëêìíïîõòóöôùúüûñç·/_,:;-";
|
|
562
|
-
const to = `aaaaaeeeeeiiiiooooouuuunc${separator}${separator}${separator}${separator}${separator}${separator}${separator}`;
|
|
563
|
-
for (let i = 0, l = from.length; i < l; i++) {
|
|
564
|
-
text = text.replace(new RegExp(from.charAt(i), "g"), to.charAt(i));
|
|
565
|
-
}
|
|
566
|
-
text = text.toString().replace(/\s+/g, separator).replace(/&/g, separator).replace(/[^\w\\-]+/g, "").replace(new RegExp("\\" + separator + "\\" + separator + "+", "g"), separator).trim().replace(/^\s+|\s+$/g, "");
|
|
567
|
-
return lowercase ? text.toLowerCase() : text;
|
|
568
|
-
}
|
|
569
|
-
function unslugify(slug) {
|
|
570
|
-
if (!slug) return "";
|
|
571
|
-
if (slug.includes("-") || slug.includes("_") || !slug.includes(" ")) {
|
|
572
|
-
const result = slug.replace(/[-_]/g, " ");
|
|
573
|
-
return result.replace(/\w\S*/g, function(txt) {
|
|
574
|
-
return txt.charAt(0).toUpperCase() + txt.substring(1);
|
|
575
|
-
}).trim();
|
|
576
|
-
} else {
|
|
577
|
-
return slug.trim();
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
function prettifyIdentifier(input) {
|
|
581
|
-
if (!input) return "";
|
|
582
|
-
let text = input;
|
|
583
|
-
text = text.replace(/([a-z])([A-Z])|([A-Z])([A-Z][a-z])/g, "$1$3 $2$4");
|
|
584
|
-
text = text.replace(/[_-]+/g, " ");
|
|
585
|
-
const s = text.trim().replace(/\b\w/g, (char) => char.toUpperCase());
|
|
586
|
-
console.log("Prettified identifier:", {
|
|
587
|
-
input,
|
|
588
|
-
s
|
|
589
|
-
});
|
|
590
|
-
return s;
|
|
591
|
-
}
|
|
592
|
-
const defaultDateFormat = "MMMM dd, yyyy, HH:mm:ss";
|
|
593
282
|
function enumToObjectEntries(enumValues) {
|
|
594
283
|
if (Array.isArray(enumValues)) {
|
|
595
284
|
return enumValues;
|
|
@@ -624,28 +313,6 @@ function segmentsToStrippedPath(paths) {
|
|
|
624
313
|
function fullPathToCollectionSegments(path) {
|
|
625
314
|
return path.split("/").filter((e, i) => i % 2 === 0);
|
|
626
315
|
}
|
|
627
|
-
function serializeRegExp(input) {
|
|
628
|
-
if (!input) return "";
|
|
629
|
-
return input.toString();
|
|
630
|
-
}
|
|
631
|
-
function hydrateRegExp(input) {
|
|
632
|
-
if (!input) return void 0;
|
|
633
|
-
const fragments = input.match(/\/(.*?)\/([a-z]*)?$/i);
|
|
634
|
-
if (fragments) {
|
|
635
|
-
return new RegExp(fragments[1], fragments[2] || "");
|
|
636
|
-
} else {
|
|
637
|
-
return new RegExp(input, "");
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
function isValidRegExp(input) {
|
|
641
|
-
const fullRegexp = input.match(/\/((?![*+?])(?:[^\r\n[/\\]|\\.|\[(?:[^\r\n\]\\]|\\.)*])+)\/((?:g(?:im?|mi?)?|i(?:gm?|mg?)?|m(?:gi?|ig?)?)?)/);
|
|
642
|
-
if (fullRegexp) return true;
|
|
643
|
-
const simpleRegexp = input.match(/((?![*+?])(?:[^\r\n[/\\]|\\.|\[(?:[^\r\n\]\\]|\\.)*])+)/);
|
|
644
|
-
return !!simpleRegexp;
|
|
645
|
-
}
|
|
646
|
-
function isDefaultFieldConfigId(id) {
|
|
647
|
-
return ["text_field", "multiline", "markdown", "url", "email", "switch", "select", "multi_select", "number_input", "number_select", "multi_number_select", "file_upload", "multi_file_upload", "reference_as_string", "reference", "multi_references", "relation", "date_time", "group", "key_value", "repeat", "custom_array", "block"].includes(id);
|
|
648
|
-
}
|
|
649
316
|
function resolveProperty(props) {
|
|
650
317
|
const {
|
|
651
318
|
property,
|
|
@@ -655,22 +322,24 @@ function resolveProperty(props) {
|
|
|
655
322
|
let resultProperty;
|
|
656
323
|
if (isPropertyBuilder(property)) {
|
|
657
324
|
const path = rest.path;
|
|
658
|
-
if (!path)
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
325
|
+
if (!path) {
|
|
326
|
+
resultProperty = property;
|
|
327
|
+
} else {
|
|
328
|
+
const usedPropertyValue = rest.propertyKey ? getIn$1(rest.values, rest.propertyKey) : void 0;
|
|
329
|
+
const dynamicProps = property.dynamicProps?.({
|
|
330
|
+
...rest,
|
|
331
|
+
path,
|
|
332
|
+
propertyValue: usedPropertyValue,
|
|
333
|
+
values: rest.values ?? {},
|
|
334
|
+
previousValues: rest.previousValues ?? rest.values ?? {}
|
|
335
|
+
});
|
|
336
|
+
resultProperty = mergeDeep(property, dynamicProps ?? {});
|
|
337
|
+
}
|
|
668
338
|
} else {
|
|
669
339
|
resultProperty = property;
|
|
670
340
|
}
|
|
671
|
-
if (resultProperty.
|
|
341
|
+
if (resultProperty?.dynamicProps && rest.path) {
|
|
672
342
|
const path = rest.path;
|
|
673
|
-
if (!path) throw Error("Trying to resolve dynamicProps without specifying the entity path");
|
|
674
343
|
const usedPropertyValue = rest.propertyKey ? getIn$1(rest.values, rest.propertyKey) : void 0;
|
|
675
344
|
const dynamicPropsResult = resultProperty.dynamicProps({
|
|
676
345
|
...rest,
|
|
@@ -712,10 +381,10 @@ function resolveProperty(props) {
|
|
|
712
381
|
return resolvedProperty;
|
|
713
382
|
}
|
|
714
383
|
if (customField.property) {
|
|
715
|
-
const {
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
384
|
+
const restConfigProperty = {
|
|
385
|
+
...customField.property
|
|
386
|
+
};
|
|
387
|
+
delete restConfigProperty.propertyConfig;
|
|
719
388
|
const customFieldProperty = resolveProperty({
|
|
720
389
|
property: {
|
|
721
390
|
name: "",
|
|
@@ -731,10 +400,14 @@ function resolveProperty(props) {
|
|
|
731
400
|
}
|
|
732
401
|
return resolvedProperty;
|
|
733
402
|
}
|
|
734
|
-
function resolveRelationProperty(property, relations) {
|
|
735
|
-
|
|
403
|
+
function resolveRelationProperty(property, relations, propertyKey) {
|
|
404
|
+
if (property.relation) {
|
|
405
|
+
return property;
|
|
406
|
+
}
|
|
407
|
+
const name = property.relationName || propertyKey;
|
|
408
|
+
const relation = name ? relations.find((rel) => rel.relationName === name) : void 0;
|
|
736
409
|
if (!relation) {
|
|
737
|
-
throw Error(`Relation ${
|
|
410
|
+
throw Error(`Relation ${name ?? "(unnamed)"} not found`);
|
|
738
411
|
}
|
|
739
412
|
return {
|
|
740
413
|
...property,
|
|
@@ -827,7 +500,7 @@ function resolveArrayProperties({
|
|
|
827
500
|
});
|
|
828
501
|
}).filter((e) => Boolean(e)) : [];
|
|
829
502
|
return resolvedProperties;
|
|
830
|
-
} else if (!property.Field) {
|
|
503
|
+
} else if (!("Field" in property && property.Field)) {
|
|
831
504
|
throw Error(`The array property (${propertyKey}) needs to declare an 'of' or a 'oneOf' property, or provide a custom \`Field\` component`);
|
|
832
505
|
} else {
|
|
833
506
|
return [];
|
|
@@ -862,209 +535,41 @@ function resolveEnumValues(input) {
|
|
|
862
535
|
return void 0;
|
|
863
536
|
}
|
|
864
537
|
}
|
|
865
|
-
function resolveEntityView$1(entityView, contextEntityViews) {
|
|
866
|
-
if (typeof entityView === "string") {
|
|
867
|
-
return contextEntityViews?.find((entry) => entry.key === entityView);
|
|
868
|
-
} else {
|
|
869
|
-
return entityView;
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
function resolveEntityAction(entityAction, contextEntityActions) {
|
|
873
|
-
if (typeof entityAction === "string") {
|
|
874
|
-
return contextEntityActions?.find((entry) => entry.key === entityAction);
|
|
875
|
-
} else {
|
|
876
|
-
return entityAction;
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
|
-
function resolvedSelectedEntityView(customViews, customizationController, selectedTab, canEdit) {
|
|
880
|
-
const resolvedEntityViews = customViews ? customViews.map((e) => resolveEntityView$1(e, customizationController.entityViews)).filter((e) => Boolean(e)) : [];
|
|
881
|
-
const selectedEntityView = resolvedEntityViews.find((e) => e.key === selectedTab);
|
|
882
|
-
const selectedSecondaryForm = customViews && resolvedEntityViews.filter((e) => e.includeActions).find((e) => e.key === selectedTab);
|
|
883
|
-
return {
|
|
884
|
-
resolvedEntityViews,
|
|
885
|
-
selectedEntityView,
|
|
886
|
-
selectedSecondaryForm
|
|
887
|
-
};
|
|
888
|
-
}
|
|
889
538
|
function getSubcollections(collection) {
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
else return s;
|
|
909
|
-
}
|
|
910
|
-
function addInitialSlash(s) {
|
|
911
|
-
if (s.startsWith("/")) return s;
|
|
912
|
-
else return `/${s}`;
|
|
913
|
-
}
|
|
914
|
-
function getLastSegment(path) {
|
|
915
|
-
const cleanPath = removeInitialAndTrailingSlashes(path);
|
|
916
|
-
if (cleanPath.includes("/")) {
|
|
917
|
-
const segments = cleanPath.split("/");
|
|
918
|
-
return segments[segments.length - 1];
|
|
919
|
-
}
|
|
920
|
-
return cleanPath;
|
|
921
|
-
}
|
|
922
|
-
function resolveCollectionPathIds(path, allCollections) {
|
|
923
|
-
let remainingPath = removeInitialAndTrailingSlashes(path);
|
|
924
|
-
if (!remainingPath) {
|
|
925
|
-
return "";
|
|
926
|
-
}
|
|
927
|
-
let currentCollections = allCollections;
|
|
928
|
-
const resolvedPathParts = [];
|
|
929
|
-
while (remainingPath.length > 0) {
|
|
930
|
-
if (!currentCollections || currentCollections.length === 0) {
|
|
931
|
-
console.warn(`resolveCollectionPathIds: Path structure implies subcollections, but none found before segment starting with "${remainingPath}" in original path "${path}". Appending remaining original path.`);
|
|
932
|
-
resolvedPathParts.push(remainingPath);
|
|
933
|
-
remainingPath = "";
|
|
934
|
-
break;
|
|
935
|
-
}
|
|
936
|
-
let foundMatch = false;
|
|
937
|
-
const potentialMatches = currentCollections.flatMap((col) => [{
|
|
938
|
-
col,
|
|
939
|
-
match: col.slug
|
|
940
|
-
}]).filter((p) => p.match && remainingPath.startsWith(p.match)).sort((a, b) => b.match.length - a.match.length);
|
|
941
|
-
if (potentialMatches.length > 0) {
|
|
942
|
-
const {
|
|
943
|
-
col: foundCollection,
|
|
944
|
-
match: matchString
|
|
945
|
-
} = potentialMatches[0];
|
|
946
|
-
resolvedPathParts.push(foundCollection.dbPath);
|
|
947
|
-
remainingPath = removeInitialSlash(remainingPath.substring(matchString.length));
|
|
948
|
-
if (remainingPath.length === 0) {
|
|
949
|
-
foundMatch = true;
|
|
950
|
-
break;
|
|
951
|
-
}
|
|
952
|
-
const idSeparatorIndex = remainingPath.indexOf("/");
|
|
953
|
-
let entityId;
|
|
954
|
-
if (idSeparatorIndex > -1) {
|
|
955
|
-
entityId = remainingPath.substring(0, idSeparatorIndex);
|
|
956
|
-
remainingPath = remainingPath.substring(idSeparatorIndex + 1);
|
|
957
|
-
} else {
|
|
958
|
-
entityId = remainingPath;
|
|
959
|
-
remainingPath = "";
|
|
960
|
-
console.warn(`resolveCollectionPathIds: Path seems to end with an entity ID "${entityId}" instead of a collection segment in original path "${path}". This might indicate an invalid input path.`);
|
|
961
|
-
}
|
|
962
|
-
resolvedPathParts.push(entityId);
|
|
963
|
-
currentCollections = getSubcollections(foundCollection);
|
|
964
|
-
foundMatch = true;
|
|
965
|
-
if (!currentCollections && remainingPath.length > 0) {
|
|
966
|
-
console.warn(`resolveCollectionPathIds: Path continues after entity ID "${entityId}", but no subcollections are defined for the preceding collection "${foundCollection.slug}" in path "${path}". Appending remaining original path.`);
|
|
967
|
-
resolvedPathParts.push(remainingPath);
|
|
968
|
-
remainingPath = "";
|
|
969
|
-
break;
|
|
539
|
+
if (collection.childCollections) {
|
|
540
|
+
return collection.childCollections() ?? [];
|
|
541
|
+
}
|
|
542
|
+
if (getDataSourceCapabilities(collection.driver).supportsSubcollections && collection.subcollections) {
|
|
543
|
+
return collection.subcollections() ?? [];
|
|
544
|
+
}
|
|
545
|
+
if (getDataSourceCapabilities(collection.driver).supportsRelations && collection.relations) {
|
|
546
|
+
const manyRelations = collection.relations.filter((r) => r.cardinality === "many");
|
|
547
|
+
return manyRelations.map((r) => {
|
|
548
|
+
const target = r.target();
|
|
549
|
+
if (!target) return void 0;
|
|
550
|
+
const relationKey = r.relationName || target.slug;
|
|
551
|
+
let customName;
|
|
552
|
+
if (collection.properties) {
|
|
553
|
+
const prop = Object.entries(collection.properties).find(([_, p]) => p.type === "relation" && p.relationName === relationKey);
|
|
554
|
+
if (prop && prop[1].name) {
|
|
555
|
+
customName = prop[1].name;
|
|
556
|
+
}
|
|
970
557
|
}
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
return resolvedPathParts.join("/");
|
|
980
|
-
}
|
|
981
|
-
function getCollectionBySlugWithin(slugOrPath, collections) {
|
|
982
|
-
const subpaths = removeInitialAndTrailingSlashes(slugOrPath).split("/");
|
|
983
|
-
if (subpaths.length % 2 === 0) {
|
|
984
|
-
throw Error(`getCollectionBySlug: Collection paths must have an odd number of segments: ${slugOrPath}`);
|
|
985
|
-
}
|
|
986
|
-
const subpathCombinations = getCollectionPathsCombinations(subpaths);
|
|
987
|
-
let result;
|
|
988
|
-
for (let i = 0; i < subpathCombinations.length; i++) {
|
|
989
|
-
const subpathCombination = subpathCombinations[i];
|
|
990
|
-
const navigationEntry = collections && collections.sort((a, b) => (a.slug ?? "").localeCompare(b.slug ?? "")).find((entry) => entry.slug === subpathCombination);
|
|
991
|
-
if (navigationEntry) {
|
|
992
|
-
if (subpathCombination === slugOrPath) {
|
|
993
|
-
result = navigationEntry;
|
|
994
|
-
} else if (navigationEntry.subcollections) {
|
|
995
|
-
const newPath = slugOrPath.replace(subpathCombination, "").split("/").slice(2).join("/");
|
|
996
|
-
if (newPath.length > 0) result = getCollectionBySlugWithin(newPath, getSubcollections(navigationEntry));
|
|
558
|
+
const baseOverrides = {
|
|
559
|
+
slug: relationKey
|
|
560
|
+
};
|
|
561
|
+
if (customName) {
|
|
562
|
+
baseOverrides.name = customName;
|
|
563
|
+
baseOverrides.singularName = customName;
|
|
997
564
|
}
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
return result;
|
|
1002
|
-
}
|
|
1003
|
-
function getCollectionPathsCombinations(subpaths) {
|
|
1004
|
-
const entries = subpaths.length > 0 && subpaths.length % 2 === 0 ? subpaths.splice(0, subpaths.length - 1) : subpaths;
|
|
1005
|
-
const length = entries.length;
|
|
1006
|
-
const result = [];
|
|
1007
|
-
for (let i = length; i > 0; i = i - 2) {
|
|
1008
|
-
result.push(entries.slice(0, i).join("/"));
|
|
1009
|
-
}
|
|
1010
|
-
return result;
|
|
1011
|
-
}
|
|
1012
|
-
function navigateToEntity({
|
|
1013
|
-
openEntityMode,
|
|
1014
|
-
collection,
|
|
1015
|
-
entityId,
|
|
1016
|
-
copy,
|
|
1017
|
-
path,
|
|
1018
|
-
selectedTab,
|
|
1019
|
-
sideEntityController,
|
|
1020
|
-
onClose,
|
|
1021
|
-
navigation
|
|
1022
|
-
}) {
|
|
1023
|
-
if (openEntityMode === "side_panel") {
|
|
1024
|
-
sideEntityController.open({
|
|
1025
|
-
entityId,
|
|
1026
|
-
path,
|
|
1027
|
-
copy,
|
|
1028
|
-
selectedTab,
|
|
1029
|
-
collection,
|
|
1030
|
-
updateUrl: true,
|
|
1031
|
-
onClose
|
|
1032
|
-
});
|
|
1033
|
-
} else {
|
|
1034
|
-
let to = navigation.buildUrlCollectionPath(entityId ? `${path ?? path}/${entityId}` : path ?? path);
|
|
1035
|
-
if (entityId && selectedTab) {
|
|
1036
|
-
to += `/${selectedTab}`;
|
|
1037
|
-
}
|
|
1038
|
-
if (!entityId) {
|
|
1039
|
-
to += "#new";
|
|
1040
|
-
}
|
|
1041
|
-
if (copy) {
|
|
1042
|
-
to += "#copy";
|
|
1043
|
-
}
|
|
1044
|
-
navigation.navigate(to);
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
const reservedKeys = ["edit", "copy", "delete"];
|
|
1048
|
-
function mergeEntityActions(currentActions, newActions) {
|
|
1049
|
-
const updatedActions = [];
|
|
1050
|
-
currentActions.forEach((action) => {
|
|
1051
|
-
const newAction = newActions.find((a) => a.key === action.key);
|
|
1052
|
-
if (newAction) {
|
|
1053
|
-
const mergedAction = {
|
|
1054
|
-
...action,
|
|
1055
|
-
...newAction
|
|
565
|
+
const targetWithOverrides = {
|
|
566
|
+
...target,
|
|
567
|
+
...baseOverrides
|
|
1056
568
|
};
|
|
1057
|
-
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
});
|
|
1062
|
-
newActions.forEach((action) => {
|
|
1063
|
-
if (!currentActions.find((a) => a.key === action.key) && (!action.key || !reservedKeys.includes(action.key))) {
|
|
1064
|
-
updatedActions.push(action);
|
|
1065
|
-
}
|
|
1066
|
-
});
|
|
1067
|
-
return updatedActions;
|
|
569
|
+
return r.overrides ? mergeDeep(targetWithOverrides, r.overrides) : targetWithOverrides;
|
|
570
|
+
}).filter((c) => Boolean(c));
|
|
571
|
+
}
|
|
572
|
+
return [];
|
|
1068
573
|
}
|
|
1069
574
|
function evaluateAST(sqlString, auth, entity) {
|
|
1070
575
|
if (!entity) return true;
|
|
@@ -1130,8 +635,8 @@ function evaluateAST(sqlString, auth, entity) {
|
|
|
1130
635
|
const userRoles = auth.user?.roles || [];
|
|
1131
636
|
return requiredRoles.every((r) => userRoles.includes(r));
|
|
1132
637
|
}
|
|
1133
|
-
const pattern1 = new RegExp(
|
|
1134
|
-
const pattern2 = new RegExp(
|
|
638
|
+
const pattern1 = new RegExp("^\\{?([a-zA-Z0-9_]+)\\}?\\s*=\\s*(?:current_setting\\s*\\(\\s*'app\\.user_id'\\s*\\)|auth\\.uid\\(\\))");
|
|
639
|
+
const pattern2 = new RegExp("^(?:current_setting\\s*\\(\\s*'app\\.user_id'\\s*\\)|auth\\.uid\\(\\))\\s*=\\s*\\{?([a-zA-Z0-9_]+)\\}?");
|
|
1135
640
|
const match1 = cleanedSQL.match(pattern1);
|
|
1136
641
|
if (match1 && match1[1]) {
|
|
1137
642
|
return entity.values[match1[1]] === auth.user?.uid;
|
|
@@ -1164,10 +669,11 @@ function evaluateRule(rule, auth, entity) {
|
|
|
1164
669
|
return true;
|
|
1165
670
|
}
|
|
1166
671
|
function checkOperation(collection, authController, entity, targetOperation) {
|
|
1167
|
-
|
|
672
|
+
const securityRules = getDataSourceCapabilities(collection.driver).supportsRLS ? collection.securityRules : void 0;
|
|
673
|
+
if (!securityRules || securityRules.length === 0) {
|
|
1168
674
|
return true;
|
|
1169
675
|
}
|
|
1170
|
-
const applicableRules =
|
|
676
|
+
const applicableRules = securityRules.filter((r) => r.operation === targetOperation || r.operation === "all" || r.operations?.includes(targetOperation) || r.operations?.includes("all"));
|
|
1171
677
|
if (applicableRules.length === 0) return false;
|
|
1172
678
|
const userRoleIds = authController.user?.roles ?? [];
|
|
1173
679
|
const userRoles = [...userRoleIds, "public"];
|
|
@@ -1204,127 +710,11 @@ function canEditEntity(collection, authController, path, entity) {
|
|
|
1204
710
|
return checkOperation(collection, authController, entity, "update");
|
|
1205
711
|
}
|
|
1206
712
|
function canCreateEntity(collection, authController, path, entity) {
|
|
1207
|
-
if (collection.collectionGroup) return false;
|
|
1208
713
|
return checkOperation(collection, authController, entity, "insert");
|
|
1209
714
|
}
|
|
1210
715
|
function canDeleteEntity(collection, authController, path, entity) {
|
|
1211
716
|
return checkOperation(collection, authController, entity, "delete");
|
|
1212
717
|
}
|
|
1213
|
-
function plural(word, amount) {
|
|
1214
|
-
if (amount !== void 0 && amount === 1) {
|
|
1215
|
-
return word;
|
|
1216
|
-
}
|
|
1217
|
-
const plurals = {
|
|
1218
|
-
"(quiz)$": "$1zes",
|
|
1219
|
-
"^(ox)$": "$1en",
|
|
1220
|
-
"([m|l])ouse$": "$1ice",
|
|
1221
|
-
"(matr|vert|ind)ix|ex$": "$1ices",
|
|
1222
|
-
"(x|ch|ss|sh)$": "$1es",
|
|
1223
|
-
"([^aeiouy]|qu)y$": "$1ies",
|
|
1224
|
-
"(hive)$": "$1s",
|
|
1225
|
-
"(?:([^f])fe|([lr])f)$": "$1$2ves",
|
|
1226
|
-
"(shea|lea|loa|thie)f$": "$1ves",
|
|
1227
|
-
sis$: "ses",
|
|
1228
|
-
"([ti])um$": "$1a",
|
|
1229
|
-
"(tomat|potat|ech|her|vet)o$": "$1oes",
|
|
1230
|
-
"(bu)s$": "$1ses",
|
|
1231
|
-
"(alias)$": "$1es",
|
|
1232
|
-
"(octop)us$": "$1i",
|
|
1233
|
-
"(ax|test)is$": "$1es",
|
|
1234
|
-
"(us)$": "$1es",
|
|
1235
|
-
"([^s]+)$": "$1s"
|
|
1236
|
-
};
|
|
1237
|
-
const irregular = {
|
|
1238
|
-
move: "moves",
|
|
1239
|
-
foot: "feet",
|
|
1240
|
-
goose: "geese",
|
|
1241
|
-
sex: "sexes",
|
|
1242
|
-
child: "children",
|
|
1243
|
-
man: "men",
|
|
1244
|
-
tooth: "teeth",
|
|
1245
|
-
person: "people"
|
|
1246
|
-
};
|
|
1247
|
-
const uncountable = ["sheep", "fish", "deer", "moose", "series", "species", "money", "rice", "information", "equipment", "bison", "cod", "offspring", "pike", "salmon", "shrimp", "swine", "trout", "aircraft", "hovercraft", "spacecraft", "sugar", "tuna", "you", "wood"];
|
|
1248
|
-
if (uncountable.indexOf(word.toLowerCase()) >= 0) {
|
|
1249
|
-
return word;
|
|
1250
|
-
}
|
|
1251
|
-
for (const w in irregular) {
|
|
1252
|
-
const pattern = new RegExp(`${w}$`, "i");
|
|
1253
|
-
const replace = irregular[w];
|
|
1254
|
-
if (pattern.test(word)) {
|
|
1255
|
-
return word.replace(pattern, replace);
|
|
1256
|
-
}
|
|
1257
|
-
}
|
|
1258
|
-
for (const reg in plurals) {
|
|
1259
|
-
const pattern = new RegExp(reg, "i");
|
|
1260
|
-
if (pattern.test(word)) {
|
|
1261
|
-
return word.replace(pattern, plurals[reg]);
|
|
1262
|
-
}
|
|
1263
|
-
}
|
|
1264
|
-
return word;
|
|
1265
|
-
}
|
|
1266
|
-
function singular(word, amount) {
|
|
1267
|
-
if (amount !== void 0 && amount !== 1) {
|
|
1268
|
-
return word;
|
|
1269
|
-
}
|
|
1270
|
-
const singulars = {
|
|
1271
|
-
"(quiz)zes$": "$1",
|
|
1272
|
-
"(matr)ices$": "$1ix",
|
|
1273
|
-
"(vert|ind)ices$": "$1ex",
|
|
1274
|
-
"^(ox)en$": "$1",
|
|
1275
|
-
"(alias)es$": "$1",
|
|
1276
|
-
"(octop|vir)i$": "$1us",
|
|
1277
|
-
"(cris|ax|test)es$": "$1is",
|
|
1278
|
-
"(shoe)s$": "$1",
|
|
1279
|
-
"(o)es$": "$1",
|
|
1280
|
-
"(bus)es$": "$1",
|
|
1281
|
-
"([m|l])ice$": "$1ouse",
|
|
1282
|
-
"(x|ch|ss|sh)es$": "$1",
|
|
1283
|
-
"(m)ovies$": "$1ovie",
|
|
1284
|
-
"(s)eries$": "$1eries",
|
|
1285
|
-
"([^aeiouy]|qu)ies$": "$1y",
|
|
1286
|
-
"([lr])ves$": "$1f",
|
|
1287
|
-
"(tive)s$": "$1",
|
|
1288
|
-
"(hive)s$": "$1",
|
|
1289
|
-
"(li|wi|kni)ves$": "$1fe",
|
|
1290
|
-
"(shea|loa|lea|thie)ves$": "$1f",
|
|
1291
|
-
"(^analy)ses$": "$1sis",
|
|
1292
|
-
"((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$": "$1$2sis",
|
|
1293
|
-
"([ti])a$": "$1um",
|
|
1294
|
-
"(n)ews$": "$1ews",
|
|
1295
|
-
"(h|bl)ouses$": "$1ouse",
|
|
1296
|
-
"(corpse)s$": "$1",
|
|
1297
|
-
"(us)es$": "$1",
|
|
1298
|
-
s$: ""
|
|
1299
|
-
};
|
|
1300
|
-
const irregular = {
|
|
1301
|
-
move: "moves",
|
|
1302
|
-
foot: "feet",
|
|
1303
|
-
goose: "geese",
|
|
1304
|
-
sex: "sexes",
|
|
1305
|
-
child: "children",
|
|
1306
|
-
man: "men",
|
|
1307
|
-
tooth: "teeth",
|
|
1308
|
-
person: "people"
|
|
1309
|
-
};
|
|
1310
|
-
const uncountable = ["sheep", "fish", "deer", "moose", "series", "species", "money", "rice", "information", "equipment", "bison", "cod", "offspring", "pike", "salmon", "shrimp", "swine", "trout", "aircraft", "hovercraft", "spacecraft", "sugar", "tuna", "you", "wood"];
|
|
1311
|
-
if (uncountable.indexOf(word.toLowerCase()) >= 0) {
|
|
1312
|
-
return word;
|
|
1313
|
-
}
|
|
1314
|
-
for (const w in irregular) {
|
|
1315
|
-
const pattern = new RegExp(`${irregular[w]}$`, "i");
|
|
1316
|
-
if (pattern.test(word)) {
|
|
1317
|
-
return word.replace(pattern, w);
|
|
1318
|
-
}
|
|
1319
|
-
}
|
|
1320
|
-
for (const reg in singulars) {
|
|
1321
|
-
const pattern = new RegExp(reg, "i");
|
|
1322
|
-
if (pattern.test(word)) {
|
|
1323
|
-
return word.replace(pattern, singulars[reg]);
|
|
1324
|
-
}
|
|
1325
|
-
}
|
|
1326
|
-
return word;
|
|
1327
|
-
}
|
|
1328
718
|
function getEntityImagePreviewPropertyKey(collection) {
|
|
1329
719
|
for (const key in collection.properties) {
|
|
1330
720
|
const property = collection.properties[key];
|
|
@@ -1346,46 +736,136 @@ function getEntityImagePreviewPropertyKey(collection) {
|
|
|
1346
736
|
}
|
|
1347
737
|
for (const key in collection.properties) {
|
|
1348
738
|
const property = collection.properties[key];
|
|
1349
|
-
if (property.type === "array" && property.of
|
|
739
|
+
if (property.type === "array" && property.of && !Array.isArray(property.of) && property.of.type === "string" && property.of.url === "image") {
|
|
740
|
+
return key;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
for (const key in collection.properties) {
|
|
744
|
+
const property = collection.properties[key];
|
|
745
|
+
if (property.type === "string" && property.storage && !property.storage.acceptedFiles) {
|
|
746
|
+
return key;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
for (const key in collection.properties) {
|
|
750
|
+
const property = collection.properties[key];
|
|
751
|
+
if (property.type === "array" && !Array.isArray(property.of) && property.of?.type === "string" && property.of.storage && !property.of.storage.acceptedFiles) {
|
|
1350
752
|
return key;
|
|
1351
753
|
}
|
|
1352
754
|
}
|
|
1353
755
|
return void 0;
|
|
1354
756
|
}
|
|
1355
|
-
function
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
757
|
+
function removeInitialAndTrailingSlashes(s) {
|
|
758
|
+
return removeInitialSlash(removeTrailingSlash(s));
|
|
759
|
+
}
|
|
760
|
+
function removeInitialSlash(s) {
|
|
761
|
+
if (s.startsWith("/")) return s.slice(1);
|
|
762
|
+
else return s;
|
|
763
|
+
}
|
|
764
|
+
function removeTrailingSlash(s) {
|
|
765
|
+
if (s.endsWith("/")) return s.slice(0, -1);
|
|
766
|
+
else return s;
|
|
767
|
+
}
|
|
768
|
+
function addInitialSlash(s) {
|
|
769
|
+
if (s.startsWith("/")) return s;
|
|
770
|
+
else return `/${s}`;
|
|
771
|
+
}
|
|
772
|
+
function getLastSegment(path) {
|
|
773
|
+
const cleanPath = removeInitialAndTrailingSlashes(path);
|
|
774
|
+
if (cleanPath.includes("/")) {
|
|
775
|
+
const segments = cleanPath.split("/");
|
|
776
|
+
return segments[segments.length - 1];
|
|
777
|
+
}
|
|
778
|
+
return cleanPath;
|
|
779
|
+
}
|
|
780
|
+
function resolveCollectionPathIds(path, allCollections) {
|
|
781
|
+
let remainingPath = removeInitialAndTrailingSlashes(path);
|
|
782
|
+
if (!remainingPath) {
|
|
783
|
+
return "";
|
|
784
|
+
}
|
|
785
|
+
let currentCollections = allCollections;
|
|
786
|
+
const resolvedPathParts = [];
|
|
787
|
+
while (remainingPath.length > 0) {
|
|
788
|
+
if (!currentCollections || currentCollections.length === 0) {
|
|
789
|
+
console.warn(`resolveCollectionPathIds: Path structure implies subcollections, but none found before segment starting with "${remainingPath}" in original path "${path}". Appending remaining original path.`);
|
|
790
|
+
resolvedPathParts.push(remainingPath);
|
|
791
|
+
remainingPath = "";
|
|
792
|
+
break;
|
|
793
|
+
}
|
|
794
|
+
let foundMatch = false;
|
|
795
|
+
const potentialMatches = currentCollections.flatMap((col) => [{
|
|
796
|
+
col,
|
|
797
|
+
match: col.slug
|
|
798
|
+
}]).filter((p) => p.match && remainingPath.startsWith(p.match)).sort((a, b) => b.match.length - a.match.length);
|
|
799
|
+
if (potentialMatches.length > 0) {
|
|
800
|
+
const {
|
|
801
|
+
col: foundCollection,
|
|
802
|
+
match: matchString
|
|
803
|
+
} = potentialMatches[0];
|
|
804
|
+
resolvedPathParts.push(foundCollection.slug);
|
|
805
|
+
remainingPath = removeInitialSlash(remainingPath.substring(matchString.length));
|
|
806
|
+
if (remainingPath.length === 0) {
|
|
807
|
+
foundMatch = true;
|
|
808
|
+
break;
|
|
809
|
+
}
|
|
810
|
+
const idSeparatorIndex = remainingPath.indexOf("/");
|
|
811
|
+
let entityId;
|
|
812
|
+
if (idSeparatorIndex > -1) {
|
|
813
|
+
entityId = remainingPath.substring(0, idSeparatorIndex);
|
|
814
|
+
remainingPath = remainingPath.substring(idSeparatorIndex + 1);
|
|
1364
815
|
} else {
|
|
1365
|
-
|
|
816
|
+
entityId = remainingPath;
|
|
817
|
+
remainingPath = "";
|
|
818
|
+
console.warn(`resolveCollectionPathIds: Path seems to end with an entity ID "${entityId}" instead of a collection segment in original path "${path}". This might indicate an invalid input path.`);
|
|
819
|
+
}
|
|
820
|
+
resolvedPathParts.push(entityId);
|
|
821
|
+
currentCollections = getSubcollections(foundCollection);
|
|
822
|
+
foundMatch = true;
|
|
823
|
+
if (!currentCollections && remainingPath.length > 0) {
|
|
824
|
+
console.warn(`resolveCollectionPathIds: Path continues after entity ID "${entityId}", but no subcollections are defined for the preceding collection "${foundCollection.slug}" in path "${path}". Appending remaining original path.`);
|
|
825
|
+
resolvedPathParts.push(remainingPath);
|
|
826
|
+
remainingPath = "";
|
|
827
|
+
break;
|
|
1366
828
|
}
|
|
1367
|
-
} else {
|
|
1368
|
-
flatObj[newKey] = obj[key];
|
|
1369
829
|
}
|
|
1370
|
-
|
|
1371
|
-
|
|
830
|
+
if (!foundMatch) {
|
|
831
|
+
console.warn(`resolveCollectionPathIds: Collection definition not found for segment starting with "${remainingPath}" in original path "${path}". Appending remaining original path.`);
|
|
832
|
+
resolvedPathParts.push(remainingPath);
|
|
833
|
+
remainingPath = "";
|
|
834
|
+
break;
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
return resolvedPathParts.join("/");
|
|
1372
838
|
}
|
|
1373
|
-
function
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
839
|
+
function getCollectionBySlugWithin(slugOrPath, collections) {
|
|
840
|
+
const subpaths = removeInitialAndTrailingSlashes(slugOrPath).split("/");
|
|
841
|
+
if (subpaths.length % 2 === 0) {
|
|
842
|
+
throw Error(`getCollectionBySlug: Collection paths must have an odd number of segments: ${slugOrPath}`);
|
|
843
|
+
}
|
|
844
|
+
const subpathCombinations = getCollectionPathsCombinations(subpaths);
|
|
845
|
+
let result;
|
|
846
|
+
for (let i = 0; i < subpathCombinations.length; i++) {
|
|
847
|
+
const subpathCombination = subpathCombinations[i];
|
|
848
|
+
const navigationEntry = collections && collections.sort((a, b) => (a.slug ?? "").localeCompare(b.slug ?? "")).find((entry) => entry.slug === subpathCombination);
|
|
849
|
+
if (navigationEntry) {
|
|
850
|
+
if (subpathCombination === slugOrPath) {
|
|
851
|
+
result = navigationEntry;
|
|
852
|
+
} else if (getSubcollections(navigationEntry).length > 0) {
|
|
853
|
+
const newPath = slugOrPath.replace(subpathCombination, "").split("/").slice(2).join("/");
|
|
854
|
+
if (newPath.length > 0) result = getCollectionBySlugWithin(newPath, getSubcollections(navigationEntry));
|
|
1385
855
|
}
|
|
1386
|
-
}
|
|
1387
|
-
|
|
1388
|
-
}
|
|
856
|
+
}
|
|
857
|
+
if (result) break;
|
|
858
|
+
}
|
|
859
|
+
return result;
|
|
860
|
+
}
|
|
861
|
+
function getCollectionPathsCombinations(subpaths) {
|
|
862
|
+
const entries = subpaths.length > 0 && subpaths.length % 2 === 0 ? subpaths.splice(0, subpaths.length - 1) : subpaths;
|
|
863
|
+
const length = entries.length;
|
|
864
|
+
const result = [];
|
|
865
|
+
for (let i = length; i > 0; i = i - 2) {
|
|
866
|
+
result.push(entries.slice(0, i).join("/"));
|
|
867
|
+
}
|
|
868
|
+
return result;
|
|
1389
869
|
}
|
|
1390
870
|
function getNavigationEntriesFromPath(props) {
|
|
1391
871
|
const {
|
|
@@ -1426,7 +906,7 @@ function getNavigationEntriesFromPath(props) {
|
|
|
1426
906
|
throw Error("collection not found resolving path: " + collection);
|
|
1427
907
|
}
|
|
1428
908
|
const entityViews = collection.entityViews;
|
|
1429
|
-
const customView = entityViews && entityViews.map((entry) => resolveEntityView(entry, props.contextEntityViews)).filter(
|
|
909
|
+
const customView = entityViews && entityViews.map((entry) => resolveEntityView(entry, props.contextEntityViews)).filter((v) => v != null).find((entry) => entry.key === newPath);
|
|
1430
910
|
const subcollections = getSubcollections(collection);
|
|
1431
911
|
if (customView) {
|
|
1432
912
|
result.push({
|
|
@@ -1486,7 +966,7 @@ function getParentReferencesFromPath(props) {
|
|
|
1486
966
|
if (!collection) {
|
|
1487
967
|
throw Error("collection not found resolving path: " + collection);
|
|
1488
968
|
}
|
|
1489
|
-
if (collection.
|
|
969
|
+
if (getSubcollections(collection).length > 0) {
|
|
1490
970
|
result.push(...getParentReferencesFromPath({
|
|
1491
971
|
path: newPath,
|
|
1492
972
|
collections: getSubcollections(collection),
|
|
@@ -1524,9 +1004,6 @@ function buildEntityCallbacks(callbacks) {
|
|
|
1524
1004
|
function buildAdditionalFieldDelegate(additionalFieldDelegate) {
|
|
1525
1005
|
return additionalFieldDelegate;
|
|
1526
1006
|
}
|
|
1527
|
-
function buildFieldConfig(propertyConfig) {
|
|
1528
|
-
return propertyConfig;
|
|
1529
|
-
}
|
|
1530
1007
|
async function resolveStorageFilenameString({
|
|
1531
1008
|
input,
|
|
1532
1009
|
storage,
|
|
@@ -1618,9 +1095,6 @@ function replacePlaceholders({
|
|
|
1618
1095
|
if (!result) result = randomString() + "_" + file.name;
|
|
1619
1096
|
return result;
|
|
1620
1097
|
}
|
|
1621
|
-
function toArray(input) {
|
|
1622
|
-
return Array.isArray(input) ? input : input ? [input] : [];
|
|
1623
|
-
}
|
|
1624
1098
|
function hasPropertyCallbacks(properties, callbackName) {
|
|
1625
1099
|
if (!properties) return false;
|
|
1626
1100
|
for (const property of Object.values(properties)) {
|
|
@@ -1639,13 +1113,13 @@ function hasPropertyCallbacks(properties, callbackName) {
|
|
|
1639
1113
|
}
|
|
1640
1114
|
async function processProperties(properties, values, previousValues, propsContext, callbackName) {
|
|
1641
1115
|
if (!values || typeof values !== "object") return values;
|
|
1642
|
-
|
|
1116
|
+
const result = {
|
|
1643
1117
|
...values
|
|
1644
1118
|
};
|
|
1645
1119
|
for (const [key, property] of Object.entries(properties)) {
|
|
1646
1120
|
if (result[key] === void 0) continue;
|
|
1647
1121
|
let currentValue = result[key];
|
|
1648
|
-
|
|
1122
|
+
const previousValue = previousValues?.[key];
|
|
1649
1123
|
if (property.type === "array" && Array.isArray(currentValue)) {
|
|
1650
1124
|
if (property.of && !Array.isArray(property.of)) {
|
|
1651
1125
|
currentValue = await Promise.all(currentValue.map(async (item, index) => {
|
|
@@ -1697,22 +1171,6 @@ const buildPropertyCallbacks = (properties) => {
|
|
|
1697
1171
|
}
|
|
1698
1172
|
return Object.keys(propertyCallbacks).length > 0 ? propertyCallbacks : void 0;
|
|
1699
1173
|
};
|
|
1700
|
-
function hashString(str) {
|
|
1701
|
-
let hash2 = 0;
|
|
1702
|
-
let i;
|
|
1703
|
-
let chr;
|
|
1704
|
-
for (i = 0; i < str.length; i++) {
|
|
1705
|
-
chr = str.charCodeAt(i);
|
|
1706
|
-
hash2 = (hash2 << 5) - hash2 + chr;
|
|
1707
|
-
hash2 |= 0;
|
|
1708
|
-
}
|
|
1709
|
-
return Math.abs(hash2);
|
|
1710
|
-
}
|
|
1711
|
-
function generateForeignKeyName(name) {
|
|
1712
|
-
const snakeCaseName = toSnakeCase(name);
|
|
1713
|
-
const singularName = snakeCaseName.endsWith("s") ? snakeCaseName.slice(0, -1) : snakeCaseName;
|
|
1714
|
-
return `${singularName}_id`;
|
|
1715
|
-
}
|
|
1716
1174
|
function sanitizeRelation(relation, sourceCollection) {
|
|
1717
1175
|
if (!relation.target) {
|
|
1718
1176
|
throw new Error("Relation is missing a `target` collection.");
|
|
@@ -1722,7 +1180,7 @@ function sanitizeRelation(relation, sourceCollection) {
|
|
|
1722
1180
|
...relation
|
|
1723
1181
|
};
|
|
1724
1182
|
if (!newRelation.relationName) {
|
|
1725
|
-
newRelation.relationName = toSnakeCase(targetCollection.slug
|
|
1183
|
+
newRelation.relationName = toSnakeCase(targetCollection.slug);
|
|
1726
1184
|
}
|
|
1727
1185
|
if (!newRelation.direction) {
|
|
1728
1186
|
if (newRelation.foreignKeyOnTarget) newRelation.direction = "inverse";
|
|
@@ -1740,12 +1198,12 @@ function sanitizeRelation(relation, sourceCollection) {
|
|
|
1740
1198
|
if (!newRelation.foreignKeyOnTarget) {
|
|
1741
1199
|
let foundForeignKey = false;
|
|
1742
1200
|
try {
|
|
1743
|
-
const targetRelations = targetCollection.relations || [];
|
|
1201
|
+
const targetRelations = getDataSourceCapabilities(targetCollection.driver).supportsRelations ? targetCollection.relations || [] : [];
|
|
1744
1202
|
for (const targetRel of targetRelations) {
|
|
1745
1203
|
if (targetRel.direction === "owning" && targetRel.cardinality === "one" && targetRel.localKey) {
|
|
1746
1204
|
try {
|
|
1747
1205
|
const targetRelTarget = targetRel.target();
|
|
1748
|
-
if (targetRelTarget.slug === sourceCollection.slug
|
|
1206
|
+
if (targetRelTarget.slug === sourceCollection.slug) {
|
|
1749
1207
|
newRelation.foreignKeyOnTarget = targetRel.localKey;
|
|
1750
1208
|
foundForeignKey = true;
|
|
1751
1209
|
break;
|
|
@@ -1766,9 +1224,9 @@ function sanitizeRelation(relation, sourceCollection) {
|
|
|
1766
1224
|
let isManyToManyInverse = false;
|
|
1767
1225
|
if (newRelation.inverseRelationName && !newRelation.foreignKeyOnTarget) {
|
|
1768
1226
|
try {
|
|
1769
|
-
const targetRelations = targetCollection.relations || [];
|
|
1227
|
+
const targetRelations = getDataSourceCapabilities(targetCollection.driver).supportsRelations ? targetCollection.relations || [] : [];
|
|
1770
1228
|
for (const targetRel of targetRelations) {
|
|
1771
|
-
if (targetRel.cardinality === "many" && targetRel.direction === "owning"
|
|
1229
|
+
if (targetRel.cardinality === "many" && (targetRel.direction === "owning" || !targetRel.direction) && targetRel.relationName === newRelation.inverseRelationName) {
|
|
1772
1230
|
isManyToManyInverse = true;
|
|
1773
1231
|
break;
|
|
1774
1232
|
}
|
|
@@ -1795,19 +1253,26 @@ function sanitizeRelation(relation, sourceCollection) {
|
|
|
1795
1253
|
if (newRelation.cardinality === "one" && newRelation.direction === "inverse" && !newRelation.foreignKeyOnTarget && !newRelation.joinPath) {
|
|
1796
1254
|
throw new Error(`Configuration Error in relation from '${sourceCollection.name}': An 'inverse' one-to-one relation requires a 'foreignKeyOnTarget'. Check the relation config for '${newRelation.relationName}'`);
|
|
1797
1255
|
}
|
|
1798
|
-
if (newRelation.cardinality === "many" && newRelation.direction === "inverse" && !newRelation.foreignKeyOnTarget && !newRelation.joinPath) {
|
|
1256
|
+
if (newRelation.cardinality === "many" && newRelation.direction === "inverse" && !newRelation.foreignKeyOnTarget && !newRelation.joinPath && !newRelation.inverseRelationName) {
|
|
1799
1257
|
throw new Error(`Configuration Error in relation from '${sourceCollection.name}': An 'inverse' one-to-many relation requires a 'foreignKeyOnTarget'. Check the relation config for '${newRelation.relationName}'`);
|
|
1800
1258
|
}
|
|
1801
1259
|
return newRelation;
|
|
1802
1260
|
}
|
|
1261
|
+
const _resolvedRelationsCache = /* @__PURE__ */ new WeakMap();
|
|
1803
1262
|
function resolveCollectionRelations(collection) {
|
|
1263
|
+
const cached = _resolvedRelationsCache.get(collection);
|
|
1264
|
+
if (cached) return cached;
|
|
1265
|
+
if (!getDataSourceCapabilities(collection.driver).supportsRelations) return {};
|
|
1266
|
+
const relCollection = collection;
|
|
1804
1267
|
const relations = {};
|
|
1805
|
-
|
|
1806
|
-
|
|
1268
|
+
const registeredRelationNames = /* @__PURE__ */ new Set();
|
|
1269
|
+
if (relCollection.relations) {
|
|
1270
|
+
relCollection.relations.forEach((relation) => {
|
|
1807
1271
|
const normalizedRelation = sanitizeRelation(relation, collection);
|
|
1808
1272
|
const relationKey = normalizedRelation.relationName;
|
|
1809
1273
|
if (relationKey) {
|
|
1810
1274
|
relations[relationKey] = normalizedRelation;
|
|
1275
|
+
registeredRelationNames.add(relationKey);
|
|
1811
1276
|
}
|
|
1812
1277
|
});
|
|
1813
1278
|
}
|
|
@@ -1819,15 +1284,17 @@ function resolveCollectionRelations(collection) {
|
|
|
1819
1284
|
sourceCollection: collection
|
|
1820
1285
|
});
|
|
1821
1286
|
if (relation) {
|
|
1822
|
-
if (
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
}
|
|
1826
|
-
relations[propKey] = sanitizeRelation(relation, collection);
|
|
1287
|
+
if (relations[propKey]) return;
|
|
1288
|
+
if (!relation.relationName) {
|
|
1289
|
+
relation.relationName = propKey;
|
|
1827
1290
|
}
|
|
1291
|
+
const normalizedRelation = sanitizeRelation(relation, collection);
|
|
1292
|
+
relations[propKey] = normalizedRelation;
|
|
1293
|
+
registeredRelationNames.add(normalizedRelation.relationName ?? propKey);
|
|
1828
1294
|
}
|
|
1829
1295
|
});
|
|
1830
1296
|
}
|
|
1297
|
+
_resolvedRelationsCache.set(collection, relations);
|
|
1831
1298
|
return relations;
|
|
1832
1299
|
}
|
|
1833
1300
|
function resolvePropertyRelation({
|
|
@@ -1836,15 +1303,35 @@ function resolvePropertyRelation({
|
|
|
1836
1303
|
sourceCollection
|
|
1837
1304
|
}) {
|
|
1838
1305
|
if (property.type !== "relation") return void 0;
|
|
1839
|
-
const
|
|
1306
|
+
const relProp = property;
|
|
1307
|
+
if (relProp.target) {
|
|
1308
|
+
return {
|
|
1309
|
+
relationName: relProp.relationName || propertyKey,
|
|
1310
|
+
target: relProp.target,
|
|
1311
|
+
cardinality: relProp.cardinality || "one",
|
|
1312
|
+
direction: relProp.direction || "owning",
|
|
1313
|
+
inverseRelationName: relProp.inverseRelationName,
|
|
1314
|
+
localKey: relProp.localKey,
|
|
1315
|
+
foreignKeyOnTarget: relProp.foreignKeyOnTarget,
|
|
1316
|
+
through: relProp.through,
|
|
1317
|
+
joinPath: relProp.joinPath,
|
|
1318
|
+
onUpdate: relProp.onUpdate,
|
|
1319
|
+
onDelete: relProp.onDelete,
|
|
1320
|
+
overrides: relProp.overrides
|
|
1321
|
+
};
|
|
1322
|
+
}
|
|
1323
|
+
const relation = (sourceCollection.relations ?? []).find((rel) => rel.relationName === relProp.relationName);
|
|
1840
1324
|
if (!relation) {
|
|
1841
|
-
console.warn(`Unrecognized relation format for property '${propertyKey}' in collection '${sourceCollection.slug
|
|
1325
|
+
console.warn(`Unrecognized relation format for property '${propertyKey}' in collection '${sourceCollection.slug}'`);
|
|
1842
1326
|
return void 0;
|
|
1843
1327
|
}
|
|
1844
1328
|
return relation;
|
|
1845
1329
|
}
|
|
1846
1330
|
function getTableName(collection) {
|
|
1847
|
-
|
|
1331
|
+
if (getDataSourceCapabilities(collection.driver).supportsRelations) {
|
|
1332
|
+
return collection.table ?? toSnakeCase(collection.slug) ?? toSnakeCase(collection.name);
|
|
1333
|
+
}
|
|
1334
|
+
return toSnakeCase(collection.slug) ?? toSnakeCase(collection.name);
|
|
1848
1335
|
}
|
|
1849
1336
|
function getTableVarName(tableName) {
|
|
1850
1337
|
return tableName.replace(/_([a-z])/g, (_, char) => char.toUpperCase());
|
|
@@ -1857,6 +1344,14 @@ function getEnumVarName(tableName, propName) {
|
|
|
1857
1344
|
function getColumnName(fullColumn) {
|
|
1858
1345
|
return fullColumn.includes(".") ? fullColumn.split(".").pop() : fullColumn;
|
|
1859
1346
|
}
|
|
1347
|
+
function findRelation(resolvedRelations, key) {
|
|
1348
|
+
if (resolvedRelations[key]) return resolvedRelations[key];
|
|
1349
|
+
const slugKey = key.replace(/_/g, "-");
|
|
1350
|
+
if (slugKey !== key && resolvedRelations[slugKey]) return resolvedRelations[slugKey];
|
|
1351
|
+
const snakeKey = key.replace(/-/g, "_");
|
|
1352
|
+
if (snakeKey !== key && resolvedRelations[snakeKey]) return resolvedRelations[snakeKey];
|
|
1353
|
+
return void 0;
|
|
1354
|
+
}
|
|
1860
1355
|
function getIn(obj, path) {
|
|
1861
1356
|
if (!obj || !path) return void 0;
|
|
1862
1357
|
return path.split(".").reduce((acc, part) => acc && acc[part], obj);
|
|
@@ -1942,7 +1437,7 @@ function buildConditionContext(params) {
|
|
|
1942
1437
|
email: user?.email ?? null,
|
|
1943
1438
|
displayName: user?.displayName ?? null,
|
|
1944
1439
|
photoURL: user?.photoURL ?? null,
|
|
1945
|
-
roles: user?.roles ?? []
|
|
1440
|
+
roles: (user?.roles ?? []).map((r) => typeof r === "string" ? r : r.id)
|
|
1946
1441
|
},
|
|
1947
1442
|
now: Date.now()
|
|
1948
1443
|
};
|
|
@@ -1952,7 +1447,7 @@ function applyPropertyConditions(property, context) {
|
|
|
1952
1447
|
conditions
|
|
1953
1448
|
} = property;
|
|
1954
1449
|
if (!conditions) return property;
|
|
1955
|
-
|
|
1450
|
+
const result = {
|
|
1956
1451
|
...property
|
|
1957
1452
|
};
|
|
1958
1453
|
if (conditions.disabled) {
|
|
@@ -1992,8 +1487,8 @@ function applyPropertyConditions(property, context) {
|
|
|
1992
1487
|
if (context.isNew && conditions.defaultValue !== void 0) {
|
|
1993
1488
|
result.defaultValue = evaluateCondition(conditions.defaultValue, context);
|
|
1994
1489
|
}
|
|
1995
|
-
if ("
|
|
1996
|
-
result.
|
|
1490
|
+
if ("enum" in result && result.enum && (conditions.enumConditions || conditions.allowedEnumValues || conditions.excludedEnumValues)) {
|
|
1491
|
+
result.enum = applyEnumConditions(result.enum, conditions, context);
|
|
1997
1492
|
}
|
|
1998
1493
|
if (result.type === "reference") {
|
|
1999
1494
|
if (conditions.referencePath) {
|
|
@@ -2059,13 +1554,15 @@ function applyEnumConditions(enumValues, conditions, context) {
|
|
|
2059
1554
|
}
|
|
2060
1555
|
class CollectionRegistry {
|
|
2061
1556
|
// Normalized runtime layer (used by Data Grid / UI)
|
|
2062
|
-
|
|
1557
|
+
collectionsByTableName = /* @__PURE__ */ new Map();
|
|
2063
1558
|
collectionsBySlug = /* @__PURE__ */ new Map();
|
|
2064
1559
|
rootCollections = [];
|
|
1560
|
+
cachedCollectionsList = null;
|
|
2065
1561
|
// Raw configuration layer (used by Collection Editor AST generator)
|
|
2066
|
-
|
|
1562
|
+
rawCollectionsByTableName = /* @__PURE__ */ new Map();
|
|
2067
1563
|
rawCollectionsBySlug = /* @__PURE__ */ new Map();
|
|
2068
1564
|
rawRootCollections = [];
|
|
1565
|
+
cachedRawCollectionsList = null;
|
|
2069
1566
|
// Snapshot of raw input for idempotency check — compared BEFORE normalization
|
|
2070
1567
|
// to avoid the issue where normalization creates new objects that always fail equality.
|
|
2071
1568
|
lastRawInputSnapshot = null;
|
|
@@ -2075,12 +1572,14 @@ class CollectionRegistry {
|
|
|
2075
1572
|
}
|
|
2076
1573
|
}
|
|
2077
1574
|
reset() {
|
|
2078
|
-
this.
|
|
1575
|
+
this.collectionsByTableName.clear();
|
|
2079
1576
|
this.collectionsBySlug.clear();
|
|
2080
1577
|
this.rootCollections = [];
|
|
2081
|
-
this.
|
|
1578
|
+
this.cachedCollectionsList = null;
|
|
1579
|
+
this.rawCollectionsByTableName.clear();
|
|
2082
1580
|
this.rawCollectionsBySlug.clear();
|
|
2083
1581
|
this.rawRootCollections = [];
|
|
1582
|
+
this.cachedRawCollectionsList = null;
|
|
2084
1583
|
}
|
|
2085
1584
|
/**
|
|
2086
1585
|
* Registers a collection and its subcollections recursively.
|
|
@@ -2099,7 +1598,31 @@ class CollectionRegistry {
|
|
|
2099
1598
|
const normalizedCollections = collections.map((c) => this.normalizeCollection({
|
|
2100
1599
|
...c
|
|
2101
1600
|
}));
|
|
2102
|
-
normalizedCollections.forEach((c, index) =>
|
|
1601
|
+
normalizedCollections.forEach((c, index) => {
|
|
1602
|
+
const raw = cloneDeep(collections[index]);
|
|
1603
|
+
this.rootCollections.push(c);
|
|
1604
|
+
this.rawRootCollections.push(raw);
|
|
1605
|
+
const normalized = this.normalizeCollection(c);
|
|
1606
|
+
this.collectionsByTableName.set(getTableName(normalized), normalized);
|
|
1607
|
+
this.rawCollectionsByTableName.set(getTableName(raw), raw);
|
|
1608
|
+
if (normalized.slug) {
|
|
1609
|
+
this.collectionsBySlug.set(normalized.slug, normalized);
|
|
1610
|
+
}
|
|
1611
|
+
if (raw.slug) {
|
|
1612
|
+
this.rawCollectionsBySlug.set(raw.slug, raw);
|
|
1613
|
+
}
|
|
1614
|
+
});
|
|
1615
|
+
normalizedCollections.forEach((c) => {
|
|
1616
|
+
const subcollections = getSubcollections(c);
|
|
1617
|
+
if (subcollections && subcollections.length > 0) {
|
|
1618
|
+
subcollections.forEach((subCollection) => {
|
|
1619
|
+
if (!subCollection) return;
|
|
1620
|
+
this._registerRecursively(this.normalizeCollection({
|
|
1621
|
+
...subCollection
|
|
1622
|
+
}), cloneDeep(subCollection));
|
|
1623
|
+
});
|
|
1624
|
+
}
|
|
1625
|
+
});
|
|
2103
1626
|
this.lastRawInputSnapshot = rawSnapshot;
|
|
2104
1627
|
return true;
|
|
2105
1628
|
}
|
|
@@ -2110,49 +1633,127 @@ class CollectionRegistry {
|
|
|
2110
1633
|
this._registerRecursively(collection, raw);
|
|
2111
1634
|
}
|
|
2112
1635
|
_registerRecursively(collection, rawCollection) {
|
|
2113
|
-
if (this.
|
|
1636
|
+
if (this.collectionsByTableName.has(getTableName(collection))) {
|
|
2114
1637
|
return;
|
|
2115
1638
|
}
|
|
2116
1639
|
const normalizedCollection = this.normalizeCollection(collection);
|
|
2117
|
-
this.
|
|
2118
|
-
this.
|
|
1640
|
+
this.collectionsByTableName.set(getTableName(normalizedCollection), normalizedCollection);
|
|
1641
|
+
this.rawCollectionsByTableName.set(getTableName(rawCollection), rawCollection);
|
|
2119
1642
|
if (normalizedCollection.slug) {
|
|
2120
1643
|
this.collectionsBySlug.set(normalizedCollection.slug, normalizedCollection);
|
|
2121
1644
|
}
|
|
2122
1645
|
if (rawCollection.slug) {
|
|
2123
1646
|
this.rawCollectionsBySlug.set(rawCollection.slug, rawCollection);
|
|
2124
1647
|
}
|
|
2125
|
-
const subcollections = getSubcollections(
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
this._registerRecursively(this.normalizeCollection(
|
|
1648
|
+
const subcollections = getSubcollections(normalizedCollection);
|
|
1649
|
+
if (subcollections && subcollections.length > 0) {
|
|
1650
|
+
subcollections.forEach((subCollection) => {
|
|
1651
|
+
if (!subCollection) return;
|
|
1652
|
+
this._registerRecursively(this.normalizeCollection({
|
|
1653
|
+
...subCollection
|
|
1654
|
+
}), cloneDeep(subCollection));
|
|
2130
1655
|
});
|
|
2131
1656
|
}
|
|
2132
1657
|
}
|
|
2133
1658
|
normalizeCollection(collection) {
|
|
2134
|
-
const
|
|
2135
|
-
|
|
2136
|
-
|
|
1659
|
+
const result = {
|
|
1660
|
+
...collection
|
|
1661
|
+
};
|
|
1662
|
+
const extractedRelations = this.extractRelationsFromProperties(result.properties);
|
|
1663
|
+
const relResult = result;
|
|
1664
|
+
const manualRelations = getDataSourceCapabilities(result.driver).supportsRelations ? relResult.relations ?? [] : [];
|
|
1665
|
+
const mergedRelationsRaw = [...extractedRelations];
|
|
1666
|
+
for (const manual of manualRelations) {
|
|
1667
|
+
const name = manual.relationName;
|
|
1668
|
+
if (!name || !mergedRelationsRaw.find((r) => r.relationName === name)) {
|
|
1669
|
+
mergedRelationsRaw.push(manual);
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
let mergedRelations = mergedRelationsRaw;
|
|
1673
|
+
if (getDataSourceCapabilities(result.driver).supportsRelations) {
|
|
1674
|
+
mergedRelations = mergedRelationsRaw.map((r) => {
|
|
1675
|
+
try {
|
|
1676
|
+
return sanitizeRelation(r, result);
|
|
1677
|
+
} catch {
|
|
1678
|
+
return r;
|
|
1679
|
+
}
|
|
1680
|
+
});
|
|
1681
|
+
relResult.relations = mergedRelations;
|
|
1682
|
+
}
|
|
1683
|
+
const properties = this.normalizeProperties(result.properties, mergedRelations);
|
|
1684
|
+
result.properties = properties;
|
|
1685
|
+
if (!result.childCollections) {
|
|
1686
|
+
if (getDataSourceCapabilities(result.driver).supportsSubcollections && result.subcollections) {
|
|
1687
|
+
result.childCollections = result.subcollections;
|
|
1688
|
+
} else if (getDataSourceCapabilities(result.driver).supportsRelations && relResult.relations) {
|
|
1689
|
+
const manyRelations = relResult.relations.filter((r) => r.cardinality === "many");
|
|
1690
|
+
if (manyRelations.length > 0) {
|
|
1691
|
+
result.childCollections = () => manyRelations.map((r) => {
|
|
1692
|
+
const target = r.target();
|
|
1693
|
+
return r.overrides ? mergeDeep(target, r.overrides) : target;
|
|
1694
|
+
});
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
return result;
|
|
1699
|
+
}
|
|
1700
|
+
/**
|
|
1701
|
+
* Extract Relation[] from properties that have inline relation config (i.e. `target` is set).
|
|
1702
|
+
* This allows developers to define relations directly on properties without a separate
|
|
1703
|
+
* `relations[]` entry on the collection.
|
|
1704
|
+
*/
|
|
1705
|
+
extractRelationsFromProperties(properties) {
|
|
1706
|
+
const relations = [];
|
|
1707
|
+
for (const [key, property] of Object.entries(properties)) {
|
|
1708
|
+
if (property.type === "relation") {
|
|
1709
|
+
const relProp = property;
|
|
1710
|
+
const target = relProp.target ?? relProp.relation?.target;
|
|
1711
|
+
if (target) {
|
|
1712
|
+
const relationName = relProp.relationName ?? relProp.relation?.relationName ?? key;
|
|
1713
|
+
relations.push({
|
|
1714
|
+
relationName,
|
|
1715
|
+
target,
|
|
1716
|
+
cardinality: relProp.cardinality ?? relProp.relation?.cardinality ?? "one",
|
|
1717
|
+
direction: relProp.direction ?? relProp.relation?.direction ?? "owning",
|
|
1718
|
+
inverseRelationName: relProp.inverseRelationName ?? relProp.relation?.inverseRelationName,
|
|
1719
|
+
localKey: relProp.localKey ?? relProp.relation?.localKey,
|
|
1720
|
+
foreignKeyOnTarget: relProp.foreignKeyOnTarget ?? relProp.relation?.foreignKeyOnTarget,
|
|
1721
|
+
through: relProp.through ?? relProp.relation?.through,
|
|
1722
|
+
joinPath: relProp.joinPath ?? relProp.relation?.joinPath,
|
|
1723
|
+
onUpdate: relProp.onUpdate ?? relProp.relation?.onUpdate,
|
|
1724
|
+
onDelete: relProp.onDelete ?? relProp.relation?.onDelete,
|
|
1725
|
+
overrides: relProp.overrides ?? relProp.relation?.overrides
|
|
1726
|
+
});
|
|
1727
|
+
}
|
|
1728
|
+
} else if (property.type === "map" && property.properties) {
|
|
1729
|
+
relations.push(...this.extractRelationsFromProperties(property.properties));
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
return relations;
|
|
2137
1733
|
}
|
|
2138
1734
|
normalizeProperties(properties, relations) {
|
|
2139
1735
|
const newProperties = {};
|
|
2140
1736
|
for (const key in properties) {
|
|
2141
|
-
newProperties[key] = this.normalizeProperty(properties[key], relations);
|
|
1737
|
+
newProperties[key] = this.normalizeProperty(key, properties[key], relations);
|
|
2142
1738
|
}
|
|
2143
1739
|
return newProperties;
|
|
2144
1740
|
}
|
|
2145
|
-
normalizeProperty(property, relations) {
|
|
1741
|
+
normalizeProperty(key, property, relations) {
|
|
2146
1742
|
const newProperty = {
|
|
2147
1743
|
...property
|
|
2148
1744
|
};
|
|
2149
1745
|
if (newProperty.type === "map" && newProperty.properties) {
|
|
2150
1746
|
newProperty.properties = this.normalizeProperties(newProperty.properties, relations);
|
|
2151
1747
|
} else if (newProperty.type === "array") {
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
1748
|
+
const arrayProp = newProperty;
|
|
1749
|
+
if (arrayProp.of) {
|
|
1750
|
+
if (Array.isArray(arrayProp.of)) {
|
|
1751
|
+
arrayProp.of = arrayProp.of.map((p, i) => this.normalizeProperty(`${key}[${i}]`, p, relations));
|
|
1752
|
+
} else {
|
|
1753
|
+
arrayProp.of = this.normalizeProperty(`${key}.of`, arrayProp.of, relations);
|
|
1754
|
+
}
|
|
1755
|
+
} else if (arrayProp.oneOf && arrayProp.oneOf.properties) {
|
|
1756
|
+
arrayProp.oneOf.properties = this.normalizeProperties(arrayProp.oneOf.properties, relations);
|
|
2156
1757
|
}
|
|
2157
1758
|
} else if ((newProperty.type === "string" || newProperty.type === "number") && newProperty.enum) {
|
|
2158
1759
|
const stringOrNumberProperty = newProperty;
|
|
@@ -2161,11 +1762,12 @@ class CollectionRegistry {
|
|
|
2161
1762
|
}
|
|
2162
1763
|
} else if (newProperty.type === "relation") {
|
|
2163
1764
|
const relationProperty = newProperty;
|
|
2164
|
-
const
|
|
1765
|
+
const name = relationProperty.relationName || key;
|
|
1766
|
+
const relation = relations.find((r) => r.relationName === name);
|
|
2165
1767
|
if (relation) {
|
|
2166
1768
|
relationProperty.relation = relation;
|
|
2167
1769
|
} else {
|
|
2168
|
-
console.warn(`Could not find relation for property with relationName: ${
|
|
1770
|
+
console.warn(`Could not find relation for property '${key}' with relationName: ${name}`);
|
|
2169
1771
|
}
|
|
2170
1772
|
}
|
|
2171
1773
|
return newProperty;
|
|
@@ -2173,7 +1775,12 @@ class CollectionRegistry {
|
|
|
2173
1775
|
get(path) {
|
|
2174
1776
|
const bySlug = this.collectionsBySlug.get(path);
|
|
2175
1777
|
if (bySlug) return bySlug;
|
|
2176
|
-
|
|
1778
|
+
if (path.includes("-")) {
|
|
1779
|
+
const normalized = path.replace(/-/g, "_");
|
|
1780
|
+
const byNormalized = this.collectionsBySlug.get(normalized);
|
|
1781
|
+
if (byNormalized) return byNormalized;
|
|
1782
|
+
}
|
|
1783
|
+
return this.collectionsByTableName.get(path);
|
|
2177
1784
|
}
|
|
2178
1785
|
/**
|
|
2179
1786
|
* Gets the pristine, un-normalized collection exactly as it was provided.
|
|
@@ -2182,7 +1789,12 @@ class CollectionRegistry {
|
|
|
2182
1789
|
getRaw(path) {
|
|
2183
1790
|
const bySlug = this.rawCollectionsBySlug.get(path);
|
|
2184
1791
|
if (bySlug) return bySlug;
|
|
2185
|
-
|
|
1792
|
+
if (path.includes("-")) {
|
|
1793
|
+
const normalized = path.replace(/-/g, "_");
|
|
1794
|
+
const byNormalized = this.rawCollectionsBySlug.get(normalized);
|
|
1795
|
+
if (byNormalized) return byNormalized;
|
|
1796
|
+
}
|
|
1797
|
+
return this.rawCollectionsByTableName.get(path);
|
|
2186
1798
|
}
|
|
2187
1799
|
/**
|
|
2188
1800
|
* Get collection by resolving multi-segment paths through relations
|
|
@@ -2203,10 +1815,13 @@ class CollectionRegistry {
|
|
|
2203
1815
|
}
|
|
2204
1816
|
for (let i = 2; i < pathSegments.length; i += 2) {
|
|
2205
1817
|
const relationKey = pathSegments[i];
|
|
1818
|
+
if (!getDataSourceCapabilities(currentCollection.driver).supportsRelations) {
|
|
1819
|
+
throw new Error(`Relation path navigation requires a collection that supports relations, but '${currentCollection.slug}' uses driver '${currentCollection.driver}'`);
|
|
1820
|
+
}
|
|
2206
1821
|
const resolvedRelations = resolveCollectionRelations(currentCollection);
|
|
2207
|
-
const relation = resolvedRelations
|
|
1822
|
+
const relation = findRelation(resolvedRelations, relationKey);
|
|
2208
1823
|
if (!relation) {
|
|
2209
|
-
throw new Error(`Relation '${relationKey}' not found in collection '${currentCollection.slug
|
|
1824
|
+
throw new Error(`Relation '${relationKey}' not found in collection '${currentCollection.slug}'`);
|
|
2210
1825
|
}
|
|
2211
1826
|
currentCollection = relation.target();
|
|
2212
1827
|
if (i + 1 < pathSegments.length) ;
|
|
@@ -2214,10 +1829,16 @@ class CollectionRegistry {
|
|
|
2214
1829
|
return currentCollection;
|
|
2215
1830
|
}
|
|
2216
1831
|
getCollections() {
|
|
2217
|
-
|
|
1832
|
+
if (!this.cachedCollectionsList) {
|
|
1833
|
+
this.cachedCollectionsList = Array.from(this.collectionsByTableName.values());
|
|
1834
|
+
}
|
|
1835
|
+
return this.cachedCollectionsList;
|
|
2218
1836
|
}
|
|
2219
1837
|
getRawCollections() {
|
|
2220
|
-
|
|
1838
|
+
if (!this.cachedRawCollectionsList) {
|
|
1839
|
+
this.cachedRawCollectionsList = Array.from(this.rawCollectionsByTableName.values());
|
|
1840
|
+
}
|
|
1841
|
+
return this.cachedRawCollectionsList;
|
|
2221
1842
|
}
|
|
2222
1843
|
/**
|
|
2223
1844
|
* Resolves a multi-segment path like "products/123/locales" and returns
|
|
@@ -2243,13 +1864,13 @@ class CollectionRegistry {
|
|
|
2243
1864
|
entityIds.push(entityId);
|
|
2244
1865
|
if (i + 1 < pathSegments.length) {
|
|
2245
1866
|
const subcollectionSlug = pathSegments[i + 1];
|
|
2246
|
-
const subcollections = currentCollection
|
|
2247
|
-
if (!subcollections) {
|
|
2248
|
-
throw new Error(`No subcollections found for ${currentCollection.slug
|
|
1867
|
+
const subcollections = getSubcollections(currentCollection);
|
|
1868
|
+
if (!subcollections || subcollections.length === 0) {
|
|
1869
|
+
throw new Error(`No subcollections found for ${currentCollection.slug} in path: ${path}`);
|
|
2249
1870
|
}
|
|
2250
1871
|
const subcollection = subcollections.find((c) => c.slug === subcollectionSlug);
|
|
2251
1872
|
if (!subcollection) {
|
|
2252
|
-
throw new Error(`Subcollection '${subcollectionSlug}' not found in ${currentCollection.slug
|
|
1873
|
+
throw new Error(`Subcollection '${subcollectionSlug}' not found in ${currentCollection.slug}`);
|
|
2253
1874
|
}
|
|
2254
1875
|
currentCollection = subcollection;
|
|
2255
1876
|
collections.push(currentCollection);
|
|
@@ -2262,6 +1883,205 @@ class CollectionRegistry {
|
|
|
2262
1883
|
};
|
|
2263
1884
|
}
|
|
2264
1885
|
}
|
|
1886
|
+
function convertWhereToFilter(where) {
|
|
1887
|
+
if (!where) return void 0;
|
|
1888
|
+
const operatorMap = {
|
|
1889
|
+
"eq": "==",
|
|
1890
|
+
"neq": "!=",
|
|
1891
|
+
"gt": ">",
|
|
1892
|
+
"gte": ">=",
|
|
1893
|
+
"lt": "<",
|
|
1894
|
+
"lte": "<=",
|
|
1895
|
+
"in": "in",
|
|
1896
|
+
"nin": "not-in",
|
|
1897
|
+
"not-in": "not-in",
|
|
1898
|
+
"cs": "array-contains",
|
|
1899
|
+
"csa": "array-contains-any",
|
|
1900
|
+
"==": "==",
|
|
1901
|
+
"!=": "!=",
|
|
1902
|
+
">": ">",
|
|
1903
|
+
">=": ">=",
|
|
1904
|
+
"<": "<",
|
|
1905
|
+
"<=": "<=",
|
|
1906
|
+
"array-contains": "array-contains",
|
|
1907
|
+
"array-contains-any": "array-contains-any"
|
|
1908
|
+
};
|
|
1909
|
+
const filter = {};
|
|
1910
|
+
for (const [field, rawValue] of Object.entries(where)) {
|
|
1911
|
+
if (rawValue === null) {
|
|
1912
|
+
filter[field] = ["==", null];
|
|
1913
|
+
continue;
|
|
1914
|
+
}
|
|
1915
|
+
if (typeof rawValue === "boolean") {
|
|
1916
|
+
filter[field] = ["==", rawValue];
|
|
1917
|
+
continue;
|
|
1918
|
+
}
|
|
1919
|
+
if (typeof rawValue === "number") {
|
|
1920
|
+
filter[field] = ["==", rawValue];
|
|
1921
|
+
continue;
|
|
1922
|
+
}
|
|
1923
|
+
if (Array.isArray(rawValue) && rawValue.length === 2) {
|
|
1924
|
+
const [rawOp, val] = rawValue;
|
|
1925
|
+
const mappedOp = operatorMap[rawOp] ?? "==";
|
|
1926
|
+
filter[field] = [mappedOp, val];
|
|
1927
|
+
continue;
|
|
1928
|
+
}
|
|
1929
|
+
if (typeof rawValue === "string") {
|
|
1930
|
+
const dotIndex = rawValue.indexOf(".");
|
|
1931
|
+
if (dotIndex === -1) {
|
|
1932
|
+
filter[field] = ["==", rawValue];
|
|
1933
|
+
continue;
|
|
1934
|
+
}
|
|
1935
|
+
const op = rawValue.substring(0, dotIndex);
|
|
1936
|
+
let value = rawValue.substring(dotIndex + 1);
|
|
1937
|
+
if (typeof value === "string" && value.startsWith("(") && value.endsWith(")")) {
|
|
1938
|
+
value = value.slice(1, -1).split(",").map((v) => v.trim());
|
|
1939
|
+
}
|
|
1940
|
+
if (value === "null") {
|
|
1941
|
+
value = null;
|
|
1942
|
+
} else if (value === "true") {
|
|
1943
|
+
value = true;
|
|
1944
|
+
} else if (value === "false") {
|
|
1945
|
+
value = false;
|
|
1946
|
+
} else if (typeof value === "string" && !isNaN(Number(value)) && value.trim() !== "") {
|
|
1947
|
+
value = Number(value);
|
|
1948
|
+
}
|
|
1949
|
+
const mappedOp = operatorMap[op];
|
|
1950
|
+
if (mappedOp) {
|
|
1951
|
+
filter[field] = [mappedOp, value];
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
return Object.keys(filter).length > 0 ? filter : void 0;
|
|
1956
|
+
}
|
|
1957
|
+
function parseOrderBy(orderBy) {
|
|
1958
|
+
if (!orderBy) return void 0;
|
|
1959
|
+
const parts = orderBy.split(":");
|
|
1960
|
+
const field = parts[0];
|
|
1961
|
+
const direction = parts[1] || "asc";
|
|
1962
|
+
return [field, direction];
|
|
1963
|
+
}
|
|
1964
|
+
function createDriverAccessor(driver, slug) {
|
|
1965
|
+
return {
|
|
1966
|
+
async find(params) {
|
|
1967
|
+
const orderParsed = parseOrderBy(params?.orderBy);
|
|
1968
|
+
const entities = await driver.fetchCollection({
|
|
1969
|
+
path: slug,
|
|
1970
|
+
limit: params?.limit,
|
|
1971
|
+
offset: params?.offset,
|
|
1972
|
+
filter: convertWhereToFilter(params?.where),
|
|
1973
|
+
orderBy: orderParsed?.[0],
|
|
1974
|
+
order: orderParsed?.[1],
|
|
1975
|
+
searchString: params?.searchString
|
|
1976
|
+
});
|
|
1977
|
+
const limit = params?.limit ?? 20;
|
|
1978
|
+
const offset = params?.offset ?? 0;
|
|
1979
|
+
return {
|
|
1980
|
+
data: entities,
|
|
1981
|
+
meta: {
|
|
1982
|
+
total: entities.length,
|
|
1983
|
+
limit,
|
|
1984
|
+
offset,
|
|
1985
|
+
hasMore: entities.length >= limit
|
|
1986
|
+
}
|
|
1987
|
+
};
|
|
1988
|
+
},
|
|
1989
|
+
async findById(id) {
|
|
1990
|
+
return driver.fetchEntity({
|
|
1991
|
+
path: slug,
|
|
1992
|
+
entityId: id
|
|
1993
|
+
});
|
|
1994
|
+
},
|
|
1995
|
+
async create(data, id) {
|
|
1996
|
+
return driver.saveEntity({
|
|
1997
|
+
path: slug,
|
|
1998
|
+
values: data,
|
|
1999
|
+
entityId: id,
|
|
2000
|
+
status: "new"
|
|
2001
|
+
});
|
|
2002
|
+
},
|
|
2003
|
+
async update(id, data) {
|
|
2004
|
+
return driver.saveEntity({
|
|
2005
|
+
path: slug,
|
|
2006
|
+
values: data,
|
|
2007
|
+
entityId: id,
|
|
2008
|
+
status: "existing"
|
|
2009
|
+
});
|
|
2010
|
+
},
|
|
2011
|
+
async delete(id) {
|
|
2012
|
+
return driver.deleteEntity({
|
|
2013
|
+
entity: {
|
|
2014
|
+
id,
|
|
2015
|
+
path: slug,
|
|
2016
|
+
values: {}
|
|
2017
|
+
}
|
|
2018
|
+
});
|
|
2019
|
+
},
|
|
2020
|
+
count: driver.countEntities ? async (params) => {
|
|
2021
|
+
return driver.countEntities({
|
|
2022
|
+
path: slug,
|
|
2023
|
+
filter: convertWhereToFilter(params?.where)
|
|
2024
|
+
});
|
|
2025
|
+
} : void 0,
|
|
2026
|
+
listen: driver.listenCollection ? (params, onUpdate, onError) => {
|
|
2027
|
+
const orderParsed = parseOrderBy(params?.orderBy);
|
|
2028
|
+
const limit = params?.limit ?? 20;
|
|
2029
|
+
const offset = params?.offset ?? 0;
|
|
2030
|
+
return driver.listenCollection({
|
|
2031
|
+
path: slug,
|
|
2032
|
+
limit: params?.limit,
|
|
2033
|
+
offset: params?.offset,
|
|
2034
|
+
filter: convertWhereToFilter(params?.where),
|
|
2035
|
+
orderBy: orderParsed?.[0],
|
|
2036
|
+
order: orderParsed?.[1],
|
|
2037
|
+
searchString: params?.searchString,
|
|
2038
|
+
onUpdate: (entities) => {
|
|
2039
|
+
onUpdate({
|
|
2040
|
+
data: entities,
|
|
2041
|
+
meta: {
|
|
2042
|
+
total: entities.length,
|
|
2043
|
+
limit,
|
|
2044
|
+
offset,
|
|
2045
|
+
hasMore: entities.length >= limit
|
|
2046
|
+
}
|
|
2047
|
+
});
|
|
2048
|
+
},
|
|
2049
|
+
onError
|
|
2050
|
+
});
|
|
2051
|
+
} : void 0,
|
|
2052
|
+
listenById: driver.listenEntity ? (id, onUpdate, onError) => {
|
|
2053
|
+
return driver.listenEntity({
|
|
2054
|
+
path: slug,
|
|
2055
|
+
entityId: id,
|
|
2056
|
+
onUpdate: (entity) => onUpdate(entity ?? void 0),
|
|
2057
|
+
onError
|
|
2058
|
+
});
|
|
2059
|
+
} : void 0
|
|
2060
|
+
};
|
|
2061
|
+
}
|
|
2062
|
+
function buildRebaseData(driver) {
|
|
2063
|
+
const cache = /* @__PURE__ */ new Map();
|
|
2064
|
+
function getAccessor(slug) {
|
|
2065
|
+
let accessor = cache.get(slug);
|
|
2066
|
+
if (!accessor) {
|
|
2067
|
+
accessor = createDriverAccessor(driver, slug);
|
|
2068
|
+
cache.set(slug, accessor);
|
|
2069
|
+
}
|
|
2070
|
+
return accessor;
|
|
2071
|
+
}
|
|
2072
|
+
const target = {
|
|
2073
|
+
collection: getAccessor
|
|
2074
|
+
};
|
|
2075
|
+
return new Proxy(target, {
|
|
2076
|
+
get(_target, prop) {
|
|
2077
|
+
if (prop === "collection") return getAccessor;
|
|
2078
|
+
if (typeof prop === "symbol") return void 0;
|
|
2079
|
+
if (prop === "then" || prop === "toJSON" || prop === "$$typeof") return void 0;
|
|
2080
|
+
const slug = toSnakeCase(prop);
|
|
2081
|
+
return getAccessor(slug);
|
|
2082
|
+
}
|
|
2083
|
+
});
|
|
2084
|
+
}
|
|
2265
2085
|
export {
|
|
2266
2086
|
COLLECTION_PATH_SEPARATOR,
|
|
2267
2087
|
CollectionRegistry,
|
|
@@ -2275,23 +2095,22 @@ export {
|
|
|
2275
2095
|
buildEntityCallbacks,
|
|
2276
2096
|
buildEnum,
|
|
2277
2097
|
buildEnumValueConfig,
|
|
2278
|
-
buildFieldConfig,
|
|
2279
2098
|
buildProperties,
|
|
2280
2099
|
buildPropertiesOrBuilder,
|
|
2281
2100
|
buildProperty,
|
|
2282
2101
|
buildPropertyCallbacks,
|
|
2102
|
+
buildRebaseData,
|
|
2283
2103
|
canCreateEntity,
|
|
2284
2104
|
canDeleteEntity,
|
|
2285
2105
|
canEditEntity,
|
|
2286
2106
|
canReadCollection,
|
|
2287
|
-
|
|
2288
|
-
|
|
2107
|
+
createRelationRef,
|
|
2108
|
+
createRelationRefWithData,
|
|
2289
2109
|
enumToObjectEntries,
|
|
2290
2110
|
evaluateCondition,
|
|
2291
|
-
|
|
2111
|
+
findRelation,
|
|
2292
2112
|
fullPathToCollectionSegments,
|
|
2293
2113
|
getArrayResolvedProperties,
|
|
2294
|
-
getArrayValuesCount,
|
|
2295
2114
|
getCollectionBySlugWithin,
|
|
2296
2115
|
getCollectionPathsCombinations,
|
|
2297
2116
|
getColumnName,
|
|
@@ -2300,8 +2119,6 @@ export {
|
|
|
2300
2119
|
getDefaultValuesFor,
|
|
2301
2120
|
getEntityImagePreviewPropertyKey,
|
|
2302
2121
|
getEnumVarName,
|
|
2303
|
-
getHashValue,
|
|
2304
|
-
getIn$1 as getIn,
|
|
2305
2122
|
getLabelOrConfigFrom,
|
|
2306
2123
|
getLastSegment,
|
|
2307
2124
|
getLocalChangesBackup,
|
|
@@ -2313,44 +2130,18 @@ export {
|
|
|
2313
2130
|
getSubcollections,
|
|
2314
2131
|
getTableName,
|
|
2315
2132
|
getTableVarName,
|
|
2316
|
-
getValueInPath,
|
|
2317
|
-
hashString,
|
|
2318
|
-
hydrateRegExp,
|
|
2319
|
-
isDefaultFieldConfigId,
|
|
2320
|
-
isEmptyArray,
|
|
2321
|
-
isEmptyObject,
|
|
2322
|
-
isFunction,
|
|
2323
2133
|
isHidden,
|
|
2324
|
-
isInteger,
|
|
2325
|
-
isNaN$1 as isNaN,
|
|
2326
|
-
isObject,
|
|
2327
|
-
isPlainObject,
|
|
2328
2134
|
isPropertyBuilder,
|
|
2329
2135
|
isReadOnly,
|
|
2330
|
-
|
|
2331
|
-
mergeDeep,
|
|
2332
|
-
mergeEntityActions,
|
|
2333
|
-
navigateToEntity,
|
|
2334
|
-
pick,
|
|
2335
|
-
plural,
|
|
2336
|
-
prettifyIdentifier,
|
|
2337
|
-
randomColor,
|
|
2338
|
-
randomString,
|
|
2136
|
+
normalizeToEntityRelation,
|
|
2339
2137
|
registerConditionOperations,
|
|
2340
|
-
removeFunctions,
|
|
2341
|
-
removeInPath,
|
|
2342
2138
|
removeInitialAndTrailingSlashes,
|
|
2343
2139
|
removeInitialSlash,
|
|
2344
|
-
removeNulls,
|
|
2345
|
-
removePropsIfExisting,
|
|
2346
2140
|
removeTrailingSlash,
|
|
2347
|
-
removeUndefined,
|
|
2348
2141
|
resolveArrayProperties,
|
|
2349
2142
|
resolveCollectionPathIds,
|
|
2350
2143
|
resolveCollectionRelations,
|
|
2351
2144
|
resolveDefaultSelectedView,
|
|
2352
|
-
resolveEntityAction,
|
|
2353
|
-
resolveEntityView$1 as resolveEntityView,
|
|
2354
2145
|
resolveEnumValues,
|
|
2355
2146
|
resolveProperties,
|
|
2356
2147
|
resolveProperty,
|
|
@@ -2359,22 +2150,13 @@ export {
|
|
|
2359
2150
|
resolveRelationProperty,
|
|
2360
2151
|
resolveStorageFilenameString,
|
|
2361
2152
|
resolveStoragePathString,
|
|
2362
|
-
resolvedSelectedEntityView,
|
|
2363
2153
|
sanitizeData,
|
|
2364
2154
|
sanitizeRelation,
|
|
2365
2155
|
segmentsToStrippedPath,
|
|
2366
|
-
serializeRegExp,
|
|
2367
|
-
setIn,
|
|
2368
|
-
singular,
|
|
2369
|
-
slugify,
|
|
2370
2156
|
sortProperties,
|
|
2371
2157
|
stripCollectionPath,
|
|
2372
|
-
toArray,
|
|
2373
|
-
toKebabCase,
|
|
2374
|
-
toSnakeCase,
|
|
2375
2158
|
traverseValueProperty,
|
|
2376
2159
|
traverseValuesProperties,
|
|
2377
|
-
unslugify,
|
|
2378
2160
|
updateDateAutoValues
|
|
2379
2161
|
};
|
|
2380
2162
|
//# sourceMappingURL=index.es.js.map
|