@rebasepro/common 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.umd.js CHANGED
@@ -1,2536 +1,2284 @@
1
1
  (function(global, factory) {
2
- typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("@rebasepro/types"), require("@rebasepro/utils"), require("json-logic-js"), require("fast-equals")) : typeof define === "function" && define.amd ? define(["exports", "@rebasepro/types", "@rebasepro/utils", "json-logic-js", "fast-equals"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global["Rebase Util"] = {}, global.types, global.utils, global.jsonLogic, global.fastEquals));
3
- })(this, function(exports2, types, utils, jsonLogic, fastEquals) {
4
- "use strict";
5
- const DEFAULT_ONE_OF_TYPE = "type";
6
- const DEFAULT_ONE_OF_VALUE = "value";
7
- function isReadOnly(property) {
8
- if (property.ui?.readOnly) return true;
9
- if (property.type === "date") {
10
- if (property.autoValue) return true;
11
- }
12
- if (property.type === "reference") {
13
- return !property.path && !("Field" in (property.ui || {}) && property.ui?.Field);
14
- }
15
- return false;
16
- }
17
- function isHidden(property) {
18
- return typeof property.ui?.disabled === "object" && Boolean(property.ui?.disabled.hidden);
19
- }
20
- function isPropertyBuilder(property) {
21
- return typeof property?.dynamicProps === "function";
22
- }
23
- function getDefaultValuesFor(properties) {
24
- if (!properties) return {};
25
- return Object.entries(properties).map(([key, property]) => {
26
- if (!property) return {};
27
- const value = getDefaultValueFor(property);
28
- return value === void 0 ? {} : {
29
- [key]: value
30
- };
31
- }).reduce((a, b) => ({
32
- ...a,
33
- ...b
34
- }), {});
35
- }
36
- function getDefaultValueFor(property) {
37
- if (!property) return void 0;
38
- if (isPropertyBuilder(property)) return void 0;
39
- if (property.defaultValue || property.defaultValue === null) {
40
- return property.defaultValue;
41
- } else if (property.type === "map" && property.properties) {
42
- const defaultValuesFor = getDefaultValuesFor(property.properties);
43
- if (Object.keys(defaultValuesFor).length === 0) return void 0;
44
- return defaultValuesFor;
45
- } else {
46
- return getDefaultValueFortype(property.type);
47
- }
48
- }
49
- function getDefaultValueFortype(type) {
50
- if (type === "string") {
51
- return null;
52
- } else if (type === "number") {
53
- return null;
54
- } else if (type === "boolean") {
55
- return false;
56
- } else if (type === "date") {
57
- return null;
58
- } else if (type === "array") {
59
- return [];
60
- } else if (type === "map") {
61
- return {};
62
- } else if (type === "vector") {
63
- return null;
64
- } else if (type === "binary") {
65
- return null;
66
- } else {
67
- return null;
68
- }
69
- }
70
- function updateDateAutoValues({
71
- inputValues,
72
- properties,
73
- status,
74
- timestampNowValue
75
- }) {
76
- return traverseValuesProperties(inputValues, properties, (inputValue, property) => {
77
- if (property.type === "date") {
78
- if (status === "existing" && property.autoValue === "on_update") {
79
- return timestampNowValue;
80
- } else if ((status === "new" || status === "copy") && (property.autoValue === "on_update" || property.autoValue === "on_create")) {
81
- return timestampNowValue;
82
- } else {
83
- return inputValue;
84
- }
85
- } else {
86
- return inputValue;
87
- }
88
- }) ?? {};
89
- }
90
- function sanitizeData(values, properties) {
91
- const result = values;
92
- Object.entries(properties).forEach(([key, property]) => {
93
- if (values && values[key] !== void 0) result[key] = values[key];
94
- else if (property.validation?.required) result[key] = null;
95
- });
96
- return result;
97
- }
98
- function getReferenceFrom(entity) {
99
- if (typeof entity.id !== "string") throw new Error("Only string IDs are supported in references");
100
- return new types.EntityReference({
101
- id: entity.id,
102
- path: entity.path,
103
- driver: entity.driver,
104
- databaseId: entity.databaseId
105
- });
106
- }
107
- function getRelationFrom(entity) {
108
- return new types.EntityRelation(entity.id, entity.path, entity);
109
- }
110
- function normalizeToEntityRelation(value) {
111
- if (value instanceof types.EntityRelation) return value;
112
- if (!value || typeof value !== "object" || Array.isArray(value)) return null;
113
- const obj = value;
114
- const isRelationLike = obj.__type === "relation" || obj.__type === "reference" || typeof obj.isEntityRelation === "function" && obj.isEntityRelation() || typeof obj.isEntityReference === "function" && obj.isEntityReference();
115
- if (!isRelationLike) return null;
116
- return new types.EntityRelation(obj.id, obj.path, obj.data);
117
- }
118
- function traverseValuesProperties(inputValues, properties, operation) {
119
- const safeInputValues = inputValues ?? {};
120
- const updatedValues = Object.entries(properties).map(([key, property]) => {
121
- const inputValue = safeInputValues && safeInputValues[key];
122
- const updatedValue = traverseValueProperty(inputValue, property, operation);
123
- if (updatedValue === null) return null;
124
- if (updatedValue === void 0) return void 0;
125
- return {
126
- [key]: updatedValue
127
- };
128
- }).reduce((a, b) => ({
129
- ...a,
130
- ...b
131
- }), {});
132
- const result = utils.mergeDeep(safeInputValues, updatedValues);
133
- if (!result || Object.keys(result).length === 0) return void 0;
134
- return result;
135
- }
136
- function traverseValueProperty(inputValue, property, operation) {
137
- let value;
138
- if (property.type === "map" && property.properties) {
139
- value = traverseValuesProperties(inputValue, property.properties, operation);
140
- } else if (property.type === "array") {
141
- const of = property.of;
142
- if (of && Array.isArray(inputValue) && !Array.isArray(of)) {
143
- value = inputValue.map((e) => traverseValueProperty(e, of, operation));
144
- } else if (of && Array.isArray(inputValue) && Array.isArray(of)) {
145
- value = inputValue.map((e, i) => {
146
- if (i < of.length) return traverseValueProperty(e, of[i], operation);
147
- return null;
148
- }).filter(Boolean);
149
- } else if (property.oneOf && Array.isArray(inputValue)) {
150
- const typeField = property.oneOf?.typeField ?? DEFAULT_ONE_OF_TYPE;
151
- const valueField = property.oneOf?.valueField ?? DEFAULT_ONE_OF_VALUE;
152
- value = inputValue.map((e) => {
153
- if (e === null) return null;
154
- if (typeof e !== "object") return e;
155
- const rec = e;
156
- const type = rec[typeField];
157
- const childProperty = property.oneOf?.properties[type];
158
- if (!type || !childProperty) return e;
159
- return {
160
- [typeField]: type,
161
- [valueField]: traverseValueProperty(rec[valueField], childProperty, operation)
162
- };
163
- });
164
- } else {
165
- value = inputValue;
166
- }
167
- } else {
168
- value = operation(inputValue, property);
169
- }
170
- return value;
171
- }
172
- function createRelationRef(id, path) {
173
- return {
174
- id,
175
- path,
176
- __type: "relation"
177
- };
178
- }
179
- function createRelationRefWithData(id, path, data) {
180
- return {
181
- id,
182
- path,
183
- __type: "relation",
184
- data
185
- };
186
- }
187
- function sortProperties(properties, propertiesOrder) {
188
- try {
189
- const propertiesKeys = Object.keys(properties);
190
- if (!propertiesOrder || propertiesOrder.length === 0) {
191
- return propertiesKeys.map((key) => {
192
- const property = properties[key];
193
- if (!isPropertyBuilder(property) && property?.type === "map" && property.properties) {
194
- return {
195
- [key]: {
196
- ...property,
197
- properties: sortProperties(property.properties, property.propertiesOrder)
198
- }
199
- };
200
- } else {
201
- return {
202
- [key]: property
203
- };
204
- }
205
- }).reduce((a, b) => ({
206
- ...a,
207
- ...b
208
- }), {});
209
- }
210
- const validOrderKeys = propertiesOrder.filter((key) => {
211
- return !key.includes(".") && properties[key];
212
- });
213
- const processedKeys = new Set(validOrderKeys);
214
- const orderedResult = validOrderKeys.map((key) => {
215
- const property = properties[key];
216
- if (!isPropertyBuilder(property) && property?.type === "map" && property.properties) {
217
- return {
218
- [key]: {
219
- ...property,
220
- properties: sortProperties(property.properties, property.propertiesOrder)
221
- }
222
- };
223
- } else {
224
- return {
225
- [key]: property
226
- };
227
- }
228
- }).reduce((a, b) => ({
229
- ...a,
230
- ...b
231
- }), {});
232
- const missingProperties = propertiesKeys.filter((key) => !processedKeys.has(key)).map((key) => {
233
- const property = properties[key];
234
- if (!isPropertyBuilder(property) && property?.type === "map" && property.properties) {
235
- return {
236
- [key]: {
237
- ...property,
238
- properties: sortProperties(property.properties, property.propertiesOrder)
239
- }
240
- };
241
- } else {
242
- return {
243
- [key]: property
244
- };
245
- }
246
- }).reduce((a, b) => ({
247
- ...a,
248
- ...b
249
- }), {});
250
- return {
251
- ...orderedResult,
252
- ...missingProperties
253
- };
254
- } catch (e) {
255
- console.error("Error sorting properties", e);
256
- return properties;
257
- }
258
- }
259
- function resolveDefaultSelectedView(defaultSelectedView, params) {
260
- if (!defaultSelectedView) {
261
- return void 0;
262
- } else if (typeof defaultSelectedView === "string") {
263
- return defaultSelectedView;
264
- } else {
265
- return defaultSelectedView(params);
266
- }
267
- }
268
- function getLocalChangesBackup(collection) {
269
- if (!collection.localChangesBackup) {
270
- return "manual_apply";
271
- }
272
- return collection.localChangesBackup;
273
- }
274
- function getPrimaryKeys(collection) {
275
- const properties = collection.properties;
276
- if (!properties) {
277
- return ["id"];
278
- }
279
- const ids = Object.entries(properties).filter(([key, prop]) => typeof prop === "object" && prop !== null && "isId" in prop && Boolean(prop.isId)).map(([key]) => key);
280
- if (ids.length > 0) {
281
- return ids;
282
- }
283
- return ["id"];
284
- }
285
- function enumToObjectEntries(enumValues) {
286
- if (Array.isArray(enumValues)) {
287
- return enumValues;
288
- } else {
289
- return Object.entries(enumValues).map(([id, value]) => {
290
- if (typeof value === "string") {
291
- return {
292
- id,
293
- label: value
294
- };
295
- } else {
296
- return {
297
- ...value,
298
- id
299
- };
300
- }
301
- });
302
- }
303
- }
304
- function getLabelOrConfigFrom(enumValues, key) {
305
- if (key === null || key === void 0) return void 0;
306
- return enumValues.find((entry) => String(entry.id) === String(key));
307
- }
308
- const COLLECTION_PATH_SEPARATOR = "::";
309
- function stripCollectionPath(path) {
310
- return segmentsToStrippedPath(fullPathToCollectionSegments(path));
311
- }
312
- function segmentsToStrippedPath(paths) {
313
- if (paths.length === 1) return paths[0];
314
- return paths.reduce((a, b) => `${a}${COLLECTION_PATH_SEPARATOR}${b}`);
315
- }
316
- function fullPathToCollectionSegments(path) {
317
- return path.split("/").filter((e, i) => i % 2 === 0);
318
- }
319
- function sanitizeRelation(relation, sourceCollection, resolveCollection) {
320
- if (!relation.target) {
321
- throw new Error("Relation is missing a `target` collection.");
322
- }
323
- const rawTarget = relation.target;
324
- let targetCollection;
325
- if (typeof rawTarget === "string") {
326
- if (resolveCollection) {
327
- targetCollection = resolveCollection(rawTarget);
328
- }
329
- if (!targetCollection) {
330
- targetCollection = {
331
- slug: rawTarget,
332
- name: rawTarget
333
- };
334
- }
335
- } else if (typeof rawTarget === "function") {
336
- const evaluated = rawTarget();
337
- if (typeof evaluated === "string") {
338
- if (resolveCollection) {
339
- targetCollection = resolveCollection(evaluated);
340
- }
341
- if (!targetCollection) {
342
- targetCollection = {
343
- slug: evaluated,
344
- name: evaluated
345
- };
346
- }
347
- } else {
348
- targetCollection = evaluated;
349
- }
350
- } else if (rawTarget && typeof rawTarget === "object") {
351
- targetCollection = rawTarget;
352
- }
353
- if (!targetCollection) {
354
- throw new Error("Relation is missing a valid `target` collection.");
355
- }
356
- const newRelation = {
357
- ...relation
358
- };
359
- newRelation.target = () => {
360
- if (typeof rawTarget === "string") {
361
- return resolveCollection && resolveCollection(rawTarget) || targetCollection;
362
- } else if (typeof rawTarget === "function") {
363
- const evaluated = rawTarget();
364
- if (typeof evaluated === "string") {
365
- return resolveCollection && resolveCollection(evaluated) || targetCollection;
366
- }
367
- return evaluated;
368
- }
369
- return targetCollection;
370
- };
371
- if (!newRelation.relationName) {
372
- newRelation.relationName = utils.toSnakeCase(targetCollection.slug);
373
- }
374
- if (!newRelation.direction) {
375
- if (newRelation.foreignKeyOnTarget) newRelation.direction = "inverse";
376
- else if (newRelation.through) newRelation.direction = "owning";
377
- else if (newRelation.cardinality === "many") newRelation.direction = "inverse";
378
- else newRelation.direction = "owning";
379
- }
380
- if (!newRelation.joinPath) {
381
- const sourceName = utils.toSnakeCase(sourceCollection.slug ?? sourceCollection.name);
382
- if (newRelation.cardinality === "one" && newRelation.direction === "owning") {
383
- if (!newRelation.localKey) {
384
- newRelation.localKey = utils.generateForeignKeyName(newRelation.relationName);
385
- }
386
- } else if (newRelation.cardinality === "one" && newRelation.direction === "inverse") {
387
- if (!newRelation.foreignKeyOnTarget) {
388
- let foundForeignKey = false;
389
- try {
390
- const targetRelations = types.getDataSourceCapabilities(targetCollection.driver).supportsRelations ? targetCollection.relations || [] : [];
391
- for (const targetRel of targetRelations) {
392
- if (targetRel.direction === "owning" && targetRel.cardinality === "one" && targetRel.localKey) {
393
- try {
394
- const targetRelTarget = targetRel.target();
395
- if (targetRelTarget.slug === sourceCollection.slug) {
396
- newRelation.foreignKeyOnTarget = targetRel.localKey;
397
- foundForeignKey = true;
398
- break;
399
- }
400
- } catch (e) {
401
- continue;
402
- }
403
- }
404
- }
405
- } catch (e) {
406
- }
407
- if (!foundForeignKey) {
408
- const keyPrefix = newRelation.inverseRelationName ? utils.toSnakeCase(newRelation.inverseRelationName) : sourceName;
409
- newRelation.foreignKeyOnTarget = utils.generateForeignKeyName(keyPrefix);
410
- }
411
- }
412
- } else if (newRelation.cardinality === "many" && newRelation.direction === "inverse") {
413
- let isManyToManyInverse = false;
414
- if (newRelation.inverseRelationName && !newRelation.foreignKeyOnTarget) {
415
- try {
416
- const targetRelations = types.getDataSourceCapabilities(targetCollection.driver).supportsRelations ? targetCollection.relations || [] : [];
417
- for (const targetRel of targetRelations) {
418
- if (targetRel.cardinality === "many" && (targetRel.direction === "owning" || !targetRel.direction) && targetRel.relationName === newRelation.inverseRelationName) {
419
- isManyToManyInverse = true;
420
- break;
421
- }
422
- }
423
- if (!isManyToManyInverse && targetCollection.properties) {
424
- for (const [propKey, prop] of Object.entries(targetCollection.properties)) {
425
- if (prop.type !== "relation") continue;
426
- const relProp = prop;
427
- const relName = relProp.relationName || propKey;
428
- if (relName === newRelation.inverseRelationName && relProp.cardinality === "many" && (relProp.direction === "owning" || !relProp.direction)) {
429
- isManyToManyInverse = true;
430
- break;
431
- }
432
- }
433
- }
434
- } catch (e) {
435
- }
436
- }
437
- if (!isManyToManyInverse && !newRelation.foreignKeyOnTarget) {
438
- newRelation.foreignKeyOnTarget = utils.generateForeignKeyName(sourceName);
439
- }
440
- } else if (newRelation.cardinality === "many" && newRelation.direction === "owning") {
441
- const sourceTableName = getTableName(sourceCollection);
442
- const targetTableName = getTableName(targetCollection);
443
- newRelation.through = {
444
- table: newRelation.through?.table ?? [sourceTableName, targetTableName].sort().join("_"),
445
- sourceColumn: newRelation.through?.sourceColumn ?? utils.generateForeignKeyName(sourceName),
446
- targetColumn: newRelation.through?.targetColumn ?? utils.generateForeignKeyName(newRelation.relationName)
447
- };
448
- }
449
- }
450
- if (newRelation.cardinality === "one" && newRelation.direction === "owning" && !newRelation.localKey && !newRelation.joinPath) {
451
- throw new Error(`Configuration Error in relation from '${sourceCollection.name}': An 'owning' one-to-one relation requires a 'localKey'. Check the relation config for '${newRelation.relationName}'`);
452
- }
453
- if (newRelation.cardinality === "one" && newRelation.direction === "inverse" && !newRelation.foreignKeyOnTarget && !newRelation.joinPath) {
454
- 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}'`);
455
- }
456
- if (newRelation.cardinality === "many" && newRelation.direction === "inverse" && !newRelation.foreignKeyOnTarget && !newRelation.joinPath && !newRelation.inverseRelationName) {
457
- 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}'`);
458
- }
459
- return newRelation;
460
- }
461
- const _resolvedRelationsCache = /* @__PURE__ */ new WeakMap();
462
- function resolveCollectionRelations(collection) {
463
- const cached = _resolvedRelationsCache.get(collection);
464
- if (cached) return cached;
465
- if (!types.getDataSourceCapabilities(collection.driver).supportsRelations) return {};
466
- const relCollection = collection;
467
- const relations = {};
468
- const registeredRelationNames = /* @__PURE__ */ new Set();
469
- if (relCollection.relations) {
470
- relCollection.relations.forEach((relation) => {
471
- try {
472
- const normalizedRelation = sanitizeRelation(relation, collection);
473
- const relationKey = normalizedRelation.relationName;
474
- if (relationKey) {
475
- relations[relationKey] = normalizedRelation;
476
- registeredRelationNames.add(relationKey);
477
- }
478
- } catch (e) {
479
- }
480
- });
481
- }
482
- if (collection.properties) {
483
- Object.entries(collection.properties).forEach(([propKey, prop]) => {
484
- const relation = resolvePropertyRelation({
485
- propertyKey: propKey,
486
- property: prop,
487
- sourceCollection: collection
488
- });
489
- if (relation) {
490
- if (relations[propKey]) return;
491
- if (!relation.relationName) {
492
- relation.relationName = propKey;
493
- }
494
- const normalizedRelation = sanitizeRelation(relation, collection);
495
- relations[propKey] = normalizedRelation;
496
- registeredRelationNames.add(normalizedRelation.relationName ?? propKey);
497
- }
498
- });
499
- }
500
- _resolvedRelationsCache.set(collection, relations);
501
- return relations;
502
- }
503
- function resolvePropertyRelation({
504
- propertyKey,
505
- property,
506
- sourceCollection
507
- }) {
508
- if (property.type !== "relation") return void 0;
509
- const relProp = property;
510
- if (relProp.target) {
511
- return {
512
- relationName: relProp.relationName || propertyKey,
513
- target: relProp.target,
514
- cardinality: relProp.cardinality || "one",
515
- direction: relProp.direction || "owning",
516
- inverseRelationName: relProp.inverseRelationName,
517
- localKey: relProp.localKey,
518
- foreignKeyOnTarget: relProp.foreignKeyOnTarget,
519
- through: relProp.through,
520
- joinPath: relProp.joinPath,
521
- onUpdate: relProp.onUpdate,
522
- onDelete: relProp.onDelete,
523
- overrides: relProp.overrides
524
- };
525
- }
526
- console.warn(`Unrecognized or missing relation target for property '${propertyKey}' in collection '${sourceCollection.slug}'`);
527
- return void 0;
528
- }
529
- function getTableName(collection) {
530
- if (types.getDataSourceCapabilities(collection.driver).supportsRelations) {
531
- return collection.table ?? utils.toSnakeCase(collection.slug) ?? utils.toSnakeCase(collection.name);
532
- }
533
- return utils.toSnakeCase(collection.slug) ?? utils.toSnakeCase(collection.name);
534
- }
535
- function getTableVarName(tableName) {
536
- return tableName.replace(/_([a-z])/g, (_, char) => char.toUpperCase());
537
- }
538
- function getEnumVarName(tableName, propName) {
539
- const tableVar = getTableVarName(tableName);
540
- const propVar = propName.charAt(0).toUpperCase() + propName.slice(1);
541
- return `${tableVar}${propVar}`;
542
- }
543
- function getColumnName(fullColumn) {
544
- return fullColumn.includes(".") ? fullColumn.split(".").pop() : fullColumn;
545
- }
546
- function findRelation(resolvedRelations, key) {
547
- if (resolvedRelations[key]) return resolvedRelations[key];
548
- const slugKey = key.replace(/_/g, "-");
549
- if (slugKey !== key && resolvedRelations[slugKey]) return resolvedRelations[slugKey];
550
- const snakeKey = key.replace(/-/g, "_");
551
- if (snakeKey !== key && resolvedRelations[snakeKey]) return resolvedRelations[snakeKey];
552
- return void 0;
553
- }
554
- function resolveProperty(props) {
555
- const {
556
- property,
557
- ignoreMissingFields = false,
558
- ...rest
559
- } = props;
560
- let resultProperty;
561
- if (isPropertyBuilder(property)) {
562
- const path = rest.path;
563
- if (!path) {
564
- resultProperty = property;
565
- } else {
566
- const usedPropertyValue = rest.propertyKey ? utils.getIn(rest.values, rest.propertyKey) : void 0;
567
- const dynamicProps = property.dynamicProps?.({
568
- ...rest,
569
- path,
570
- propertyValue: usedPropertyValue,
571
- values: rest.values ?? {},
572
- previousValues: rest.previousValues ?? rest.values ?? {}
573
- });
574
- resultProperty = utils.mergeDeep(property, dynamicProps ?? {});
575
- }
576
- } else {
577
- resultProperty = property;
578
- }
579
- if (resultProperty?.dynamicProps && rest.path) {
580
- const path = rest.path;
581
- const usedPropertyValue = rest.propertyKey ? utils.getIn(rest.values, rest.propertyKey) : void 0;
582
- const dynamicPropsResult = resultProperty.dynamicProps({
583
- ...rest,
584
- path,
585
- propertyValue: usedPropertyValue,
586
- values: rest.values ?? {},
587
- previousValues: rest.previousValues ?? rest.values ?? {}
588
- });
589
- if (dynamicPropsResult) {
590
- resultProperty = utils.mergeDeep(resultProperty, dynamicPropsResult);
591
- }
592
- }
593
- let resolvedProperty;
594
- if (resultProperty?.type === "map" && resultProperty.properties) {
595
- const properties = resolveProperties({
596
- ignoreMissingFields,
597
- ...rest,
598
- properties: resultProperty.properties
599
- });
600
- resolvedProperty = {
601
- ...resultProperty,
602
- properties
603
- };
604
- } else if (resultProperty?.type === "array") {
605
- resolvedProperty = resultProperty;
606
- } else if ((resultProperty?.type === "string" || resultProperty?.type === "number") && resultProperty.enum) {
607
- resolvedProperty = resolvePropertyEnum(resultProperty);
608
- } else {
609
- resolvedProperty = resultProperty;
610
- }
611
- if (resolvedProperty?.propertyConfig && !utils.isDefaultFieldConfigId(resolvedProperty.propertyConfig)) {
612
- const cmsFields = rest.propertyConfigs;
613
- if (!cmsFields && !ignoreMissingFields) {
614
- throw Error(`Trying to resolve a property with key '${resolvedProperty.propertyConfig}' that inherits from a custom property config but no custom property configs were provided. Use the property 'propertyConfigs' in your app config to provide them`);
615
- }
616
- const customField = cmsFields?.[resolvedProperty.propertyConfig];
617
- if (!customField) {
618
- console.warn(`Trying to resolve a property with key '${resolvedProperty.propertyConfig}' that inherits from a custom property config but no custom property config with that key was found. Check the 'propertyConfigs' in your app config`);
619
- return resolvedProperty;
620
- }
621
- if (customField.property) {
622
- const restConfigProperty = {
623
- ...customField.property
624
- };
625
- delete restConfigProperty.propertyConfig;
626
- const customFieldProperty = resolveProperty({
627
- property: {
628
- name: "",
629
- ...restConfigProperty
630
- },
631
- ignoreMissingFields,
632
- ...rest
633
- });
634
- if (customFieldProperty) {
635
- resolvedProperty = utils.mergeDeep(customFieldProperty, resolvedProperty);
636
- }
637
- }
638
- }
639
- return resolvedProperty;
640
- }
641
- function resolveRelationProperty(property, relations, propertyKey) {
642
- if (property.relation) {
643
- return property;
644
- }
645
- const name = property.relationName || propertyKey;
646
- const relation = name ? relations.find((rel) => rel.relationName === name) : void 0;
647
- if (!relation) {
648
- throw Error(`Relation ${name ?? "(unnamed)"} not found`);
649
- }
650
- return {
651
- ...property,
652
- relation
653
- };
654
- }
655
- function resolvePropertyEnum(property) {
656
- if (typeof property.enum === "object") {
657
- return {
658
- ...property,
659
- enum: enumToObjectEntries(property.enum)?.filter((value) => value && (value.id || value.id === 0) && value.label) ?? []
660
- };
661
- }
662
- return property;
663
- }
664
- function resolveProperties({
665
- propertyKey,
666
- properties,
667
- ignoreMissingFields,
668
- ...props
669
- }) {
670
- return Object.entries(properties).map(([key, property]) => {
671
- const childResolvedProperty = resolveProperty({
672
- propertyKey: propertyKey ? `${propertyKey}.${key}` : void 0,
673
- property,
674
- ignoreMissingFields,
675
- ...props
676
- });
677
- if (!childResolvedProperty) return {};
678
- return {
679
- [key]: childResolvedProperty
680
- };
681
- }).filter((a) => a !== null).reduce((a, b) => ({
682
- ...a,
683
- ...b
684
- }), {});
685
- }
686
- function resolveArrayProperties({
687
- propertyKey,
688
- property,
689
- ignoreMissingFields = false,
690
- ...props
691
- }) {
692
- const propertyValue = propertyKey ? utils.getIn(props.values, propertyKey) : void 0;
693
- if (property.of) {
694
- if (Array.isArray(property.of)) {
695
- return property.of.map((p, index) => {
696
- return resolveProperty({
697
- propertyKey: `${propertyKey}.${index}`,
698
- property: p,
699
- ignoreMissingFields,
700
- ...props,
701
- index
702
- });
703
- });
704
- } else {
705
- const of = property.of;
706
- const resolvedProperties = getArrayResolvedProperties({
707
- propertyValue,
708
- propertyKey,
709
- property,
710
- ignoreMissingFields,
711
- ...props
712
- });
713
- const {
714
- values,
715
- previousValues,
716
- ...rest
717
- } = props;
718
- const ofProperty = resolveProperty({
719
- // we don't want to pass the values of the parent entity
720
- property: of,
721
- ignoreMissingFields,
722
- ...rest
723
- });
724
- if (!ofProperty && !ignoreMissingFields) throw Error("When using a property builder as the 'of' prop of an ArrayProperty, you must return a valid child property");
725
- return resolvedProperties;
726
- }
727
- } else if (property.oneOf) {
728
- const typeField = property.oneOf?.typeField ?? DEFAULT_ONE_OF_TYPE;
729
- const resolvedProperties = Array.isArray(propertyValue) ? propertyValue.map((v, index) => {
730
- const type = v && v[typeField];
731
- const childProperty = property.oneOf?.properties[type];
732
- if (!type || !childProperty) return null;
733
- return resolveProperty({
734
- propertyKey: `${propertyKey}.${index}`,
735
- property: childProperty,
736
- ignoreMissingFields,
737
- ...props
738
- });
739
- }).filter((e) => Boolean(e)) : [];
740
- return resolvedProperties;
741
- } else if (!("Field" in (property.ui || {}) && property.ui?.Field)) {
742
- throw Error(`The array property (${propertyKey}) needs to declare an 'of' or a 'oneOf' property, or provide a custom \`Field\` component`);
743
- } else {
744
- return [];
745
- }
746
- }
747
- function getArrayResolvedProperties({
748
- propertyKey,
749
- propertyValue,
750
- property,
751
- ...props
752
- }) {
753
- const of = property.of;
754
- if (!of) throw Error(`Trying to resolve an array property (${propertyKey}) without providing an 'of' property`);
755
- return Array.isArray(propertyValue) ? propertyValue.map((v, index) => {
756
- return resolveProperty({
757
- propertyKey: `${propertyKey}.${index}`,
758
- property: Array.isArray(of) ? of[index] : of,
759
- ...props,
760
- index
761
- });
762
- }).filter((e) => Boolean(e)) : [];
763
- }
764
- function resolveEnumValues(input) {
765
- if (typeof input === "object") {
766
- return Object.entries(input).map(([id, value]) => typeof value === "string" ? {
767
- id,
768
- label: value
769
- } : value);
770
- } else if (Array.isArray(input)) {
771
- return input;
772
- } else {
773
- return void 0;
774
- }
775
- }
776
- function getSubcollections(collection) {
777
- if (collection.childCollections) {
778
- return collection.childCollections() ?? [];
779
- }
780
- if (types.getDataSourceCapabilities(collection.driver).supportsSubcollections && collection.subcollections) {
781
- return collection.subcollections() ?? [];
782
- }
783
- if (types.getDataSourceCapabilities(collection.driver).supportsRelations) {
784
- const resolvedRelations = resolveCollectionRelations(collection);
785
- const manyRelations = Object.values(resolvedRelations).filter((r) => r.cardinality === "many");
786
- return manyRelations.map((r) => {
787
- const target = r.target();
788
- if (!target) return void 0;
789
- const relationKey = r.relationName || target.slug;
790
- let customName;
791
- if (collection.properties) {
792
- const prop = Object.entries(collection.properties).find(([_, p]) => p.type === "relation" && p.relationName === relationKey);
793
- if (prop && prop[1].name) {
794
- customName = prop[1].name;
795
- }
796
- }
797
- const baseOverrides = {
798
- slug: relationKey
799
- };
800
- if (customName) {
801
- baseOverrides.name = customName;
802
- baseOverrides.singularName = customName;
803
- }
804
- const targetWithOverrides = {
805
- ...target,
806
- ...baseOverrides
807
- };
808
- return r.overrides ? utils.mergeDeep(targetWithOverrides, r.overrides) : targetWithOverrides;
809
- }).filter((c) => Boolean(c));
810
- }
811
- return [];
812
- }
813
- function evaluateAST(sqlString, auth, entity) {
814
- if (!entity) return true;
815
- let cleanedSQL = sqlString.trim();
816
- while (cleanedSQL.startsWith("(") && cleanedSQL.endsWith(")")) {
817
- let openCount = 0;
818
- let isEnclosing = true;
819
- for (let i = 0; i < cleanedSQL.length - 1; i++) {
820
- if (cleanedSQL[i] === "(") openCount++;
821
- else if (cleanedSQL[i] === ")") openCount--;
822
- if (openCount === 0) {
823
- isEnclosing = false;
824
- break;
825
- }
826
- }
827
- if (isEnclosing) {
828
- cleanedSQL = cleanedSQL.substring(1, cleanedSQL.length - 1).trim();
829
- } else {
830
- break;
831
- }
832
- }
833
- const splitByTopLevel = (str, delimiter) => {
834
- const parts = [];
835
- let current = "";
836
- let openCount = 0;
837
- let i = 0;
838
- while (i < str.length) {
839
- if (str[i] === "(") openCount++;
840
- else if (str[i] === ")") openCount--;
841
- if (openCount === 0 && str.substring(i).toUpperCase().startsWith(delimiter)) {
842
- parts.push(current);
843
- current = "";
844
- i += delimiter.length;
845
- } else {
846
- current += str[i];
847
- i++;
848
- }
849
- }
850
- parts.push(current);
851
- return parts;
852
- };
853
- const orParts = splitByTopLevel(cleanedSQL, " OR ");
854
- if (orParts.length > 1) {
855
- return orParts.some((part) => evaluateAST(part, auth, entity));
856
- }
857
- const andParts = splitByTopLevel(cleanedSQL, " AND ");
858
- if (andParts.length > 1) {
859
- return andParts.every((part) => evaluateAST(part, auth, entity));
860
- }
861
- const upperSQL = cleanedSQL.toUpperCase();
862
- if (upperSQL.includes(" IN ") || upperSQL.includes(" EXISTS ")) {
863
- return true;
864
- }
865
- const roleIntersectMatch = cleanedSQL.match(/string_to_array\s*\(\s*auth\.roles\(\)\s*,\s*','\s*\)\s*&&\s*ARRAY\[(.*?)\]/i);
866
- if (roleIntersectMatch && roleIntersectMatch[1]) {
867
- const requiredRoles = roleIntersectMatch[1].split(",").map((r) => r.trim().replace(/'/g, ""));
868
- const userRoles = auth.user?.roles || [];
869
- return requiredRoles.some((r) => userRoles.includes(r));
870
- }
871
- const roleContainMatch = cleanedSQL.match(/string_to_array\s*\(\s*auth\.roles\(\)\s*,\s*','\s*\)\s*@>\s*ARRAY\[(.*?)\]/i);
872
- if (roleContainMatch && roleContainMatch[1]) {
873
- const requiredRoles = roleContainMatch[1].split(",").map((r) => r.trim().replace(/'/g, ""));
874
- const userRoles = auth.user?.roles || [];
875
- return requiredRoles.every((r) => userRoles.includes(r));
876
- }
877
- const pattern1 = new RegExp("^\\{?([a-zA-Z0-9_]+)\\}?\\s*=\\s*(?:current_setting\\s*\\(\\s*'app\\.user_id'\\s*\\)|auth\\.uid\\(\\))");
878
- const pattern2 = new RegExp("^(?:current_setting\\s*\\(\\s*'app\\.user_id'\\s*\\)|auth\\.uid\\(\\))\\s*=\\s*\\{?([a-zA-Z0-9_]+)\\}?");
879
- const match1 = cleanedSQL.match(pattern1);
880
- if (match1 && match1[1]) {
881
- return entity.values[match1[1]] === auth.user?.uid;
882
- }
883
- const match2 = cleanedSQL.match(pattern2);
884
- if (match2 && match2[1]) {
885
- return entity.values[match2[1]] === auth.user?.uid;
886
- }
887
- const simpleEqualityMatch = cleanedSQL.match(/^\{?([\w_]+)\}?\s*(=|!=)\s*'([^']+)'$/i);
888
- if (simpleEqualityMatch) {
889
- const field = simpleEqualityMatch[1];
890
- const operator = simpleEqualityMatch[2];
891
- const value = simpleEqualityMatch[3];
892
- const entityValue = entity.values[field];
893
- if (operator === "=") return entityValue === value;
894
- if (operator === "!=") return entityValue !== value;
895
- }
896
- return true;
897
- }
898
- function evaluateRule(rule, auth, entity) {
899
- if (rule.access === "public") return true;
900
- if (rule.ownerField) {
901
- if (!entity) ;
902
- else {
903
- if (entity.values[rule.ownerField] !== auth.user?.uid) return false;
904
- }
905
- }
906
- if (rule.using && !evaluateAST(rule.using, auth, entity)) return false;
907
- if (rule.withCheck && !evaluateAST(rule.withCheck, auth, entity)) return false;
908
- return true;
909
- }
910
- function checkOperation(collection, authController, entity, targetOperation) {
911
- const securityRules = types.getDataSourceCapabilities(collection.driver).supportsRLS ? collection.securityRules : void 0;
912
- if (!securityRules || securityRules.length === 0) {
913
- return true;
914
- }
915
- const applicableRules = securityRules.filter((r) => r.operation === targetOperation || r.operation === "all" || r.operations?.includes(targetOperation) || r.operations?.includes("all"));
916
- if (applicableRules.length === 0) return false;
917
- const userRoleIds = authController.user?.roles ?? [];
918
- const userRoles = [...userRoleIds, "public"];
919
- const roleApplicableRules = applicableRules.filter((rule) => {
920
- if (!rule.roles || rule.roles.length === 0) return true;
921
- return rule.roles.some((r) => userRoles.includes(r));
922
- });
923
- if (roleApplicableRules.length === 0) return false;
924
- let grantedByPermissive = false;
925
- let deniedByRestrictive = false;
926
- for (const rule of roleApplicableRules) {
927
- const mode = rule.mode || "permissive";
928
- const passed = evaluateRule(rule, authController, entity);
929
- if (mode === "restrictive" && !passed) {
930
- deniedByRestrictive = true;
931
- break;
932
- }
933
- if (mode === "permissive" && passed) {
934
- grantedByPermissive = true;
935
- }
936
- }
937
- if (deniedByRestrictive) return false;
938
- const hasPermissive = roleApplicableRules.some((r) => (r.mode || "permissive") === "permissive");
939
- if (hasPermissive) {
940
- return grantedByPermissive;
941
- } else {
942
- return false;
943
- }
944
- }
945
- function canReadCollection(collection, authController) {
946
- return checkOperation(collection, authController, null, "select");
947
- }
948
- function canEditEntity(collection, authController, path, entity) {
949
- return checkOperation(collection, authController, entity, "update");
950
- }
951
- function canCreateEntity(collection, authController, path, entity) {
952
- return checkOperation(collection, authController, entity, "insert");
953
- }
954
- function canDeleteEntity(collection, authController, path, entity) {
955
- return checkOperation(collection, authController, entity, "delete");
956
- }
957
- function getEntityImagePreviewPropertyKey(collection) {
958
- for (const key in collection.properties) {
959
- const property = collection.properties[key];
960
- if (property.type === "string" && property.storage?.acceptedFiles?.includes("image/*")) {
961
- return key;
962
- }
963
- }
964
- for (const key in collection.properties) {
965
- const property = collection.properties[key];
966
- if (property.type === "array" && !Array.isArray(property.of) && property.of?.type === "string" && property.of.storage?.acceptedFiles?.includes("image/*")) {
967
- return key;
968
- }
969
- }
970
- for (const key in collection.properties) {
971
- const property = collection.properties[key];
972
- if (property.type === "string" && property.ui?.url === "image") {
973
- return key;
974
- }
975
- }
976
- for (const key in collection.properties) {
977
- const property = collection.properties[key];
978
- if (property.type === "array" && property.of && !Array.isArray(property.of) && property.of.type === "string" && property.of.url === "image") {
979
- return key;
980
- }
981
- }
982
- for (const key in collection.properties) {
983
- const property = collection.properties[key];
984
- if (property.type === "string" && property.storage && !property.storage.acceptedFiles) {
985
- return key;
986
- }
987
- }
988
- for (const key in collection.properties) {
989
- const property = collection.properties[key];
990
- if (property.type === "array" && !Array.isArray(property.of) && property.of?.type === "string" && property.of.storage && !property.of.storage.acceptedFiles) {
991
- return key;
992
- }
993
- }
994
- return void 0;
995
- }
996
- function removeInitialAndTrailingSlashes(s) {
997
- return removeInitialSlash(removeTrailingSlash(s));
998
- }
999
- function removeInitialSlash(s) {
1000
- if (s.startsWith("/")) return s.slice(1);
1001
- else return s;
1002
- }
1003
- function removeTrailingSlash(s) {
1004
- if (s.endsWith("/")) return s.slice(0, -1);
1005
- else return s;
1006
- }
1007
- function addInitialSlash(s) {
1008
- if (s.startsWith("/")) return s;
1009
- else return `/${s}`;
1010
- }
1011
- function getLastSegment(path) {
1012
- const cleanPath = removeInitialAndTrailingSlashes(path);
1013
- if (cleanPath.includes("/")) {
1014
- const segments = cleanPath.split("/");
1015
- return segments[segments.length - 1];
1016
- }
1017
- return cleanPath;
1018
- }
1019
- function resolveCollectionPathIds(path, allCollections) {
1020
- let remainingPath = removeInitialAndTrailingSlashes(path);
1021
- if (!remainingPath) {
1022
- return "";
1023
- }
1024
- let currentCollections = allCollections;
1025
- const resolvedPathParts = [];
1026
- while (remainingPath.length > 0) {
1027
- if (!currentCollections || currentCollections.length === 0) {
1028
- console.warn(`resolveCollectionPathIds: Path structure implies subcollections, but none found before segment starting with "${remainingPath}" in original path "${path}". Appending remaining original path.`);
1029
- resolvedPathParts.push(remainingPath);
1030
- remainingPath = "";
1031
- break;
1032
- }
1033
- let foundMatch = false;
1034
- const potentialMatches = currentCollections.flatMap((col) => [{
1035
- col,
1036
- match: col.slug
1037
- }]).filter((p) => p.match && remainingPath.startsWith(p.match)).sort((a, b) => b.match.length - a.match.length);
1038
- if (potentialMatches.length > 0) {
1039
- const {
1040
- col: foundCollection,
1041
- match: matchString
1042
- } = potentialMatches[0];
1043
- resolvedPathParts.push(foundCollection.slug);
1044
- remainingPath = removeInitialSlash(remainingPath.substring(matchString.length));
1045
- if (remainingPath.length === 0) {
1046
- foundMatch = true;
1047
- break;
1048
- }
1049
- const idSeparatorIndex = remainingPath.indexOf("/");
1050
- let entityId;
1051
- if (idSeparatorIndex > -1) {
1052
- entityId = remainingPath.substring(0, idSeparatorIndex);
1053
- remainingPath = remainingPath.substring(idSeparatorIndex + 1);
1054
- } else {
1055
- entityId = remainingPath;
1056
- remainingPath = "";
1057
- 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.`);
1058
- }
1059
- resolvedPathParts.push(entityId);
1060
- currentCollections = getSubcollections(foundCollection);
1061
- foundMatch = true;
1062
- if (!currentCollections && remainingPath.length > 0) {
1063
- 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.`);
1064
- resolvedPathParts.push(remainingPath);
1065
- remainingPath = "";
1066
- break;
1067
- }
1068
- }
1069
- if (!foundMatch) {
1070
- console.warn(`resolveCollectionPathIds: Collection definition not found for segment starting with "${remainingPath}" in original path "${path}". Appending remaining original path.`);
1071
- resolvedPathParts.push(remainingPath);
1072
- remainingPath = "";
1073
- break;
1074
- }
1075
- }
1076
- return resolvedPathParts.join("/");
1077
- }
1078
- function getCollectionBySlugWithin(slugOrPath, collections) {
1079
- const subpaths = removeInitialAndTrailingSlashes(slugOrPath).split("/");
1080
- if (subpaths.length % 2 === 0) {
1081
- throw Error(`getCollectionBySlug: Collection paths must have an odd number of segments: ${slugOrPath}`);
1082
- }
1083
- const subpathCombinations = getCollectionPathsCombinations(subpaths);
1084
- let result;
1085
- for (let i = 0; i < subpathCombinations.length; i++) {
1086
- const subpathCombination = subpathCombinations[i];
1087
- const navigationEntry = collections && collections.sort((a, b) => (a.slug ?? "").localeCompare(b.slug ?? "")).find((entry) => entry.slug === subpathCombination);
1088
- if (navigationEntry) {
1089
- if (subpathCombination === slugOrPath) {
1090
- result = navigationEntry;
1091
- } else if (getSubcollections(navigationEntry).length > 0) {
1092
- const newPath = slugOrPath.replace(subpathCombination, "").split("/").slice(2).join("/");
1093
- if (newPath.length > 0) result = getCollectionBySlugWithin(newPath, getSubcollections(navigationEntry));
1094
- }
1095
- }
1096
- if (result) break;
1097
- }
1098
- return result;
1099
- }
1100
- function getCollectionPathsCombinations(subpaths) {
1101
- const entries = subpaths.length > 0 && subpaths.length % 2 === 0 ? subpaths.splice(0, subpaths.length - 1) : subpaths;
1102
- const length = entries.length;
1103
- const result = [];
1104
- for (let i = length; i > 0; i = i - 2) {
1105
- result.push(entries.slice(0, i).join("/"));
1106
- }
1107
- return result;
1108
- }
1109
- function getNavigationEntriesFromPath(props) {
1110
- const {
1111
- path,
1112
- collections = [],
1113
- currentFullPath
1114
- } = props;
1115
- const subpaths = removeInitialAndTrailingSlashes(path).split("/");
1116
- const subpathCombinations = getCollectionPathsCombinations(subpaths);
1117
- const result = [];
1118
- for (let i = 0; i < subpathCombinations.length; i++) {
1119
- const subpathCombination = subpathCombinations[i];
1120
- const collection = collections && collections.find((entry) => entry.slug === subpathCombination);
1121
- if (collection) {
1122
- const collectionPath = currentFullPath && currentFullPath.length > 0 ? currentFullPath + "/" + collection.slug : collection.slug;
1123
- result.push({
1124
- type: "collection",
1125
- id: collection.slug,
1126
- slug: collectionPath,
1127
- path: collectionPath,
1128
- collection
1129
- });
1130
- const restOfThePath = removeInitialAndTrailingSlashes(removeInitialAndTrailingSlashes(path).replace(subpathCombination, ""));
1131
- const nextSegments = restOfThePath.length > 0 ? restOfThePath.split("/") : [];
1132
- if (nextSegments.length > 0) {
1133
- const entityId = nextSegments[0];
1134
- const path2 = collectionPath + "/" + entityId;
1135
- result.push({
1136
- type: "entity",
1137
- entityId,
1138
- slug: collectionPath,
1139
- path: path2,
1140
- parentCollection: collection
1141
- });
1142
- if (nextSegments.length > 1) {
1143
- const newPath = nextSegments.slice(1).join("/");
1144
- if (!collection) {
1145
- throw Error("collection not found resolving path: " + collection);
1146
- }
1147
- const entityViews = collection.entityViews;
1148
- const customView = entityViews && entityViews.map((entry) => resolveEntityView(entry, props.contextEntityViews)).filter((v) => v != null).find((entry) => entry.key === newPath);
1149
- const subcollections = getSubcollections(collection);
1150
- if (customView) {
1151
- result.push({
1152
- type: "custom_view",
1153
- slug: collectionPath,
1154
- entityId,
1155
- path: path2 + "/" + customView.key,
1156
- view: customView
1157
- });
1158
- } else if (subcollections) {
1159
- result.push(...getNavigationEntriesFromPath({
1160
- path: newPath,
1161
- collections: subcollections,
1162
- currentFullPath: path2,
1163
- contextEntityViews: props.contextEntityViews
1164
- }));
1165
- }
1166
- }
1167
- }
1168
- break;
1169
- }
1170
- }
1171
- return result;
1172
- }
1173
- function resolveEntityView(entityView, contextEntityViews) {
1174
- if (typeof entityView === "string") {
1175
- return contextEntityViews?.find((entry) => entry.key === entityView);
1176
- } else {
1177
- return entityView;
1178
- }
1179
- }
1180
- function getParentReferencesFromPath(props) {
1181
- const {
1182
- path,
1183
- collections = [],
1184
- currentFullPath
1185
- } = props;
1186
- const subpaths = removeInitialAndTrailingSlashes(path).split("/");
1187
- const subpathCombinations = getCollectionPathsCombinations(subpaths);
1188
- const result = [];
1189
- for (let i = 0; i < subpathCombinations.length; i++) {
1190
- const subpathCombination = subpathCombinations[i];
1191
- const collection = collections && collections.find((entry) => entry.slug === subpathCombination);
1192
- if (collection) {
1193
- const collectionPath = currentFullPath && currentFullPath.length > 0 ? currentFullPath + "/" + collection.slug : collection.slug;
1194
- const restOfThePath = removeInitialAndTrailingSlashes(removeInitialAndTrailingSlashes(path).replace(subpathCombination, ""));
1195
- const nextSegments = restOfThePath.length > 0 ? restOfThePath.split("/") : [];
1196
- if (nextSegments.length > 0) {
1197
- const entityId = nextSegments[0];
1198
- const path2 = collectionPath + "/" + entityId;
1199
- result.push(new types.EntityReference({
1200
- id: entityId,
1201
- path: collectionPath
1202
- }));
1203
- if (nextSegments.length > 1) {
1204
- const newPath = nextSegments.slice(1).join("/");
1205
- if (!collection) {
1206
- throw Error("collection not found resolving path: " + collection);
1207
- }
1208
- if (getSubcollections(collection).length > 0) {
1209
- result.push(...getParentReferencesFromPath({
1210
- path: newPath,
1211
- collections: getSubcollections(collection),
1212
- currentFullPath: path2
1213
- }));
1214
- }
1215
- }
1216
- }
1217
- break;
1218
- }
1219
- }
1220
- return result;
1221
- }
1222
- function buildCollection(collection) {
1223
- return collection;
1224
- }
1225
- function buildProperty(property) {
1226
- return property;
1227
- }
1228
- function buildProperties(properties) {
1229
- return properties;
1230
- }
1231
- function buildPropertiesOrBuilder(propertiesOrBuilder) {
1232
- return propertiesOrBuilder;
1233
- }
1234
- function buildEnum(enumValues) {
1235
- return enumValues;
1236
- }
1237
- function buildEnumValueConfig(enumValueConfig) {
1238
- return enumValueConfig;
1239
- }
1240
- function buildEntityCallbacks(callbacks) {
1241
- return callbacks;
1242
- }
1243
- function buildAdditionalFieldDelegate(additionalFieldDelegate) {
1244
- return additionalFieldDelegate;
1245
- }
1246
- async function resolveStorageFilenameString({
1247
- input,
1248
- storage,
1249
- values,
1250
- entityId,
1251
- path,
1252
- property,
1253
- file,
1254
- propertyKey
1255
- }) {
1256
- let result;
1257
- if (typeof input === "function") {
1258
- result = await input({
1259
- path,
1260
- entityId,
1261
- values,
1262
- property,
1263
- file,
1264
- storage,
1265
- propertyKey
1266
- });
1267
- if (!result) console.warn("Storage callback returned empty result. Using default name value");
1268
- } else {
1269
- result = replacePlaceholders({
1270
- file,
1271
- input,
1272
- entityId,
1273
- propertyKey,
1274
- path
1275
- });
1276
- }
1277
- if (!result) result = utils.randomString() + "_" + file.name;
1278
- return result;
1279
- }
1280
- function resolveStoragePathString({
1281
- input,
1282
- storage,
1283
- values,
1284
- entityId,
1285
- path,
1286
- property,
1287
- file,
1288
- propertyKey
1289
- }) {
1290
- let result;
1291
- if (typeof input === "function") {
1292
- result = input({
1293
- path,
1294
- entityId,
1295
- values,
1296
- property,
1297
- file,
1298
- storage,
1299
- propertyKey
1300
- });
1301
- if (!result) console.warn("Storage callback returned empty result. Using default name value");
1302
- } else {
1303
- result = replacePlaceholders({
1304
- file,
1305
- input,
1306
- entityId,
1307
- propertyKey,
1308
- path
1309
- });
1310
- }
1311
- if (!result) result = utils.randomString() + "_" + file.name;
1312
- return result;
1313
- }
1314
- function replacePlaceholders({
1315
- file,
1316
- input,
1317
- entityId,
1318
- propertyKey,
1319
- path
1320
- }) {
1321
- const ext = file.name.split(".").pop();
1322
- let result = input.replace("{propertyKey}", propertyKey).replace("{rand}", utils.randomString()).replace("{file}", file.name).replace("{file.type}", file.type);
1323
- if (entityId) {
1324
- result = result.replace("{entityId}", String(entityId));
1325
- }
1326
- if (path) {
1327
- result = result.replace("{path}", path);
1328
- }
1329
- if (ext) {
1330
- result = result.replace("{file.ext}", ext);
1331
- const name = file.name.replace(`.${ext}`, "");
1332
- result = result.replace("{file.name}", name);
1333
- }
1334
- if (!result) result = utils.randomString() + "_" + file.name;
1335
- return result;
1336
- }
1337
- function hasPropertyCallbacks(properties, callbackName) {
1338
- if (!properties) return false;
1339
- for (const property of Object.values(properties)) {
1340
- if (property.callbacks?.[callbackName]) return true;
1341
- if (property.type === "map" && property.properties) {
1342
- if (hasPropertyCallbacks(property.properties, callbackName)) return true;
1343
- } else if (property.type === "array" && property.of) {
1344
- const ofs = Array.isArray(property.of) ? property.of : [property.of];
1345
- for (const of of ofs) {
1346
- if (of.callbacks?.[callbackName]) return true;
1347
- if (of.type === "map" && of.properties && hasPropertyCallbacks(of.properties, callbackName)) return true;
1348
- }
1349
- }
1350
- }
1351
- return false;
1352
- }
1353
- async function processProperties(properties, values, previousValues, propsContext, callbackName) {
1354
- if (!values || typeof values !== "object") return values;
1355
- const result = {
1356
- ...values
1357
- };
1358
- for (const [key, property] of Object.entries(properties)) {
1359
- if (result[key] === void 0) continue;
1360
- let currentValue = result[key];
1361
- const previousValue = previousValues?.[key];
1362
- if (property.type === "array" && Array.isArray(currentValue)) {
1363
- if (property.of && !Array.isArray(property.of)) {
1364
- currentValue = await Promise.all(currentValue.map(async (item, index) => {
1365
- const prevItem = Array.isArray(previousValue) ? previousValue[index] : void 0;
1366
- const singlePropData = {
1367
- "_tmp": property.of
1368
- };
1369
- const res = await processProperties(singlePropData, {
1370
- "_tmp": item
1371
- }, {
1372
- "_tmp": prevItem
1373
- }, propsContext, callbackName);
1374
- return res["_tmp"];
1375
- }));
1376
- }
1377
- } else if (property.type === "map" && property.properties && typeof currentValue === "object") {
1378
- currentValue = await processProperties(property.properties, currentValue, previousValue ?? {}, propsContext, callbackName);
1379
- }
1380
- if (property.callbacks?.[callbackName]) {
1381
- const cbRes = await Promise.resolve(property.callbacks[callbackName]({
1382
- ...propsContext,
1383
- value: currentValue,
1384
- previousValue
1385
- }));
1386
- if (cbRes !== void 0) {
1387
- currentValue = cbRes;
1388
- }
1389
- }
1390
- result[key] = currentValue;
1391
- }
1392
- return result;
1393
- }
1394
- const buildPropertyCallbacks = (properties) => {
1395
- if (!properties) return void 0;
1396
- const propertyCallbacks = {};
1397
- if (hasPropertyCallbacks(properties, "afterRead")) {
1398
- propertyCallbacks.afterRead = async (props) => {
1399
- const processedValues = await processProperties(properties, props.entity.values, props.entity.values, props, "afterRead");
1400
- return {
1401
- ...props.entity,
1402
- values: processedValues
1403
- };
1404
- };
1405
- }
1406
- if (hasPropertyCallbacks(properties, "beforeSave")) {
1407
- propertyCallbacks.beforeSave = async (props) => {
1408
- return await processProperties(properties, props.values, props.previousValues ?? {}, props, "beforeSave");
1409
- };
1410
- }
1411
- return Object.keys(propertyCallbacks).length > 0 ? propertyCallbacks : void 0;
1412
- };
1413
- function getIn(obj, path) {
1414
- if (!obj || !path) return void 0;
1415
- return path.split(".").reduce((acc, part) => acc && acc[part], obj);
1416
- }
1417
- let operationsRegistered = false;
1418
- function registerConditionOperations() {
1419
- if (operationsRegistered) return;
1420
- jsonLogic.add_operation("hasRole", function(roleId) {
1421
- return this?.user?.roles?.includes(roleId) ?? false;
1422
- });
1423
- jsonLogic.add_operation("hasAnyRole", function(roleIds) {
1424
- if (!this?.user?.roles || !Array.isArray(roleIds)) return false;
1425
- return roleIds.some((role) => this.user.roles.includes(role));
1426
- });
1427
- jsonLogic.add_operation("isToday", (timestamp) => {
1428
- if (!timestamp) return false;
1429
- const date = new Date(timestamp);
1430
- const today = /* @__PURE__ */ new Date();
1431
- return date.getFullYear() === today.getFullYear() && date.getMonth() === today.getMonth() && date.getDate() === today.getDate();
1432
- });
1433
- jsonLogic.add_operation("isPast", (timestamp) => {
1434
- if (!timestamp) return false;
1435
- return timestamp < Date.now();
1436
- });
1437
- jsonLogic.add_operation("isFuture", (timestamp) => {
1438
- if (!timestamp) return false;
1439
- return timestamp > Date.now();
1440
- });
1441
- operationsRegistered = true;
1442
- }
1443
- function evaluateCondition(rule, context) {
1444
- registerConditionOperations();
1445
- return jsonLogic.apply(rule, context);
1446
- }
1447
- function serializeValueForConditions(value) {
1448
- if (value === null || value === void 0) {
1449
- return value;
1450
- }
1451
- if (value instanceof Date) {
1452
- return value.getTime();
1453
- }
1454
- if (typeof value?.toMillis === "function") {
1455
- return value.toMillis();
1456
- }
1457
- if (typeof value?.toDate === "function") {
1458
- return value.toDate().getTime();
1459
- }
1460
- if (Array.isArray(value)) {
1461
- return value.map(serializeValueForConditions);
1462
- }
1463
- if (typeof value === "object") {
1464
- const result = {};
1465
- for (const key of Object.keys(value)) {
1466
- result[key] = serializeValueForConditions(value[key]);
1467
- }
1468
- return result;
1469
- }
1470
- return value;
1471
- }
1472
- function buildConditionContext(params) {
1473
- const {
1474
- propertyKey,
1475
- values,
1476
- previousValues,
1477
- path,
1478
- entityId,
1479
- index,
1480
- authController
1481
- } = params;
1482
- const user = authController.user;
1483
- const serializedValues = serializeValueForConditions(values ?? {});
1484
- const serializedPreviousValues = serializeValueForConditions(previousValues ?? values ?? {});
1485
- return {
1486
- values: serializedValues,
1487
- previousValues: serializedPreviousValues,
1488
- propertyValue: propertyKey ? getIn(serializedValues, propertyKey) : void 0,
1489
- path,
1490
- entityId,
1491
- isNew: !entityId,
1492
- index,
1493
- user: {
1494
- uid: user?.uid ?? "",
1495
- email: user?.email ?? null,
1496
- displayName: user?.displayName ?? null,
1497
- photoURL: user?.photoURL ?? null,
1498
- roles: (user?.roles ?? []).map((r) => typeof r === "string" ? r : r.id)
1499
- },
1500
- now: Date.now()
1501
- };
1502
- }
1503
- function applyPropertyConditions(property, context) {
1504
- const {
1505
- conditions
1506
- } = property;
1507
- if (!conditions) return property;
1508
- const result = {
1509
- ...property
1510
- };
1511
- if (conditions.disabled) {
1512
- const isDisabled = evaluateCondition(conditions.disabled, context);
1513
- if (isDisabled) {
1514
- result.ui = result.ui || {};
1515
- result.ui.disabled = {
1516
- clearOnDisabled: conditions.clearOnDisabled ?? false,
1517
- disabledMessage: conditions.disabledMessage,
1518
- hidden: false
1519
- };
1520
- }
1521
- }
1522
- if (conditions.hidden) {
1523
- const isHidden2 = evaluateCondition(conditions.hidden, context);
1524
- if (isHidden2) {
1525
- result.ui = result.ui || {};
1526
- result.ui.disabled = {
1527
- ...typeof result.ui?.disabled === "object" ? result.ui.disabled : {},
1528
- hidden: true,
1529
- clearOnDisabled: conditions.clearOnDisabled ?? false
1530
- };
1531
- }
1532
- }
1533
- if (conditions.readOnly) {
1534
- const isReadOnly2 = evaluateCondition(conditions.readOnly, context);
1535
- if (isReadOnly2) {
1536
- result.ui = result.ui || {};
1537
- result.ui.readOnly = true;
1538
- }
1539
- }
1540
- if (conditions.required !== void 0) {
1541
- const isRequired = evaluateCondition(conditions.required, context);
1542
- result.validation = {
1543
- ...result.validation,
1544
- required: isRequired,
1545
- requiredMessage: conditions.requiredMessage
1546
- };
1547
- }
1548
- if (context.isNew && conditions.defaultValue !== void 0) {
1549
- result.defaultValue = evaluateCondition(conditions.defaultValue, context);
1550
- }
1551
- if ("enum" in result && result.enum && (conditions.enumConditions || conditions.allowedEnumValues || conditions.excludedEnumValues)) {
1552
- result.enum = applyEnumConditions(result.enum, conditions, context);
1553
- }
1554
- if (result.type === "reference") {
1555
- if (conditions.referencePath) {
1556
- result.path = evaluateCondition(conditions.referencePath, context);
1557
- }
1558
- if (conditions.referenceFilter) {
1559
- result.fixedFilter = evaluateCondition(conditions.referenceFilter, context);
1560
- }
1561
- }
1562
- if (result.type === "array") {
1563
- if (conditions.canAddElements !== void 0) {
1564
- result.canAddElements = evaluateCondition(conditions.canAddElements, context);
1565
- }
1566
- if (conditions.sortable !== void 0) {
1567
- result.sortable = evaluateCondition(conditions.sortable, context);
1568
- }
1569
- }
1570
- return result;
1571
- }
1572
- function objectToArray(obj) {
1573
- if (Array.isArray(obj)) return obj.map(String);
1574
- if (obj && typeof obj === "object") {
1575
- const keys = Object.keys(obj);
1576
- if (keys.length > 0 && keys.every((k) => !isNaN(Number(k)))) {
1577
- return keys.sort((a, b) => Number(a) - Number(b)).map((k) => obj[k]).filter((v) => typeof v === "string" || typeof v === "number").map(String);
1578
- }
1579
- }
1580
- return [];
1581
- }
1582
- function applyEnumConditions(enumValues, conditions, context) {
1583
- let result = [...enumValues];
1584
- if (conditions.allowedEnumValues) {
1585
- const allowed = evaluateCondition(conditions.allowedEnumValues, context);
1586
- const allowedArray = objectToArray(allowed);
1587
- if (allowedArray.length > 0) {
1588
- result = result.filter((ev) => allowedArray.includes(String(ev.id)));
1589
- }
1590
- }
1591
- if (conditions.excludedEnumValues) {
1592
- const excluded = evaluateCondition(conditions.excludedEnumValues, context);
1593
- const excludedArray = objectToArray(excluded);
1594
- if (excludedArray.length > 0) {
1595
- result = result.filter((ev) => !excludedArray.includes(String(ev.id)));
1596
- }
1597
- }
1598
- if (conditions.enumConditions) {
1599
- result = result.map((ev) => {
1600
- const evConditions = conditions.enumConditions?.[ev.id];
1601
- if (!evConditions) return ev;
1602
- if (evConditions.hidden && evaluateCondition(evConditions.hidden, context)) {
1603
- return null;
1604
- }
1605
- if (evConditions.disabled && evaluateCondition(evConditions.disabled, context)) {
1606
- return {
1607
- ...ev,
1608
- disabled: true
1609
- };
1610
- }
1611
- return ev;
1612
- }).filter((ev) => ev !== null);
1613
- }
1614
- return result;
1615
- }
1616
- class CollectionRegistry {
1617
- // Normalized runtime layer (used by Data Grid / UI)
1618
- collectionsByTableName = /* @__PURE__ */ new Map();
1619
- collectionsBySlug = /* @__PURE__ */ new Map();
1620
- rootCollections = [];
1621
- cachedCollectionsList = null;
1622
- // Raw configuration layer (used by Collection Editor AST generator)
1623
- rawCollectionsByTableName = /* @__PURE__ */ new Map();
1624
- rawCollectionsBySlug = /* @__PURE__ */ new Map();
1625
- rawRootCollections = [];
1626
- cachedRawCollectionsList = null;
1627
- // Snapshot of raw input for idempotency check — compared BEFORE normalization
1628
- // to avoid the issue where normalization creates new objects that always fail equality.
1629
- lastRawInputSnapshot = null;
1630
- constructor(collections) {
1631
- if (collections) {
1632
- this.registerMultiple(collections);
1633
- }
1634
- }
1635
- reset() {
1636
- this.collectionsByTableName.clear();
1637
- this.collectionsBySlug.clear();
1638
- this.rootCollections = [];
1639
- this.cachedCollectionsList = null;
1640
- this.rawCollectionsByTableName.clear();
1641
- this.rawCollectionsBySlug.clear();
1642
- this.rawRootCollections = [];
1643
- this.cachedRawCollectionsList = null;
1644
- }
1645
- /**
1646
- * Registers a collection and its subcollections recursively.
1647
- * Returns true if the collections have changed, false otherwise.
1648
- *
1649
- * Idempotent: compares the raw input (before normalization) against a stored
1650
- * snapshot. Only re-normalizes and re-registers when the raw input actually changed.
1651
- * @param collections
1652
- */
1653
- registerMultiple(collections) {
1654
- const rawSnapshot = collections.map((c) => utils.removeFunctions(c));
1655
- if (this.lastRawInputSnapshot && fastEquals.deepEqual(this.lastRawInputSnapshot, rawSnapshot)) {
1656
- return false;
1657
- }
1658
- this.reset();
1659
- collections.forEach((c) => {
1660
- if (c.slug) {
1661
- this.collectionsBySlug.set(c.slug, c);
1662
- }
1663
- this.collectionsByTableName.set(getTableName(c), c);
1664
- });
1665
- const normalizedCollections = collections.map((c) => this.normalizeCollection({
1666
- ...c
1667
- }));
1668
- normalizedCollections.forEach((c, index) => {
1669
- const raw = utils.deepClone(collections[index]);
1670
- this.rootCollections.push(c);
1671
- this.rawRootCollections.push(raw);
1672
- const normalized = this.normalizeCollection(c);
1673
- this.collectionsByTableName.set(getTableName(normalized), normalized);
1674
- this.rawCollectionsByTableName.set(getTableName(raw), raw);
1675
- if (normalized.slug) {
1676
- this.collectionsBySlug.set(normalized.slug, normalized);
1677
- }
1678
- if (raw.slug) {
1679
- this.rawCollectionsBySlug.set(raw.slug, raw);
1680
- }
1681
- });
1682
- normalizedCollections.forEach((c) => {
1683
- const subcollections = getSubcollections(c);
1684
- if (subcollections && subcollections.length > 0) {
1685
- subcollections.forEach((subCollection) => {
1686
- if (!subCollection) return;
1687
- this._registerRecursively(this.normalizeCollection({
1688
- ...subCollection
1689
- }), utils.deepClone(subCollection));
1690
- });
1691
- }
1692
- });
1693
- this.lastRawInputSnapshot = rawSnapshot;
1694
- return true;
1695
- }
1696
- register(collection, rawCollection) {
1697
- const raw = rawCollection ? utils.deepClone(rawCollection) : utils.deepClone(collection);
1698
- this.rootCollections.push(collection);
1699
- this.rawRootCollections.push(raw);
1700
- this._registerRecursively(collection, raw);
1701
- }
1702
- _registerRecursively(collection, rawCollection) {
1703
- if (this.collectionsByTableName.has(getTableName(collection))) {
1704
- return;
1705
- }
1706
- const normalizedCollection = this.normalizeCollection(collection);
1707
- this.collectionsByTableName.set(getTableName(normalizedCollection), normalizedCollection);
1708
- this.rawCollectionsByTableName.set(getTableName(rawCollection), rawCollection);
1709
- if (normalizedCollection.slug) {
1710
- this.collectionsBySlug.set(normalizedCollection.slug, normalizedCollection);
1711
- }
1712
- if (rawCollection.slug) {
1713
- this.rawCollectionsBySlug.set(rawCollection.slug, rawCollection);
1714
- }
1715
- const subcollections = getSubcollections(normalizedCollection);
1716
- if (subcollections && subcollections.length > 0) {
1717
- subcollections.forEach((subCollection) => {
1718
- if (!subCollection) return;
1719
- this._registerRecursively(this.normalizeCollection({
1720
- ...subCollection
1721
- }), utils.deepClone(subCollection));
1722
- });
1723
- }
1724
- }
1725
- normalizeCollection(collection) {
1726
- const result = {
1727
- ...collection
1728
- };
1729
- const extractedRelations = this.extractRelationsFromProperties(result.properties);
1730
- const relResult = result;
1731
- const manualRelations = types.getDataSourceCapabilities(result.driver).supportsRelations ? relResult.relations ?? [] : [];
1732
- const mergedRelationsRaw = [...extractedRelations];
1733
- for (const manual of manualRelations) {
1734
- const name = manual.relationName;
1735
- if (!name) {
1736
- mergedRelationsRaw.push(manual);
1737
- } else {
1738
- const existingIndex = mergedRelationsRaw.findIndex((r) => r.relationName === name);
1739
- if (existingIndex === -1) {
1740
- mergedRelationsRaw.push(manual);
1741
- } else {
1742
- mergedRelationsRaw[existingIndex] = {
1743
- ...manual,
1744
- ...mergedRelationsRaw[existingIndex]
1745
- };
1746
- }
1747
- }
1748
- }
1749
- let mergedRelations = mergedRelationsRaw;
1750
- if (types.getDataSourceCapabilities(result.driver).supportsRelations) {
1751
- mergedRelations = mergedRelationsRaw.map((r) => {
1752
- try {
1753
- return sanitizeRelation(r, result, (slug) => this.get(slug));
1754
- } catch {
1755
- return r;
1756
- }
1757
- });
1758
- relResult.relations = mergedRelations;
1759
- }
1760
- const properties = this.normalizeProperties(result.properties, mergedRelations);
1761
- result.properties = properties;
1762
- if (!result.childCollections) {
1763
- if (types.getDataSourceCapabilities(result.driver).supportsSubcollections && result.subcollections) {
1764
- result.childCollections = result.subcollections;
1765
- } else if (types.getDataSourceCapabilities(result.driver).supportsRelations && relResult.relations) {
1766
- const manyRelations = relResult.relations.filter((r) => r.cardinality === "many");
1767
- if (manyRelations.length > 0) {
1768
- result.childCollections = () => manyRelations.map((r) => {
1769
- const target = r.target();
1770
- return r.overrides ? utils.mergeDeep(target, r.overrides) : target;
1771
- });
1772
- }
1773
- }
1774
- }
1775
- return result;
1776
- }
1777
- /**
1778
- * Extract Relation[] from properties that have inline relation config (i.e. `target` is set).
1779
- * This allows developers to define relations directly on properties without a separate
1780
- * `relations[]` entry on the collection.
1781
- */
1782
- extractRelationsFromProperties(properties) {
1783
- const relations = [];
1784
- for (const [key, property] of Object.entries(properties)) {
1785
- if (property.type === "relation") {
1786
- const relProp = property;
1787
- const target = relProp.target ?? relProp.relation?.target;
1788
- if (target) {
1789
- const relationName = relProp.relationName ?? relProp.relation?.relationName ?? key;
1790
- relations.push({
1791
- relationName,
1792
- target,
1793
- cardinality: relProp.cardinality ?? relProp.relation?.cardinality ?? "one",
1794
- direction: relProp.direction ?? relProp.relation?.direction ?? "owning",
1795
- inverseRelationName: relProp.inverseRelationName ?? relProp.relation?.inverseRelationName,
1796
- localKey: relProp.localKey ?? relProp.relation?.localKey,
1797
- foreignKeyOnTarget: relProp.foreignKeyOnTarget ?? relProp.relation?.foreignKeyOnTarget,
1798
- through: relProp.through ?? relProp.relation?.through,
1799
- joinPath: relProp.joinPath ?? relProp.relation?.joinPath,
1800
- onUpdate: relProp.onUpdate ?? relProp.relation?.onUpdate,
1801
- onDelete: relProp.onDelete ?? relProp.relation?.onDelete,
1802
- overrides: relProp.overrides ?? relProp.relation?.overrides
1803
- });
1804
- }
1805
- } else if (property.type === "map" && property.properties) {
1806
- relations.push(...this.extractRelationsFromProperties(property.properties));
1807
- }
1808
- }
1809
- return relations;
1810
- }
1811
- normalizeProperties(properties, relations) {
1812
- const newProperties = {};
1813
- for (const key in properties) {
1814
- newProperties[key] = this.normalizeProperty(key, properties[key], relations);
1815
- }
1816
- return newProperties;
1817
- }
1818
- normalizeProperty(key, property, relations) {
1819
- const newProperty = {
1820
- ...property
1821
- };
1822
- if (newProperty.type === "map" && newProperty.properties) {
1823
- newProperty.properties = this.normalizeProperties(newProperty.properties, relations);
1824
- } else if (newProperty.type === "array") {
1825
- const arrayProp = newProperty;
1826
- if (arrayProp.of) {
1827
- if (Array.isArray(arrayProp.of)) {
1828
- arrayProp.of = arrayProp.of.map((p, i) => this.normalizeProperty(`${key}[${i}]`, p, relations));
1829
- } else {
1830
- arrayProp.of = this.normalizeProperty(`${key}.of`, arrayProp.of, relations);
1831
- }
1832
- } else if (arrayProp.oneOf && arrayProp.oneOf.properties) {
1833
- arrayProp.oneOf.properties = this.normalizeProperties(arrayProp.oneOf.properties, relations);
1834
- }
1835
- } else if ((newProperty.type === "string" || newProperty.type === "number") && newProperty.enum) {
1836
- const stringOrNumberProperty = newProperty;
1837
- if (typeof stringOrNumberProperty.enum === "object" && !Array.isArray(stringOrNumberProperty.enum)) {
1838
- stringOrNumberProperty.enum = enumToObjectEntries(stringOrNumberProperty.enum)?.filter((value) => value && (value.id || value.id === 0) && value.label) ?? [];
1839
- }
1840
- } else if (newProperty.type === "relation") {
1841
- const relationProperty = newProperty;
1842
- const name = relationProperty.relationName || key;
1843
- const relation = relations.find((r) => r.relationName === name);
1844
- if (relation) {
1845
- relationProperty.relation = relation;
1846
- } else {
1847
- console.warn(`Could not find relation for property '${key}' with relationName: ${name}`);
1848
- }
1849
- }
1850
- return newProperty;
1851
- }
1852
- get(path) {
1853
- const bySlug = this.collectionsBySlug.get(path);
1854
- if (bySlug) return bySlug;
1855
- if (path.includes("-")) {
1856
- const normalized = path.replace(/-/g, "_");
1857
- const byNormalized = this.collectionsBySlug.get(normalized);
1858
- if (byNormalized) return byNormalized;
1859
- }
1860
- return this.collectionsByTableName.get(path);
1861
- }
1862
- /**
1863
- * Gets the pristine, un-normalized collection exactly as it was provided.
1864
- * Useful for the AST editor so it doesn't accidentally serialize injected metadata back to disk.
1865
- */
1866
- getRaw(path) {
1867
- const bySlug = this.rawCollectionsBySlug.get(path);
1868
- if (bySlug) return bySlug;
1869
- if (path.includes("-")) {
1870
- const normalized = path.replace(/-/g, "_");
1871
- const byNormalized = this.rawCollectionsBySlug.get(normalized);
1872
- if (byNormalized) return byNormalized;
1873
- }
1874
- return this.rawCollectionsByTableName.get(path);
1875
- }
1876
- /**
1877
- * Get collection by resolving multi-segment paths through relations
1878
- * e.g., "authors/70/posts" resolves to the posts collection
1879
- */
1880
- getCollectionByPath(collectionPath) {
1881
- if (!collectionPath.includes("/")) {
1882
- return this.get(collectionPath);
1883
- }
1884
- const pathSegments = collectionPath.split("/").filter((p) => p);
1885
- if (pathSegments.length < 3 || pathSegments.length % 2 === 0) {
1886
- throw new Error(`Invalid relation path: ${collectionPath}. Expected format: collection/id/relation or collection/id/relation/id/relation`);
1887
- }
1888
- const rootCollectionPath = pathSegments[0];
1889
- let currentCollection = this.get(rootCollectionPath);
1890
- if (!currentCollection) {
1891
- throw new Error(`Root collection not found: ${rootCollectionPath}`);
1892
- }
1893
- for (let i = 2; i < pathSegments.length; i += 2) {
1894
- const relationKey = pathSegments[i];
1895
- if (!types.getDataSourceCapabilities(currentCollection.driver).supportsRelations) {
1896
- throw new Error(`Relation path navigation requires a collection that supports relations, but '${currentCollection.slug}' uses driver '${currentCollection.driver}'`);
1897
- }
1898
- const resolvedRelations = resolveCollectionRelations(currentCollection);
1899
- const relation = findRelation(resolvedRelations, relationKey);
1900
- if (!relation) {
1901
- throw new Error(`Relation '${relationKey}' not found in collection '${currentCollection.slug}'`);
1902
- }
1903
- const target = relation.target();
1904
- const targetRelationKey = relation.relationName || target.slug;
1905
- const targetSlug = relation.overrides?.slug ?? targetRelationKey;
1906
- currentCollection = this.get(targetSlug) || this.normalizeCollection(target);
1907
- if (i + 1 < pathSegments.length) ;
1908
- }
1909
- return currentCollection;
1910
- }
1911
- getCollections() {
1912
- if (!this.cachedCollectionsList) {
1913
- this.cachedCollectionsList = Array.from(this.collectionsByTableName.values());
1914
- }
1915
- return this.cachedCollectionsList;
1916
- }
1917
- getRawCollections() {
1918
- if (!this.cachedRawCollectionsList) {
1919
- this.cachedRawCollectionsList = Array.from(this.rawCollectionsByTableName.values());
1920
- }
1921
- return this.cachedRawCollectionsList;
1922
- }
1923
- /**
1924
- * Resolves a multi-segment path like "products/123/locales" and returns
1925
- * information about the collections and entity IDs along the path
1926
- */
1927
- resolvePathToCollections(path) {
1928
- const pathSegments = path.split("/").filter((p) => p);
1929
- if (pathSegments.length === 0) {
1930
- throw new Error(`Invalid path: ${path}`);
1931
- }
1932
- if (pathSegments.length % 2 !== 1) {
1933
- throw new Error(`Invalid collection path: ${path}. It must have an odd number of segments.`);
1934
- }
1935
- const collections = [];
1936
- const entityIds = [];
1937
- let currentCollection = this.get(pathSegments[0]);
1938
- if (!currentCollection) {
1939
- throw new Error(`Unknown collection path or slug: ${pathSegments[0]}`);
1940
- }
1941
- collections.push(currentCollection);
1942
- for (let i = 1; i < pathSegments.length; i += 2) {
1943
- const entityId = pathSegments[i];
1944
- entityIds.push(entityId);
1945
- if (i + 1 < pathSegments.length) {
1946
- const subcollectionSlug = pathSegments[i + 1];
1947
- const subcollections = getSubcollections(currentCollection);
1948
- if (!subcollections || subcollections.length === 0) {
1949
- throw new Error(`No subcollections found for ${currentCollection.slug} in path: ${path}`);
1950
- }
1951
- const subcollection = subcollections.find((c) => c.slug === subcollectionSlug);
1952
- if (!subcollection) {
1953
- throw new Error(`Subcollection '${subcollectionSlug}' not found in ${currentCollection.slug}`);
1954
- }
1955
- currentCollection = this.get(subcollection.slug) || this.normalizeCollection(subcollection);
1956
- collections.push(currentCollection);
1957
- }
1958
- }
1959
- return {
1960
- collections,
1961
- entityIds,
1962
- finalCollection: currentCollection
1963
- };
1964
- }
1965
- }
1966
- const defaultUsersCollection = {
1967
- name: "Users",
1968
- singularName: "User",
1969
- slug: "users",
1970
- table: "users",
1971
- schema: "rebase",
1972
- icon: "Users",
1973
- group: "Settings",
1974
- openEntityMode: "dialog",
1975
- disableDefaultActions: ["copy"],
1976
- securityRules: [{
1977
- operation: "select",
1978
- roles: ["admin"]
1979
- }, {
1980
- operations: ["insert", "update", "delete"],
1981
- roles: ["admin"]
1982
- }],
1983
- sort: ["createdAt", "desc"],
1984
- properties: {
1985
- id: {
1986
- name: "ID",
1987
- type: "string",
1988
- isId: "uuid",
1989
- ui: {
1990
- readOnly: true
1991
- }
1992
- },
1993
- email: {
1994
- name: "Email",
1995
- type: "string",
1996
- validation: {
1997
- required: true,
1998
- unique: true
1999
- }
2000
- },
2001
- displayName: {
2002
- name: "Name",
2003
- type: "string",
2004
- columnName: "display_name",
2005
- validation: {
2006
- required: true
2007
- }
2008
- },
2009
- photoURL: {
2010
- name: "Photo URL",
2011
- type: "string",
2012
- columnName: "photo_url",
2013
- url: "image"
2014
- },
2015
- roles: {
2016
- name: "Roles",
2017
- type: "array",
2018
- columnType: "text[]",
2019
- of: {
2020
- name: "Role",
2021
- type: "string",
2022
- enum: {
2023
- admin: "Admin",
2024
- editor: "Editor",
2025
- viewer: "Viewer"
2026
- }
2027
- }
2028
- },
2029
- passwordHash: {
2030
- name: "Password Hash",
2031
- type: "string",
2032
- columnName: "password_hash",
2033
- ui: {
2034
- hideFromCollection: true,
2035
- disabled: {
2036
- hidden: true
2037
- }
2038
- }
2039
- },
2040
- emailVerified: {
2041
- name: "Email Verified",
2042
- type: "boolean",
2043
- columnName: "email_verified",
2044
- defaultValue: false,
2045
- ui: {
2046
- hideFromCollection: true,
2047
- disabled: {
2048
- hidden: true
2049
- }
2050
- }
2051
- },
2052
- emailVerificationToken: {
2053
- name: "Email Verification Token",
2054
- type: "string",
2055
- columnName: "email_verification_token",
2056
- ui: {
2057
- hideFromCollection: true,
2058
- disabled: {
2059
- hidden: true
2060
- }
2061
- }
2062
- },
2063
- emailVerificationSentAt: {
2064
- name: "Email Verification Sent At",
2065
- type: "date",
2066
- columnName: "email_verification_sent_at",
2067
- ui: {
2068
- hideFromCollection: true,
2069
- disabled: {
2070
- hidden: true
2071
- }
2072
- }
2073
- },
2074
- metadata: {
2075
- name: "Metadata",
2076
- type: "map",
2077
- defaultValue: {},
2078
- ui: {
2079
- hideFromCollection: true,
2080
- disabled: {
2081
- hidden: true
2082
- }
2083
- }
2084
- },
2085
- createdAt: {
2086
- name: "Created At",
2087
- type: "date",
2088
- columnName: "created_at",
2089
- autoValue: "on_create",
2090
- ui: {
2091
- readOnly: true
2092
- }
2093
- },
2094
- updatedAt: {
2095
- name: "Updated At",
2096
- type: "date",
2097
- columnName: "updated_at",
2098
- autoValue: "on_update",
2099
- ui: {
2100
- hideFromCollection: true,
2101
- disabled: {
2102
- hidden: true
2103
- }
2104
- }
2105
- }
2106
- },
2107
- listProperties: ["displayName", "email", "roles", "createdAt"],
2108
- propertiesOrder: ["id", "email", "displayName", "roles", "createdAt"]
2109
- };
2110
- function or(...conditions) {
2111
- return {
2112
- type: "or",
2113
- conditions
2114
- };
2115
- }
2116
- function and(...conditions) {
2117
- return {
2118
- type: "and",
2119
- conditions
2120
- };
2121
- }
2122
- function cond(column, operator, value) {
2123
- return {
2124
- column,
2125
- operator,
2126
- value
2127
- };
2128
- }
2129
- class QueryBuilder {
2130
- constructor(collection) {
2131
- this.collection = collection;
2132
- }
2133
- params = {
2134
- where: {}
2135
- };
2136
- where(columnOrCondition, operator, value) {
2137
- if (typeof columnOrCondition === "object" && columnOrCondition !== null && "type" in columnOrCondition) {
2138
- this.params.logical = columnOrCondition;
2139
- return this;
2140
- }
2141
- if (!this.params.where) {
2142
- this.params.where = {};
2143
- }
2144
- const column = columnOrCondition;
2145
- const condition = [operator, value];
2146
- const existing = this.params.where[column];
2147
- if (existing === void 0) {
2148
- this.params.where[column] = condition;
2149
- } else if (Array.isArray(existing) && existing.length > 0 && Array.isArray(existing[0])) {
2150
- this.params.where[column].push(condition);
2151
- } else {
2152
- let firstCondition;
2153
- if (Array.isArray(existing) && existing.length === 2 && typeof existing[0] === "string") {
2154
- firstCondition = existing;
2155
- } else {
2156
- firstCondition = ["==", existing];
2157
- }
2158
- this.params.where[column] = [firstCondition, condition];
2159
- }
2160
- return this;
2161
- }
2162
- /**
2163
- * Order the results by a specific column.
2164
- * @example
2165
- * client.collection('users').orderBy('createdAt', 'desc').find()
2166
- */
2167
- orderBy(column, ascending = "asc") {
2168
- this.params.orderBy = `${column}:${ascending}`;
2169
- return this;
2170
- }
2171
- /**
2172
- * Limit the number of results returned.
2173
- */
2174
- limit(count) {
2175
- this.params.limit = count;
2176
- return this;
2177
- }
2178
- /**
2179
- * Skip the first N results.
2180
- */
2181
- offset(count) {
2182
- this.params.offset = count;
2183
- return this;
2184
- }
2185
- /**
2186
- * Set a free-text search string if supported by the backend.
2187
- */
2188
- search(searchString) {
2189
- this.params.searchString = searchString;
2190
- return this;
2191
- }
2192
- /**
2193
- * Include related entities in the response.
2194
- * Relations will be populated with full entity data instead of just IDs.
2195
- *
2196
- * @param relations - Relation names to include, or "*" for all.
2197
- * @example
2198
- * // Include specific relations
2199
- * client.data.posts.include("tags", "author").find()
2200
- *
2201
- * // Include all relations
2202
- * client.data.posts.include("*").find()
2203
- */
2204
- include(...relations) {
2205
- this.params.include = relations;
2206
- return this;
2207
- }
2208
- /**
2209
- * Execute the find query and return the results.
2210
- */
2211
- async find() {
2212
- return this.collection.find(this.params);
2213
- }
2214
- /**
2215
- * Listen to realtime updates matching this query.
2216
- */
2217
- listen(onUpdate, onError) {
2218
- if (!this.collection.listen) {
2219
- throw new Error("Listen is only available when RebaseClient is configured with a websocketUrl.");
2220
- }
2221
- return this.collection.listen(this.params, onUpdate, onError);
2222
- }
2223
- }
2224
- function convertWhereToFilter(where) {
2225
- if (!where) return void 0;
2226
- const operatorMap = {
2227
- "eq": "==",
2228
- "neq": "!=",
2229
- "gt": ">",
2230
- "gte": ">=",
2231
- "lt": "<",
2232
- "lte": "<=",
2233
- "in": "in",
2234
- "nin": "not-in",
2235
- "not-in": "not-in",
2236
- "cs": "array-contains",
2237
- "csa": "array-contains-any",
2238
- "==": "==",
2239
- "!=": "!=",
2240
- ">": ">",
2241
- ">=": ">=",
2242
- "<": "<",
2243
- "<=": "<=",
2244
- "array-contains": "array-contains",
2245
- "array-contains-any": "array-contains-any"
2246
- };
2247
- const filter = {};
2248
- for (const [field, rawValue] of Object.entries(where)) {
2249
- if (rawValue === null) {
2250
- filter[field] = ["==", null];
2251
- continue;
2252
- }
2253
- if (typeof rawValue === "boolean") {
2254
- filter[field] = ["==", rawValue];
2255
- continue;
2256
- }
2257
- if (typeof rawValue === "number") {
2258
- filter[field] = ["==", rawValue];
2259
- continue;
2260
- }
2261
- if (Array.isArray(rawValue)) {
2262
- const conditions = Array.isArray(rawValue[0]) ? rawValue : [rawValue];
2263
- const mappedConditions = conditions.map(([rawOp, val]) => {
2264
- const mappedOp = operatorMap[rawOp] ?? "==";
2265
- return [mappedOp, val];
2266
- });
2267
- filter[field] = Array.isArray(rawValue[0]) ? mappedConditions : mappedConditions[0];
2268
- continue;
2269
- }
2270
- if (typeof rawValue === "string") {
2271
- const dotIndex = rawValue.indexOf(".");
2272
- if (dotIndex === -1) {
2273
- filter[field] = ["==", rawValue];
2274
- continue;
2275
- }
2276
- const op = rawValue.substring(0, dotIndex);
2277
- let value = rawValue.substring(dotIndex + 1);
2278
- if (typeof value === "string" && value.startsWith("(") && value.endsWith(")")) {
2279
- value = value.slice(1, -1).split(",").map((v) => v.trim());
2280
- }
2281
- if (value === "null") {
2282
- value = null;
2283
- } else if (value === "true") {
2284
- value = true;
2285
- } else if (value === "false") {
2286
- value = false;
2287
- } else if (typeof value === "string" && !isNaN(Number(value)) && value.trim() !== "") {
2288
- value = Number(value);
2289
- }
2290
- const mappedOp = operatorMap[op];
2291
- if (mappedOp) {
2292
- filter[field] = [mappedOp, value];
2293
- }
2294
- }
2295
- }
2296
- return Object.keys(filter).length > 0 ? filter : void 0;
2297
- }
2298
- function parseOrderBy(orderBy) {
2299
- if (!orderBy) return void 0;
2300
- const parts = orderBy.split(":");
2301
- const field = parts[0];
2302
- const direction = parts[1] || "asc";
2303
- return [field, direction];
2304
- }
2305
- function createDriverAccessor(driver, slug) {
2306
- const accessor = {
2307
- async find(params) {
2308
- const orderParsed = parseOrderBy(params?.orderBy);
2309
- const entities = await driver.fetchCollection({
2310
- path: slug,
2311
- limit: params?.limit,
2312
- offset: params?.offset,
2313
- filter: convertWhereToFilter(params?.where),
2314
- orderBy: orderParsed?.[0],
2315
- order: orderParsed?.[1],
2316
- searchString: params?.searchString
2317
- });
2318
- const limit = params?.limit ?? 20;
2319
- const offset = params?.offset ?? 0;
2320
- return {
2321
- data: entities,
2322
- meta: {
2323
- total: entities.length,
2324
- limit,
2325
- offset,
2326
- hasMore: entities.length >= limit
2327
- }
2328
- };
2329
- },
2330
- async findById(id) {
2331
- return driver.fetchEntity({
2332
- path: slug,
2333
- entityId: id
2334
- });
2335
- },
2336
- async create(data, id) {
2337
- return driver.saveEntity({
2338
- path: slug,
2339
- values: data,
2340
- entityId: id,
2341
- status: "new"
2342
- });
2343
- },
2344
- async update(id, data) {
2345
- return driver.saveEntity({
2346
- path: slug,
2347
- values: data,
2348
- entityId: id,
2349
- status: "existing"
2350
- });
2351
- },
2352
- async delete(id) {
2353
- return driver.deleteEntity({
2354
- entity: {
2355
- id,
2356
- path: slug,
2357
- values: {}
2358
- }
2359
- });
2360
- },
2361
- deleteAll: driver.deleteAll ? async () => {
2362
- return driver.deleteAll(slug);
2363
- } : void 0,
2364
- count: driver.countEntities ? async (params) => {
2365
- return driver.countEntities({
2366
- path: slug,
2367
- filter: convertWhereToFilter(params?.where)
2368
- });
2369
- } : void 0,
2370
- listen: driver.listenCollection ? (params, onUpdate, onError) => {
2371
- const orderParsed = parseOrderBy(params?.orderBy);
2372
- const limit = params?.limit ?? 20;
2373
- const offset = params?.offset ?? 0;
2374
- return driver.listenCollection({
2375
- path: slug,
2376
- limit: params?.limit,
2377
- offset: params?.offset,
2378
- filter: convertWhereToFilter(params?.where),
2379
- orderBy: orderParsed?.[0],
2380
- order: orderParsed?.[1],
2381
- searchString: params?.searchString,
2382
- onUpdate: (entities) => {
2383
- onUpdate({
2384
- data: entities,
2385
- meta: {
2386
- total: entities.length,
2387
- limit,
2388
- offset,
2389
- hasMore: entities.length >= limit
2390
- }
2391
- });
2392
- },
2393
- onError
2394
- });
2395
- } : void 0,
2396
- listenById: driver.listenEntity ? (id, onUpdate, onError) => {
2397
- return driver.listenEntity({
2398
- path: slug,
2399
- entityId: id,
2400
- onUpdate: (entity) => onUpdate(entity ?? void 0),
2401
- onError
2402
- });
2403
- } : void 0,
2404
- // Fluent Query Builder
2405
- where(columnOrCondition, operator, value) {
2406
- const builder = new QueryBuilder(accessor);
2407
- if (typeof columnOrCondition === "object") {
2408
- return builder.where(columnOrCondition);
2409
- }
2410
- return builder.where(columnOrCondition, operator, value);
2411
- },
2412
- orderBy(column, ascending) {
2413
- return new QueryBuilder(accessor).orderBy(column, ascending);
2414
- },
2415
- limit(count) {
2416
- return new QueryBuilder(accessor).limit(count);
2417
- },
2418
- offset(count) {
2419
- return new QueryBuilder(accessor).offset(count);
2420
- },
2421
- search(searchString) {
2422
- return new QueryBuilder(accessor).search(searchString);
2423
- },
2424
- include(...relations) {
2425
- return new QueryBuilder(accessor).include(...relations);
2426
- }
2427
- };
2428
- return accessor;
2429
- }
2430
- function buildRebaseData(driver) {
2431
- const cache = /* @__PURE__ */ new Map();
2432
- function getAccessor(slug) {
2433
- let accessor = cache.get(slug);
2434
- if (!accessor) {
2435
- accessor = createDriverAccessor(driver, slug);
2436
- cache.set(slug, accessor);
2437
- }
2438
- return accessor;
2439
- }
2440
- const target = {
2441
- collection: getAccessor
2442
- };
2443
- return new Proxy(target, {
2444
- get(_target, prop) {
2445
- if (prop === "collection") return getAccessor;
2446
- if (typeof prop === "symbol") return void 0;
2447
- if (prop === "then" || prop === "toJSON" || prop === "$$typeof") return void 0;
2448
- const slug = utils.toSnakeCase(prop);
2449
- return getAccessor(slug);
2450
- }
2451
- });
2452
- }
2453
- exports2.COLLECTION_PATH_SEPARATOR = COLLECTION_PATH_SEPARATOR;
2454
- exports2.CollectionRegistry = CollectionRegistry;
2455
- exports2.DEFAULT_ONE_OF_TYPE = DEFAULT_ONE_OF_TYPE;
2456
- exports2.DEFAULT_ONE_OF_VALUE = DEFAULT_ONE_OF_VALUE;
2457
- exports2.QueryBuilder = QueryBuilder;
2458
- exports2.addInitialSlash = addInitialSlash;
2459
- exports2.and = and;
2460
- exports2.applyPropertyConditions = applyPropertyConditions;
2461
- exports2.buildAdditionalFieldDelegate = buildAdditionalFieldDelegate;
2462
- exports2.buildCollection = buildCollection;
2463
- exports2.buildConditionContext = buildConditionContext;
2464
- exports2.buildEntityCallbacks = buildEntityCallbacks;
2465
- exports2.buildEnum = buildEnum;
2466
- exports2.buildEnumValueConfig = buildEnumValueConfig;
2467
- exports2.buildProperties = buildProperties;
2468
- exports2.buildPropertiesOrBuilder = buildPropertiesOrBuilder;
2469
- exports2.buildProperty = buildProperty;
2470
- exports2.buildPropertyCallbacks = buildPropertyCallbacks;
2471
- exports2.buildRebaseData = buildRebaseData;
2472
- exports2.canCreateEntity = canCreateEntity;
2473
- exports2.canDeleteEntity = canDeleteEntity;
2474
- exports2.canEditEntity = canEditEntity;
2475
- exports2.canReadCollection = canReadCollection;
2476
- exports2.checkOperation = checkOperation;
2477
- exports2.cond = cond;
2478
- exports2.createRelationRef = createRelationRef;
2479
- exports2.createRelationRefWithData = createRelationRefWithData;
2480
- exports2.defaultUsersCollection = defaultUsersCollection;
2481
- exports2.enumToObjectEntries = enumToObjectEntries;
2482
- exports2.evaluateCondition = evaluateCondition;
2483
- exports2.findRelation = findRelation;
2484
- exports2.fullPathToCollectionSegments = fullPathToCollectionSegments;
2485
- exports2.getArrayResolvedProperties = getArrayResolvedProperties;
2486
- exports2.getCollectionBySlugWithin = getCollectionBySlugWithin;
2487
- exports2.getCollectionPathsCombinations = getCollectionPathsCombinations;
2488
- exports2.getColumnName = getColumnName;
2489
- exports2.getDefaultValueFor = getDefaultValueFor;
2490
- exports2.getDefaultValueFortype = getDefaultValueFortype;
2491
- exports2.getDefaultValuesFor = getDefaultValuesFor;
2492
- exports2.getEntityImagePreviewPropertyKey = getEntityImagePreviewPropertyKey;
2493
- exports2.getEnumVarName = getEnumVarName;
2494
- exports2.getLabelOrConfigFrom = getLabelOrConfigFrom;
2495
- exports2.getLastSegment = getLastSegment;
2496
- exports2.getLocalChangesBackup = getLocalChangesBackup;
2497
- exports2.getNavigationEntriesFromPath = getNavigationEntriesFromPath;
2498
- exports2.getParentReferencesFromPath = getParentReferencesFromPath;
2499
- exports2.getPrimaryKeys = getPrimaryKeys;
2500
- exports2.getReferenceFrom = getReferenceFrom;
2501
- exports2.getRelationFrom = getRelationFrom;
2502
- exports2.getSubcollections = getSubcollections;
2503
- exports2.getTableName = getTableName;
2504
- exports2.getTableVarName = getTableVarName;
2505
- exports2.isHidden = isHidden;
2506
- exports2.isPropertyBuilder = isPropertyBuilder;
2507
- exports2.isReadOnly = isReadOnly;
2508
- exports2.normalizeToEntityRelation = normalizeToEntityRelation;
2509
- exports2.or = or;
2510
- exports2.registerConditionOperations = registerConditionOperations;
2511
- exports2.removeInitialAndTrailingSlashes = removeInitialAndTrailingSlashes;
2512
- exports2.removeInitialSlash = removeInitialSlash;
2513
- exports2.removeTrailingSlash = removeTrailingSlash;
2514
- exports2.resolveArrayProperties = resolveArrayProperties;
2515
- exports2.resolveCollectionPathIds = resolveCollectionPathIds;
2516
- exports2.resolveCollectionRelations = resolveCollectionRelations;
2517
- exports2.resolveDefaultSelectedView = resolveDefaultSelectedView;
2518
- exports2.resolveEnumValues = resolveEnumValues;
2519
- exports2.resolveProperties = resolveProperties;
2520
- exports2.resolveProperty = resolveProperty;
2521
- exports2.resolvePropertyEnum = resolvePropertyEnum;
2522
- exports2.resolvePropertyRelation = resolvePropertyRelation;
2523
- exports2.resolveRelationProperty = resolveRelationProperty;
2524
- exports2.resolveStorageFilenameString = resolveStorageFilenameString;
2525
- exports2.resolveStoragePathString = resolveStoragePathString;
2526
- exports2.sanitizeData = sanitizeData;
2527
- exports2.sanitizeRelation = sanitizeRelation;
2528
- exports2.segmentsToStrippedPath = segmentsToStrippedPath;
2529
- exports2.sortProperties = sortProperties;
2530
- exports2.stripCollectionPath = stripCollectionPath;
2531
- exports2.traverseValueProperty = traverseValueProperty;
2532
- exports2.traverseValuesProperties = traverseValuesProperties;
2533
- exports2.updateDateAutoValues = updateDateAutoValues;
2534
- Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
2
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("@rebasepro/types"), require("@rebasepro/utils"), require("json-logic-js"), require("fast-equals")) : typeof define === "function" && define.amd ? define([
3
+ "exports",
4
+ "@rebasepro/types",
5
+ "@rebasepro/utils",
6
+ "json-logic-js",
7
+ "fast-equals"
8
+ ], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global["Rebase Util"] = {}, global._rebasepro_types, global._rebasepro_utils, global.jsonLogic, global.fastEquals));
9
+ })(this, function(exports, _rebasepro_types, _rebasepro_utils, json_logic_js, fast_equals) {
10
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
11
+ //#region \0rolldown/runtime.js
12
+ var __create = Object.create;
13
+ var __defProp = Object.defineProperty;
14
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
15
+ var __getOwnPropNames = Object.getOwnPropertyNames;
16
+ var __getProtoOf = Object.getPrototypeOf;
17
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
20
+ key = keys[i];
21
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
22
+ get: ((k) => from[k]).bind(null, key),
23
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
24
+ });
25
+ }
26
+ return to;
27
+ };
28
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
29
+ value: mod,
30
+ enumerable: true
31
+ }) : target, mod));
32
+ //#endregion
33
+ json_logic_js = __toESM(json_logic_js, 1);
34
+ //#region src/util/common.ts
35
+ var DEFAULT_ONE_OF_TYPE = "type";
36
+ var DEFAULT_ONE_OF_VALUE = "value";
37
+ //#endregion
38
+ //#region src/util/entities.ts
39
+ function isReadOnly(property) {
40
+ if (property.ui?.readOnly) return true;
41
+ if (property.type === "date") {
42
+ if (property.autoValue) return true;
43
+ }
44
+ if (property.type === "reference") return !property.path && !("Field" in (property.ui || {}) && property.ui?.Field);
45
+ return false;
46
+ }
47
+ function isHidden(property) {
48
+ return typeof property.ui?.disabled === "object" && Boolean(property.ui?.disabled.hidden);
49
+ }
50
+ function isPropertyBuilder(property) {
51
+ return typeof property?.dynamicProps === "function";
52
+ }
53
+ function getDefaultValuesFor(properties) {
54
+ if (!properties) return {};
55
+ return Object.entries(properties).map(([key, property]) => {
56
+ if (!property) return {};
57
+ const value = getDefaultValueFor(property);
58
+ return value === void 0 ? {} : { [key]: value };
59
+ }).reduce((a, b) => ({
60
+ ...a,
61
+ ...b
62
+ }), {});
63
+ }
64
+ function getDefaultValueFor(property) {
65
+ if (!property) return void 0;
66
+ if (isPropertyBuilder(property)) return void 0;
67
+ if (property.defaultValue || property.defaultValue === null) return property.defaultValue;
68
+ else if (property.type === "map" && property.properties) {
69
+ const defaultValuesFor = getDefaultValuesFor(property.properties);
70
+ if (Object.keys(defaultValuesFor).length === 0) return void 0;
71
+ return defaultValuesFor;
72
+ } else return getDefaultValueFortype(property.type);
73
+ }
74
+ function getDefaultValueFortype(type) {
75
+ if (type === "string") return null;
76
+ else if (type === "number") return null;
77
+ else if (type === "boolean") return false;
78
+ else if (type === "date") return null;
79
+ else if (type === "array") return [];
80
+ else if (type === "map") return {};
81
+ else if (type === "vector") return null;
82
+ else if (type === "binary") return null;
83
+ else return null;
84
+ }
85
+ /**
86
+ * Update the automatic values in an entity before save
87
+ * @group Driver
88
+ */
89
+ function updateDateAutoValues({ inputValues, properties, status, timestampNowValue }) {
90
+ return traverseValuesProperties(inputValues, properties, (inputValue, property) => {
91
+ if (property.type === "date") if (status === "existing" && property.autoValue === "on_update") return timestampNowValue;
92
+ else if ((status === "new" || status === "copy") && (property.autoValue === "on_update" || property.autoValue === "on_create")) return timestampNowValue;
93
+ else return inputValue;
94
+ else return inputValue;
95
+ }) ?? {};
96
+ }
97
+ /**
98
+ * Add missing required fields, expected in the collection, to the values of an entity
99
+ * @param values
100
+ * @param properties
101
+ * @group Driver
102
+ */
103
+ function sanitizeData(values, properties) {
104
+ const result = values;
105
+ Object.entries(properties).forEach(([key, property]) => {
106
+ if (values && values[key] !== void 0) result[key] = values[key];
107
+ else if (property.validation?.required) result[key] = null;
108
+ });
109
+ return result;
110
+ }
111
+ function getReferenceFrom(entity) {
112
+ if (typeof entity.id !== "string") throw new Error("Only string IDs are supported in references");
113
+ return new _rebasepro_types.EntityReference({
114
+ id: entity.id,
115
+ path: entity.path,
116
+ driver: entity.driver,
117
+ databaseId: entity.databaseId
118
+ });
119
+ }
120
+ function getRelationFrom(entity) {
121
+ return new _rebasepro_types.EntityRelation(entity.id, entity.path, entity);
122
+ }
123
+ /**
124
+ * Normalize a value into a proper EntityRelation instance.
125
+ * Handles EntityRelation class instances, and plain objects
126
+ * with `__type === "relation"` or an `isEntityRelation()` method.
127
+ *
128
+ * When `propertyType` is `"relation"`, also accepts plain objects that
129
+ * have `id` and `path` fields — these are relation-shaped objects from
130
+ * edge cases in the data pipeline (REST fallback, stale cache, custom data source).
131
+ *
132
+ * Returns null if the value cannot be coerced.
133
+ */
134
+ function normalizeToEntityRelation(value, propertyType) {
135
+ if (value instanceof _rebasepro_types.EntityRelation) return value;
136
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
137
+ const obj = value;
138
+ if (!(obj.__type === "relation" || obj.__type === "reference" || typeof obj.isEntityRelation === "function" && obj.isEntityRelation() || typeof obj.isEntityReference === "function" && obj.isEntityReference() || propertyType === "relation" && typeof obj.id !== "undefined" && typeof obj.path === "string")) return null;
139
+ return new _rebasepro_types.EntityRelation(obj.id, obj.path, obj.data);
140
+ }
141
+ function traverseValuesProperties(inputValues, properties, operation) {
142
+ const safeInputValues = inputValues ?? {};
143
+ const result = (0, _rebasepro_utils.mergeDeep)(safeInputValues, Object.entries(properties).map(([key, property]) => {
144
+ const updatedValue = traverseValueProperty(safeInputValues && safeInputValues[key], property, operation);
145
+ if (updatedValue === null) return null;
146
+ if (updatedValue === void 0) return void 0;
147
+ return { [key]: updatedValue };
148
+ }).reduce((a, b) => ({
149
+ ...a,
150
+ ...b
151
+ }), {}));
152
+ if (!result || Object.keys(result).length === 0) return void 0;
153
+ return result;
154
+ }
155
+ function traverseValueProperty(inputValue, property, operation) {
156
+ let value;
157
+ if (property.type === "map" && property.properties) value = traverseValuesProperties(inputValue, property.properties, operation);
158
+ else if (property.type === "array") {
159
+ const of = property.of;
160
+ if (of && Array.isArray(inputValue) && !Array.isArray(of)) value = inputValue.map((e) => traverseValueProperty(e, of, operation));
161
+ else if (of && Array.isArray(inputValue) && Array.isArray(of)) value = inputValue.map((e, i) => {
162
+ if (i < of.length) return traverseValueProperty(e, of[i], operation);
163
+ return null;
164
+ }).filter(Boolean);
165
+ else if (property.oneOf && Array.isArray(inputValue)) {
166
+ const typeField = property.oneOf?.typeField ?? "type";
167
+ const valueField = property.oneOf?.valueField ?? "value";
168
+ value = inputValue.map((e) => {
169
+ if (e === null) return null;
170
+ if (typeof e !== "object") return e;
171
+ const rec = e;
172
+ const type = rec[typeField];
173
+ const childProperty = property.oneOf?.properties[type];
174
+ if (!type || !childProperty) return e;
175
+ return {
176
+ [typeField]: type,
177
+ [valueField]: traverseValueProperty(rec[valueField], childProperty, operation)
178
+ };
179
+ });
180
+ } else value = inputValue;
181
+ } else value = operation(inputValue, property);
182
+ return value;
183
+ }
184
+ /**
185
+ * Create a lightweight relation stub for CMS views.
186
+ * Replaces inline `{ id, path, __type: "relation" }` object literals.
187
+ */
188
+ function createRelationRef(id, path) {
189
+ return {
190
+ id,
191
+ path,
192
+ __type: "relation"
193
+ };
194
+ }
195
+ /**
196
+ * Create a hydrated relation reference that includes the full entity data.
197
+ * Used when entity data has been pre-fetched (e.g., via batch loading or JOINs).
198
+ */
199
+ function createRelationRefWithData(id, path, data) {
200
+ return {
201
+ id,
202
+ path,
203
+ __type: "relation",
204
+ data
205
+ };
206
+ }
207
+ //#endregion
208
+ //#region src/util/collections.ts
209
+ function sortProperties(properties, propertiesOrder) {
210
+ try {
211
+ const propertiesKeys = Object.keys(properties);
212
+ if (!propertiesOrder || propertiesOrder.length === 0) return propertiesKeys.map((key) => {
213
+ const property = properties[key];
214
+ if (!isPropertyBuilder(property) && property?.type === "map" && property.properties) return { [key]: {
215
+ ...property,
216
+ properties: sortProperties(property.properties, property.propertiesOrder)
217
+ } };
218
+ else return { [key]: property };
219
+ }).reduce((a, b) => ({
220
+ ...a,
221
+ ...b
222
+ }), {});
223
+ const validOrderKeys = propertiesOrder.filter((key) => {
224
+ return !key.includes(".") && properties[key];
225
+ });
226
+ const processedKeys = new Set(validOrderKeys);
227
+ const orderedResult = validOrderKeys.map((key) => {
228
+ const property = properties[key];
229
+ if (!isPropertyBuilder(property) && property?.type === "map" && property.properties) return { [key]: {
230
+ ...property,
231
+ properties: sortProperties(property.properties, property.propertiesOrder)
232
+ } };
233
+ else return { [key]: property };
234
+ }).reduce((a, b) => ({
235
+ ...a,
236
+ ...b
237
+ }), {});
238
+ const missingProperties = propertiesKeys.filter((key) => !processedKeys.has(key)).map((key) => {
239
+ const property = properties[key];
240
+ if (!isPropertyBuilder(property) && property?.type === "map" && property.properties) return { [key]: {
241
+ ...property,
242
+ properties: sortProperties(property.properties, property.propertiesOrder)
243
+ } };
244
+ else return { [key]: property };
245
+ }).reduce((a, b) => ({
246
+ ...a,
247
+ ...b
248
+ }), {});
249
+ return {
250
+ ...orderedResult,
251
+ ...missingProperties
252
+ };
253
+ } catch (e) {
254
+ console.error("Error sorting properties", e);
255
+ return properties;
256
+ }
257
+ }
258
+ function resolveDefaultSelectedView(defaultSelectedView, params) {
259
+ if (!defaultSelectedView) return;
260
+ else if (typeof defaultSelectedView === "string") return defaultSelectedView;
261
+ else return defaultSelectedView(params);
262
+ }
263
+ function getLocalChangesBackup(collection) {
264
+ if (!collection.localChangesBackup) return "manual_apply";
265
+ return collection.localChangesBackup;
266
+ }
267
+ /**
268
+ * Returns the primary keys for an entity collection by inspecting the properties
269
+ * and finding any properties with `isId`.
270
+ * Fallbacks to `["id"]` if no properties are marked as `isId: true`.
271
+ * @param collection
272
+ */
273
+ function getPrimaryKeys(collection) {
274
+ const properties = collection.properties;
275
+ if (!properties) return ["id"];
276
+ const ids = Object.entries(properties).filter(([key, prop]) => typeof prop === "object" && prop !== null && "isId" in prop && Boolean(prop.isId)).map(([key]) => key);
277
+ if (ids.length > 0) return ids;
278
+ return ["id"];
279
+ }
280
+ //#endregion
281
+ //#region src/util/enums.ts
282
+ function enumToObjectEntries(enumValues) {
283
+ if (Array.isArray(enumValues)) return enumValues;
284
+ else return Object.entries(enumValues).map(([id, value]) => {
285
+ if (typeof value === "string") return {
286
+ id,
287
+ label: value
288
+ };
289
+ else return {
290
+ ...value,
291
+ id
292
+ };
293
+ });
294
+ }
295
+ function getLabelOrConfigFrom(enumValues, key) {
296
+ if (key === null || key === void 0) return void 0;
297
+ return enumValues.find((entry) => String(entry.id) === String(key));
298
+ }
299
+ //#endregion
300
+ //#region src/util/paths.ts
301
+ var COLLECTION_PATH_SEPARATOR = "::";
302
+ /**
303
+ * Remove the entity ids from a given path
304
+ * `products/B44RG6APH/locales` => `products::locales`
305
+ * @param path
306
+ */
307
+ function stripCollectionPath(path) {
308
+ return segmentsToStrippedPath(fullPathToCollectionSegments(path));
309
+ }
310
+ function segmentsToStrippedPath(paths) {
311
+ if (paths.length === 1) return paths[0];
312
+ return paths.reduce((a, b) => `${a}::${b}`);
313
+ }
314
+ /**
315
+ * Extract the collection path routes
316
+ * `products/B44RG6APH/locales` => [`products`, `locales`]
317
+ * @param path
318
+ */
319
+ function fullPathToCollectionSegments(path) {
320
+ return path.split("/").filter((e, i) => i % 2 === 0);
321
+ }
322
+ //#endregion
323
+ //#region src/util/relations.ts
324
+ function sanitizeRelation(relation, sourceCollection, resolveCollection) {
325
+ if (!relation.target) throw new Error("Relation is missing a `target` collection.");
326
+ const rawTarget = relation.target;
327
+ let targetCollection;
328
+ if (typeof rawTarget === "string") {
329
+ if (resolveCollection) targetCollection = resolveCollection(rawTarget);
330
+ if (!targetCollection) targetCollection = {
331
+ slug: rawTarget,
332
+ name: rawTarget
333
+ };
334
+ } else if (typeof rawTarget === "function") {
335
+ const evaluated = rawTarget();
336
+ if (typeof evaluated === "string") {
337
+ if (resolveCollection) targetCollection = resolveCollection(evaluated);
338
+ if (!targetCollection) targetCollection = {
339
+ slug: evaluated,
340
+ name: evaluated
341
+ };
342
+ } else targetCollection = evaluated;
343
+ } else if (rawTarget && typeof rawTarget === "object") targetCollection = rawTarget;
344
+ if (!targetCollection) throw new Error("Relation is missing a valid `target` collection.");
345
+ const newRelation = { ...relation };
346
+ newRelation.target = () => {
347
+ if (typeof rawTarget === "string") return resolveCollection && resolveCollection(rawTarget) || targetCollection;
348
+ else if (typeof rawTarget === "function") {
349
+ const evaluated = rawTarget();
350
+ if (typeof evaluated === "string") return resolveCollection && resolveCollection(evaluated) || targetCollection;
351
+ return evaluated;
352
+ }
353
+ return targetCollection;
354
+ };
355
+ if (!newRelation.relationName) newRelation.relationName = (0, _rebasepro_utils.toSnakeCase)(targetCollection.slug);
356
+ if (!newRelation.direction) if (newRelation.foreignKeyOnTarget) newRelation.direction = "inverse";
357
+ else if (newRelation.through) newRelation.direction = "owning";
358
+ else if (newRelation.cardinality === "many") newRelation.direction = "inverse";
359
+ else newRelation.direction = "owning";
360
+ if (!newRelation.joinPath) {
361
+ const sourceName = (0, _rebasepro_utils.toSnakeCase)(sourceCollection.slug ?? sourceCollection.name);
362
+ if (newRelation.cardinality === "one" && newRelation.direction === "owning") {
363
+ if (!newRelation.localKey) newRelation.localKey = (0, _rebasepro_utils.generateForeignKeyName)(newRelation.relationName);
364
+ } else if (newRelation.cardinality === "one" && newRelation.direction === "inverse") {
365
+ if (!newRelation.foreignKeyOnTarget) {
366
+ let foundForeignKey = false;
367
+ try {
368
+ const targetRelations = (0, _rebasepro_types.getDataSourceCapabilities)(targetCollection.driver).supportsRelations ? targetCollection.relations || [] : [];
369
+ for (const targetRel of targetRelations) if (targetRel.direction === "owning" && targetRel.cardinality === "one" && targetRel.localKey) try {
370
+ if (targetRel.target().slug === sourceCollection.slug) {
371
+ newRelation.foreignKeyOnTarget = targetRel.localKey;
372
+ foundForeignKey = true;
373
+ break;
374
+ }
375
+ } catch (e) {
376
+ continue;
377
+ }
378
+ } catch (e) {}
379
+ if (!foundForeignKey) newRelation.foreignKeyOnTarget = (0, _rebasepro_utils.generateForeignKeyName)(newRelation.inverseRelationName ? (0, _rebasepro_utils.toSnakeCase)(newRelation.inverseRelationName) : sourceName);
380
+ }
381
+ } else if (newRelation.cardinality === "many" && newRelation.direction === "inverse") {
382
+ let isManyToManyInverse = false;
383
+ if (newRelation.inverseRelationName && !newRelation.foreignKeyOnTarget) try {
384
+ const targetRelations = (0, _rebasepro_types.getDataSourceCapabilities)(targetCollection.driver).supportsRelations ? targetCollection.relations || [] : [];
385
+ for (const targetRel of targetRelations) if (targetRel.cardinality === "many" && (targetRel.direction === "owning" || !targetRel.direction) && targetRel.relationName === newRelation.inverseRelationName) {
386
+ isManyToManyInverse = true;
387
+ break;
388
+ }
389
+ if (!isManyToManyInverse && targetCollection.properties) for (const [propKey, prop] of Object.entries(targetCollection.properties)) {
390
+ if (prop.type !== "relation") continue;
391
+ const relProp = prop;
392
+ if ((relProp.relationName || propKey) === newRelation.inverseRelationName && relProp.cardinality === "many" && (relProp.direction === "owning" || !relProp.direction)) {
393
+ isManyToManyInverse = true;
394
+ break;
395
+ }
396
+ }
397
+ } catch (e) {}
398
+ if (!isManyToManyInverse && !newRelation.foreignKeyOnTarget) newRelation.foreignKeyOnTarget = (0, _rebasepro_utils.generateForeignKeyName)(sourceName);
399
+ } else if (newRelation.cardinality === "many" && newRelation.direction === "owning") {
400
+ const sourceTableName = getTableName(sourceCollection);
401
+ const targetTableName = getTableName(targetCollection);
402
+ newRelation.through = {
403
+ table: newRelation.through?.table ?? [sourceTableName, targetTableName].sort().join("_"),
404
+ sourceColumn: newRelation.through?.sourceColumn ?? (0, _rebasepro_utils.generateForeignKeyName)(sourceName),
405
+ targetColumn: newRelation.through?.targetColumn ?? (0, _rebasepro_utils.generateForeignKeyName)(newRelation.relationName)
406
+ };
407
+ }
408
+ }
409
+ if (newRelation.cardinality === "one" && newRelation.direction === "owning" && !newRelation.localKey && !newRelation.joinPath) throw new Error(`Configuration Error in relation from '${sourceCollection.name}': An 'owning' one-to-one relation requires a 'localKey'. Check the relation config for '${newRelation.relationName}'`);
410
+ if (newRelation.cardinality === "one" && newRelation.direction === "inverse" && !newRelation.foreignKeyOnTarget && !newRelation.joinPath) 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}'`);
411
+ if (newRelation.cardinality === "many" && newRelation.direction === "inverse" && !newRelation.foreignKeyOnTarget && !newRelation.joinPath && !newRelation.inverseRelationName) 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}'`);
412
+ return newRelation;
413
+ }
414
+ /** WeakMap cache — same collection instance always yields the same relation map. */
415
+ var _resolvedRelationsCache = /* @__PURE__ */ new WeakMap();
416
+ function resolveCollectionRelations(collection) {
417
+ const cached = _resolvedRelationsCache.get(collection);
418
+ if (cached) return cached;
419
+ if (!(0, _rebasepro_types.getDataSourceCapabilities)(collection.driver).supportsRelations) return {};
420
+ const relCollection = collection;
421
+ const relations = {};
422
+ const registeredRelationNames = /* @__PURE__ */ new Set();
423
+ if (relCollection.relations) relCollection.relations.forEach((relation) => {
424
+ try {
425
+ const normalizedRelation = sanitizeRelation(relation, collection);
426
+ const relationKey = normalizedRelation.relationName;
427
+ if (relationKey) {
428
+ relations[relationKey] = normalizedRelation;
429
+ registeredRelationNames.add(relationKey);
430
+ }
431
+ } catch (e) {}
432
+ });
433
+ if (collection.properties) Object.entries(collection.properties).forEach(([propKey, prop]) => {
434
+ const relation = resolvePropertyRelation({
435
+ propertyKey: propKey,
436
+ property: prop,
437
+ sourceCollection: collection
438
+ });
439
+ if (relation) {
440
+ if (relations[propKey]) return;
441
+ if (!relation.relationName) relation.relationName = propKey;
442
+ const normalizedRelation = sanitizeRelation(relation, collection);
443
+ relations[propKey] = normalizedRelation;
444
+ registeredRelationNames.add(normalizedRelation.relationName ?? propKey);
445
+ }
446
+ });
447
+ _resolvedRelationsCache.set(collection, relations);
448
+ return relations;
449
+ }
450
+ function resolvePropertyRelation({ propertyKey, property, sourceCollection }) {
451
+ if (property.type !== "relation") return void 0;
452
+ const relProp = property;
453
+ if (relProp.target) return {
454
+ relationName: relProp.relationName || propertyKey,
455
+ target: relProp.target,
456
+ cardinality: relProp.cardinality || "one",
457
+ direction: relProp.direction || "owning",
458
+ inverseRelationName: relProp.inverseRelationName,
459
+ localKey: relProp.localKey,
460
+ foreignKeyOnTarget: relProp.foreignKeyOnTarget,
461
+ through: relProp.through,
462
+ joinPath: relProp.joinPath,
463
+ onUpdate: relProp.onUpdate,
464
+ onDelete: relProp.onDelete,
465
+ overrides: relProp.overrides
466
+ };
467
+ console.warn(`Unrecognized or missing relation target for property '${propertyKey}' in collection '${sourceCollection.slug}'`);
468
+ }
469
+ function getTableName(collection) {
470
+ if ((0, _rebasepro_types.getDataSourceCapabilities)(collection.driver).supportsRelations) return collection.table ?? (0, _rebasepro_utils.toSnakeCase)(collection.slug) ?? (0, _rebasepro_utils.toSnakeCase)(collection.name);
471
+ return (0, _rebasepro_utils.toSnakeCase)(collection.slug) ?? (0, _rebasepro_utils.toSnakeCase)(collection.name);
472
+ }
473
+ function getTableVarName(tableName) {
474
+ return tableName.replace(/_([a-z])/g, (_, char) => char.toUpperCase());
475
+ }
476
+ function getEnumVarName(tableName, propName) {
477
+ return `${getTableVarName(tableName)}${propName.charAt(0).toUpperCase() + propName.slice(1)}`;
478
+ }
479
+ function getColumnName(fullColumn) {
480
+ return fullColumn.includes(".") ? fullColumn.split(".").pop() : fullColumn;
481
+ }
482
+ /**
483
+ * Look up a relation by key with forgiving normalization.
484
+ *
485
+ * `resolveCollectionRelations` stores each relation under a single canonical
486
+ * key (no aliases). This helper tries the given key as-is, then falls back to
487
+ * slug form (underscores → hyphens) and snake_case form (hyphens → underscores)
488
+ * so that callers that receive a key from external input (URL path segments,
489
+ * user-provided config, etc.) can still find the right entry.
490
+ */
491
+ function findRelation(resolvedRelations, key) {
492
+ if (resolvedRelations[key]) return resolvedRelations[key];
493
+ const slugKey = key.replace(/_/g, "-");
494
+ if (slugKey !== key && resolvedRelations[slugKey]) return resolvedRelations[slugKey];
495
+ const snakeKey = key.replace(/-/g, "_");
496
+ if (snakeKey !== key && resolvedRelations[snakeKey]) return resolvedRelations[snakeKey];
497
+ }
498
+ //#endregion
499
+ //#region src/util/resolutions.ts
500
+ function resolveProperty(props) {
501
+ const { property, ignoreMissingFields = false, ...rest } = props;
502
+ let resultProperty;
503
+ if (isPropertyBuilder(property)) {
504
+ const path = rest.path;
505
+ if (!path) resultProperty = property;
506
+ else {
507
+ const usedPropertyValue = rest.propertyKey ? (0, _rebasepro_utils.getIn)(rest.values, rest.propertyKey) : void 0;
508
+ const dynamicProps = property.dynamicProps?.({
509
+ ...rest,
510
+ path,
511
+ propertyValue: usedPropertyValue,
512
+ values: rest.values ?? {},
513
+ previousValues: rest.previousValues ?? rest.values ?? {}
514
+ });
515
+ resultProperty = (0, _rebasepro_utils.mergeDeep)(property, dynamicProps ?? {});
516
+ }
517
+ } else resultProperty = property;
518
+ if (resultProperty?.dynamicProps && rest.path) {
519
+ const path = rest.path;
520
+ const usedPropertyValue = rest.propertyKey ? (0, _rebasepro_utils.getIn)(rest.values, rest.propertyKey) : void 0;
521
+ const dynamicPropsResult = resultProperty.dynamicProps({
522
+ ...rest,
523
+ path,
524
+ propertyValue: usedPropertyValue,
525
+ values: rest.values ?? {},
526
+ previousValues: rest.previousValues ?? rest.values ?? {}
527
+ });
528
+ if (dynamicPropsResult) resultProperty = (0, _rebasepro_utils.mergeDeep)(resultProperty, dynamicPropsResult);
529
+ }
530
+ let resolvedProperty;
531
+ if (resultProperty?.type === "map" && resultProperty.properties) {
532
+ const properties = resolveProperties({
533
+ ignoreMissingFields,
534
+ ...rest,
535
+ properties: resultProperty.properties
536
+ });
537
+ resolvedProperty = {
538
+ ...resultProperty,
539
+ properties
540
+ };
541
+ } else if (resultProperty?.type === "array") resolvedProperty = resultProperty;
542
+ else if ((resultProperty?.type === "string" || resultProperty?.type === "number") && resultProperty.enum) resolvedProperty = resolvePropertyEnum(resultProperty);
543
+ else resolvedProperty = resultProperty;
544
+ if (resolvedProperty?.propertyConfig && !(0, _rebasepro_utils.isDefaultFieldConfigId)(resolvedProperty.propertyConfig)) {
545
+ const cmsFields = rest.propertyConfigs;
546
+ if (!cmsFields && !ignoreMissingFields) throw Error(`Trying to resolve a property with key '${resolvedProperty.propertyConfig}' that inherits from a custom property config but no custom property configs were provided. Use the property 'propertyConfigs' in your app config to provide them`);
547
+ const customField = cmsFields?.[resolvedProperty.propertyConfig];
548
+ if (!customField) {
549
+ console.warn(`Trying to resolve a property with key '${resolvedProperty.propertyConfig}' that inherits from a custom property config but no custom property config with that key was found. Check the 'propertyConfigs' in your app config`);
550
+ return resolvedProperty;
551
+ }
552
+ if (customField.property) {
553
+ const restConfigProperty = { ...customField.property };
554
+ delete restConfigProperty.propertyConfig;
555
+ const customFieldProperty = resolveProperty({
556
+ property: {
557
+ name: "",
558
+ ...restConfigProperty
559
+ },
560
+ ignoreMissingFields,
561
+ ...rest
562
+ });
563
+ if (customFieldProperty) resolvedProperty = (0, _rebasepro_utils.mergeDeep)(customFieldProperty, resolvedProperty);
564
+ }
565
+ }
566
+ return resolvedProperty;
567
+ }
568
+ function resolveRelationProperty(property, relations, propertyKey) {
569
+ if (property.relation) return property;
570
+ const name = property.relationName || propertyKey;
571
+ const relation = name ? relations.find((rel) => rel.relationName === name) : void 0;
572
+ if (!relation) throw Error(`Relation ${name ?? "(unnamed)"} not found`);
573
+ return {
574
+ ...property,
575
+ relation
576
+ };
577
+ }
578
+ /**
579
+ * Resolve enum aliases for a string or number property
580
+ * @param property
581
+ */
582
+ function resolvePropertyEnum(property) {
583
+ if (typeof property.enum === "object") return {
584
+ ...property,
585
+ enum: enumToObjectEntries(property.enum)?.filter((value) => value && (value.id || value.id === 0) && value.label) ?? []
586
+ };
587
+ return property;
588
+ }
589
+ /**
590
+ * Resolve enums and arrays for properties
591
+ * @param properties
592
+ * @param value
593
+ */
594
+ function resolveProperties({ propertyKey, properties, ignoreMissingFields, ...props }) {
595
+ return Object.entries(properties).map(([key, property]) => {
596
+ const childResolvedProperty = resolveProperty({
597
+ propertyKey: propertyKey ? `${propertyKey}.${key}` : void 0,
598
+ property,
599
+ ignoreMissingFields,
600
+ ...props
601
+ });
602
+ if (!childResolvedProperty) return {};
603
+ return { [key]: childResolvedProperty };
604
+ }).filter((a) => a !== null).reduce((a, b) => ({
605
+ ...a,
606
+ ...b
607
+ }), {});
608
+ }
609
+ function resolveArrayProperties({ propertyKey, property, ignoreMissingFields = false, ...props }) {
610
+ const propertyValue = propertyKey ? (0, _rebasepro_utils.getIn)(props.values, propertyKey) : void 0;
611
+ if (property.of) if (Array.isArray(property.of)) return property.of.map((p, index) => {
612
+ return resolveProperty({
613
+ propertyKey: `${propertyKey}.${index}`,
614
+ property: p,
615
+ ignoreMissingFields,
616
+ ...props,
617
+ index
618
+ });
619
+ });
620
+ else {
621
+ const of = property.of;
622
+ const resolvedProperties = getArrayResolvedProperties({
623
+ propertyValue,
624
+ propertyKey,
625
+ property,
626
+ ignoreMissingFields,
627
+ ...props
628
+ });
629
+ const { values, previousValues, ...rest } = props;
630
+ if (!resolveProperty({
631
+ property: of,
632
+ ignoreMissingFields,
633
+ ...rest
634
+ }) && !ignoreMissingFields) throw Error("When using a property builder as the 'of' prop of an ArrayProperty, you must return a valid child property");
635
+ return resolvedProperties;
636
+ }
637
+ else if (property.oneOf) {
638
+ const typeField = property.oneOf?.typeField ?? "type";
639
+ return Array.isArray(propertyValue) ? propertyValue.map((v, index) => {
640
+ const type = v && v[typeField];
641
+ const childProperty = property.oneOf?.properties[type];
642
+ if (!type || !childProperty) return null;
643
+ return resolveProperty({
644
+ propertyKey: `${propertyKey}.${index}`,
645
+ property: childProperty,
646
+ ignoreMissingFields,
647
+ ...props
648
+ });
649
+ }).filter((e) => Boolean(e)) : [];
650
+ } else if (!("Field" in (property.ui || {}) && property.ui?.Field)) throw Error(`The array property (${propertyKey}) needs to declare an 'of' or a 'oneOf' property, or provide a custom \`Field\` component`);
651
+ else return [];
652
+ }
653
+ function getArrayResolvedProperties({ propertyKey, propertyValue, property, ...props }) {
654
+ const of = property.of;
655
+ if (!of) throw Error(`Trying to resolve an array property (${propertyKey}) without providing an 'of' property`);
656
+ return Array.isArray(propertyValue) ? propertyValue.map((v, index) => {
657
+ return resolveProperty({
658
+ propertyKey: `${propertyKey}.${index}`,
659
+ property: Array.isArray(of) ? of[index] : of,
660
+ ...props,
661
+ index
662
+ });
663
+ }).filter((e) => Boolean(e)) : [];
664
+ }
665
+ function resolveEnumValues(input) {
666
+ if (typeof input === "object") return Object.entries(input).map(([id, value]) => typeof value === "string" ? {
667
+ id,
668
+ label: value
669
+ } : value);
670
+ else if (Array.isArray(input)) return input;
671
+ else return;
672
+ }
673
+ function getSubcollections(collection) {
674
+ if (collection.childCollections) return collection.childCollections() ?? [];
675
+ if ((0, _rebasepro_types.getDataSourceCapabilities)(collection.driver).supportsSubcollections && collection.subcollections) return collection.subcollections() ?? [];
676
+ if ((0, _rebasepro_types.getDataSourceCapabilities)(collection.driver).supportsRelations) {
677
+ const resolvedRelations = resolveCollectionRelations(collection);
678
+ return Object.values(resolvedRelations).filter((r) => r.cardinality === "many").map((r) => {
679
+ const target = r.target();
680
+ if (!target) return void 0;
681
+ const relationKey = r.relationName || target.slug;
682
+ let customName;
683
+ if (collection.properties) {
684
+ const prop = Object.entries(collection.properties).find(([_, p]) => p.type === "relation" && p.relationName === relationKey);
685
+ if (prop && prop[1].name) customName = prop[1].name;
686
+ }
687
+ const baseOverrides = { slug: relationKey };
688
+ if (customName) {
689
+ baseOverrides.name = customName;
690
+ baseOverrides.singularName = customName;
691
+ }
692
+ const targetWithOverrides = {
693
+ ...target,
694
+ ...baseOverrides
695
+ };
696
+ return r.overrides ? (0, _rebasepro_utils.mergeDeep)(targetWithOverrides, r.overrides) : targetWithOverrides;
697
+ }).filter((c) => Boolean(c));
698
+ }
699
+ return [];
700
+ }
701
+ //#endregion
702
+ //#region src/util/permissions.ts
703
+ function evaluateAST(sqlString, auth, entity) {
704
+ if (!entity) return true;
705
+ let cleanedSQL = sqlString.trim();
706
+ while (cleanedSQL.startsWith("(") && cleanedSQL.endsWith(")")) {
707
+ let openCount = 0;
708
+ let isEnclosing = true;
709
+ for (let i = 0; i < cleanedSQL.length - 1; i++) {
710
+ if (cleanedSQL[i] === "(") openCount++;
711
+ else if (cleanedSQL[i] === ")") openCount--;
712
+ if (openCount === 0) {
713
+ isEnclosing = false;
714
+ break;
715
+ }
716
+ }
717
+ if (isEnclosing) cleanedSQL = cleanedSQL.substring(1, cleanedSQL.length - 1).trim();
718
+ else break;
719
+ }
720
+ const splitByTopLevel = (str, delimiter) => {
721
+ const parts = [];
722
+ let current = "";
723
+ let openCount = 0;
724
+ let i = 0;
725
+ while (i < str.length) {
726
+ if (str[i] === "(") openCount++;
727
+ else if (str[i] === ")") openCount--;
728
+ if (openCount === 0 && str.substring(i).toUpperCase().startsWith(delimiter)) {
729
+ parts.push(current);
730
+ current = "";
731
+ i += delimiter.length;
732
+ } else {
733
+ current += str[i];
734
+ i++;
735
+ }
736
+ }
737
+ parts.push(current);
738
+ return parts;
739
+ };
740
+ const orParts = splitByTopLevel(cleanedSQL, " OR ");
741
+ if (orParts.length > 1) return orParts.some((part) => evaluateAST(part, auth, entity));
742
+ const andParts = splitByTopLevel(cleanedSQL, " AND ");
743
+ if (andParts.length > 1) return andParts.every((part) => evaluateAST(part, auth, entity));
744
+ const upperSQL = cleanedSQL.toUpperCase();
745
+ if (upperSQL.includes(" IN ") || upperSQL.includes(" EXISTS ")) return true;
746
+ const roleIntersectMatch = cleanedSQL.match(/string_to_array\s*\(\s*auth\.roles\(\)\s*,\s*','\s*\)\s*&&\s*ARRAY\[(.*?)\]/i);
747
+ if (roleIntersectMatch && roleIntersectMatch[1]) {
748
+ const requiredRoles = roleIntersectMatch[1].split(",").map((r) => r.trim().replace(/'/g, ""));
749
+ const userRoles = auth.user?.roles || [];
750
+ return requiredRoles.some((r) => userRoles.includes(r));
751
+ }
752
+ const roleContainMatch = cleanedSQL.match(/string_to_array\s*\(\s*auth\.roles\(\)\s*,\s*','\s*\)\s*@>\s*ARRAY\[(.*?)\]/i);
753
+ if (roleContainMatch && roleContainMatch[1]) {
754
+ const requiredRoles = roleContainMatch[1].split(",").map((r) => r.trim().replace(/'/g, ""));
755
+ const userRoles = auth.user?.roles || [];
756
+ return requiredRoles.every((r) => userRoles.includes(r));
757
+ }
758
+ const pattern1 = /* @__PURE__ */ new RegExp("^\\{?([a-zA-Z0-9_]+)\\}?\\s*=\\s*(?:current_setting\\s*\\(\\s*'app\\.user_id'\\s*\\)|auth\\.uid\\(\\))");
759
+ const pattern2 = /* @__PURE__ */ new RegExp("^(?:current_setting\\s*\\(\\s*'app\\.user_id'\\s*\\)|auth\\.uid\\(\\))\\s*=\\s*\\{?([a-zA-Z0-9_]+)\\}?");
760
+ const match1 = cleanedSQL.match(pattern1);
761
+ if (match1 && match1[1]) return entity.values[match1[1]] === auth.user?.uid;
762
+ const match2 = cleanedSQL.match(pattern2);
763
+ if (match2 && match2[1]) return entity.values[match2[1]] === auth.user?.uid;
764
+ const simpleEqualityMatch = cleanedSQL.match(/^\{?([\w_]+)\}?\s*(=|!=)\s*'([^']+)'$/i);
765
+ if (simpleEqualityMatch) {
766
+ const field = simpleEqualityMatch[1];
767
+ const operator = simpleEqualityMatch[2];
768
+ const value = simpleEqualityMatch[3];
769
+ const entityValue = entity.values[field];
770
+ if (operator === "=") return entityValue === value;
771
+ if (operator === "!=") return entityValue !== value;
772
+ }
773
+ return true;
774
+ }
775
+ function evaluateRule(rule, auth, entity) {
776
+ if (rule.access === "public") return true;
777
+ if (rule.ownerField) {
778
+ if (!entity) {} else if (entity.values[rule.ownerField] !== auth.user?.uid) return false;
779
+ }
780
+ if (rule.using && !evaluateAST(rule.using, auth, entity)) return false;
781
+ if (rule.withCheck && !evaluateAST(rule.withCheck, auth, entity)) return false;
782
+ return true;
783
+ }
784
+ function checkOperation(collection, authContext, entity, targetOperation) {
785
+ const securityRules = (0, _rebasepro_types.getDataSourceCapabilities)(collection.driver).supportsRLS ? collection.securityRules : void 0;
786
+ if (!securityRules || securityRules.length === 0) return true;
787
+ const applicableRules = securityRules.filter((r) => r.operation === targetOperation || r.operation === "all" || r.operations?.includes(targetOperation) || r.operations?.includes("all"));
788
+ if (applicableRules.length === 0) return false;
789
+ const userRoles = [...authContext.user?.roles ?? [], "public"];
790
+ const roleApplicableRules = applicableRules.filter((rule) => {
791
+ if (!rule.roles || rule.roles.length === 0) return true;
792
+ return rule.roles.some((r) => userRoles.includes(r));
793
+ });
794
+ if (roleApplicableRules.length === 0) return false;
795
+ let grantedByPermissive = false;
796
+ let deniedByRestrictive = false;
797
+ for (const rule of roleApplicableRules) {
798
+ const mode = rule.mode || "permissive";
799
+ const passed = evaluateRule(rule, authContext, entity);
800
+ if (mode === "restrictive" && !passed) {
801
+ deniedByRestrictive = true;
802
+ break;
803
+ }
804
+ if (mode === "permissive" && passed) grantedByPermissive = true;
805
+ }
806
+ if (deniedByRestrictive) return false;
807
+ if (roleApplicableRules.some((r) => (r.mode || "permissive") === "permissive")) return grantedByPermissive;
808
+ else return false;
809
+ }
810
+ function canReadCollection(collection, authContext) {
811
+ return checkOperation(collection, authContext, null, "select");
812
+ }
813
+ function canEditEntity(collection, authContext, path, entity) {
814
+ return checkOperation(collection, authContext, entity, "update");
815
+ }
816
+ function canCreateEntity(collection, authContext, path, entity) {
817
+ return checkOperation(collection, authContext, entity, "insert");
818
+ }
819
+ function canDeleteEntity(collection, authContext, path, entity) {
820
+ return checkOperation(collection, authContext, entity, "delete");
821
+ }
822
+ //#endregion
823
+ //#region src/util/references.ts
824
+ function getEntityImagePreviewPropertyKey(collection) {
825
+ for (const key in collection.properties) {
826
+ const property = collection.properties[key];
827
+ if (property.type === "string" && property.storage?.acceptedFiles?.includes("image/*")) return key;
828
+ }
829
+ for (const key in collection.properties) {
830
+ const property = collection.properties[key];
831
+ if (property.type === "array" && !Array.isArray(property.of) && property.of?.type === "string" && property.of.storage?.acceptedFiles?.includes("image/*")) return key;
832
+ }
833
+ for (const key in collection.properties) {
834
+ const property = collection.properties[key];
835
+ if (property.type === "string" && property.ui?.url === "image") return key;
836
+ }
837
+ for (const key in collection.properties) {
838
+ const property = collection.properties[key];
839
+ if (property.type === "array" && property.of && !Array.isArray(property.of) && property.of.type === "string" && property.of.url === "image") return key;
840
+ }
841
+ for (const key in collection.properties) {
842
+ const property = collection.properties[key];
843
+ if (property.type === "string" && property.storage && !property.storage.acceptedFiles) return key;
844
+ }
845
+ for (const key in collection.properties) {
846
+ const property = collection.properties[key];
847
+ if (property.type === "array" && !Array.isArray(property.of) && property.of?.type === "string" && property.of.storage && !property.of.storage.acceptedFiles) return key;
848
+ }
849
+ }
850
+ //#endregion
851
+ //#region src/util/navigation_utils.ts
852
+ function removeInitialAndTrailingSlashes(s) {
853
+ return removeInitialSlash(removeTrailingSlash(s));
854
+ }
855
+ function removeInitialSlash(s) {
856
+ if (s.startsWith("/")) return s.slice(1);
857
+ else return s;
858
+ }
859
+ function removeTrailingSlash(s) {
860
+ if (s.endsWith("/")) return s.slice(0, -1);
861
+ else return s;
862
+ }
863
+ function addInitialSlash(s) {
864
+ if (s.startsWith("/")) return s;
865
+ else return `/${s}`;
866
+ }
867
+ function getLastSegment(path) {
868
+ const cleanPath = removeInitialAndTrailingSlashes(path);
869
+ if (cleanPath.includes("/")) {
870
+ const segments = cleanPath.split("/");
871
+ return segments[segments.length - 1];
872
+ }
873
+ return cleanPath;
874
+ }
875
+ function resolveCollectionPathIds(path, allCollections) {
876
+ let remainingPath = removeInitialAndTrailingSlashes(path);
877
+ if (!remainingPath) return "";
878
+ let currentCollections = allCollections;
879
+ const resolvedPathParts = [];
880
+ while (remainingPath.length > 0) {
881
+ if (!currentCollections || currentCollections.length === 0) {
882
+ console.warn(`resolveCollectionPathIds: Path structure implies subcollections, but none found before segment starting with "${remainingPath}" in original path "${path}". Appending remaining original path.`);
883
+ resolvedPathParts.push(remainingPath);
884
+ remainingPath = "";
885
+ break;
886
+ }
887
+ let foundMatch = false;
888
+ const potentialMatches = currentCollections.flatMap((col) => [{
889
+ col,
890
+ match: col.slug
891
+ }]).filter((p) => p.match && remainingPath.startsWith(p.match)).sort((a, b) => b.match.length - a.match.length);
892
+ if (potentialMatches.length > 0) {
893
+ const { col: foundCollection, match: matchString } = potentialMatches[0];
894
+ resolvedPathParts.push(foundCollection.slug);
895
+ remainingPath = removeInitialSlash(remainingPath.substring(matchString.length));
896
+ if (remainingPath.length === 0) {
897
+ foundMatch = true;
898
+ break;
899
+ }
900
+ const idSeparatorIndex = remainingPath.indexOf("/");
901
+ let entityId;
902
+ if (idSeparatorIndex > -1) {
903
+ entityId = remainingPath.substring(0, idSeparatorIndex);
904
+ remainingPath = remainingPath.substring(idSeparatorIndex + 1);
905
+ } else {
906
+ entityId = remainingPath;
907
+ remainingPath = "";
908
+ 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.`);
909
+ }
910
+ resolvedPathParts.push(entityId);
911
+ currentCollections = getSubcollections(foundCollection);
912
+ foundMatch = true;
913
+ if (!currentCollections && remainingPath.length > 0) {
914
+ 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.`);
915
+ resolvedPathParts.push(remainingPath);
916
+ remainingPath = "";
917
+ break;
918
+ }
919
+ }
920
+ if (!foundMatch) {
921
+ console.warn(`resolveCollectionPathIds: Collection definition not found for segment starting with "${remainingPath}" in original path "${path}". Appending remaining original path.`);
922
+ resolvedPathParts.push(remainingPath);
923
+ remainingPath = "";
924
+ break;
925
+ }
926
+ }
927
+ return resolvedPathParts.join("/");
928
+ }
929
+ /**
930
+ * Find the corresponding view at any depth for a given path.
931
+ * Note that path or segments of the paths can be collection aliases.
932
+ * @param slugOrPath
933
+ * @param collections
934
+ */
935
+ function getCollectionBySlugWithin(slugOrPath, collections) {
936
+ const subpaths = removeInitialAndTrailingSlashes(slugOrPath).split("/");
937
+ if (subpaths.length % 2 === 0) throw Error(`getCollectionBySlug: Collection paths must have an odd number of segments: ${slugOrPath}`);
938
+ const subpathCombinations = getCollectionPathsCombinations(subpaths);
939
+ let result;
940
+ for (let i = 0; i < subpathCombinations.length; i++) {
941
+ const subpathCombination = subpathCombinations[i];
942
+ const navigationEntry = collections && collections.sort((a, b) => (a.slug ?? "").localeCompare(b.slug ?? "")).find((entry) => entry.slug === subpathCombination);
943
+ if (navigationEntry) {
944
+ if (subpathCombination === slugOrPath) result = navigationEntry;
945
+ else if (getSubcollections(navigationEntry).length > 0) {
946
+ const newPath = slugOrPath.replace(subpathCombination, "").split("/").slice(2).join("/");
947
+ if (newPath.length > 0) result = getCollectionBySlugWithin(newPath, getSubcollections(navigationEntry));
948
+ }
949
+ }
950
+ if (result) break;
951
+ }
952
+ return result;
953
+ }
954
+ /**
955
+ * Get the subcollection combinations from a path:
956
+ * "sites/es/locales" => ["sites/es/locales", "sites"]
957
+ * @param subpaths
958
+ */
959
+ function getCollectionPathsCombinations(subpaths) {
960
+ const entries = subpaths.length > 0 && subpaths.length % 2 === 0 ? subpaths.splice(0, subpaths.length - 1) : subpaths;
961
+ const length = entries.length;
962
+ const result = [];
963
+ for (let i = length; i > 0; i = i - 2) result.push(entries.slice(0, i).join("/"));
964
+ return result;
965
+ }
966
+ //#endregion
967
+ //#region src/util/navigation_from_path.ts
968
+ function getNavigationEntriesFromPath(props) {
969
+ const { path, collections = [], currentFullPath } = props;
970
+ const subpathCombinations = getCollectionPathsCombinations(removeInitialAndTrailingSlashes(path).split("/"));
971
+ const result = [];
972
+ for (let i = 0; i < subpathCombinations.length; i++) {
973
+ const subpathCombination = subpathCombinations[i];
974
+ const collection = collections && collections.find((entry) => entry.slug === subpathCombination);
975
+ if (collection) {
976
+ const collectionPath = currentFullPath && currentFullPath.length > 0 ? currentFullPath + "/" + collection.slug : collection.slug;
977
+ result.push({
978
+ type: "collection",
979
+ id: collection.slug,
980
+ slug: collectionPath,
981
+ path: collectionPath,
982
+ collection
983
+ });
984
+ const restOfThePath = removeInitialAndTrailingSlashes(removeInitialAndTrailingSlashes(path).replace(subpathCombination, ""));
985
+ const nextSegments = restOfThePath.length > 0 ? restOfThePath.split("/") : [];
986
+ if (nextSegments.length > 0) {
987
+ const entityId = nextSegments[0];
988
+ const path = collectionPath + "/" + entityId;
989
+ result.push({
990
+ type: "entity",
991
+ entityId,
992
+ slug: collectionPath,
993
+ path,
994
+ parentCollection: collection
995
+ });
996
+ if (nextSegments.length > 1) {
997
+ const newPath = nextSegments.slice(1).join("/");
998
+ if (!collection) throw Error("collection not found resolving path: " + collection);
999
+ const entityViews = collection.entityViews;
1000
+ const customView = entityViews && entityViews.map((entry) => resolveEntityView(entry, props.contextEntityViews)).filter((v) => v != null).find((entry) => entry.key === newPath);
1001
+ const subcollections = getSubcollections(collection);
1002
+ if (customView) result.push({
1003
+ type: "custom_view",
1004
+ slug: collectionPath,
1005
+ entityId,
1006
+ path: path + "/" + customView.key,
1007
+ view: customView
1008
+ });
1009
+ else if (subcollections) result.push(...getNavigationEntriesFromPath({
1010
+ path: newPath,
1011
+ collections: subcollections,
1012
+ currentFullPath: path,
1013
+ contextEntityViews: props.contextEntityViews
1014
+ }));
1015
+ }
1016
+ }
1017
+ break;
1018
+ }
1019
+ }
1020
+ return result;
1021
+ }
1022
+ function resolveEntityView(entityView, contextEntityViews) {
1023
+ if (typeof entityView === "string") return contextEntityViews?.find((entry) => entry.key === entityView);
1024
+ else return entityView;
1025
+ }
1026
+ //#endregion
1027
+ //#region src/util/parent_references_from_path.ts
1028
+ function getParentReferencesFromPath(props) {
1029
+ const { path, collections = [], currentFullPath } = props;
1030
+ const subpathCombinations = getCollectionPathsCombinations(removeInitialAndTrailingSlashes(path).split("/"));
1031
+ const result = [];
1032
+ for (let i = 0; i < subpathCombinations.length; i++) {
1033
+ const subpathCombination = subpathCombinations[i];
1034
+ const collection = collections && collections.find((entry) => entry.slug === subpathCombination);
1035
+ if (collection) {
1036
+ const collectionPath = currentFullPath && currentFullPath.length > 0 ? currentFullPath + "/" + collection.slug : collection.slug;
1037
+ const restOfThePath = removeInitialAndTrailingSlashes(removeInitialAndTrailingSlashes(path).replace(subpathCombination, ""));
1038
+ const nextSegments = restOfThePath.length > 0 ? restOfThePath.split("/") : [];
1039
+ if (nextSegments.length > 0) {
1040
+ const entityId = nextSegments[0];
1041
+ const path = collectionPath + "/" + entityId;
1042
+ result.push(new _rebasepro_types.EntityReference({
1043
+ id: entityId,
1044
+ path: collectionPath
1045
+ }));
1046
+ if (nextSegments.length > 1) {
1047
+ const newPath = nextSegments.slice(1).join("/");
1048
+ if (!collection) throw Error("collection not found resolving path: " + collection);
1049
+ if (getSubcollections(collection).length > 0) result.push(...getParentReferencesFromPath({
1050
+ path: newPath,
1051
+ collections: getSubcollections(collection),
1052
+ currentFullPath: path
1053
+ }));
1054
+ }
1055
+ }
1056
+ break;
1057
+ }
1058
+ }
1059
+ return result;
1060
+ }
1061
+ //#endregion
1062
+ //#region src/util/builders.ts
1063
+ /**
1064
+ * Identity function we use to defeat the type system of Typescript and build
1065
+ * collection views with all its properties
1066
+ * @param collection
1067
+ * @group Builder
1068
+ */
1069
+ function buildCollection(collection) {
1070
+ return collection;
1071
+ }
1072
+ /**
1073
+ * Identity function we use to defeat the type system of Typescript and preserve
1074
+ * the property keys.
1075
+ * @param property
1076
+ * @group Builder
1077
+ */
1078
+ function buildProperty(property) {
1079
+ return property;
1080
+ }
1081
+ /**
1082
+ * Identity function we use to defeat the type system of Typescript and preserve
1083
+ * the properties keys.
1084
+ * @param properties
1085
+ * @group Builder
1086
+ */
1087
+ function buildProperties(properties) {
1088
+ return properties;
1089
+ }
1090
+ /**
1091
+ * Identity function we use to defeat the type system of Typescript and preserve
1092
+ * the properties keys.
1093
+ * @param propertiesOrBuilder
1094
+ * @group Builder
1095
+ */
1096
+ function buildPropertiesOrBuilder(propertiesOrBuilder) {
1097
+ return propertiesOrBuilder;
1098
+ }
1099
+ /**
1100
+ * Identity function we use to defeat the type system of Typescript and preserve
1101
+ * the properties keys.
1102
+ * @param enumValues
1103
+ * @group Builder
1104
+ */
1105
+ function buildEnum(enumValues) {
1106
+ return enumValues;
1107
+ }
1108
+ /**
1109
+ * Identity function we use to defeat the type system of Typescript and preserve
1110
+ * the properties keys.
1111
+ * @param enumValueConfig
1112
+ * @group Builder
1113
+ */
1114
+ function buildEnumValueConfig(enumValueConfig) {
1115
+ return enumValueConfig;
1116
+ }
1117
+ /**
1118
+ * Identity function we use to defeat the type system of Typescript and preserve
1119
+ * the properties keys.
1120
+ * @param callbacks
1121
+ * @group Builder
1122
+ */
1123
+ function buildEntityCallbacks(callbacks) {
1124
+ return callbacks;
1125
+ }
1126
+ /**
1127
+ * Identity function we use to defeat the type system of Typescript and build
1128
+ * additional field delegates views with all its properties
1129
+ * @param additionalFieldDelegate
1130
+ * @group Builder
1131
+ */
1132
+ function buildAdditionalFieldDelegate(additionalFieldDelegate) {
1133
+ return additionalFieldDelegate;
1134
+ }
1135
+ //#endregion
1136
+ //#region src/util/storage.ts
1137
+ async function resolveStorageFilenameString({ input, storage, values, entityId, path, property, file, propertyKey }) {
1138
+ let result;
1139
+ if (typeof input === "function") {
1140
+ result = await input({
1141
+ path,
1142
+ entityId,
1143
+ values,
1144
+ property,
1145
+ file,
1146
+ storage,
1147
+ propertyKey
1148
+ });
1149
+ if (!result) console.warn("Storage callback returned empty result. Using default name value");
1150
+ } else result = replacePlaceholders({
1151
+ file,
1152
+ input,
1153
+ entityId,
1154
+ propertyKey,
1155
+ path
1156
+ });
1157
+ if (!result) result = (0, _rebasepro_utils.randomString)() + "_" + file.name;
1158
+ return result;
1159
+ }
1160
+ function resolveStoragePathString({ input, storage, values, entityId, path, property, file, propertyKey }) {
1161
+ let result;
1162
+ if (typeof input === "function") {
1163
+ result = input({
1164
+ path,
1165
+ entityId,
1166
+ values,
1167
+ property,
1168
+ file,
1169
+ storage,
1170
+ propertyKey
1171
+ });
1172
+ if (!result) console.warn("Storage callback returned empty result. Using default name value");
1173
+ } else result = replacePlaceholders({
1174
+ file,
1175
+ input,
1176
+ entityId,
1177
+ propertyKey,
1178
+ path
1179
+ });
1180
+ if (!result) result = (0, _rebasepro_utils.randomString)() + "_" + file.name;
1181
+ return result;
1182
+ }
1183
+ function replacePlaceholders({ file, input, entityId, propertyKey, path }) {
1184
+ const ext = file.name.split(".").pop();
1185
+ let result = input.replace("{propertyKey}", propertyKey).replace("{rand}", (0, _rebasepro_utils.randomString)()).replace("{file}", file.name).replace("{file.type}", file.type);
1186
+ if (entityId) result = result.replace("{entityId}", String(entityId));
1187
+ if (path) result = result.replace("{path}", path);
1188
+ if (ext) {
1189
+ result = result.replace("{file.ext}", ext);
1190
+ const name = file.name.replace(`.${ext}`, "");
1191
+ result = result.replace("{file.name}", name);
1192
+ }
1193
+ if (!result) result = (0, _rebasepro_utils.randomString)() + "_" + file.name;
1194
+ return result;
1195
+ }
1196
+ //#endregion
1197
+ //#region src/util/callbacks.ts
1198
+ /**
1199
+ * Helper function to recursively check if there are any callbacks in the properties.
1200
+ */
1201
+ function hasPropertyCallbacks(properties, callbackName) {
1202
+ if (!properties) return false;
1203
+ for (const property of Object.values(properties)) {
1204
+ if (property.callbacks?.[callbackName]) return true;
1205
+ if (property.type === "map" && property.properties) {
1206
+ if (hasPropertyCallbacks(property.properties, callbackName)) return true;
1207
+ } else if (property.type === "array" && property.of) {
1208
+ const ofs = Array.isArray(property.of) ? property.of : [property.of];
1209
+ for (const of of ofs) {
1210
+ if (of.callbacks?.[callbackName]) return true;
1211
+ if (of.type === "map" && of.properties && hasPropertyCallbacks(of.properties, callbackName)) return true;
1212
+ }
1213
+ }
1214
+ }
1215
+ return false;
1216
+ }
1217
+ /**
1218
+ * Recursively process properties to apply field-level hooks.
1219
+ */
1220
+ async function processProperties(properties, values, previousValues, propsContext, callbackName) {
1221
+ if (!values || typeof values !== "object") return values;
1222
+ const result = { ...values };
1223
+ for (const [key, property] of Object.entries(properties)) {
1224
+ if (result[key] === void 0) continue;
1225
+ let currentValue = result[key];
1226
+ const previousValue = previousValues?.[key];
1227
+ if (property.type === "array" && Array.isArray(currentValue)) {
1228
+ if (property.of && !Array.isArray(property.of)) currentValue = await Promise.all(currentValue.map(async (item, index) => {
1229
+ const prevItem = Array.isArray(previousValue) ? previousValue[index] : void 0;
1230
+ return (await processProperties({ "_tmp": property.of }, { "_tmp": item }, { "_tmp": prevItem }, propsContext, callbackName))["_tmp"];
1231
+ }));
1232
+ } else if (property.type === "map" && property.properties && typeof currentValue === "object") currentValue = await processProperties(property.properties, currentValue, previousValue ?? {}, propsContext, callbackName);
1233
+ if (property.callbacks?.[callbackName]) {
1234
+ const cbRes = await Promise.resolve(property.callbacks[callbackName]({
1235
+ ...propsContext,
1236
+ value: currentValue,
1237
+ previousValue
1238
+ }));
1239
+ if (cbRes !== void 0) currentValue = cbRes;
1240
+ }
1241
+ result[key] = currentValue;
1242
+ }
1243
+ return result;
1244
+ }
1245
+ /**
1246
+ * Helper function to extract field-level PropertyCallbacks from a properties schema
1247
+ * and wrap them into an EntityCallbacks object recursively.
1248
+ */
1249
+ var buildPropertyCallbacks = (properties) => {
1250
+ if (!properties) return void 0;
1251
+ const propertyCallbacks = {};
1252
+ if (hasPropertyCallbacks(properties, "afterRead")) propertyCallbacks.afterRead = async (props) => {
1253
+ const processedValues = await processProperties(properties, props.entity.values, props.entity.values, props, "afterRead");
1254
+ return {
1255
+ ...props.entity,
1256
+ values: processedValues
1257
+ };
1258
+ };
1259
+ if (hasPropertyCallbacks(properties, "beforeSave")) propertyCallbacks.beforeSave = async (props) => {
1260
+ return await processProperties(properties, props.values, props.previousValues ?? {}, props, "beforeSave");
1261
+ };
1262
+ return Object.keys(propertyCallbacks).length > 0 ? propertyCallbacks : void 0;
1263
+ };
1264
+ //#endregion
1265
+ //#region src/util/conditions.ts
1266
+ /**
1267
+ * Access a nested property from an object via dot notation.
1268
+ */
1269
+ function getIn(obj, path) {
1270
+ if (!obj || !path) return void 0;
1271
+ return path.split(".").reduce((acc, part) => acc && acc[part], obj);
1272
+ }
1273
+ var operationsRegistered = false;
1274
+ /**
1275
+ * Register custom JSON Logic operations for Rebase.
1276
+ * Call this once at app initialization.
1277
+ */
1278
+ function registerConditionOperations() {
1279
+ if (operationsRegistered) return;
1280
+ json_logic_js.default.add_operation("hasRole", function(roleId) {
1281
+ return this?.user?.roles?.includes(roleId) ?? false;
1282
+ });
1283
+ json_logic_js.default.add_operation("hasAnyRole", function(roleIds) {
1284
+ if (!this?.user?.roles || !Array.isArray(roleIds)) return false;
1285
+ return roleIds.some((role) => this.user.roles.includes(role));
1286
+ });
1287
+ json_logic_js.default.add_operation("isToday", (timestamp) => {
1288
+ if (!timestamp) return false;
1289
+ const date = new Date(timestamp);
1290
+ const today = /* @__PURE__ */ new Date();
1291
+ return date.getFullYear() === today.getFullYear() && date.getMonth() === today.getMonth() && date.getDate() === today.getDate();
1292
+ });
1293
+ json_logic_js.default.add_operation("isPast", (timestamp) => {
1294
+ if (!timestamp) return false;
1295
+ return timestamp < Date.now();
1296
+ });
1297
+ json_logic_js.default.add_operation("isFuture", (timestamp) => {
1298
+ if (!timestamp) return false;
1299
+ return timestamp > Date.now();
1300
+ });
1301
+ operationsRegistered = true;
1302
+ }
1303
+ /**
1304
+ * Evaluate a JSON Logic rule against the given context.
1305
+ */
1306
+ function evaluateCondition(rule, context) {
1307
+ registerConditionOperations();
1308
+ return json_logic_js.default.apply(rule, context);
1309
+ }
1310
+ /**
1311
+ * Convert a value to a format suitable for JSON Logic evaluation.
1312
+ * Specifically handles Date objects by converting them to Unix timestamps.
1313
+ */
1314
+ function serializeValueForConditions(value) {
1315
+ if (value === null || value === void 0) return value;
1316
+ if (value instanceof Date) return value.getTime();
1317
+ if (typeof value?.toMillis === "function") return value.toMillis();
1318
+ if (typeof value?.toDate === "function") return value.toDate().getTime();
1319
+ if (Array.isArray(value)) return value.map(serializeValueForConditions);
1320
+ if (typeof value === "object") {
1321
+ const result = {};
1322
+ for (const key of Object.keys(value)) result[key] = serializeValueForConditions(value[key]);
1323
+ return result;
1324
+ }
1325
+ return value;
1326
+ }
1327
+ /**
1328
+ * Build a ConditionContext from the current property resolution context.
1329
+ */
1330
+ function buildConditionContext(params) {
1331
+ const { propertyKey, values, previousValues, path, entityId, index, authController } = params;
1332
+ const user = authController.user;
1333
+ const serializedValues = serializeValueForConditions(values ?? {});
1334
+ return {
1335
+ values: serializedValues,
1336
+ previousValues: serializeValueForConditions(previousValues ?? values ?? {}),
1337
+ propertyValue: propertyKey ? getIn(serializedValues, propertyKey) : void 0,
1338
+ path,
1339
+ entityId,
1340
+ isNew: !entityId,
1341
+ index,
1342
+ user: {
1343
+ uid: user?.uid ?? "",
1344
+ email: user?.email ?? null,
1345
+ displayName: user?.displayName ?? null,
1346
+ photoURL: user?.photoURL ?? null,
1347
+ roles: (user?.roles ?? []).map((r) => typeof r === "string" ? r : r.id)
1348
+ },
1349
+ now: Date.now()
1350
+ };
1351
+ }
1352
+ /**
1353
+ * Apply PropertyConditions to a resolved property, evaluating all JSON Logic rules.
1354
+ */
1355
+ function applyPropertyConditions(property, context) {
1356
+ const { conditions } = property;
1357
+ if (!conditions) return property;
1358
+ const result = { ...property };
1359
+ if (conditions.disabled) {
1360
+ if (evaluateCondition(conditions.disabled, context)) {
1361
+ result.ui = result.ui || {};
1362
+ result.ui.disabled = {
1363
+ clearOnDisabled: conditions.clearOnDisabled ?? false,
1364
+ disabledMessage: conditions.disabledMessage,
1365
+ hidden: false
1366
+ };
1367
+ }
1368
+ }
1369
+ if (conditions.hidden) {
1370
+ if (evaluateCondition(conditions.hidden, context)) {
1371
+ result.ui = result.ui || {};
1372
+ result.ui.disabled = {
1373
+ ...typeof result.ui?.disabled === "object" ? result.ui.disabled : {},
1374
+ hidden: true,
1375
+ clearOnDisabled: conditions.clearOnDisabled ?? false
1376
+ };
1377
+ }
1378
+ }
1379
+ if (conditions.readOnly) {
1380
+ if (evaluateCondition(conditions.readOnly, context)) {
1381
+ result.ui = result.ui || {};
1382
+ result.ui.readOnly = true;
1383
+ }
1384
+ }
1385
+ if (conditions.required !== void 0) {
1386
+ const isRequired = evaluateCondition(conditions.required, context);
1387
+ result.validation = {
1388
+ ...result.validation,
1389
+ required: isRequired,
1390
+ requiredMessage: conditions.requiredMessage
1391
+ };
1392
+ }
1393
+ if (context.isNew && conditions.defaultValue !== void 0) result.defaultValue = evaluateCondition(conditions.defaultValue, context);
1394
+ if ("enum" in result && result.enum && (conditions.enumConditions || conditions.allowedEnumValues || conditions.excludedEnumValues)) result.enum = applyEnumConditions(result.enum, conditions, context);
1395
+ if (result.type === "reference") {
1396
+ if (conditions.referencePath) result.path = evaluateCondition(conditions.referencePath, context);
1397
+ if (conditions.referenceFilter) result.fixedFilter = evaluateCondition(conditions.referenceFilter, context);
1398
+ }
1399
+ if (result.type === "array") {
1400
+ if (conditions.canAddElements !== void 0) result.canAddElements = evaluateCondition(conditions.canAddElements, context);
1401
+ if (conditions.sortable !== void 0) result.sortable = evaluateCondition(conditions.sortable, context);
1402
+ }
1403
+ return result;
1404
+ }
1405
+ /**
1406
+ * Convert an object with numeric keys back to an array.
1407
+ * Firestore stores arrays as {"0": "a", "1": "b"} to avoid nested arrays.
1408
+ */
1409
+ function objectToArray(obj) {
1410
+ if (Array.isArray(obj)) return obj.map(String);
1411
+ if (obj && typeof obj === "object") {
1412
+ const keys = Object.keys(obj);
1413
+ if (keys.length > 0 && keys.every((k) => !isNaN(Number(k)))) return keys.sort((a, b) => Number(a) - Number(b)).map((k) => obj[k]).filter((v) => typeof v === "string" || typeof v === "number").map(String);
1414
+ }
1415
+ return [];
1416
+ }
1417
+ /**
1418
+ * Apply enum-specific conditions to filter and modify enum values.
1419
+ */
1420
+ function applyEnumConditions(enumValues, conditions, context) {
1421
+ let result = [...enumValues];
1422
+ if (conditions.allowedEnumValues) {
1423
+ const allowedArray = objectToArray(evaluateCondition(conditions.allowedEnumValues, context));
1424
+ if (allowedArray.length > 0) result = result.filter((ev) => allowedArray.includes(String(ev.id)));
1425
+ }
1426
+ if (conditions.excludedEnumValues) {
1427
+ const excludedArray = objectToArray(evaluateCondition(conditions.excludedEnumValues, context));
1428
+ if (excludedArray.length > 0) result = result.filter((ev) => !excludedArray.includes(String(ev.id)));
1429
+ }
1430
+ if (conditions.enumConditions) result = result.map((ev) => {
1431
+ const evConditions = conditions.enumConditions?.[ev.id];
1432
+ if (!evConditions) return ev;
1433
+ if (evConditions.hidden && evaluateCondition(evConditions.hidden, context)) return null;
1434
+ if (evConditions.disabled && evaluateCondition(evConditions.disabled, context)) return {
1435
+ ...ev,
1436
+ disabled: true
1437
+ };
1438
+ return ev;
1439
+ }).filter((ev) => ev !== null);
1440
+ return result;
1441
+ }
1442
+ //#endregion
1443
+ //#region src/collections/CollectionRegistry.ts
1444
+ var CollectionRegistry = class {
1445
+ collectionsByTableName = /* @__PURE__ */ new Map();
1446
+ collectionsBySlug = /* @__PURE__ */ new Map();
1447
+ rootCollections = [];
1448
+ cachedCollectionsList = null;
1449
+ rawCollectionsByTableName = /* @__PURE__ */ new Map();
1450
+ rawCollectionsBySlug = /* @__PURE__ */ new Map();
1451
+ rawRootCollections = [];
1452
+ cachedRawCollectionsList = null;
1453
+ lastRawInputSnapshot = null;
1454
+ constructor(collections) {
1455
+ if (collections) this.registerMultiple(collections);
1456
+ }
1457
+ reset() {
1458
+ this.collectionsByTableName.clear();
1459
+ this.collectionsBySlug.clear();
1460
+ this.rootCollections = [];
1461
+ this.cachedCollectionsList = null;
1462
+ this.rawCollectionsByTableName.clear();
1463
+ this.rawCollectionsBySlug.clear();
1464
+ this.rawRootCollections = [];
1465
+ this.cachedRawCollectionsList = null;
1466
+ }
1467
+ /**
1468
+ * Registers a collection and its subcollections recursively.
1469
+ * Returns true if the collections have changed, false otherwise.
1470
+ *
1471
+ * Idempotent: compares the raw input (before normalization) against a stored
1472
+ * snapshot. Only re-normalizes and re-registers when the raw input actually changed.
1473
+ * @param collections
1474
+ */
1475
+ registerMultiple(collections) {
1476
+ const rawSnapshot = collections.map((c) => (0, _rebasepro_utils.removeFunctions)(c));
1477
+ if (this.lastRawInputSnapshot && (0, fast_equals.deepEqual)(this.lastRawInputSnapshot, rawSnapshot)) return false;
1478
+ this.reset();
1479
+ collections.forEach((c) => {
1480
+ if (c.slug) this.collectionsBySlug.set(c.slug, c);
1481
+ this.collectionsByTableName.set(getTableName(c), c);
1482
+ });
1483
+ const normalizedCollections = collections.map((c) => this.normalizeCollection({ ...c }));
1484
+ normalizedCollections.forEach((c, index) => {
1485
+ const raw = (0, _rebasepro_utils.deepClone)(collections[index]);
1486
+ this.rootCollections.push(c);
1487
+ this.rawRootCollections.push(raw);
1488
+ const normalized = this.normalizeCollection(c);
1489
+ this.collectionsByTableName.set(getTableName(normalized), normalized);
1490
+ this.rawCollectionsByTableName.set(getTableName(raw), raw);
1491
+ if (normalized.slug) this.collectionsBySlug.set(normalized.slug, normalized);
1492
+ if (raw.slug) this.rawCollectionsBySlug.set(raw.slug, raw);
1493
+ });
1494
+ normalizedCollections.forEach((c) => {
1495
+ const subcollections = getSubcollections(c);
1496
+ if (subcollections && subcollections.length > 0) subcollections.forEach((subCollection) => {
1497
+ if (!subCollection) return;
1498
+ this._registerRecursively(this.normalizeCollection({ ...subCollection }), (0, _rebasepro_utils.deepClone)(subCollection));
1499
+ });
1500
+ });
1501
+ this.lastRawInputSnapshot = rawSnapshot;
1502
+ return true;
1503
+ }
1504
+ register(collection, rawCollection) {
1505
+ const raw = rawCollection ? (0, _rebasepro_utils.deepClone)(rawCollection) : (0, _rebasepro_utils.deepClone)(collection);
1506
+ this.rootCollections.push(collection);
1507
+ this.rawRootCollections.push(raw);
1508
+ this._registerRecursively(collection, raw);
1509
+ }
1510
+ _registerRecursively(collection, rawCollection) {
1511
+ if (this.collectionsByTableName.has(getTableName(collection))) return;
1512
+ const normalizedCollection = this.normalizeCollection(collection);
1513
+ this.collectionsByTableName.set(getTableName(normalizedCollection), normalizedCollection);
1514
+ this.rawCollectionsByTableName.set(getTableName(rawCollection), rawCollection);
1515
+ if (normalizedCollection.slug) this.collectionsBySlug.set(normalizedCollection.slug, normalizedCollection);
1516
+ if (rawCollection.slug) this.rawCollectionsBySlug.set(rawCollection.slug, rawCollection);
1517
+ const subcollections = getSubcollections(normalizedCollection);
1518
+ if (subcollections && subcollections.length > 0) subcollections.forEach((subCollection) => {
1519
+ if (!subCollection) return;
1520
+ this._registerRecursively(this.normalizeCollection({ ...subCollection }), (0, _rebasepro_utils.deepClone)(subCollection));
1521
+ });
1522
+ }
1523
+ normalizeCollection(collection) {
1524
+ const result = { ...collection };
1525
+ const extractedRelations = this.extractRelationsFromProperties(result.properties);
1526
+ const relResult = result;
1527
+ const manualRelations = (0, _rebasepro_types.getDataSourceCapabilities)(result.driver).supportsRelations ? relResult.relations ?? [] : [];
1528
+ const mergedRelationsRaw = [...extractedRelations];
1529
+ for (const manual of manualRelations) {
1530
+ const name = manual.relationName;
1531
+ if (!name) mergedRelationsRaw.push(manual);
1532
+ else {
1533
+ const existingIndex = mergedRelationsRaw.findIndex((r) => r.relationName === name);
1534
+ if (existingIndex === -1) mergedRelationsRaw.push(manual);
1535
+ else mergedRelationsRaw[existingIndex] = {
1536
+ ...manual,
1537
+ ...mergedRelationsRaw[existingIndex]
1538
+ };
1539
+ }
1540
+ }
1541
+ let mergedRelations = mergedRelationsRaw;
1542
+ if ((0, _rebasepro_types.getDataSourceCapabilities)(result.driver).supportsRelations) {
1543
+ mergedRelations = mergedRelationsRaw.map((r) => {
1544
+ try {
1545
+ return sanitizeRelation(r, result, (slug) => this.get(slug));
1546
+ } catch {
1547
+ return r;
1548
+ }
1549
+ });
1550
+ relResult.relations = mergedRelations;
1551
+ }
1552
+ result.properties = this.normalizeProperties(result.properties, mergedRelations);
1553
+ if (!result.childCollections) {
1554
+ if ((0, _rebasepro_types.getDataSourceCapabilities)(result.driver).supportsSubcollections && result.subcollections) result.childCollections = result.subcollections;
1555
+ else if ((0, _rebasepro_types.getDataSourceCapabilities)(result.driver).supportsRelations && relResult.relations) {
1556
+ const manyRelations = relResult.relations.filter((r) => r.cardinality === "many");
1557
+ if (manyRelations.length > 0) result.childCollections = () => manyRelations.map((r) => {
1558
+ const target = r.target();
1559
+ return r.overrides ? (0, _rebasepro_utils.mergeDeep)(target, r.overrides) : target;
1560
+ });
1561
+ }
1562
+ }
1563
+ return result;
1564
+ }
1565
+ /**
1566
+ * Extract Relation[] from properties that have inline relation config (i.e. `target` is set).
1567
+ * This allows developers to define relations directly on properties without a separate
1568
+ * `relations[]` entry on the collection.
1569
+ */
1570
+ extractRelationsFromProperties(properties) {
1571
+ const relations = [];
1572
+ for (const [key, property] of Object.entries(properties)) if (property.type === "relation") {
1573
+ const relProp = property;
1574
+ const target = relProp.target ?? relProp.relation?.target;
1575
+ if (target) {
1576
+ const relationName = relProp.relationName ?? relProp.relation?.relationName ?? key;
1577
+ relations.push({
1578
+ relationName,
1579
+ target,
1580
+ cardinality: relProp.cardinality ?? relProp.relation?.cardinality ?? "one",
1581
+ direction: relProp.direction ?? relProp.relation?.direction ?? "owning",
1582
+ inverseRelationName: relProp.inverseRelationName ?? relProp.relation?.inverseRelationName,
1583
+ localKey: relProp.localKey ?? relProp.relation?.localKey,
1584
+ foreignKeyOnTarget: relProp.foreignKeyOnTarget ?? relProp.relation?.foreignKeyOnTarget,
1585
+ through: relProp.through ?? relProp.relation?.through,
1586
+ joinPath: relProp.joinPath ?? relProp.relation?.joinPath,
1587
+ onUpdate: relProp.onUpdate ?? relProp.relation?.onUpdate,
1588
+ onDelete: relProp.onDelete ?? relProp.relation?.onDelete,
1589
+ overrides: relProp.overrides ?? relProp.relation?.overrides
1590
+ });
1591
+ }
1592
+ } else if (property.type === "map" && property.properties) relations.push(...this.extractRelationsFromProperties(property.properties));
1593
+ return relations;
1594
+ }
1595
+ normalizeProperties(properties, relations) {
1596
+ const newProperties = {};
1597
+ for (const key in properties) newProperties[key] = this.normalizeProperty(key, properties[key], relations);
1598
+ return newProperties;
1599
+ }
1600
+ normalizeProperty(key, property, relations) {
1601
+ const newProperty = { ...property };
1602
+ if (newProperty.type === "map" && newProperty.properties) newProperty.properties = this.normalizeProperties(newProperty.properties, relations);
1603
+ else if (newProperty.type === "array") {
1604
+ const arrayProp = newProperty;
1605
+ if (arrayProp.of) if (Array.isArray(arrayProp.of)) arrayProp.of = arrayProp.of.map((p, i) => this.normalizeProperty(`${key}[${i}]`, p, relations));
1606
+ else arrayProp.of = this.normalizeProperty(`${key}.of`, arrayProp.of, relations);
1607
+ else if (arrayProp.oneOf && arrayProp.oneOf.properties) arrayProp.oneOf.properties = this.normalizeProperties(arrayProp.oneOf.properties, relations);
1608
+ } else if ((newProperty.type === "string" || newProperty.type === "number") && newProperty.enum) {
1609
+ const stringOrNumberProperty = newProperty;
1610
+ if (typeof stringOrNumberProperty.enum === "object" && !Array.isArray(stringOrNumberProperty.enum)) stringOrNumberProperty.enum = enumToObjectEntries(stringOrNumberProperty.enum)?.filter((value) => value && (value.id || value.id === 0) && value.label) ?? [];
1611
+ } else if (newProperty.type === "relation") {
1612
+ const relationProperty = newProperty;
1613
+ const name = relationProperty.relationName || key;
1614
+ const relation = relations.find((r) => r.relationName === name);
1615
+ if (relation) relationProperty.relation = relation;
1616
+ else console.warn(`Could not find relation for property '${key}' with relationName: ${name}`);
1617
+ }
1618
+ return newProperty;
1619
+ }
1620
+ get(path) {
1621
+ const bySlug = this.collectionsBySlug.get(path);
1622
+ if (bySlug) return bySlug;
1623
+ if (path.includes("-")) {
1624
+ const normalized = path.replace(/-/g, "_");
1625
+ const byNormalized = this.collectionsBySlug.get(normalized);
1626
+ if (byNormalized) return byNormalized;
1627
+ }
1628
+ return this.collectionsByTableName.get(path);
1629
+ }
1630
+ /**
1631
+ * Gets the pristine, un-normalized collection exactly as it was provided.
1632
+ * Useful for the AST editor so it doesn't accidentally serialize injected metadata back to disk.
1633
+ */
1634
+ getRaw(path) {
1635
+ const bySlug = this.rawCollectionsBySlug.get(path);
1636
+ if (bySlug) return bySlug;
1637
+ if (path.includes("-")) {
1638
+ const normalized = path.replace(/-/g, "_");
1639
+ const byNormalized = this.rawCollectionsBySlug.get(normalized);
1640
+ if (byNormalized) return byNormalized;
1641
+ }
1642
+ return this.rawCollectionsByTableName.get(path);
1643
+ }
1644
+ /**
1645
+ * Get collection by resolving multi-segment paths through relations
1646
+ * e.g., "authors/70/posts" resolves to the posts collection
1647
+ */
1648
+ getCollectionByPath(collectionPath) {
1649
+ if (!collectionPath.includes("/")) return this.get(collectionPath);
1650
+ const pathSegments = collectionPath.split("/").filter((p) => p);
1651
+ if (pathSegments.length < 3 || pathSegments.length % 2 === 0) throw new Error(`Invalid relation path: ${collectionPath}. Expected format: collection/id/relation or collection/id/relation/id/relation`);
1652
+ const rootCollectionPath = pathSegments[0];
1653
+ let currentCollection = this.get(rootCollectionPath);
1654
+ if (!currentCollection) throw new Error(`Root collection not found: ${rootCollectionPath}`);
1655
+ for (let i = 2; i < pathSegments.length; i += 2) {
1656
+ const relationKey = pathSegments[i];
1657
+ if (!(0, _rebasepro_types.getDataSourceCapabilities)(currentCollection.driver).supportsRelations) throw new Error(`Relation path navigation requires a collection that supports relations, but '${currentCollection.slug}' uses driver '${currentCollection.driver}'`);
1658
+ const relation = findRelation(resolveCollectionRelations(currentCollection), relationKey);
1659
+ if (!relation) throw new Error(`Relation '${relationKey}' not found in collection '${currentCollection.slug}'`);
1660
+ const target = relation.target();
1661
+ const targetRelationKey = relation.relationName || target.slug;
1662
+ const targetSlug = relation.overrides?.slug ?? targetRelationKey;
1663
+ currentCollection = this.get(targetSlug) || this.normalizeCollection(target);
1664
+ if (i + 1 < pathSegments.length) {}
1665
+ }
1666
+ return currentCollection;
1667
+ }
1668
+ getCollections() {
1669
+ if (!this.cachedCollectionsList) this.cachedCollectionsList = Array.from(this.collectionsByTableName.values());
1670
+ return this.cachedCollectionsList;
1671
+ }
1672
+ getRawCollections() {
1673
+ if (!this.cachedRawCollectionsList) this.cachedRawCollectionsList = Array.from(this.rawCollectionsByTableName.values());
1674
+ return this.cachedRawCollectionsList;
1675
+ }
1676
+ /**
1677
+ * Resolves a multi-segment path like "products/123/locales" and returns
1678
+ * information about the collections and entity IDs along the path
1679
+ */
1680
+ resolvePathToCollections(path) {
1681
+ const pathSegments = path.split("/").filter((p) => p);
1682
+ if (pathSegments.length === 0) throw new Error(`Invalid path: ${path}`);
1683
+ if (pathSegments.length % 2 !== 1) throw new Error(`Invalid collection path: ${path}. It must have an odd number of segments.`);
1684
+ const collections = [];
1685
+ const entityIds = [];
1686
+ let currentCollection = this.get(pathSegments[0]);
1687
+ if (!currentCollection) throw new Error(`Unknown collection path or slug: ${pathSegments[0]}`);
1688
+ collections.push(currentCollection);
1689
+ for (let i = 1; i < pathSegments.length; i += 2) {
1690
+ const entityId = pathSegments[i];
1691
+ entityIds.push(entityId);
1692
+ if (i + 1 < pathSegments.length) {
1693
+ const subcollectionSlug = pathSegments[i + 1];
1694
+ const subcollections = getSubcollections(currentCollection);
1695
+ if (!subcollections || subcollections.length === 0) throw new Error(`No subcollections found for ${currentCollection.slug} in path: ${path}`);
1696
+ const subcollection = subcollections.find((c) => c.slug === subcollectionSlug);
1697
+ if (!subcollection) throw new Error(`Subcollection '${subcollectionSlug}' not found in ${currentCollection.slug}`);
1698
+ currentCollection = this.get(subcollection.slug) || this.normalizeCollection(subcollection);
1699
+ collections.push(currentCollection);
1700
+ }
1701
+ }
1702
+ return {
1703
+ collections,
1704
+ entityIds,
1705
+ finalCollection: currentCollection
1706
+ };
1707
+ }
1708
+ };
1709
+ //#endregion
1710
+ //#region src/collections/default-collections.ts
1711
+ /**
1712
+ * Default users collection.
1713
+ *
1714
+ * Prepended to the developer's collections array by the admin and server.
1715
+ * Slug-based dedup (Map keyed by slug, last-write-wins) lets developers
1716
+ * override by defining their own collection with `slug: "users"`.
1717
+ */
1718
+ var defaultUsersCollection = {
1719
+ name: "Users",
1720
+ singularName: "User",
1721
+ slug: "users",
1722
+ auth: true,
1723
+ table: "users",
1724
+ schema: "rebase",
1725
+ icon: "Users",
1726
+ group: "Settings",
1727
+ openEntityMode: "dialog",
1728
+ disableDefaultActions: ["copy"],
1729
+ securityRules: [{
1730
+ operation: "select",
1731
+ roles: ["admin"]
1732
+ }, {
1733
+ operations: [
1734
+ "insert",
1735
+ "update",
1736
+ "delete"
1737
+ ],
1738
+ roles: ["admin"]
1739
+ }],
1740
+ sort: ["createdAt", "desc"],
1741
+ properties: {
1742
+ id: {
1743
+ name: "ID",
1744
+ type: "string",
1745
+ isId: "uuid",
1746
+ ui: { readOnly: true }
1747
+ },
1748
+ email: {
1749
+ name: "Email",
1750
+ type: "string",
1751
+ validation: {
1752
+ required: true,
1753
+ unique: true
1754
+ }
1755
+ },
1756
+ displayName: {
1757
+ name: "Name",
1758
+ type: "string",
1759
+ columnName: "display_name",
1760
+ validation: { required: true }
1761
+ },
1762
+ photoURL: {
1763
+ name: "Photo URL",
1764
+ type: "string",
1765
+ columnName: "photo_url",
1766
+ url: "image"
1767
+ },
1768
+ roles: {
1769
+ name: "Roles",
1770
+ type: "array",
1771
+ columnType: "text[]",
1772
+ of: {
1773
+ name: "Role",
1774
+ type: "string",
1775
+ enum: {
1776
+ admin: "Admin",
1777
+ editor: "Editor",
1778
+ viewer: "Viewer"
1779
+ }
1780
+ }
1781
+ },
1782
+ passwordHash: {
1783
+ name: "Password Hash",
1784
+ type: "string",
1785
+ columnName: "password_hash",
1786
+ ui: {
1787
+ hideFromCollection: true,
1788
+ disabled: { hidden: true }
1789
+ }
1790
+ },
1791
+ emailVerified: {
1792
+ name: "Email Verified",
1793
+ type: "boolean",
1794
+ columnName: "email_verified",
1795
+ defaultValue: false,
1796
+ ui: {
1797
+ hideFromCollection: true,
1798
+ disabled: { hidden: true }
1799
+ }
1800
+ },
1801
+ emailVerificationToken: {
1802
+ name: "Email Verification Token",
1803
+ type: "string",
1804
+ columnName: "email_verification_token",
1805
+ ui: {
1806
+ hideFromCollection: true,
1807
+ disabled: { hidden: true }
1808
+ }
1809
+ },
1810
+ emailVerificationSentAt: {
1811
+ name: "Email Verification Sent At",
1812
+ type: "date",
1813
+ columnName: "email_verification_sent_at",
1814
+ ui: {
1815
+ hideFromCollection: true,
1816
+ disabled: { hidden: true }
1817
+ }
1818
+ },
1819
+ metadata: {
1820
+ name: "Metadata",
1821
+ type: "map",
1822
+ keyValue: true,
1823
+ properties: {},
1824
+ defaultValue: {},
1825
+ ui: {
1826
+ hideFromCollection: true,
1827
+ disabled: { hidden: true }
1828
+ }
1829
+ },
1830
+ createdAt: {
1831
+ name: "Created At",
1832
+ type: "date",
1833
+ columnName: "created_at",
1834
+ autoValue: "on_create",
1835
+ ui: { readOnly: true }
1836
+ },
1837
+ updatedAt: {
1838
+ name: "Updated At",
1839
+ type: "date",
1840
+ columnName: "updated_at",
1841
+ autoValue: "on_update",
1842
+ ui: {
1843
+ hideFromCollection: true,
1844
+ disabled: { hidden: true }
1845
+ }
1846
+ }
1847
+ },
1848
+ listProperties: [
1849
+ "displayName",
1850
+ "email",
1851
+ "roles",
1852
+ "createdAt"
1853
+ ],
1854
+ propertiesOrder: [
1855
+ "id",
1856
+ "email",
1857
+ "displayName",
1858
+ "roles",
1859
+ "createdAt"
1860
+ ]
1861
+ };
1862
+ //#endregion
1863
+ //#region src/data/query_builder.ts
1864
+ function or(...conditions) {
1865
+ return {
1866
+ type: "or",
1867
+ conditions
1868
+ };
1869
+ }
1870
+ function and(...conditions) {
1871
+ return {
1872
+ type: "and",
1873
+ conditions
1874
+ };
1875
+ }
1876
+ function cond(column, operator, value) {
1877
+ return {
1878
+ column,
1879
+ operator,
1880
+ value
1881
+ };
1882
+ }
1883
+ var QueryBuilder = class {
1884
+ collection;
1885
+ params = { where: {} };
1886
+ constructor(collection) {
1887
+ this.collection = collection;
1888
+ }
1889
+ where(columnOrCondition, operator, value) {
1890
+ if (typeof columnOrCondition === "object" && columnOrCondition !== null && "type" in columnOrCondition) {
1891
+ this.params.logical = columnOrCondition;
1892
+ return this;
1893
+ }
1894
+ if (!this.params.where) this.params.where = {};
1895
+ const column = columnOrCondition;
1896
+ const condition = [operator, value];
1897
+ const existing = this.params.where[column];
1898
+ if (existing === void 0) this.params.where[column] = condition;
1899
+ else if (Array.isArray(existing) && existing.length > 0 && Array.isArray(existing[0])) this.params.where[column].push(condition);
1900
+ else {
1901
+ let firstCondition;
1902
+ if (Array.isArray(existing) && existing.length === 2 && typeof existing[0] === "string") firstCondition = existing;
1903
+ else firstCondition = ["==", existing];
1904
+ this.params.where[column] = [firstCondition, condition];
1905
+ }
1906
+ return this;
1907
+ }
1908
+ /**
1909
+ * Order the results by a specific column.
1910
+ * @example
1911
+ * client.collection('users').orderBy('createdAt', 'desc').find()
1912
+ */
1913
+ orderBy(column, ascending = "asc") {
1914
+ this.params.orderBy = `${column}:${ascending}`;
1915
+ return this;
1916
+ }
1917
+ /**
1918
+ * Limit the number of results returned.
1919
+ */
1920
+ limit(count) {
1921
+ this.params.limit = count;
1922
+ return this;
1923
+ }
1924
+ /**
1925
+ * Skip the first N results.
1926
+ */
1927
+ offset(count) {
1928
+ this.params.offset = count;
1929
+ return this;
1930
+ }
1931
+ /**
1932
+ * Set a free-text search string if supported by the backend.
1933
+ */
1934
+ search(searchString) {
1935
+ this.params.searchString = searchString;
1936
+ return this;
1937
+ }
1938
+ /**
1939
+ * Include related entities in the response.
1940
+ * Relations will be populated with full entity data instead of just IDs.
1941
+ *
1942
+ * @param relations - Relation names to include, or "*" for all.
1943
+ * @example
1944
+ * // Include specific relations
1945
+ * client.data.posts.include("tags", "author").find()
1946
+ *
1947
+ * // Include all relations
1948
+ * client.data.posts.include("*").find()
1949
+ */
1950
+ include(...relations) {
1951
+ this.params.include = relations;
1952
+ return this;
1953
+ }
1954
+ /**
1955
+ * Execute the find query and return the results.
1956
+ */
1957
+ async find() {
1958
+ return this.collection.find(this.params);
1959
+ }
1960
+ /**
1961
+ * Listen to realtime updates matching this query.
1962
+ */
1963
+ listen(onUpdate, onError) {
1964
+ if (!this.collection.listen) throw new Error("Listen is only available when RebaseClient is configured with a websocketUrl.");
1965
+ return this.collection.listen(this.params, onUpdate, onError);
1966
+ }
1967
+ };
1968
+ //#endregion
1969
+ //#region src/data/buildRebaseData.ts
1970
+ /**
1971
+ * Convert where-clause filter object to the internal DataDriver FilterValues format.
1972
+ *
1973
+ * Supports multiple value formats:
1974
+ * - PostgREST string: { status: "eq.published", age: "gte.18" }
1975
+ * - Equality shorthand: { company_profile_id: null, status: "active", age: 18 }
1976
+ * - Tuple syntax: { age: [">=", 18], role: ["in", ["admin", "editor"]] }
1977
+ *
1978
+ * Internal: { status: ["==", "published"], age: [">=", 18] }
1979
+ */
1980
+ function convertWhereToFilter(where) {
1981
+ if (!where) return void 0;
1982
+ const operatorMap = {
1983
+ "eq": "==",
1984
+ "neq": "!=",
1985
+ "gt": ">",
1986
+ "gte": ">=",
1987
+ "lt": "<",
1988
+ "lte": "<=",
1989
+ "in": "in",
1990
+ "nin": "not-in",
1991
+ "not-in": "not-in",
1992
+ "cs": "array-contains",
1993
+ "csa": "array-contains-any",
1994
+ "==": "==",
1995
+ "!=": "!=",
1996
+ ">": ">",
1997
+ ">=": ">=",
1998
+ "<": "<",
1999
+ "<=": "<=",
2000
+ "array-contains": "array-contains",
2001
+ "array-contains-any": "array-contains-any"
2002
+ };
2003
+ const filter = {};
2004
+ for (const [field, rawValue] of Object.entries(where)) {
2005
+ if (rawValue === null) {
2006
+ filter[field] = ["==", null];
2007
+ continue;
2008
+ }
2009
+ if (typeof rawValue === "boolean") {
2010
+ filter[field] = ["==", rawValue];
2011
+ continue;
2012
+ }
2013
+ if (typeof rawValue === "number") {
2014
+ filter[field] = ["==", rawValue];
2015
+ continue;
2016
+ }
2017
+ if (Array.isArray(rawValue)) {
2018
+ const mappedConditions = (Array.isArray(rawValue[0]) ? rawValue : [rawValue]).map(([rawOp, val]) => {
2019
+ return [operatorMap[rawOp] ?? "==", val];
2020
+ });
2021
+ filter[field] = Array.isArray(rawValue[0]) ? mappedConditions : mappedConditions[0];
2022
+ continue;
2023
+ }
2024
+ if (typeof rawValue === "string") {
2025
+ const dotIndex = rawValue.indexOf(".");
2026
+ if (dotIndex === -1) {
2027
+ filter[field] = ["==", rawValue];
2028
+ continue;
2029
+ }
2030
+ const op = rawValue.substring(0, dotIndex);
2031
+ let value = rawValue.substring(dotIndex + 1);
2032
+ if (typeof value === "string" && value.startsWith("(") && value.endsWith(")")) value = value.slice(1, -1).split(",").map((v) => v.trim());
2033
+ if (value === "null") value = null;
2034
+ else if (value === "true") value = true;
2035
+ else if (value === "false") value = false;
2036
+ else if (typeof value === "string" && !isNaN(Number(value)) && value.trim() !== "") value = Number(value);
2037
+ const mappedOp = operatorMap[op];
2038
+ if (mappedOp) filter[field] = [mappedOp, value];
2039
+ }
2040
+ }
2041
+ return Object.keys(filter).length > 0 ? filter : void 0;
2042
+ }
2043
+ /**
2044
+ * Parse an orderBy string like "created_at:desc" into [field, direction].
2045
+ */
2046
+ function parseOrderBy(orderBy) {
2047
+ if (!orderBy) return void 0;
2048
+ const parts = orderBy.split(":");
2049
+ return [parts[0], parts[1] || "asc"];
2050
+ }
2051
+ function createDriverAccessor(driver, slug) {
2052
+ const accessor = {
2053
+ async find(params) {
2054
+ const orderParsed = parseOrderBy(params?.orderBy);
2055
+ const entities = await driver.fetchCollection({
2056
+ path: slug,
2057
+ limit: params?.limit,
2058
+ offset: params?.offset,
2059
+ filter: convertWhereToFilter(params?.where),
2060
+ orderBy: orderParsed?.[0],
2061
+ order: orderParsed?.[1],
2062
+ searchString: params?.searchString
2063
+ });
2064
+ const limit = params?.limit ?? 20;
2065
+ const offset = params?.offset ?? 0;
2066
+ return {
2067
+ data: entities,
2068
+ meta: {
2069
+ total: entities.length,
2070
+ limit,
2071
+ offset,
2072
+ hasMore: entities.length >= limit
2073
+ }
2074
+ };
2075
+ },
2076
+ async findById(id) {
2077
+ return driver.fetchEntity({
2078
+ path: slug,
2079
+ entityId: id
2080
+ });
2081
+ },
2082
+ async create(data, id) {
2083
+ return driver.saveEntity({
2084
+ path: slug,
2085
+ values: data,
2086
+ entityId: id,
2087
+ status: "new"
2088
+ });
2089
+ },
2090
+ async update(id, data) {
2091
+ return driver.saveEntity({
2092
+ path: slug,
2093
+ values: data,
2094
+ entityId: id,
2095
+ status: "existing"
2096
+ });
2097
+ },
2098
+ async delete(id) {
2099
+ return driver.deleteEntity({ entity: {
2100
+ id,
2101
+ path: slug,
2102
+ values: {}
2103
+ } });
2104
+ },
2105
+ deleteAll: driver.deleteAll ? async () => {
2106
+ return driver.deleteAll(slug);
2107
+ } : void 0,
2108
+ count: driver.countEntities ? async (params) => {
2109
+ return driver.countEntities({
2110
+ path: slug,
2111
+ filter: convertWhereToFilter(params?.where)
2112
+ });
2113
+ } : void 0,
2114
+ listen: driver.listenCollection ? (params, onUpdate, onError) => {
2115
+ const orderParsed = parseOrderBy(params?.orderBy);
2116
+ const limit = params?.limit ?? 20;
2117
+ const offset = params?.offset ?? 0;
2118
+ return driver.listenCollection({
2119
+ path: slug,
2120
+ limit: params?.limit,
2121
+ offset: params?.offset,
2122
+ filter: convertWhereToFilter(params?.where),
2123
+ orderBy: orderParsed?.[0],
2124
+ order: orderParsed?.[1],
2125
+ searchString: params?.searchString,
2126
+ onUpdate: (entities) => {
2127
+ onUpdate({
2128
+ data: entities,
2129
+ meta: {
2130
+ total: entities.length,
2131
+ limit,
2132
+ offset,
2133
+ hasMore: entities.length >= limit
2134
+ }
2135
+ });
2136
+ },
2137
+ onError
2138
+ });
2139
+ } : void 0,
2140
+ listenById: driver.listenEntity ? (id, onUpdate, onError) => {
2141
+ return driver.listenEntity({
2142
+ path: slug,
2143
+ entityId: id,
2144
+ onUpdate: (entity) => onUpdate(entity ?? void 0),
2145
+ onError
2146
+ });
2147
+ } : void 0,
2148
+ where(columnOrCondition, operator, value) {
2149
+ const builder = new QueryBuilder(accessor);
2150
+ if (typeof columnOrCondition === "object") return builder.where(columnOrCondition);
2151
+ return builder.where(columnOrCondition, operator, value);
2152
+ },
2153
+ orderBy(column, ascending) {
2154
+ return new QueryBuilder(accessor).orderBy(column, ascending);
2155
+ },
2156
+ limit(count) {
2157
+ return new QueryBuilder(accessor).limit(count);
2158
+ },
2159
+ offset(count) {
2160
+ return new QueryBuilder(accessor).offset(count);
2161
+ },
2162
+ search(searchString) {
2163
+ return new QueryBuilder(accessor).search(searchString);
2164
+ },
2165
+ include(...relations) {
2166
+ return new QueryBuilder(accessor).include(...relations);
2167
+ }
2168
+ };
2169
+ return accessor;
2170
+ }
2171
+ /**
2172
+ * Build a `RebaseData` object from a `DataDriver` using JavaScript Proxy.
2173
+ *
2174
+ * This is the key bridge: any property access like `data.products` returns
2175
+ * a `CollectionAccessor` backed by the underlying DataDriver, without
2176
+ * needing per-collection code generation.
2177
+ *
2178
+ * @example
2179
+ * const data = buildRebaseData(driver);
2180
+ * await data.products.create({ name: "Camera", price: 299 });
2181
+ * const { data: items } = await data.products.find({ where: { status: "eq.published" } });
2182
+ */
2183
+ function buildRebaseData(driver) {
2184
+ const cache = /* @__PURE__ */ new Map();
2185
+ function getAccessor(slug) {
2186
+ let accessor = cache.get(slug);
2187
+ if (!accessor) {
2188
+ accessor = createDriverAccessor(driver, slug);
2189
+ cache.set(slug, accessor);
2190
+ }
2191
+ return accessor;
2192
+ }
2193
+ return new Proxy({ collection: getAccessor }, { get(_target, prop) {
2194
+ if (prop === "collection") return getAccessor;
2195
+ if (typeof prop === "symbol") return void 0;
2196
+ if (prop === "then" || prop === "toJSON" || prop === "$$typeof") return void 0;
2197
+ return getAccessor((0, _rebasepro_utils.toSnakeCase)(prop));
2198
+ } });
2199
+ }
2200
+ //#endregion
2201
+ exports.COLLECTION_PATH_SEPARATOR = COLLECTION_PATH_SEPARATOR;
2202
+ exports.CollectionRegistry = CollectionRegistry;
2203
+ exports.DEFAULT_ONE_OF_TYPE = DEFAULT_ONE_OF_TYPE;
2204
+ exports.DEFAULT_ONE_OF_VALUE = DEFAULT_ONE_OF_VALUE;
2205
+ exports.QueryBuilder = QueryBuilder;
2206
+ exports.addInitialSlash = addInitialSlash;
2207
+ exports.and = and;
2208
+ exports.applyPropertyConditions = applyPropertyConditions;
2209
+ exports.buildAdditionalFieldDelegate = buildAdditionalFieldDelegate;
2210
+ exports.buildCollection = buildCollection;
2211
+ exports.buildConditionContext = buildConditionContext;
2212
+ exports.buildEntityCallbacks = buildEntityCallbacks;
2213
+ exports.buildEnum = buildEnum;
2214
+ exports.buildEnumValueConfig = buildEnumValueConfig;
2215
+ exports.buildProperties = buildProperties;
2216
+ exports.buildPropertiesOrBuilder = buildPropertiesOrBuilder;
2217
+ exports.buildProperty = buildProperty;
2218
+ exports.buildPropertyCallbacks = buildPropertyCallbacks;
2219
+ exports.buildRebaseData = buildRebaseData;
2220
+ exports.canCreateEntity = canCreateEntity;
2221
+ exports.canDeleteEntity = canDeleteEntity;
2222
+ exports.canEditEntity = canEditEntity;
2223
+ exports.canReadCollection = canReadCollection;
2224
+ exports.checkOperation = checkOperation;
2225
+ exports.cond = cond;
2226
+ exports.createRelationRef = createRelationRef;
2227
+ exports.createRelationRefWithData = createRelationRefWithData;
2228
+ exports.defaultUsersCollection = defaultUsersCollection;
2229
+ exports.enumToObjectEntries = enumToObjectEntries;
2230
+ exports.evaluateCondition = evaluateCondition;
2231
+ exports.findRelation = findRelation;
2232
+ exports.fullPathToCollectionSegments = fullPathToCollectionSegments;
2233
+ exports.getArrayResolvedProperties = getArrayResolvedProperties;
2234
+ exports.getCollectionBySlugWithin = getCollectionBySlugWithin;
2235
+ exports.getCollectionPathsCombinations = getCollectionPathsCombinations;
2236
+ exports.getColumnName = getColumnName;
2237
+ exports.getDefaultValueFor = getDefaultValueFor;
2238
+ exports.getDefaultValueFortype = getDefaultValueFortype;
2239
+ exports.getDefaultValuesFor = getDefaultValuesFor;
2240
+ exports.getEntityImagePreviewPropertyKey = getEntityImagePreviewPropertyKey;
2241
+ exports.getEnumVarName = getEnumVarName;
2242
+ exports.getLabelOrConfigFrom = getLabelOrConfigFrom;
2243
+ exports.getLastSegment = getLastSegment;
2244
+ exports.getLocalChangesBackup = getLocalChangesBackup;
2245
+ exports.getNavigationEntriesFromPath = getNavigationEntriesFromPath;
2246
+ exports.getParentReferencesFromPath = getParentReferencesFromPath;
2247
+ exports.getPrimaryKeys = getPrimaryKeys;
2248
+ exports.getReferenceFrom = getReferenceFrom;
2249
+ exports.getRelationFrom = getRelationFrom;
2250
+ exports.getSubcollections = getSubcollections;
2251
+ exports.getTableName = getTableName;
2252
+ exports.getTableVarName = getTableVarName;
2253
+ exports.isHidden = isHidden;
2254
+ exports.isPropertyBuilder = isPropertyBuilder;
2255
+ exports.isReadOnly = isReadOnly;
2256
+ exports.normalizeToEntityRelation = normalizeToEntityRelation;
2257
+ exports.or = or;
2258
+ exports.registerConditionOperations = registerConditionOperations;
2259
+ exports.removeInitialAndTrailingSlashes = removeInitialAndTrailingSlashes;
2260
+ exports.removeInitialSlash = removeInitialSlash;
2261
+ exports.removeTrailingSlash = removeTrailingSlash;
2262
+ exports.resolveArrayProperties = resolveArrayProperties;
2263
+ exports.resolveCollectionPathIds = resolveCollectionPathIds;
2264
+ exports.resolveCollectionRelations = resolveCollectionRelations;
2265
+ exports.resolveDefaultSelectedView = resolveDefaultSelectedView;
2266
+ exports.resolveEnumValues = resolveEnumValues;
2267
+ exports.resolveProperties = resolveProperties;
2268
+ exports.resolveProperty = resolveProperty;
2269
+ exports.resolvePropertyEnum = resolvePropertyEnum;
2270
+ exports.resolvePropertyRelation = resolvePropertyRelation;
2271
+ exports.resolveRelationProperty = resolveRelationProperty;
2272
+ exports.resolveStorageFilenameString = resolveStorageFilenameString;
2273
+ exports.resolveStoragePathString = resolveStoragePathString;
2274
+ exports.sanitizeData = sanitizeData;
2275
+ exports.sanitizeRelation = sanitizeRelation;
2276
+ exports.segmentsToStrippedPath = segmentsToStrippedPath;
2277
+ exports.sortProperties = sortProperties;
2278
+ exports.stripCollectionPath = stripCollectionPath;
2279
+ exports.traverseValueProperty = traverseValueProperty;
2280
+ exports.traverseValuesProperties = traverseValuesProperties;
2281
+ exports.updateDateAutoValues = updateDateAutoValues;
2535
2282
  });
2536
- //# sourceMappingURL=index.umd.js.map
2283
+
2284
+ //# sourceMappingURL=index.umd.js.map