@rebasepro/common 0.2.1 → 0.2.4

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.
Files changed (40) hide show
  1. package/dist/collections/default-collections.d.ts +12 -0
  2. package/dist/collections/index.d.ts +1 -0
  3. package/dist/data/query_builder.d.ts +51 -0
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.es.js +471 -240
  6. package/dist/index.es.js.map +1 -1
  7. package/dist/index.umd.js +470 -239
  8. package/dist/index.umd.js.map +1 -1
  9. package/dist/util/permissions.d.ts +1 -0
  10. package/package.json +7 -4
  11. package/src/collections/CollectionRegistry.ts +12 -1
  12. package/src/collections/default-collections.ts +79 -0
  13. package/src/collections/index.ts +1 -0
  14. package/src/data/buildRebaseData.ts +25 -5
  15. package/src/data/query_builder.ts +117 -0
  16. package/src/index.ts +2 -0
  17. package/src/util/permissions.ts +1 -1
  18. package/src/util/relations.ts +15 -15
  19. package/src/util/resolutions.ts +5 -2
  20. package/src/collections/CollectionRegistry.d.ts +0 -56
  21. package/src/collections/index.d.ts +0 -1
  22. package/src/data/buildRebaseData.d.ts +0 -14
  23. package/src/index.d.ts +0 -3
  24. package/src/util/builders.d.ts +0 -57
  25. package/src/util/callbacks.d.ts +0 -6
  26. package/src/util/collections.d.ts +0 -11
  27. package/src/util/common.d.ts +0 -2
  28. package/src/util/conditions.d.ts +0 -26
  29. package/src/util/entities.d.ts +0 -58
  30. package/src/util/enums.d.ts +0 -3
  31. package/src/util/index.d.ts +0 -16
  32. package/src/util/navigation_from_path.d.ts +0 -34
  33. package/src/util/navigation_utils.d.ts +0 -20
  34. package/src/util/parent_references_from_path.d.ts +0 -6
  35. package/src/util/paths.d.ts +0 -14
  36. package/src/util/permissions.d.ts +0 -5
  37. package/src/util/references.d.ts +0 -2
  38. package/src/util/relations.d.ts +0 -22
  39. package/src/util/resolutions.d.ts +0 -72
  40. package/src/util/storage.d.ts +0 -24
package/dist/index.es.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { EntityReference, EntityRelation, getDataSourceCapabilities } from "@rebasepro/types";
2
- import { mergeDeep, getIn as getIn$1, isDefaultFieldConfigId, randomString, toSnakeCase, generateForeignKeyName, removeFunctions, deepClone } from "@rebasepro/utils";
2
+ import { mergeDeep, toSnakeCase, generateForeignKeyName, getIn as getIn$1, isDefaultFieldConfigId, randomString, removeFunctions, deepClone } from "@rebasepro/utils";
3
3
  import jsonLogic from "json-logic-js";
4
4
  import { deepEqual } from "fast-equals";
5
5
  const DEFAULT_ONE_OF_TYPE = "type";
