@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
package/dist/index.es.js
CHANGED
|
@@ -1,299 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import { EntityReference, EntityRelation, isFirebaseCollection, isPostgresCollection } 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" || typeof obj.isEntityRelation === "function" && obj.isEntityRelation();
|
|
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 ?? {};
|
|
@@ -538,58 +264,6 @@ function getPrimaryKeys(collection) {
|
|
|
538
264
|
}
|
|
539
265
|
return ["id"];
|
|
540
266
|
}
|
|
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
267
|
function enumToObjectEntries(enumValues) {
|
|
594
268
|
if (Array.isArray(enumValues)) {
|
|
595
269
|
return enumValues;
|
|
@@ -624,28 +298,6 @@ function segmentsToStrippedPath(paths) {
|
|
|
624
298
|
function fullPathToCollectionSegments(path) {
|
|
625
299
|
return path.split("/").filter((e, i) => i % 2 === 0);
|
|
626
300
|
}
|
|
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
301
|
function resolveProperty(props) {
|
|
650
302
|
const {
|
|
651
303
|
property,
|
|
@@ -712,10 +364,10 @@ function resolveProperty(props) {
|
|
|
712
364
|
return resolvedProperty;
|
|
713
365
|
}
|
|
714
366
|
if (customField.property) {
|
|
715
|
-
const {
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
367
|
+
const restConfigProperty = {
|
|
368
|
+
...customField.property
|
|
369
|
+
};
|
|
370
|
+
delete restConfigProperty.propertyConfig;
|
|
719
371
|
const customFieldProperty = resolveProperty({
|
|
720
372
|
property: {
|
|
721
373
|
name: "",
|
|
@@ -827,7 +479,7 @@ function resolveArrayProperties({
|
|
|
827
479
|
});
|
|
828
480
|
}).filter((e) => Boolean(e)) : [];
|
|
829
481
|
return resolvedProperties;
|
|
830
|
-
} else if (!property.Field) {
|
|
482
|
+
} else if (!("Field" in property && property.Field)) {
|
|
831
483
|
throw Error(`The array property (${propertyKey}) needs to declare an 'of' or a 'oneOf' property, or provide a custom \`Field\` component`);
|
|
832
484
|
} else {
|
|
833
485
|
return [];
|
|
@@ -862,209 +514,21 @@ function resolveEnumValues(input) {
|
|
|
862
514
|
return void 0;
|
|
863
515
|
}
|
|
864
516
|
}
|
|
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
517
|
function getSubcollections(collection) {
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
subcollections.push(...(collection.relations ?? []).filter((rel) => rel.cardinality === "many").map((relation) => {
|
|
893
|
-
const targetCollection = relation.target();
|
|
894
|
-
const overrides = relation.overrides;
|
|
895
|
-
return overrides ? mergeDeep(targetCollection, overrides) : targetCollection;
|
|
896
|
-
}) ?? []);
|
|
897
|
-
return subcollections;
|
|
898
|
-
}
|
|
899
|
-
function removeInitialAndTrailingSlashes(s) {
|
|
900
|
-
return removeInitialSlash(removeTrailingSlash(s));
|
|
901
|
-
}
|
|
902
|
-
function removeInitialSlash(s) {
|
|
903
|
-
if (s.startsWith("/")) return s.slice(1);
|
|
904
|
-
else return s;
|
|
905
|
-
}
|
|
906
|
-
function removeTrailingSlash(s) {
|
|
907
|
-
if (s.endsWith("/")) return s.slice(0, -1);
|
|
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];
|
|
518
|
+
if (collection.childCollections) {
|
|
519
|
+
return collection.childCollections() ?? [];
|
|
919
520
|
}
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
function resolveCollectionPathIds(path, allCollections) {
|
|
923
|
-
let remainingPath = removeInitialAndTrailingSlashes(path);
|
|
924
|
-
if (!remainingPath) {
|
|
925
|
-
return "";
|
|
521
|
+
if (isFirebaseCollection(collection) && collection.subcollections) {
|
|
522
|
+
return collection.subcollections() ?? [];
|
|
926
523
|
}
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
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;
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
if (!foundMatch) {
|
|
973
|
-
console.warn(`resolveCollectionPathIds: Collection definition not found for segment starting with "${remainingPath}" in original path "${path}". Appending remaining original path.`);
|
|
974
|
-
resolvedPathParts.push(remainingPath);
|
|
975
|
-
remainingPath = "";
|
|
976
|
-
break;
|
|
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));
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
if (result) break;
|
|
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
|
|
524
|
+
if (isPostgresCollection(collection) && collection.relations) {
|
|
525
|
+
const manyRelations = collection.relations.filter((r) => r.cardinality === "many");
|
|
526
|
+
return manyRelations.map((r) => {
|
|
527
|
+
const target = r.target();
|
|
528
|
+
return r.overrides ? mergeDeep(target, r.overrides) : target;
|
|
1032
529
|
});
|
|
1033
|
-
}
|
|
1034
|
-
|
|
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
|
|
1056
|
-
};
|
|
1057
|
-
updatedActions.push(mergedAction);
|
|
1058
|
-
} else {
|
|
1059
|
-
updatedActions.push(action);
|
|
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;
|
|
530
|
+
}
|
|
531
|
+
return [];
|
|
1068
532
|
}
|
|
1069
533
|
function evaluateAST(sqlString, auth, entity) {
|
|
1070
534
|
if (!entity) return true;
|
|
@@ -1164,10 +628,11 @@ function evaluateRule(rule, auth, entity) {
|
|
|
1164
628
|
return true;
|
|
1165
629
|
}
|
|
1166
630
|
function checkOperation(collection, authController, entity, targetOperation) {
|
|
1167
|
-
|
|
631
|
+
const securityRules = isPostgresCollection(collection) ? collection.securityRules : void 0;
|
|
632
|
+
if (!securityRules || securityRules.length === 0) {
|
|
1168
633
|
return true;
|
|
1169
634
|
}
|
|
1170
|
-
const applicableRules =
|
|
635
|
+
const applicableRules = securityRules.filter((r) => r.operation === targetOperation || r.operation === "all" || r.operations?.includes(targetOperation) || r.operations?.includes("all"));
|
|
1171
636
|
if (applicableRules.length === 0) return false;
|
|
1172
637
|
const userRoleIds = authController.user?.roles ?? [];
|
|
1173
638
|
const userRoles = [...userRoleIds, "public"];
|
|
@@ -1204,127 +669,11 @@ function canEditEntity(collection, authController, path, entity) {
|
|
|
1204
669
|
return checkOperation(collection, authController, entity, "update");
|
|
1205
670
|
}
|
|
1206
671
|
function canCreateEntity(collection, authController, path, entity) {
|
|
1207
|
-
if (collection.collectionGroup) return false;
|
|
1208
672
|
return checkOperation(collection, authController, entity, "insert");
|
|
1209
673
|
}
|
|
1210
674
|
function canDeleteEntity(collection, authController, path, entity) {
|
|
1211
675
|
return checkOperation(collection, authController, entity, "delete");
|
|
1212
676
|
}
|
|
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
677
|
function getEntityImagePreviewPropertyKey(collection) {
|
|
1329
678
|
for (const key in collection.properties) {
|
|
1330
679
|
const property = collection.properties[key];
|
|
@@ -1346,46 +695,136 @@ function getEntityImagePreviewPropertyKey(collection) {
|
|
|
1346
695
|
}
|
|
1347
696
|
for (const key in collection.properties) {
|
|
1348
697
|
const property = collection.properties[key];
|
|
1349
|
-
if (property.type === "array" && property.of
|
|
698
|
+
if (property.type === "array" && property.of && !Array.isArray(property.of) && property.of.type === "string" && property.of.url === "image") {
|
|
699
|
+
return key;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
for (const key in collection.properties) {
|
|
703
|
+
const property = collection.properties[key];
|
|
704
|
+
if (property.type === "string" && property.storage && !property.storage.acceptedFiles) {
|
|
705
|
+
return key;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
for (const key in collection.properties) {
|
|
709
|
+
const property = collection.properties[key];
|
|
710
|
+
if (property.type === "array" && !Array.isArray(property.of) && property.of?.type === "string" && property.of.storage && !property.of.storage.acceptedFiles) {
|
|
1350
711
|
return key;
|
|
1351
712
|
}
|
|
1352
713
|
}
|
|
1353
714
|
return void 0;
|
|
1354
715
|
}
|
|
1355
|
-
function
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
716
|
+
function removeInitialAndTrailingSlashes(s) {
|
|
717
|
+
return removeInitialSlash(removeTrailingSlash(s));
|
|
718
|
+
}
|
|
719
|
+
function removeInitialSlash(s) {
|
|
720
|
+
if (s.startsWith("/")) return s.slice(1);
|
|
721
|
+
else return s;
|
|
722
|
+
}
|
|
723
|
+
function removeTrailingSlash(s) {
|
|
724
|
+
if (s.endsWith("/")) return s.slice(0, -1);
|
|
725
|
+
else return s;
|
|
726
|
+
}
|
|
727
|
+
function addInitialSlash(s) {
|
|
728
|
+
if (s.startsWith("/")) return s;
|
|
729
|
+
else return `/${s}`;
|
|
730
|
+
}
|
|
731
|
+
function getLastSegment(path) {
|
|
732
|
+
const cleanPath = removeInitialAndTrailingSlashes(path);
|
|
733
|
+
if (cleanPath.includes("/")) {
|
|
734
|
+
const segments = cleanPath.split("/");
|
|
735
|
+
return segments[segments.length - 1];
|
|
736
|
+
}
|
|
737
|
+
return cleanPath;
|
|
738
|
+
}
|
|
739
|
+
function resolveCollectionPathIds(path, allCollections) {
|
|
740
|
+
let remainingPath = removeInitialAndTrailingSlashes(path);
|
|
741
|
+
if (!remainingPath) {
|
|
742
|
+
return "";
|
|
743
|
+
}
|
|
744
|
+
let currentCollections = allCollections;
|
|
745
|
+
const resolvedPathParts = [];
|
|
746
|
+
while (remainingPath.length > 0) {
|
|
747
|
+
if (!currentCollections || currentCollections.length === 0) {
|
|
748
|
+
console.warn(`resolveCollectionPathIds: Path structure implies subcollections, but none found before segment starting with "${remainingPath}" in original path "${path}". Appending remaining original path.`);
|
|
749
|
+
resolvedPathParts.push(remainingPath);
|
|
750
|
+
remainingPath = "";
|
|
751
|
+
break;
|
|
752
|
+
}
|
|
753
|
+
let foundMatch = false;
|
|
754
|
+
const potentialMatches = currentCollections.flatMap((col) => [{
|
|
755
|
+
col,
|
|
756
|
+
match: col.slug
|
|
757
|
+
}]).filter((p) => p.match && remainingPath.startsWith(p.match)).sort((a, b) => b.match.length - a.match.length);
|
|
758
|
+
if (potentialMatches.length > 0) {
|
|
759
|
+
const {
|
|
760
|
+
col: foundCollection,
|
|
761
|
+
match: matchString
|
|
762
|
+
} = potentialMatches[0];
|
|
763
|
+
resolvedPathParts.push(foundCollection.slug);
|
|
764
|
+
remainingPath = removeInitialSlash(remainingPath.substring(matchString.length));
|
|
765
|
+
if (remainingPath.length === 0) {
|
|
766
|
+
foundMatch = true;
|
|
767
|
+
break;
|
|
768
|
+
}
|
|
769
|
+
const idSeparatorIndex = remainingPath.indexOf("/");
|
|
770
|
+
let entityId;
|
|
771
|
+
if (idSeparatorIndex > -1) {
|
|
772
|
+
entityId = remainingPath.substring(0, idSeparatorIndex);
|
|
773
|
+
remainingPath = remainingPath.substring(idSeparatorIndex + 1);
|
|
1364
774
|
} else {
|
|
1365
|
-
|
|
775
|
+
entityId = remainingPath;
|
|
776
|
+
remainingPath = "";
|
|
777
|
+
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.`);
|
|
1366
778
|
}
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
Object.entries(obj).forEach(([key, value]) => {
|
|
1376
|
-
if (Array.isArray(value)) {
|
|
1377
|
-
acc[key] = Math.max(acc[key] || 0, value.length);
|
|
779
|
+
resolvedPathParts.push(entityId);
|
|
780
|
+
currentCollections = getSubcollections(foundCollection);
|
|
781
|
+
foundMatch = true;
|
|
782
|
+
if (!currentCollections && remainingPath.length > 0) {
|
|
783
|
+
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.`);
|
|
784
|
+
resolvedPathParts.push(remainingPath);
|
|
785
|
+
remainingPath = "";
|
|
786
|
+
break;
|
|
1378
787
|
}
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
788
|
+
}
|
|
789
|
+
if (!foundMatch) {
|
|
790
|
+
console.warn(`resolveCollectionPathIds: Collection definition not found for segment starting with "${remainingPath}" in original path "${path}". Appending remaining original path.`);
|
|
791
|
+
resolvedPathParts.push(remainingPath);
|
|
792
|
+
remainingPath = "";
|
|
793
|
+
break;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
return resolvedPathParts.join("/");
|
|
797
|
+
}
|
|
798
|
+
function getCollectionBySlugWithin(slugOrPath, collections) {
|
|
799
|
+
const subpaths = removeInitialAndTrailingSlashes(slugOrPath).split("/");
|
|
800
|
+
if (subpaths.length % 2 === 0) {
|
|
801
|
+
throw Error(`getCollectionBySlug: Collection paths must have an odd number of segments: ${slugOrPath}`);
|
|
802
|
+
}
|
|
803
|
+
const subpathCombinations = getCollectionPathsCombinations(subpaths);
|
|
804
|
+
let result;
|
|
805
|
+
for (let i = 0; i < subpathCombinations.length; i++) {
|
|
806
|
+
const subpathCombination = subpathCombinations[i];
|
|
807
|
+
const navigationEntry = collections && collections.sort((a, b) => (a.slug ?? "").localeCompare(b.slug ?? "")).find((entry) => entry.slug === subpathCombination);
|
|
808
|
+
if (navigationEntry) {
|
|
809
|
+
if (subpathCombination === slugOrPath) {
|
|
810
|
+
result = navigationEntry;
|
|
811
|
+
} else if (getSubcollections(navigationEntry).length > 0) {
|
|
812
|
+
const newPath = slugOrPath.replace(subpathCombination, "").split("/").slice(2).join("/");
|
|
813
|
+
if (newPath.length > 0) result = getCollectionBySlugWithin(newPath, getSubcollections(navigationEntry));
|
|
1385
814
|
}
|
|
1386
|
-
}
|
|
1387
|
-
|
|
1388
|
-
}
|
|
815
|
+
}
|
|
816
|
+
if (result) break;
|
|
817
|
+
}
|
|
818
|
+
return result;
|
|
819
|
+
}
|
|
820
|
+
function getCollectionPathsCombinations(subpaths) {
|
|
821
|
+
const entries = subpaths.length > 0 && subpaths.length % 2 === 0 ? subpaths.splice(0, subpaths.length - 1) : subpaths;
|
|
822
|
+
const length = entries.length;
|
|
823
|
+
const result = [];
|
|
824
|
+
for (let i = length; i > 0; i = i - 2) {
|
|
825
|
+
result.push(entries.slice(0, i).join("/"));
|
|
826
|
+
}
|
|
827
|
+
return result;
|
|
1389
828
|
}
|
|
1390
829
|
function getNavigationEntriesFromPath(props) {
|
|
1391
830
|
const {
|
|
@@ -1426,7 +865,7 @@ function getNavigationEntriesFromPath(props) {
|
|
|
1426
865
|
throw Error("collection not found resolving path: " + collection);
|
|
1427
866
|
}
|
|
1428
867
|
const entityViews = collection.entityViews;
|
|
1429
|
-
const customView = entityViews && entityViews.map((entry) => resolveEntityView(entry, props.contextEntityViews)).filter(
|
|
868
|
+
const customView = entityViews && entityViews.map((entry) => resolveEntityView(entry, props.contextEntityViews)).filter((v) => v != null).find((entry) => entry.key === newPath);
|
|
1430
869
|
const subcollections = getSubcollections(collection);
|
|
1431
870
|
if (customView) {
|
|
1432
871
|
result.push({
|
|
@@ -1486,7 +925,7 @@ function getParentReferencesFromPath(props) {
|
|
|
1486
925
|
if (!collection) {
|
|
1487
926
|
throw Error("collection not found resolving path: " + collection);
|
|
1488
927
|
}
|
|
1489
|
-
if (collection.
|
|
928
|
+
if (getSubcollections(collection).length > 0) {
|
|
1490
929
|
result.push(...getParentReferencesFromPath({
|
|
1491
930
|
path: newPath,
|
|
1492
931
|
collections: getSubcollections(collection),
|
|
@@ -1524,9 +963,6 @@ function buildEntityCallbacks(callbacks) {
|
|
|
1524
963
|
function buildAdditionalFieldDelegate(additionalFieldDelegate) {
|
|
1525
964
|
return additionalFieldDelegate;
|
|
1526
965
|
}
|
|
1527
|
-
function buildFieldConfig(propertyConfig) {
|
|
1528
|
-
return propertyConfig;
|
|
1529
|
-
}
|
|
1530
966
|
async function resolveStorageFilenameString({
|
|
1531
967
|
input,
|
|
1532
968
|
storage,
|
|
@@ -1618,9 +1054,6 @@ function replacePlaceholders({
|
|
|
1618
1054
|
if (!result) result = randomString() + "_" + file.name;
|
|
1619
1055
|
return result;
|
|
1620
1056
|
}
|
|
1621
|
-
function toArray(input) {
|
|
1622
|
-
return Array.isArray(input) ? input : input ? [input] : [];
|
|
1623
|
-
}
|
|
1624
1057
|
function hasPropertyCallbacks(properties, callbackName) {
|
|
1625
1058
|
if (!properties) return false;
|
|
1626
1059
|
for (const property of Object.values(properties)) {
|
|
@@ -1639,13 +1072,13 @@ function hasPropertyCallbacks(properties, callbackName) {
|
|
|
1639
1072
|
}
|
|
1640
1073
|
async function processProperties(properties, values, previousValues, propsContext, callbackName) {
|
|
1641
1074
|
if (!values || typeof values !== "object") return values;
|
|
1642
|
-
|
|
1075
|
+
const result = {
|
|
1643
1076
|
...values
|
|
1644
1077
|
};
|
|
1645
1078
|
for (const [key, property] of Object.entries(properties)) {
|
|
1646
1079
|
if (result[key] === void 0) continue;
|
|
1647
1080
|
let currentValue = result[key];
|
|
1648
|
-
|
|
1081
|
+
const previousValue = previousValues?.[key];
|
|
1649
1082
|
if (property.type === "array" && Array.isArray(currentValue)) {
|
|
1650
1083
|
if (property.of && !Array.isArray(property.of)) {
|
|
1651
1084
|
currentValue = await Promise.all(currentValue.map(async (item, index) => {
|
|
@@ -1697,22 +1130,6 @@ const buildPropertyCallbacks = (properties) => {
|
|
|
1697
1130
|
}
|
|
1698
1131
|
return Object.keys(propertyCallbacks).length > 0 ? propertyCallbacks : void 0;
|
|
1699
1132
|
};
|
|
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
1133
|
function sanitizeRelation(relation, sourceCollection) {
|
|
1717
1134
|
if (!relation.target) {
|
|
1718
1135
|
throw new Error("Relation is missing a `target` collection.");
|
|
@@ -1722,7 +1139,7 @@ function sanitizeRelation(relation, sourceCollection) {
|
|
|
1722
1139
|
...relation
|
|
1723
1140
|
};
|
|
1724
1141
|
if (!newRelation.relationName) {
|
|
1725
|
-
newRelation.relationName = toSnakeCase(targetCollection.slug
|
|
1142
|
+
newRelation.relationName = toSnakeCase(targetCollection.slug);
|
|
1726
1143
|
}
|
|
1727
1144
|
if (!newRelation.direction) {
|
|
1728
1145
|
if (newRelation.foreignKeyOnTarget) newRelation.direction = "inverse";
|
|
@@ -1745,7 +1162,7 @@ function sanitizeRelation(relation, sourceCollection) {
|
|
|
1745
1162
|
if (targetRel.direction === "owning" && targetRel.cardinality === "one" && targetRel.localKey) {
|
|
1746
1163
|
try {
|
|
1747
1164
|
const targetRelTarget = targetRel.target();
|
|
1748
|
-
if (targetRelTarget.slug === sourceCollection.slug
|
|
1165
|
+
if (targetRelTarget.slug === sourceCollection.slug) {
|
|
1749
1166
|
newRelation.foreignKeyOnTarget = targetRel.localKey;
|
|
1750
1167
|
foundForeignKey = true;
|
|
1751
1168
|
break;
|
|
@@ -1768,7 +1185,7 @@ function sanitizeRelation(relation, sourceCollection) {
|
|
|
1768
1185
|
try {
|
|
1769
1186
|
const targetRelations = targetCollection.relations || [];
|
|
1770
1187
|
for (const targetRel of targetRelations) {
|
|
1771
|
-
if (targetRel.cardinality === "many" && targetRel.direction === "owning"
|
|
1188
|
+
if (targetRel.cardinality === "many" && (targetRel.direction === "owning" || !targetRel.direction) && targetRel.relationName === newRelation.inverseRelationName) {
|
|
1772
1189
|
isManyToManyInverse = true;
|
|
1773
1190
|
break;
|
|
1774
1191
|
}
|
|
@@ -1795,7 +1212,7 @@ function sanitizeRelation(relation, sourceCollection) {
|
|
|
1795
1212
|
if (newRelation.cardinality === "one" && newRelation.direction === "inverse" && !newRelation.foreignKeyOnTarget && !newRelation.joinPath) {
|
|
1796
1213
|
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
1214
|
}
|
|
1798
|
-
if (newRelation.cardinality === "many" && newRelation.direction === "inverse" && !newRelation.foreignKeyOnTarget && !newRelation.joinPath) {
|
|
1215
|
+
if (newRelation.cardinality === "many" && newRelation.direction === "inverse" && !newRelation.foreignKeyOnTarget && !newRelation.joinPath && !newRelation.inverseRelationName) {
|
|
1799
1216
|
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
1217
|
}
|
|
1801
1218
|
return newRelation;
|
|
@@ -1836,15 +1253,18 @@ function resolvePropertyRelation({
|
|
|
1836
1253
|
sourceCollection
|
|
1837
1254
|
}) {
|
|
1838
1255
|
if (property.type !== "relation") return void 0;
|
|
1839
|
-
const relation = sourceCollection.relations
|
|
1256
|
+
const relation = (sourceCollection.relations ?? []).find((rel) => rel.relationName === property.relationName);
|
|
1840
1257
|
if (!relation) {
|
|
1841
|
-
console.warn(`Unrecognized relation format for property '${propertyKey}' in collection '${sourceCollection.slug
|
|
1258
|
+
console.warn(`Unrecognized relation format for property '${propertyKey}' in collection '${sourceCollection.slug}'`);
|
|
1842
1259
|
return void 0;
|
|
1843
1260
|
}
|
|
1844
1261
|
return relation;
|
|
1845
1262
|
}
|
|
1846
1263
|
function getTableName(collection) {
|
|
1847
|
-
|
|
1264
|
+
if (isPostgresCollection(collection)) {
|
|
1265
|
+
return collection.table ?? toSnakeCase(collection.slug) ?? toSnakeCase(collection.name);
|
|
1266
|
+
}
|
|
1267
|
+
return toSnakeCase(collection.slug) ?? toSnakeCase(collection.name);
|
|
1848
1268
|
}
|
|
1849
1269
|
function getTableVarName(tableName) {
|
|
1850
1270
|
return tableName.replace(/_([a-z])/g, (_, char) => char.toUpperCase());
|
|
@@ -1942,7 +1362,7 @@ function buildConditionContext(params) {
|
|
|
1942
1362
|
email: user?.email ?? null,
|
|
1943
1363
|
displayName: user?.displayName ?? null,
|
|
1944
1364
|
photoURL: user?.photoURL ?? null,
|
|
1945
|
-
roles: user?.roles ?? []
|
|
1365
|
+
roles: (user?.roles ?? []).map((r) => typeof r === "string" ? r : r.id)
|
|
1946
1366
|
},
|
|
1947
1367
|
now: Date.now()
|
|
1948
1368
|
};
|
|
@@ -1992,8 +1412,8 @@ function applyPropertyConditions(property, context) {
|
|
|
1992
1412
|
if (context.isNew && conditions.defaultValue !== void 0) {
|
|
1993
1413
|
result.defaultValue = evaluateCondition(conditions.defaultValue, context);
|
|
1994
1414
|
}
|
|
1995
|
-
if ("
|
|
1996
|
-
result.
|
|
1415
|
+
if ("enum" in result && result.enum && (conditions.enumConditions || conditions.allowedEnumValues || conditions.excludedEnumValues)) {
|
|
1416
|
+
result.enum = applyEnumConditions(result.enum, conditions, context);
|
|
1997
1417
|
}
|
|
1998
1418
|
if (result.type === "reference") {
|
|
1999
1419
|
if (conditions.referencePath) {
|
|
@@ -2059,11 +1479,11 @@ function applyEnumConditions(enumValues, conditions, context) {
|
|
|
2059
1479
|
}
|
|
2060
1480
|
class CollectionRegistry {
|
|
2061
1481
|
// Normalized runtime layer (used by Data Grid / UI)
|
|
2062
|
-
|
|
1482
|
+
collectionsByTableName = /* @__PURE__ */ new Map();
|
|
2063
1483
|
collectionsBySlug = /* @__PURE__ */ new Map();
|
|
2064
1484
|
rootCollections = [];
|
|
2065
1485
|
// Raw configuration layer (used by Collection Editor AST generator)
|
|
2066
|
-
|
|
1486
|
+
rawCollectionsByTableName = /* @__PURE__ */ new Map();
|
|
2067
1487
|
rawCollectionsBySlug = /* @__PURE__ */ new Map();
|
|
2068
1488
|
rawRootCollections = [];
|
|
2069
1489
|
// Snapshot of raw input for idempotency check — compared BEFORE normalization
|
|
@@ -2075,10 +1495,10 @@ class CollectionRegistry {
|
|
|
2075
1495
|
}
|
|
2076
1496
|
}
|
|
2077
1497
|
reset() {
|
|
2078
|
-
this.
|
|
1498
|
+
this.collectionsByTableName.clear();
|
|
2079
1499
|
this.collectionsBySlug.clear();
|
|
2080
1500
|
this.rootCollections = [];
|
|
2081
|
-
this.
|
|
1501
|
+
this.rawCollectionsByTableName.clear();
|
|
2082
1502
|
this.rawCollectionsBySlug.clear();
|
|
2083
1503
|
this.rawRootCollections = [];
|
|
2084
1504
|
}
|
|
@@ -2099,7 +1519,29 @@ class CollectionRegistry {
|
|
|
2099
1519
|
const normalizedCollections = collections.map((c) => this.normalizeCollection({
|
|
2100
1520
|
...c
|
|
2101
1521
|
}));
|
|
2102
|
-
normalizedCollections.forEach((c, index) =>
|
|
1522
|
+
normalizedCollections.forEach((c, index) => {
|
|
1523
|
+
const raw = cloneDeep(collections[index]);
|
|
1524
|
+
this.rootCollections.push(c);
|
|
1525
|
+
this.rawRootCollections.push(raw);
|
|
1526
|
+
const normalized = this.normalizeCollection(c);
|
|
1527
|
+
this.collectionsByTableName.set(getTableName(normalized), normalized);
|
|
1528
|
+
this.rawCollectionsByTableName.set(getTableName(raw), raw);
|
|
1529
|
+
if (normalized.slug) {
|
|
1530
|
+
this.collectionsBySlug.set(normalized.slug, normalized);
|
|
1531
|
+
}
|
|
1532
|
+
if (raw.slug) {
|
|
1533
|
+
this.rawCollectionsBySlug.set(raw.slug, raw);
|
|
1534
|
+
}
|
|
1535
|
+
});
|
|
1536
|
+
normalizedCollections.forEach((c, index) => {
|
|
1537
|
+
const subcollections = getSubcollections(c);
|
|
1538
|
+
const rawSubcollections = getSubcollections(collections[index]);
|
|
1539
|
+
if (subcollections && rawSubcollections) {
|
|
1540
|
+
subcollections.forEach((subCollection, subIndex) => {
|
|
1541
|
+
this._registerRecursively(this.normalizeCollection(subCollection), cloneDeep(rawSubcollections[subIndex]));
|
|
1542
|
+
});
|
|
1543
|
+
}
|
|
1544
|
+
});
|
|
2103
1545
|
this.lastRawInputSnapshot = rawSnapshot;
|
|
2104
1546
|
return true;
|
|
2105
1547
|
}
|
|
@@ -2110,12 +1552,12 @@ class CollectionRegistry {
|
|
|
2110
1552
|
this._registerRecursively(collection, raw);
|
|
2111
1553
|
}
|
|
2112
1554
|
_registerRecursively(collection, rawCollection) {
|
|
2113
|
-
if (this.
|
|
1555
|
+
if (this.collectionsByTableName.has(getTableName(collection))) {
|
|
2114
1556
|
return;
|
|
2115
1557
|
}
|
|
2116
1558
|
const normalizedCollection = this.normalizeCollection(collection);
|
|
2117
|
-
this.
|
|
2118
|
-
this.
|
|
1559
|
+
this.collectionsByTableName.set(getTableName(normalizedCollection), normalizedCollection);
|
|
1560
|
+
this.rawCollectionsByTableName.set(getTableName(rawCollection), rawCollection);
|
|
2119
1561
|
if (normalizedCollection.slug) {
|
|
2120
1562
|
this.collectionsBySlug.set(normalizedCollection.slug, normalizedCollection);
|
|
2121
1563
|
}
|
|
@@ -2131,8 +1573,22 @@ class CollectionRegistry {
|
|
|
2131
1573
|
}
|
|
2132
1574
|
}
|
|
2133
1575
|
normalizeCollection(collection) {
|
|
2134
|
-
const
|
|
1576
|
+
const relations = isPostgresCollection(collection) ? collection.relations ?? [] : [];
|
|
1577
|
+
const properties = this.normalizeProperties(collection.properties, relations);
|
|
2135
1578
|
collection.properties = properties;
|
|
1579
|
+
if (!collection.childCollections) {
|
|
1580
|
+
if (isFirebaseCollection(collection) && collection.subcollections) {
|
|
1581
|
+
collection.childCollections = collection.subcollections;
|
|
1582
|
+
} else if (isPostgresCollection(collection) && collection.relations) {
|
|
1583
|
+
const manyRelations = collection.relations.filter((r) => r.cardinality === "many");
|
|
1584
|
+
if (manyRelations.length > 0) {
|
|
1585
|
+
collection.childCollections = () => manyRelations.map((r) => {
|
|
1586
|
+
const target = r.target();
|
|
1587
|
+
return r.overrides ? mergeDeep(target, r.overrides) : target;
|
|
1588
|
+
});
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
2136
1592
|
return collection;
|
|
2137
1593
|
}
|
|
2138
1594
|
normalizeProperties(properties, relations) {
|
|
@@ -2149,10 +1605,15 @@ class CollectionRegistry {
|
|
|
2149
1605
|
if (newProperty.type === "map" && newProperty.properties) {
|
|
2150
1606
|
newProperty.properties = this.normalizeProperties(newProperty.properties, relations);
|
|
2151
1607
|
} else if (newProperty.type === "array") {
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
1608
|
+
const arrayProp = newProperty;
|
|
1609
|
+
if (arrayProp.of) {
|
|
1610
|
+
if (Array.isArray(arrayProp.of)) {
|
|
1611
|
+
arrayProp.of = arrayProp.of.map((p) => this.normalizeProperty(p, relations));
|
|
1612
|
+
} else {
|
|
1613
|
+
arrayProp.of = this.normalizeProperty(arrayProp.of, relations);
|
|
1614
|
+
}
|
|
1615
|
+
} else if (arrayProp.oneOf && arrayProp.oneOf.properties) {
|
|
1616
|
+
arrayProp.oneOf.properties = this.normalizeProperties(arrayProp.oneOf.properties, relations);
|
|
2156
1617
|
}
|
|
2157
1618
|
} else if ((newProperty.type === "string" || newProperty.type === "number") && newProperty.enum) {
|
|
2158
1619
|
const stringOrNumberProperty = newProperty;
|
|
@@ -2173,7 +1634,7 @@ class CollectionRegistry {
|
|
|
2173
1634
|
get(path) {
|
|
2174
1635
|
const bySlug = this.collectionsBySlug.get(path);
|
|
2175
1636
|
if (bySlug) return bySlug;
|
|
2176
|
-
return this.
|
|
1637
|
+
return this.collectionsByTableName.get(path);
|
|
2177
1638
|
}
|
|
2178
1639
|
/**
|
|
2179
1640
|
* Gets the pristine, un-normalized collection exactly as it was provided.
|
|
@@ -2182,7 +1643,7 @@ class CollectionRegistry {
|
|
|
2182
1643
|
getRaw(path) {
|
|
2183
1644
|
const bySlug = this.rawCollectionsBySlug.get(path);
|
|
2184
1645
|
if (bySlug) return bySlug;
|
|
2185
|
-
return this.
|
|
1646
|
+
return this.rawCollectionsByTableName.get(path);
|
|
2186
1647
|
}
|
|
2187
1648
|
/**
|
|
2188
1649
|
* Get collection by resolving multi-segment paths through relations
|
|
@@ -2203,10 +1664,13 @@ class CollectionRegistry {
|
|
|
2203
1664
|
}
|
|
2204
1665
|
for (let i = 2; i < pathSegments.length; i += 2) {
|
|
2205
1666
|
const relationKey = pathSegments[i];
|
|
1667
|
+
if (!isPostgresCollection(currentCollection)) {
|
|
1668
|
+
throw new Error(`Relation path navigation requires a PostgreSQL collection, but '${currentCollection.slug}' uses driver '${currentCollection.driver}'`);
|
|
1669
|
+
}
|
|
2206
1670
|
const resolvedRelations = resolveCollectionRelations(currentCollection);
|
|
2207
1671
|
const relation = resolvedRelations[relationKey];
|
|
2208
1672
|
if (!relation) {
|
|
2209
|
-
throw new Error(`Relation '${relationKey}' not found in collection '${currentCollection.slug
|
|
1673
|
+
throw new Error(`Relation '${relationKey}' not found in collection '${currentCollection.slug}'`);
|
|
2210
1674
|
}
|
|
2211
1675
|
currentCollection = relation.target();
|
|
2212
1676
|
if (i + 1 < pathSegments.length) ;
|
|
@@ -2214,10 +1678,10 @@ class CollectionRegistry {
|
|
|
2214
1678
|
return currentCollection;
|
|
2215
1679
|
}
|
|
2216
1680
|
getCollections() {
|
|
2217
|
-
return Array.from(this.
|
|
1681
|
+
return Array.from(this.collectionsByTableName.values());
|
|
2218
1682
|
}
|
|
2219
1683
|
getRawCollections() {
|
|
2220
|
-
return Array.from(this.
|
|
1684
|
+
return Array.from(this.rawCollectionsByTableName.values());
|
|
2221
1685
|
}
|
|
2222
1686
|
/**
|
|
2223
1687
|
* Resolves a multi-segment path like "products/123/locales" and returns
|
|
@@ -2243,13 +1707,13 @@ class CollectionRegistry {
|
|
|
2243
1707
|
entityIds.push(entityId);
|
|
2244
1708
|
if (i + 1 < pathSegments.length) {
|
|
2245
1709
|
const subcollectionSlug = pathSegments[i + 1];
|
|
2246
|
-
const subcollections = currentCollection
|
|
2247
|
-
if (!subcollections) {
|
|
2248
|
-
throw new Error(`No subcollections found for ${currentCollection.slug
|
|
1710
|
+
const subcollections = getSubcollections(currentCollection);
|
|
1711
|
+
if (!subcollections || subcollections.length === 0) {
|
|
1712
|
+
throw new Error(`No subcollections found for ${currentCollection.slug} in path: ${path}`);
|
|
2249
1713
|
}
|
|
2250
1714
|
const subcollection = subcollections.find((c) => c.slug === subcollectionSlug);
|
|
2251
1715
|
if (!subcollection) {
|
|
2252
|
-
throw new Error(`Subcollection '${subcollectionSlug}' not found in ${currentCollection.slug
|
|
1716
|
+
throw new Error(`Subcollection '${subcollectionSlug}' not found in ${currentCollection.slug}`);
|
|
2253
1717
|
}
|
|
2254
1718
|
currentCollection = subcollection;
|
|
2255
1719
|
collections.push(currentCollection);
|
|
@@ -2262,6 +1726,166 @@ class CollectionRegistry {
|
|
|
2262
1726
|
};
|
|
2263
1727
|
}
|
|
2264
1728
|
}
|
|
1729
|
+
function convertWhereToFilter(where) {
|
|
1730
|
+
if (!where) return void 0;
|
|
1731
|
+
const operatorMap = {
|
|
1732
|
+
"eq": "==",
|
|
1733
|
+
"neq": "!=",
|
|
1734
|
+
"gt": ">",
|
|
1735
|
+
"gte": ">=",
|
|
1736
|
+
"lt": "<",
|
|
1737
|
+
"lte": "<=",
|
|
1738
|
+
"in": "in",
|
|
1739
|
+
"nin": "not-in",
|
|
1740
|
+
"cs": "array-contains",
|
|
1741
|
+
"csa": "array-contains-any"
|
|
1742
|
+
};
|
|
1743
|
+
const filter = {};
|
|
1744
|
+
for (const [field, rawValue] of Object.entries(where)) {
|
|
1745
|
+
const dotIndex = rawValue.indexOf(".");
|
|
1746
|
+
if (dotIndex === -1) continue;
|
|
1747
|
+
const op = rawValue.substring(0, dotIndex);
|
|
1748
|
+
let value = rawValue.substring(dotIndex + 1);
|
|
1749
|
+
if (typeof value === "string" && value.startsWith("(") && value.endsWith(")")) {
|
|
1750
|
+
value = value.slice(1, -1).split(",").map((v) => v.trim());
|
|
1751
|
+
}
|
|
1752
|
+
if (typeof value === "string" && !isNaN(Number(value)) && value.trim() !== "") {
|
|
1753
|
+
value = Number(value);
|
|
1754
|
+
}
|
|
1755
|
+
const mappedOp = operatorMap[op];
|
|
1756
|
+
if (mappedOp) {
|
|
1757
|
+
filter[field] = [mappedOp, value];
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
return Object.keys(filter).length > 0 ? filter : void 0;
|
|
1761
|
+
}
|
|
1762
|
+
function parseOrderBy(orderBy) {
|
|
1763
|
+
if (!orderBy) return void 0;
|
|
1764
|
+
const parts = orderBy.split(":");
|
|
1765
|
+
const field = parts[0];
|
|
1766
|
+
const direction = parts[1] || "asc";
|
|
1767
|
+
return [field, direction];
|
|
1768
|
+
}
|
|
1769
|
+
function createDriverAccessor(driver, slug) {
|
|
1770
|
+
return {
|
|
1771
|
+
async find(params) {
|
|
1772
|
+
const orderParsed = parseOrderBy(params?.orderBy);
|
|
1773
|
+
const entities = await driver.fetchCollection({
|
|
1774
|
+
path: slug,
|
|
1775
|
+
limit: params?.limit,
|
|
1776
|
+
startAfter: params?.offset,
|
|
1777
|
+
filter: convertWhereToFilter(params?.where),
|
|
1778
|
+
orderBy: orderParsed?.[0],
|
|
1779
|
+
order: orderParsed?.[1],
|
|
1780
|
+
searchString: params?.searchString
|
|
1781
|
+
});
|
|
1782
|
+
const limit = params?.limit ?? 20;
|
|
1783
|
+
const offset = params?.offset ?? 0;
|
|
1784
|
+
return {
|
|
1785
|
+
data: entities,
|
|
1786
|
+
meta: {
|
|
1787
|
+
total: entities.length,
|
|
1788
|
+
limit,
|
|
1789
|
+
offset,
|
|
1790
|
+
hasMore: entities.length >= limit
|
|
1791
|
+
}
|
|
1792
|
+
};
|
|
1793
|
+
},
|
|
1794
|
+
async findById(id) {
|
|
1795
|
+
return driver.fetchEntity({
|
|
1796
|
+
path: slug,
|
|
1797
|
+
entityId: id
|
|
1798
|
+
});
|
|
1799
|
+
},
|
|
1800
|
+
async create(data, id) {
|
|
1801
|
+
return driver.saveEntity({
|
|
1802
|
+
path: slug,
|
|
1803
|
+
values: data,
|
|
1804
|
+
entityId: id,
|
|
1805
|
+
status: "new"
|
|
1806
|
+
});
|
|
1807
|
+
},
|
|
1808
|
+
async update(id, data) {
|
|
1809
|
+
return driver.saveEntity({
|
|
1810
|
+
path: slug,
|
|
1811
|
+
values: data,
|
|
1812
|
+
entityId: id,
|
|
1813
|
+
status: "existing"
|
|
1814
|
+
});
|
|
1815
|
+
},
|
|
1816
|
+
async delete(id) {
|
|
1817
|
+
return driver.deleteEntity({
|
|
1818
|
+
entity: {
|
|
1819
|
+
id,
|
|
1820
|
+
path: slug,
|
|
1821
|
+
values: {}
|
|
1822
|
+
}
|
|
1823
|
+
});
|
|
1824
|
+
},
|
|
1825
|
+
count: driver.countEntities ? async (params) => {
|
|
1826
|
+
return driver.countEntities({
|
|
1827
|
+
path: slug,
|
|
1828
|
+
filter: convertWhereToFilter(params?.where)
|
|
1829
|
+
});
|
|
1830
|
+
} : void 0,
|
|
1831
|
+
listen: driver.listenCollection ? (params, onUpdate, onError) => {
|
|
1832
|
+
const orderParsed = parseOrderBy(params?.orderBy);
|
|
1833
|
+
const limit = params?.limit ?? 20;
|
|
1834
|
+
const offset = params?.offset ?? 0;
|
|
1835
|
+
return driver.listenCollection({
|
|
1836
|
+
path: slug,
|
|
1837
|
+
limit: params?.limit,
|
|
1838
|
+
startAfter: params?.offset,
|
|
1839
|
+
filter: convertWhereToFilter(params?.where),
|
|
1840
|
+
orderBy: orderParsed?.[0],
|
|
1841
|
+
order: orderParsed?.[1],
|
|
1842
|
+
searchString: params?.searchString,
|
|
1843
|
+
onUpdate: (entities) => {
|
|
1844
|
+
onUpdate({
|
|
1845
|
+
data: entities,
|
|
1846
|
+
meta: {
|
|
1847
|
+
total: entities.length,
|
|
1848
|
+
limit,
|
|
1849
|
+
offset,
|
|
1850
|
+
hasMore: entities.length >= limit
|
|
1851
|
+
}
|
|
1852
|
+
});
|
|
1853
|
+
},
|
|
1854
|
+
onError
|
|
1855
|
+
});
|
|
1856
|
+
} : void 0,
|
|
1857
|
+
listenById: driver.listenEntity ? (id, onUpdate, onError) => {
|
|
1858
|
+
return driver.listenEntity({
|
|
1859
|
+
path: slug,
|
|
1860
|
+
entityId: id,
|
|
1861
|
+
onUpdate: (entity) => onUpdate(entity ?? void 0),
|
|
1862
|
+
onError
|
|
1863
|
+
});
|
|
1864
|
+
} : void 0
|
|
1865
|
+
};
|
|
1866
|
+
}
|
|
1867
|
+
function buildRebaseData(driver) {
|
|
1868
|
+
const cache = /* @__PURE__ */ new Map();
|
|
1869
|
+
function getAccessor(slug) {
|
|
1870
|
+
let accessor = cache.get(slug);
|
|
1871
|
+
if (!accessor) {
|
|
1872
|
+
accessor = createDriverAccessor(driver, slug);
|
|
1873
|
+
cache.set(slug, accessor);
|
|
1874
|
+
}
|
|
1875
|
+
return accessor;
|
|
1876
|
+
}
|
|
1877
|
+
const target = {
|
|
1878
|
+
collection: getAccessor
|
|
1879
|
+
};
|
|
1880
|
+
return new Proxy(target, {
|
|
1881
|
+
get(_target, prop) {
|
|
1882
|
+
if (prop === "collection") return getAccessor;
|
|
1883
|
+
if (typeof prop === "symbol") return void 0;
|
|
1884
|
+
if (prop === "then" || prop === "toJSON" || prop === "$$typeof") return void 0;
|
|
1885
|
+
return getAccessor(prop);
|
|
1886
|
+
}
|
|
1887
|
+
});
|
|
1888
|
+
}
|
|
2265
1889
|
export {
|
|
2266
1890
|
COLLECTION_PATH_SEPARATOR,
|
|
2267
1891
|
CollectionRegistry,
|
|
@@ -2275,23 +1899,19 @@ export {
|
|
|
2275
1899
|
buildEntityCallbacks,
|
|
2276
1900
|
buildEnum,
|
|
2277
1901
|
buildEnumValueConfig,
|
|
2278
|
-
buildFieldConfig,
|
|
2279
1902
|
buildProperties,
|
|
2280
1903
|
buildPropertiesOrBuilder,
|
|
2281
1904
|
buildProperty,
|
|
2282
1905
|
buildPropertyCallbacks,
|
|
1906
|
+
buildRebaseData,
|
|
2283
1907
|
canCreateEntity,
|
|
2284
1908
|
canDeleteEntity,
|
|
2285
1909
|
canEditEntity,
|
|
2286
1910
|
canReadCollection,
|
|
2287
|
-
clone,
|
|
2288
|
-
defaultDateFormat,
|
|
2289
1911
|
enumToObjectEntries,
|
|
2290
1912
|
evaluateCondition,
|
|
2291
|
-
flattenObject,
|
|
2292
1913
|
fullPathToCollectionSegments,
|
|
2293
1914
|
getArrayResolvedProperties,
|
|
2294
|
-
getArrayValuesCount,
|
|
2295
1915
|
getCollectionBySlugWithin,
|
|
2296
1916
|
getCollectionPathsCombinations,
|
|
2297
1917
|
getColumnName,
|
|
@@ -2300,8 +1920,6 @@ export {
|
|
|
2300
1920
|
getDefaultValuesFor,
|
|
2301
1921
|
getEntityImagePreviewPropertyKey,
|
|
2302
1922
|
getEnumVarName,
|
|
2303
|
-
getHashValue,
|
|
2304
|
-
getIn$1 as getIn,
|
|
2305
1923
|
getLabelOrConfigFrom,
|
|
2306
1924
|
getLastSegment,
|
|
2307
1925
|
getLocalChangesBackup,
|
|
@@ -2313,44 +1931,18 @@ export {
|
|
|
2313
1931
|
getSubcollections,
|
|
2314
1932
|
getTableName,
|
|
2315
1933
|
getTableVarName,
|
|
2316
|
-
getValueInPath,
|
|
2317
|
-
hashString,
|
|
2318
|
-
hydrateRegExp,
|
|
2319
|
-
isDefaultFieldConfigId,
|
|
2320
|
-
isEmptyArray,
|
|
2321
|
-
isEmptyObject,
|
|
2322
|
-
isFunction,
|
|
2323
1934
|
isHidden,
|
|
2324
|
-
isInteger,
|
|
2325
|
-
isNaN$1 as isNaN,
|
|
2326
|
-
isObject,
|
|
2327
|
-
isPlainObject,
|
|
2328
1935
|
isPropertyBuilder,
|
|
2329
1936
|
isReadOnly,
|
|
2330
|
-
|
|
2331
|
-
mergeDeep,
|
|
2332
|
-
mergeEntityActions,
|
|
2333
|
-
navigateToEntity,
|
|
2334
|
-
pick,
|
|
2335
|
-
plural,
|
|
2336
|
-
prettifyIdentifier,
|
|
2337
|
-
randomColor,
|
|
2338
|
-
randomString,
|
|
1937
|
+
normalizeToEntityRelation,
|
|
2339
1938
|
registerConditionOperations,
|
|
2340
|
-
removeFunctions,
|
|
2341
|
-
removeInPath,
|
|
2342
1939
|
removeInitialAndTrailingSlashes,
|
|
2343
1940
|
removeInitialSlash,
|
|
2344
|
-
removeNulls,
|
|
2345
|
-
removePropsIfExisting,
|
|
2346
1941
|
removeTrailingSlash,
|
|
2347
|
-
removeUndefined,
|
|
2348
1942
|
resolveArrayProperties,
|
|
2349
1943
|
resolveCollectionPathIds,
|
|
2350
1944
|
resolveCollectionRelations,
|
|
2351
1945
|
resolveDefaultSelectedView,
|
|
2352
|
-
resolveEntityAction,
|
|
2353
|
-
resolveEntityView$1 as resolveEntityView,
|
|
2354
1946
|
resolveEnumValues,
|
|
2355
1947
|
resolveProperties,
|
|
2356
1948
|
resolveProperty,
|
|
@@ -2359,22 +1951,13 @@ export {
|
|
|
2359
1951
|
resolveRelationProperty,
|
|
2360
1952
|
resolveStorageFilenameString,
|
|
2361
1953
|
resolveStoragePathString,
|
|
2362
|
-
resolvedSelectedEntityView,
|
|
2363
1954
|
sanitizeData,
|
|
2364
1955
|
sanitizeRelation,
|
|
2365
1956
|
segmentsToStrippedPath,
|
|
2366
|
-
serializeRegExp,
|
|
2367
|
-
setIn,
|
|
2368
|
-
singular,
|
|
2369
|
-
slugify,
|
|
2370
1957
|
sortProperties,
|
|
2371
1958
|
stripCollectionPath,
|
|
2372
|
-
toArray,
|
|
2373
|
-
toKebabCase,
|
|
2374
|
-
toSnakeCase,
|
|
2375
1959
|
traverseValueProperty,
|
|
2376
1960
|
traverseValuesProperties,
|
|
2377
|
-
unslugify,
|
|
2378
1961
|
updateDateAutoValues
|
|
2379
1962
|
};
|
|
2380
1963
|
//# sourceMappingURL=index.es.js.map
|