@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.
- package/dist/collections/default-collections.d.ts +12 -0
- package/dist/collections/index.d.ts +1 -0
- package/dist/data/query_builder.d.ts +51 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +471 -240
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +470 -239
- package/dist/index.umd.js.map +1 -1
- package/dist/util/permissions.d.ts +1 -0
- package/package.json +7 -4
- package/src/collections/CollectionRegistry.ts +12 -1
- package/src/collections/default-collections.ts +79 -0
- package/src/collections/index.ts +1 -0
- package/src/data/buildRebaseData.ts +25 -5
- package/src/data/query_builder.ts +117 -0
- package/src/index.ts +2 -0
- package/src/util/permissions.ts +1 -1
- package/src/util/relations.ts +15 -15
- package/src/util/resolutions.ts +5 -2
- package/src/collections/CollectionRegistry.d.ts +0 -56
- package/src/collections/index.d.ts +0 -1
- package/src/data/buildRebaseData.d.ts +0 -14
- package/src/index.d.ts +0 -3
- package/src/util/builders.d.ts +0 -57
- package/src/util/callbacks.d.ts +0 -6
- package/src/util/collections.d.ts +0 -11
- package/src/util/common.d.ts +0 -2
- package/src/util/conditions.d.ts +0 -26
- package/src/util/entities.d.ts +0 -58
- package/src/util/enums.d.ts +0 -3
- package/src/util/index.d.ts +0 -16
- package/src/util/navigation_from_path.d.ts +0 -34
- package/src/util/navigation_utils.d.ts +0 -20
- package/src/util/parent_references_from_path.d.ts +0 -6
- package/src/util/paths.d.ts +0 -14
- package/src/util/permissions.d.ts +0 -5
- package/src/util/references.d.ts +0 -2
- package/src/util/relations.d.ts +0 -22
- package/src/util/resolutions.d.ts +0 -72
- package/src/util/storage.d.ts +0 -24
package/dist/index.umd.js
CHANGED
|
@@ -316,6 +316,241 @@
|
|
|
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 = 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
|
+
}
|
|
319
554
|
function resolveProperty(props) {
|
|
320
555
|
const {
|
|
321
556
|
property,
|
|
@@ -545,8 +780,9 @@
|
|
|
545
780
|
if (types.getDataSourceCapabilities(collection.driver).supportsSubcollections && collection.subcollections) {
|
|
546
781
|
return collection.subcollections() ?? [];
|
|
547
782
|
}
|
|
548
|
-
if (types.getDataSourceCapabilities(collection.driver).supportsRelations
|
|
549
|
-
const
|
|
783
|
+
if (types.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 @@
|
|
|
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 = utils.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 = utils.toSnakeCase(sourceCollection.slug ?? sourceCollection.name);
|
|
1238
|
-
if (newRelation.cardinality === "one" && newRelation.direction === "owning") {
|
|
1239
|
-
if (!newRelation.localKey) {
|
|
1240
|
-
newRelation.localKey = utils.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 = types.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 ? utils.toSnakeCase(newRelation.inverseRelationName) : sourceName;
|
|
1265
|
-
newRelation.foreignKeyOnTarget = utils.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 = types.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 = utils.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 ?? utils.generateForeignKeyName(sourceName),
|
|
1302
|
-
targetColumn: newRelation.through?.targetColumn ?? utils.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 (!types.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 (types.getDataSourceCapabilities(collection.driver).supportsRelations) {
|
|
1388
|
-
return collection.table ?? utils.toSnakeCase(collection.slug) ?? utils.toSnakeCase(collection.name);
|
|
1389
|
-
}
|
|
1390
|
-
return utils.toSnakeCase(collection.slug) ?? utils.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 @@
|
|
|
1730
1732
|
const mergedRelationsRaw = [...extractedRelations];
|
|
1731
1733
|
for (const manual of manualRelations) {
|
|
1732
1734
|
const name = manual.relationName;
|
|
1733
|
-
if (!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 @@
|
|
|
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 @@
|
|
|
2030
2238
|
return [field, direction];
|
|
2031
2239
|
}
|
|
2032
2240
|
function createDriverAccessor(driver, slug) {
|
|
2033
|
-
|
|
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 @@
|
|
|
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();
|
|
@@ -2154,6 +2382,7 @@
|
|
|
2154
2382
|
exports2.CollectionRegistry = CollectionRegistry;
|
|
2155
2383
|
exports2.DEFAULT_ONE_OF_TYPE = DEFAULT_ONE_OF_TYPE;
|
|
2156
2384
|
exports2.DEFAULT_ONE_OF_VALUE = DEFAULT_ONE_OF_VALUE;
|
|
2385
|
+
exports2.QueryBuilder = QueryBuilder;
|
|
2157
2386
|
exports2.addInitialSlash = addInitialSlash;
|
|
2158
2387
|
exports2.applyPropertyConditions = applyPropertyConditions;
|
|
2159
2388
|
exports2.buildAdditionalFieldDelegate = buildAdditionalFieldDelegate;
|
|
@@ -2171,8 +2400,10 @@
|
|
|
2171
2400
|
exports2.canDeleteEntity = canDeleteEntity;
|
|
2172
2401
|
exports2.canEditEntity = canEditEntity;
|
|
2173
2402
|
exports2.canReadCollection = canReadCollection;
|
|
2403
|
+
exports2.checkOperation = checkOperation;
|
|
2174
2404
|
exports2.createRelationRef = createRelationRef;
|
|
2175
2405
|
exports2.createRelationRefWithData = createRelationRefWithData;
|
|
2406
|
+
exports2.defaultUsersCollection = defaultUsersCollection;
|
|
2176
2407
|
exports2.enumToObjectEntries = enumToObjectEntries;
|
|
2177
2408
|
exports2.evaluateCondition = evaluateCondition;
|
|
2178
2409
|
exports2.findRelation = findRelation;
|