@@ -316,6 +316,241 @@ function segmentsToStrippedPath(paths) {
316
316
  function fullPathToCollectionSegments(path) {
317
317
  return path.split("/").filter((e, i) => i % 2 === 0);
318
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 = 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 = toSnakeCase(sourceCollection.slug ?? sourceCollection.name);
382
+ if (newRelation.cardinality === "one" && newRelation.direction === "owning") {
383
+ if (!newRelation.localKey) {
384
+ newRelation.localKey = 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 = 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 ? toSnakeCase(newRelation.inverseRelationName) : sourceName;
409
+ newRelation.foreignKeyOnTarget = 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 = 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 = 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 ?? generateForeignKeyName(sourceName),
446
+ targetColumn: newRelation.through?.targetColumn ?? 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 (!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 (getDataSourceCapabilities(collection.driver).supportsRelations) {
531
+ return collection.table ?? toSnakeCase(collection.slug) ?? toSnakeCase(collection.name);
532
+ }
533
+ return toSnakeCase(collection.slug) ?? 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
+ }
319
554
  function resolveProperty(props) {
320
555
  const {
321
556
  property,
@@ -545,8 +780,9 @@ function getSubcollections(collection) {
545
780
  if (getDataSourceCapabilities(collection.driver).supportsSubcollections && collection.subcollections) {
546
781
  return collection.subcollections() ?? [];
547
782
  }
548
- if (getDataSourceCapabilities(collection.driver).supportsRelations && collection.relations) {
549
- const manyRelations = collection.relations.filter((r) => r.cardinality === "many");
783
+ if (getDataSourceCapabilities(collection.driver).supportsRelations) {
784
+ const resolvedRelations = resolveCollectionRelations(collection);
785
+ const manyRelations = Object.values(resolvedRelations).filter((r) => r.cardinality === "many");
550
786
  return manyRelations.map((r) => {
551
787
  const target = r.target();
552
788
  if (!target) return void 0;
@@ -1174,240 +1410,6 @@ const buildPropertyCallbacks = (properties) => {
1174
1410
  }
1175
1411
  return Object.keys(propertyCallbacks).length > 0 ? propertyCallbacks : void 0;
1176
1412
  };
1177
- function sanitizeRelation(relation, sourceCollection, resolveCollection) {
1178
- if (!relation.target) {
1179
- throw new Error("Relation is missing a `target` collection.");
1180
- }
1181
- const rawTarget = relation.target;
1182
- let targetCollection;
1183
- if (typeof rawTarget === "string") {
1184
- if (resolveCollection) {
1185
- targetCollection = resolveCollection(rawTarget);
1186
- }
1187
- if (!targetCollection) {
1188
- targetCollection = {
1189
- slug: rawTarget,
1190
- name: rawTarget
1191
- };
1192
- }
1193
- } else if (typeof rawTarget === "function") {
1194
- const evaluated = rawTarget();
1195
- if (typeof evaluated === "string") {
1196
- if (resolveCollection) {
1197
- targetCollection = resolveCollection(evaluated);
1198
- }
1199
- if (!targetCollection) {
1200
- targetCollection = {
1201
- slug: evaluated,
1202
- name: evaluated
1203
- };
1204
- }
1205
- } else {
1206
- targetCollection = evaluated;
1207
- }
1208
- }
1209
- if (!targetCollection) {
1210
- throw new Error("Relation is missing a valid `target` collection.");
1211
- }
1212
- const newRelation = {
1213
- ...relation
1214
- };
1215
- newRelation.target = () => {
1216
- if (typeof rawTarget === "string") {
1217
- return resolveCollection && resolveCollection(rawTarget) || targetCollection;
1218
- } else if (typeof rawTarget === "function") {
1219
- const evaluated = rawTarget();
1220
- if (typeof evaluated === "string") {
1221
- return resolveCollection && resolveCollection(evaluated) || targetCollection;
1222
- }
1223
- return evaluated;
1224
- }
1225
- return targetCollection;
1226
- };
1227
- if (!newRelation.relationName) {
1228
- newRelation.relationName = toSnakeCase(targetCollection.slug);
1229
- }
1230
- if (!newRelation.direction) {
1231
- if (newRelation.foreignKeyOnTarget) newRelation.direction = "inverse";
1232
- else if (newRelation.through) newRelation.direction = "owning";
1233
- else if (newRelation.cardinality === "many") newRelation.direction = "inverse";
1234
- else newRelation.direction = "owning";
1235
- }
1236
- if (!newRelation.joinPath) {
1237
- const sourceName = toSnakeCase(sourceCollection.slug ?? sourceCollection.name);
1238
- if (newRelation.cardinality === "one" && newRelation.direction === "owning") {
1239
- if (!newRelation.localKey) {
1240
- newRelation.localKey = generateForeignKeyName(newRelation.relationName);
1241
- }
1242
- } else if (newRelation.cardinality === "one" && newRelation.direction === "inverse") {
1243
- if (!newRelation.foreignKeyOnTarget) {
1244
- let foundForeignKey = false;
1245
- try {
1246
- const targetRelations = getDataSourceCapabilities(targetCollection.driver).supportsRelations ? targetCollection.relations || [] : [];
1247
- for (const targetRel of targetRelations) {
1248
- if (targetRel.direction === "owning" && targetRel.cardinality === "one" && targetRel.localKey) {
1249
- try {
1250
- const targetRelTarget = targetRel.target();
1251
- if (targetRelTarget.slug === sourceCollection.slug) {
1252
- newRelation.foreignKeyOnTarget = targetRel.localKey;
1253
- foundForeignKey = true;
1254
- break;
1255
- }
1256
- } catch (e) {
1257
- continue;
1258
- }
1259
- }
1260
- }
1261
- } catch (e) {
1262
- }
1263
- if (!foundForeignKey) {
1264
- const keyPrefix = newRelation.inverseRelationName ? toSnakeCase(newRelation.inverseRelationName) : sourceName;
1265
- newRelation.foreignKeyOnTarget = generateForeignKeyName(keyPrefix);
1266
- }
1267
- }
1268
- } else if (newRelation.cardinality === "many" && newRelation.direction === "inverse") {
1269
- let isManyToManyInverse = false;
1270
- if (newRelation.inverseRelationName && !newRelation.foreignKeyOnTarget) {
1271
- try {
1272
- const targetRelations = getDataSourceCapabilities(targetCollection.driver).supportsRelations ? targetCollection.relations || [] : [];
1273
- for (const targetRel of targetRelations) {
1274
- if (targetRel.cardinality === "many" && (targetRel.direction === "owning" || !targetRel.direction) && targetRel.relationName === newRelation.inverseRelationName) {
1275
- isManyToManyInverse = true;
1276
- break;
1277
- }
1278
- }
1279
- if (!isManyToManyInverse && targetCollection.properties) {
1280
- for (const [propKey, prop] of Object.entries(targetCollection.properties)) {
1281
- if (prop.type !== "relation") continue;
1282
- const relProp = prop;
1283
- const relName = relProp.relationName || propKey;
1284
- if (relName === newRelation.inverseRelationName && relProp.cardinality === "many" && (relProp.direction === "owning" || !relProp.direction)) {
1285
- isManyToManyInverse = true;
1286
- break;
1287
- }
1288
- }
1289
- }
1290
- } catch (e) {
1291
- }
1292
- }
1293
- if (!isManyToManyInverse && !newRelation.foreignKeyOnTarget) {
1294
- newRelation.foreignKeyOnTarget = generateForeignKeyName(sourceName);
1295
- }
1296
- } else if (newRelation.cardinality === "many" && newRelation.direction === "owning") {
1297
- const sourceTableName = getTableName(sourceCollection);
1298
- const targetTableName = getTableName(targetCollection);
1299
- newRelation.through = {
1300
- table: newRelation.through?.table ?? [sourceTableName, targetTableName].sort().join("_"),
1301
- sourceColumn: newRelation.through?.sourceColumn ?? generateForeignKeyName(sourceName),
1302
- targetColumn: newRelation.through?.targetColumn ?? generateForeignKeyName(newRelation.relationName)
1303
- };
1304
- }
1305
- }
1306
- if (newRelation.cardinality === "one" && newRelation.direction === "owning" && !newRelation.localKey && !newRelation.joinPath) {
1307
- 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}'`);
1308
- }
1309
- if (newRelation.cardinality === "one" && newRelation.direction === "inverse" && !newRelation.foreignKeyOnTarget && !newRelation.joinPath) {
1310
- 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}'`);
1311
- }
1312
- if (newRelation.cardinality === "many" && newRelation.direction === "inverse" && !newRelation.foreignKeyOnTarget && !newRelation.joinPath && !newRelation.inverseRelationName) {
1313
- 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}'`);
1314
- }
1315
- return newRelation;
1316
- }
1317
- const _resolvedRelationsCache = /* @__PURE__ */ new WeakMap();
1318
- function resolveCollectionRelations(collection) {
1319
- const cached = _resolvedRelationsCache.get(collection);
1320
- if (cached) return cached;
1321
- if (!getDataSourceCapabilities(collection.driver).supportsRelations) return {};
1322
- const relCollection = collection;
1323
- const relations = {};
1324
- const registeredRelationNames = /* @__PURE__ */ new Set();
1325
- if (relCollection.relations) {
1326
- relCollection.relations.forEach((relation) => {
1327
- const normalizedRelation = sanitizeRelation(relation, collection);
1328
- const relationKey = normalizedRelation.relationName;
1329
- if (relationKey) {
1330
- relations[relationKey] = normalizedRelation;
1331
- registeredRelationNames.add(relationKey);
1332
- }
1333
- });
1334
- }
1335
- if (collection.properties) {
1336
- Object.entries(collection.properties).forEach(([propKey, prop]) => {
1337
- const relation = resolvePropertyRelation({
1338
- propertyKey: propKey,
1339
- property: prop,
1340
- sourceCollection: collection
1341
- });
1342
- if (relation) {
1343
- if (relations[propKey]) return;
1344
- if (!relation.relationName) {
1345
- relation.relationName = propKey;
1346
- }
1347
- const normalizedRelation = sanitizeRelation(relation, collection);
1348
- relations[propKey] = normalizedRelation;
1349
- registeredRelationNames.add(normalizedRelation.relationName ?? propKey);
1350
- }
1351
- });
1352
- }
1353
- _resolvedRelationsCache.set(collection, relations);
1354
- return relations;
1355
- }
1356
- function resolvePropertyRelation({
1357
- propertyKey,
1358
- property,
1359
- sourceCollection
1360
- }) {
1361
- if (property.type !== "relation") return void 0;
1362
- const relProp = property;
1363
- if (relProp.target) {
1364
- return {
1365
- relationName: relProp.relationName || propertyKey,
1366
- target: relProp.target,
1367
- cardinality: relProp.cardinality || "one",
1368
- direction: relProp.direction || "owning",
1369
- inverseRelationName: relProp.inverseRelationName,
1370
- localKey: relProp.localKey,
1371
- foreignKeyOnTarget: relProp.foreignKeyOnTarget,
1372
- through: relProp.through,
1373
- joinPath: relProp.joinPath,
1374
- onUpdate: relProp.onUpdate,
1375
- onDelete: relProp.onDelete,
1376
- overrides: relProp.overrides
1377
- };
1378
- }
1379
- const relation = (sourceCollection.relations ?? []).find((rel) => rel.relationName === relProp.relationName);
1380
- if (!relation) {
1381
- console.warn(`Unrecognized relation format for property '${propertyKey}' in collection '${sourceCollection.slug}'`);
1382
- return void 0;
1383
- }
1384
- return relation;
1385
- }
1386
- function getTableName(collection) {
1387
- if (getDataSourceCapabilities(collection.driver).supportsRelations) {
1388
- return collection.table ?? toSnakeCase(collection.slug) ?? toSnakeCase(collection.name);
1389
- }
1390
- return toSnakeCase(collection.slug) ?? toSnakeCase(collection.name);
1391
- }
1392
- function getTableVarName(tableName) {
1393
- return tableName.replace(/_([a-z])/g, (_, char) => char.toUpperCase());
1394
- }
1395
- function getEnumVarName(tableName, propName) {
1396
- const tableVar = getTableVarName(tableName);
1397
- const propVar = propName.charAt(0).toUpperCase() + propName.slice(1);
1398
- return `${tableVar}${propVar}`;
1399
- }
1400
- function getColumnName(fullColumn) {
1401
- return fullColumn.includes(".") ? fullColumn.split(".").pop() : fullColumn;
1402
- }
1403
- function findRelation(resolvedRelations, key) {
1404
- if (resolvedRelations[key]) return resolvedRelations[key];
1405
- const slugKey = key.replace(/_/g, "-");
1406
- if (slugKey !== key && resolvedRelations[slugKey]) return resolvedRelations[slugKey];
1407
- const snakeKey = key.replace(/-/g, "_");
1408
- if (snakeKey !== key && resolvedRelations[snakeKey]) return resolvedRelations[snakeKey];
1409
- return void 0;
1410
- }
1411
1413
  function getIn(obj, path) {
1412
1414
  if (!obj || !path) return void 0;
1413
1415
  return path.split(".").reduce((acc, part) => acc && acc[part], obj);
@@ -1730,8 +1732,18 @@ class CollectionRegistry {
1730
1732
  const mergedRelationsRaw = [...extractedRelations];
1731
1733
  for (const manual of manualRelations) {
1732
1734
  const name = manual.relationName;
1733
- if (!name || !mergedRelationsRaw.find((r) => r.relationName === name)) {
1735
+ if (!name) {
1734
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
+ }
1735
1747
  }
1736
1748
  }
1737
1749
  let mergedRelations = mergedRelationsRaw;
@@ -1951,6 +1963,202 @@ class CollectionRegistry {
1951
1963
  };
1952
1964
  }
1953
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
+ properties: {
1975
+ id: {
1976
+ name: "ID",
1977
+ type: "string",
1978
+ isId: "uuid"
1979
+ },
1980
+ email: {
1981
+ name: "Email",
1982
+ type: "string",
1983
+ validation: {
1984
+ required: true,
1985
+ unique: true
1986
+ }
1987
+ },
1988
+ password_hash: {
1989
+ name: "Password Hash",
1990
+ type: "string",
1991
+ ui: {
1992
+ hideFromCollection: true
1993
+ }
1994
+ },
1995
+ display_name: {
1996
+ name: "Display Name",
1997
+ type: "string"
1998
+ },
1999
+ photo_url: {
2000
+ name: "Photo URL",
2001
+ type: "string"
2002
+ },
2003
+ email_verified: {
2004
+ name: "Email Verified",
2005
+ type: "boolean",
2006
+ defaultValue: false
2007
+ },
2008
+ email_verification_token: {
2009
+ name: "Email Verification Token",
2010
+ type: "string",
2011
+ ui: {
2012
+ hideFromCollection: true
2013
+ }
2014
+ },
2015
+ email_verification_sent_at: {
2016
+ name: "Email Verification Sent At",
2017
+ type: "date",
2018
+ ui: {
2019
+ hideFromCollection: true
2020
+ }
2021
+ },
2022
+ metadata: {
2023
+ name: "Metadata",
2024
+ type: "map",
2025
+ defaultValue: {},
2026
+ ui: {
2027
+ hideFromCollection: true
2028
+ }
2029
+ },
2030
+ created_at: {
2031
+ name: "Created At",
2032
+ type: "date",
2033
+ autoValue: "on_create",
2034
+ ui: {
2035
+ readOnly: true,
2036
+ hideFromCollection: true
2037
+ }
2038
+ },
2039
+ updated_at: {
2040
+ name: "Updated At",
2041
+ type: "date",
2042
+ autoValue: "on_update",
2043
+ ui: {
2044
+ readOnly: true,
2045
+ hideFromCollection: true
2046
+ }
2047
+ }
2048
+ }
2049
+ };
2050
+ function mapOperator(op) {
2051
+ switch (op) {
2052
+ case "==":
2053
+ return "eq";
2054
+ case "!=":
2055
+ return "neq";
2056
+ case ">":
2057
+ return "gt";
2058
+ case ">=":
2059
+ return "gte";
2060
+ case "<":
2061
+ return "lt";
2062
+ case "<=":
2063
+ return "lte";
2064
+ case "array-contains":
2065
+ return "cs";
2066
+ case "array-contains-any":
2067
+ return "csa";
2068
+ case "not-in":
2069
+ return "nin";
2070
+ default:
2071
+ return op;
2072
+ }
2073
+ }
2074
+ class QueryBuilder {
2075
+ constructor(collection) {
2076
+ this.collection = collection;
2077
+ }
2078
+ params = {
2079
+ where: {}
2080
+ };
2081
+ /**
2082
+ * Add a filter condition to your query.
2083
+ * @example
2084
+ * client.collection('users').where('age', '>=', 18).find()
2085
+ */
2086
+ where(column, operator, value) {
2087
+ if (!this.params.where) {
2088
+ this.params.where = {};
2089
+ }
2090
+ const mappedOp = mapOperator(operator);
2091
+ let formattedValue = value;
2092
+ if (Array.isArray(value) && ["in", "nin", "cs", "csa"].includes(mappedOp)) {
2093
+ formattedValue = `(${value.join(",")})`;
2094
+ } else if (value === null) {
2095
+ formattedValue = "null";
2096
+ }
2097
+ this.params.where[column] = mappedOp === "eq" ? String(formattedValue) : `${mappedOp}.${formattedValue}`;
2098
+ return this;
2099
+ }
2100
+ /**
2101
+ * Order the results by a specific column.
2102
+ * @example
2103
+ * client.collection('users').orderBy('createdAt', 'desc').find()
2104
+ */
2105
+ orderBy(column, ascending = "asc") {
2106
+ this.params.orderBy = `${column}:${ascending}`;
2107
+ return this;
2108
+ }
2109
+ /**
2110
+ * Limit the number of results returned.
2111
+ */
2112
+ limit(count) {
2113
+ this.params.limit = count;
2114
+ return this;
2115
+ }
2116
+ /**
2117
+ * Skip the first N results.
2118
+ */
2119
+ offset(count) {
2120
+ this.params.offset = count;
2121
+ return this;
2122
+ }
2123
+ /**
2124
+ * Set a free-text search string if supported by the backend.
2125
+ */
2126
+ search(searchString) {
2127
+ this.params.searchString = searchString;
2128
+ return this;
2129
+ }
2130
+ /**
2131
+ * Include related entities in the response.
2132
+ * Relations will be populated with full entity data instead of just IDs.
2133
+ *
2134
+ * @param relations - Relation names to include, or "*" for all.
2135
+ * @example
2136
+ * // Include specific relations
2137
+ * client.data.posts.include("tags", "author").find()
2138
+ *
2139
+ * // Include all relations
2140
+ * client.data.posts.include("*").find()
2141
+ */
2142
+ include(...relations) {
2143
+ this.params.include = relations;
2144
+ return this;
2145
+ }
2146
+ /**
2147
+ * Execute the find query and return the results.
2148
+ */
2149
+ async find() {
2150
+ return this.collection.find(this.params);
2151
+ }
2152
+ /**
2153
+ * Listen to realtime updates matching this query.
2154
+ */
2155
+ listen(onUpdate, onError) {
2156
+ if (!this.collection.listen) {
2157
+ throw new Error("Listen is only available when RebaseClient is configured with a websocketUrl.");
2158
+ }
2159
+ return this.collection.listen(this.params, onUpdate, onError);
2160
+ }
2161
+ }
1954
2162
  function convertWhereToFilter(where) {
1955
2163
  if (!where) return void 0;
1956
2164
  const operatorMap = {
@@ -2030,7 +2238,7 @@ function parseOrderBy(orderBy) {
2030
2238
  return [field, direction];
2031
2239
  }
2032
2240
  function createDriverAccessor(driver, slug) {
2033
- return {
2241
+ const accessor = {
2034
2242
  async find(params) {
2035
2243
  const orderParsed = parseOrderBy(params?.orderBy);
2036
2244
  const entities = await driver.fetchCollection({
@@ -2124,8 +2332,28 @@ function createDriverAccessor(driver, slug) {
2124
2332
  onUpdate: (entity) => onUpdate(entity ?? void 0),
2125
2333
  onError
2126
2334
  });
2127
- } : void 0
2335
+ } : void 0,
2336
+ // Fluent Query Builder
2337
+ where(column, operator, value) {
2338
+ return new QueryBuilder(accessor).where(column, operator, value);
2339
+ },
2340
+ orderBy(column, ascending) {
2341
+ return new QueryBuilder(accessor).orderBy(column, ascending);
2342
+ },
2343
+ limit(count) {
2344
+ return new QueryBuilder(accessor).limit(count);
2345
+ },
2346
+ offset(count) {
2347
+ return new QueryBuilder(accessor).offset(count);
2348
+ },
2349
+ search(searchString) {
2350
+ return new QueryBuilder(accessor).search(searchString);
2351
+ },
2352
+ include(...relations) {
2353
+ return new QueryBuilder(accessor).include(...relations);
2354
+ }
2128
2355
  };
2356
+ return accessor;
2129
2357
  }
2130
2358
  function buildRebaseData(driver) {
2131
2359
  const cache = /* @__PURE__ */ new Map();
@@ -2155,6 +2383,7 @@ export {
2155
2383
  CollectionRegistry,
2156
2384
  DEFAULT_ONE_OF_TYPE,
2157
2385
  DEFAULT_ONE_OF_VALUE,
2386
+ QueryBuilder,
2158
2387
  addInitialSlash,
2159
2388
  applyPropertyConditions,
2160
2389
  buildAdditionalFieldDelegate,
@@ -2172,8 +2401,10 @@ export {
2172
2401
  canDeleteEntity,
2173
2402
  canEditEntity,
2174
2403
  canReadCollection,
2404
+ checkOperation,
2175
2405
  createRelationRef,
2176
2406
  createRelationRefWithData,
2407
+ defaultUsersCollection,
2177
2408
  enumToObjectEntries,
2178
2409
  evaluateCondition,
2179
2410
  findRelation,