@rebasepro/common 0.1.2 → 0.2.3
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/LICENSE +1 -1
- package/dist/data/query_builder.d.ts +51 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +396 -188
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +395 -187
- package/dist/index.umd.js.map +1 -1
- package/dist/util/entities.d.ts +2 -2
- package/dist/util/relations.d.ts +1 -1
- package/package.json +16 -15
- package/src/collections/CollectionRegistry.ts +24 -37
- package/src/data/buildRebaseData.ts +25 -5
- package/src/data/query_builder.ts +117 -0
- package/src/index.ts +2 -0
- package/src/types/json-logic-js.d.ts +1 -1
- package/src/util/builders.ts +1 -0
- package/src/util/entities.ts +6 -2
- package/src/util/relations.ts +83 -22
- package/src/util/resolutions.ts +6 -3
package/dist/index.umd.js
CHANGED
|
@@ -59,6 +59,10 @@
|
|
|
59
59
|
return [];
|
|
60
60
|
} else if (type === "map") {
|
|
61
61
|
return {};
|
|
62
|
+
} else if (type === "vector") {
|
|
63
|
+
return null;
|
|
64
|
+
} else if (type === "binary") {
|
|
65
|
+
return null;
|
|
62
66
|
} else {
|
|
63
67
|
return null;
|
|
64
68
|
}
|
|
@@ -312,6 +316,241 @@
|
|
|
312
316
|
function fullPathToCollectionSegments(path) {
|
|
313
317
|
return path.split("/").filter((e, i) => i % 2 === 0);
|
|
314
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
|
+
}
|
|
315
554
|
function resolveProperty(props) {
|
|
316
555
|
const {
|
|
317
556
|
property,
|
|
@@ -541,8 +780,9 @@
|
|
|
541
780
|
if (types.getDataSourceCapabilities(collection.driver).supportsSubcollections && collection.subcollections) {
|
|
542
781
|
return collection.subcollections() ?? [];
|
|
543
782
|
}
|
|
544
|
-
if (types.getDataSourceCapabilities(collection.driver).supportsRelations
|
|
545
|
-
const
|
|
783
|
+
if (types.getDataSourceCapabilities(collection.driver).supportsRelations) {
|
|
784
|
+
const resolvedRelations = resolveCollectionRelations(collection);
|
|
785
|
+
const manyRelations = Object.values(resolvedRelations).filter((r) => r.cardinality === "many");
|
|
546
786
|
return manyRelations.map((r) => {
|
|
547
787
|
const target = r.target();
|
|
548
788
|
if (!target) return void 0;
|
|
@@ -1170,187 +1410,6 @@
|
|
|
1170
1410
|
}
|
|
1171
1411
|
return Object.keys(propertyCallbacks).length > 0 ? propertyCallbacks : void 0;
|
|
1172
1412
|
};
|
|
1173
|
-
function sanitizeRelation(relation, sourceCollection) {
|
|
1174
|
-
if (!relation.target) {
|
|
1175
|
-
throw new Error("Relation is missing a `target` collection.");
|
|
1176
|
-
}
|
|
1177
|
-
const targetCollection = relation.target();
|
|
1178
|
-
const newRelation = {
|
|
1179
|
-
...relation
|
|
1180
|
-
};
|
|
1181
|
-
if (!newRelation.relationName) {
|
|
1182
|
-
newRelation.relationName = utils.toSnakeCase(targetCollection.slug);
|
|
1183
|
-
}
|
|
1184
|
-
if (!newRelation.direction) {
|
|
1185
|
-
if (newRelation.foreignKeyOnTarget) newRelation.direction = "inverse";
|
|
1186
|
-
else if (newRelation.through) newRelation.direction = "owning";
|
|
1187
|
-
else if (newRelation.cardinality === "many") newRelation.direction = "inverse";
|
|
1188
|
-
else newRelation.direction = "owning";
|
|
1189
|
-
}
|
|
1190
|
-
if (!newRelation.joinPath) {
|
|
1191
|
-
const sourceName = utils.toSnakeCase(sourceCollection.slug ?? sourceCollection.name);
|
|
1192
|
-
if (newRelation.cardinality === "one" && newRelation.direction === "owning") {
|
|
1193
|
-
if (!newRelation.localKey) {
|
|
1194
|
-
newRelation.localKey = utils.generateForeignKeyName(newRelation.relationName);
|
|
1195
|
-
}
|
|
1196
|
-
} else if (newRelation.cardinality === "one" && newRelation.direction === "inverse") {
|
|
1197
|
-
if (!newRelation.foreignKeyOnTarget) {
|
|
1198
|
-
let foundForeignKey = false;
|
|
1199
|
-
try {
|
|
1200
|
-
const targetRelations = types.getDataSourceCapabilities(targetCollection.driver).supportsRelations ? targetCollection.relations || [] : [];
|
|
1201
|
-
for (const targetRel of targetRelations) {
|
|
1202
|
-
if (targetRel.direction === "owning" && targetRel.cardinality === "one" && targetRel.localKey) {
|
|
1203
|
-
try {
|
|
1204
|
-
const targetRelTarget = targetRel.target();
|
|
1205
|
-
if (targetRelTarget.slug === sourceCollection.slug) {
|
|
1206
|
-
newRelation.foreignKeyOnTarget = targetRel.localKey;
|
|
1207
|
-
foundForeignKey = true;
|
|
1208
|
-
break;
|
|
1209
|
-
}
|
|
1210
|
-
} catch (e) {
|
|
1211
|
-
continue;
|
|
1212
|
-
}
|
|
1213
|
-
}
|
|
1214
|
-
}
|
|
1215
|
-
} catch (e) {
|
|
1216
|
-
}
|
|
1217
|
-
if (!foundForeignKey) {
|
|
1218
|
-
const keyPrefix = newRelation.inverseRelationName ? utils.toSnakeCase(newRelation.inverseRelationName) : sourceName;
|
|
1219
|
-
newRelation.foreignKeyOnTarget = utils.generateForeignKeyName(keyPrefix);
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
} else if (newRelation.cardinality === "many" && newRelation.direction === "inverse") {
|
|
1223
|
-
let isManyToManyInverse = false;
|
|
1224
|
-
if (newRelation.inverseRelationName && !newRelation.foreignKeyOnTarget) {
|
|
1225
|
-
try {
|
|
1226
|
-
const targetRelations = types.getDataSourceCapabilities(targetCollection.driver).supportsRelations ? targetCollection.relations || [] : [];
|
|
1227
|
-
for (const targetRel of targetRelations) {
|
|
1228
|
-
if (targetRel.cardinality === "many" && (targetRel.direction === "owning" || !targetRel.direction) && targetRel.relationName === newRelation.inverseRelationName) {
|
|
1229
|
-
isManyToManyInverse = true;
|
|
1230
|
-
break;
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
} catch (e) {
|
|
1234
|
-
}
|
|
1235
|
-
}
|
|
1236
|
-
if (!isManyToManyInverse && !newRelation.foreignKeyOnTarget) {
|
|
1237
|
-
newRelation.foreignKeyOnTarget = utils.generateForeignKeyName(sourceName);
|
|
1238
|
-
}
|
|
1239
|
-
} else if (newRelation.cardinality === "many" && newRelation.direction === "owning") {
|
|
1240
|
-
const sourceTableName = getTableName(sourceCollection);
|
|
1241
|
-
const targetTableName = getTableName(targetCollection);
|
|
1242
|
-
newRelation.through = {
|
|
1243
|
-
table: newRelation.through?.table ?? [sourceTableName, targetTableName].sort().join("_"),
|
|
1244
|
-
sourceColumn: newRelation.through?.sourceColumn ?? utils.generateForeignKeyName(sourceName),
|
|
1245
|
-
targetColumn: newRelation.through?.targetColumn ?? utils.generateForeignKeyName(newRelation.relationName)
|
|
1246
|
-
};
|
|
1247
|
-
}
|
|
1248
|
-
}
|
|
1249
|
-
if (newRelation.cardinality === "one" && newRelation.direction === "owning" && !newRelation.localKey && !newRelation.joinPath) {
|
|
1250
|
-
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}'`);
|
|
1251
|
-
}
|
|
1252
|
-
if (newRelation.cardinality === "one" && newRelation.direction === "inverse" && !newRelation.foreignKeyOnTarget && !newRelation.joinPath) {
|
|
1253
|
-
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}'`);
|
|
1254
|
-
}
|
|
1255
|
-
if (newRelation.cardinality === "many" && newRelation.direction === "inverse" && !newRelation.foreignKeyOnTarget && !newRelation.joinPath && !newRelation.inverseRelationName) {
|
|
1256
|
-
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}'`);
|
|
1257
|
-
}
|
|
1258
|
-
return newRelation;
|
|
1259
|
-
}
|
|
1260
|
-
const _resolvedRelationsCache = /* @__PURE__ */ new WeakMap();
|
|
1261
|
-
function resolveCollectionRelations(collection) {
|
|
1262
|
-
const cached = _resolvedRelationsCache.get(collection);
|
|
1263
|
-
if (cached) return cached;
|
|
1264
|
-
if (!types.getDataSourceCapabilities(collection.driver).supportsRelations) return {};
|
|
1265
|
-
const relCollection = collection;
|
|
1266
|
-
const relations = {};
|
|
1267
|
-
const registeredRelationNames = /* @__PURE__ */ new Set();
|
|
1268
|
-
if (relCollection.relations) {
|
|
1269
|
-
relCollection.relations.forEach((relation) => {
|
|
1270
|
-
const normalizedRelation = sanitizeRelation(relation, collection);
|
|
1271
|
-
const relationKey = normalizedRelation.relationName;
|
|
1272
|
-
if (relationKey) {
|
|
1273
|
-
relations[relationKey] = normalizedRelation;
|
|
1274
|
-
registeredRelationNames.add(relationKey);
|
|
1275
|
-
}
|
|
1276
|
-
});
|
|
1277
|
-
}
|
|
1278
|
-
if (collection.properties) {
|
|
1279
|
-
Object.entries(collection.properties).forEach(([propKey, prop]) => {
|
|
1280
|
-
const relation = resolvePropertyRelation({
|
|
1281
|
-
propertyKey: propKey,
|
|
1282
|
-
property: prop,
|
|
1283
|
-
sourceCollection: collection
|
|
1284
|
-
});
|
|
1285
|
-
if (relation) {
|
|
1286
|
-
if (relations[propKey]) return;
|
|
1287
|
-
if (!relation.relationName) {
|
|
1288
|
-
relation.relationName = propKey;
|
|
1289
|
-
}
|
|
1290
|
-
const normalizedRelation = sanitizeRelation(relation, collection);
|
|
1291
|
-
relations[propKey] = normalizedRelation;
|
|
1292
|
-
registeredRelationNames.add(normalizedRelation.relationName ?? propKey);
|
|
1293
|
-
}
|
|
1294
|
-
});
|
|
1295
|
-
}
|
|
1296
|
-
_resolvedRelationsCache.set(collection, relations);
|
|
1297
|
-
return relations;
|
|
1298
|
-
}
|
|
1299
|
-
function resolvePropertyRelation({
|
|
1300
|
-
propertyKey,
|
|
1301
|
-
property,
|
|
1302
|
-
sourceCollection
|
|
1303
|
-
}) {
|
|
1304
|
-
if (property.type !== "relation") return void 0;
|
|
1305
|
-
const relProp = property;
|
|
1306
|
-
if (relProp.target) {
|
|
1307
|
-
return {
|
|
1308
|
-
relationName: relProp.relationName || propertyKey,
|
|
1309
|
-
target: relProp.target,
|
|
1310
|
-
cardinality: relProp.cardinality || "one",
|
|
1311
|
-
direction: relProp.direction || "owning",
|
|
1312
|
-
inverseRelationName: relProp.inverseRelationName,
|
|
1313
|
-
localKey: relProp.localKey,
|
|
1314
|
-
foreignKeyOnTarget: relProp.foreignKeyOnTarget,
|
|
1315
|
-
through: relProp.through,
|
|
1316
|
-
joinPath: relProp.joinPath,
|
|
1317
|
-
onUpdate: relProp.onUpdate,
|
|
1318
|
-
onDelete: relProp.onDelete,
|
|
1319
|
-
overrides: relProp.overrides
|
|
1320
|
-
};
|
|
1321
|
-
}
|
|
1322
|
-
const relation = (sourceCollection.relations ?? []).find((rel) => rel.relationName === relProp.relationName);
|
|
1323
|
-
if (!relation) {
|
|
1324
|
-
console.warn(`Unrecognized relation format for property '${propertyKey}' in collection '${sourceCollection.slug}'`);
|
|
1325
|
-
return void 0;
|
|
1326
|
-
}
|
|
1327
|
-
return relation;
|
|
1328
|
-
}
|
|
1329
|
-
function getTableName(collection) {
|
|
1330
|
-
if (types.getDataSourceCapabilities(collection.driver).supportsRelations) {
|
|
1331
|
-
return collection.table ?? utils.toSnakeCase(collection.slug) ?? utils.toSnakeCase(collection.name);
|
|
1332
|
-
}
|
|
1333
|
-
return utils.toSnakeCase(collection.slug) ?? utils.toSnakeCase(collection.name);
|
|
1334
|
-
}
|
|
1335
|
-
function getTableVarName(tableName) {
|
|
1336
|
-
return tableName.replace(/_([a-z])/g, (_, char) => char.toUpperCase());
|
|
1337
|
-
}
|
|
1338
|
-
function getEnumVarName(tableName, propName) {
|
|
1339
|
-
const tableVar = getTableVarName(tableName);
|
|
1340
|
-
const propVar = propName.charAt(0).toUpperCase() + propName.slice(1);
|
|
1341
|
-
return `${tableVar}${propVar}`;
|
|
1342
|
-
}
|
|
1343
|
-
function getColumnName(fullColumn) {
|
|
1344
|
-
return fullColumn.includes(".") ? fullColumn.split(".").pop() : fullColumn;
|
|
1345
|
-
}
|
|
1346
|
-
function findRelation(resolvedRelations, key) {
|
|
1347
|
-
if (resolvedRelations[key]) return resolvedRelations[key];
|
|
1348
|
-
const slugKey = key.replace(/_/g, "-");
|
|
1349
|
-
if (slugKey !== key && resolvedRelations[slugKey]) return resolvedRelations[slugKey];
|
|
1350
|
-
const snakeKey = key.replace(/-/g, "_");
|
|
1351
|
-
if (snakeKey !== key && resolvedRelations[snakeKey]) return resolvedRelations[snakeKey];
|
|
1352
|
-
return void 0;
|
|
1353
|
-
}
|
|
1354
1413
|
function getIn(obj, path) {
|
|
1355
1414
|
if (!obj || !path) return void 0;
|
|
1356
1415
|
return path.split(".").reduce((acc, part) => acc && acc[part], obj);
|
|
@@ -1597,6 +1656,12 @@
|
|
|
1597
1656
|
return false;
|
|
1598
1657
|
}
|
|
1599
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
|
+
});
|
|
1600
1665
|
const normalizedCollections = collections.map((c) => this.normalizeCollection({
|
|
1601
1666
|
...c
|
|
1602
1667
|
}));
|
|
@@ -1667,15 +1732,25 @@
|
|
|
1667
1732
|
const mergedRelationsRaw = [...extractedRelations];
|
|
1668
1733
|
for (const manual of manualRelations) {
|
|
1669
1734
|
const name = manual.relationName;
|
|
1670
|
-
if (!name
|
|
1735
|
+
if (!name) {
|
|
1671
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
|
+
}
|
|
1672
1747
|
}
|
|
1673
1748
|
}
|
|
1674
1749
|
let mergedRelations = mergedRelationsRaw;
|
|
1675
1750
|
if (types.getDataSourceCapabilities(result.driver).supportsRelations) {
|
|
1676
1751
|
mergedRelations = mergedRelationsRaw.map((r) => {
|
|
1677
1752
|
try {
|
|
1678
|
-
return sanitizeRelation(r, result);
|
|
1753
|
+
return sanitizeRelation(r, result, (slug) => this.get(slug));
|
|
1679
1754
|
} catch {
|
|
1680
1755
|
return r;
|
|
1681
1756
|
}
|
|
@@ -1888,6 +1963,118 @@
|
|
|
1888
1963
|
};
|
|
1889
1964
|
}
|
|
1890
1965
|
}
|
|
1966
|
+
function mapOperator(op) {
|
|
1967
|
+
switch (op) {
|
|
1968
|
+
case "==":
|
|
1969
|
+
return "eq";
|
|
1970
|
+
case "!=":
|
|
1971
|
+
return "neq";
|
|
1972
|
+
case ">":
|
|
1973
|
+
return "gt";
|
|
1974
|
+
case ">=":
|
|
1975
|
+
return "gte";
|
|
1976
|
+
case "<":
|
|
1977
|
+
return "lt";
|
|
1978
|
+
case "<=":
|
|
1979
|
+
return "lte";
|
|
1980
|
+
case "array-contains":
|
|
1981
|
+
return "cs";
|
|
1982
|
+
case "array-contains-any":
|
|
1983
|
+
return "csa";
|
|
1984
|
+
case "not-in":
|
|
1985
|
+
return "nin";
|
|
1986
|
+
default:
|
|
1987
|
+
return op;
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
class QueryBuilder {
|
|
1991
|
+
constructor(collection) {
|
|
1992
|
+
this.collection = collection;
|
|
1993
|
+
}
|
|
1994
|
+
params = {
|
|
1995
|
+
where: {}
|
|
1996
|
+
};
|
|
1997
|
+
/**
|
|
1998
|
+
* Add a filter condition to your query.
|
|
1999
|
+
* @example
|
|
2000
|
+
* client.collection('users').where('age', '>=', 18).find()
|
|
2001
|
+
*/
|
|
2002
|
+
where(column, operator, value) {
|
|
2003
|
+
if (!this.params.where) {
|
|
2004
|
+
this.params.where = {};
|
|
2005
|
+
}
|
|
2006
|
+
const mappedOp = mapOperator(operator);
|
|
2007
|
+
let formattedValue = value;
|
|
2008
|
+
if (Array.isArray(value) && ["in", "nin", "cs", "csa"].includes(mappedOp)) {
|
|
2009
|
+
formattedValue = `(${value.join(",")})`;
|
|
2010
|
+
} else if (value === null) {
|
|
2011
|
+
formattedValue = "null";
|
|
2012
|
+
}
|
|
2013
|
+
this.params.where[column] = mappedOp === "eq" ? String(formattedValue) : `${mappedOp}.${formattedValue}`;
|
|
2014
|
+
return this;
|
|
2015
|
+
}
|
|
2016
|
+
/**
|
|
2017
|
+
* Order the results by a specific column.
|
|
2018
|
+
* @example
|
|
2019
|
+
* client.collection('users').orderBy('createdAt', 'desc').find()
|
|
2020
|
+
*/
|
|
2021
|
+
orderBy(column, ascending = "asc") {
|
|
2022
|
+
this.params.orderBy = `${column}:${ascending}`;
|
|
2023
|
+
return this;
|
|
2024
|
+
}
|
|
2025
|
+
/**
|
|
2026
|
+
* Limit the number of results returned.
|
|
2027
|
+
*/
|
|
2028
|
+
limit(count) {
|
|
2029
|
+
this.params.limit = count;
|
|
2030
|
+
return this;
|
|
2031
|
+
}
|
|
2032
|
+
/**
|
|
2033
|
+
* Skip the first N results.
|
|
2034
|
+
*/
|
|
2035
|
+
offset(count) {
|
|
2036
|
+
this.params.offset = count;
|
|
2037
|
+
return this;
|
|
2038
|
+
}
|
|
2039
|
+
/**
|
|
2040
|
+
* Set a free-text search string if supported by the backend.
|
|
2041
|
+
*/
|
|
2042
|
+
search(searchString) {
|
|
2043
|
+
this.params.searchString = searchString;
|
|
2044
|
+
return this;
|
|
2045
|
+
}
|
|
2046
|
+
/**
|
|
2047
|
+
* Include related entities in the response.
|
|
2048
|
+
* Relations will be populated with full entity data instead of just IDs.
|
|
2049
|
+
*
|
|
2050
|
+
* @param relations - Relation names to include, or "*" for all.
|
|
2051
|
+
* @example
|
|
2052
|
+
* // Include specific relations
|
|
2053
|
+
* client.data.posts.include("tags", "author").find()
|
|
2054
|
+
*
|
|
2055
|
+
* // Include all relations
|
|
2056
|
+
* client.data.posts.include("*").find()
|
|
2057
|
+
*/
|
|
2058
|
+
include(...relations) {
|
|
2059
|
+
this.params.include = relations;
|
|
2060
|
+
return this;
|
|
2061
|
+
}
|
|
2062
|
+
/**
|
|
2063
|
+
* Execute the find query and return the results.
|
|
2064
|
+
*/
|
|
2065
|
+
async find() {
|
|
2066
|
+
return this.collection.find(this.params);
|
|
2067
|
+
}
|
|
2068
|
+
/**
|
|
2069
|
+
* Listen to realtime updates matching this query.
|
|
2070
|
+
*/
|
|
2071
|
+
listen(onUpdate, onError) {
|
|
2072
|
+
if (!this.collection.listen) {
|
|
2073
|
+
throw new Error("Listen is only available when RebaseClient is configured with a websocketUrl.");
|
|
2074
|
+
}
|
|
2075
|
+
return this.collection.listen(this.params, onUpdate, onError);
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
1891
2078
|
function convertWhereToFilter(where) {
|
|
1892
2079
|
if (!where) return void 0;
|
|
1893
2080
|
const operatorMap = {
|
|
@@ -1967,7 +2154,7 @@
|
|
|
1967
2154
|
return [field, direction];
|
|
1968
2155
|
}
|
|
1969
2156
|
function createDriverAccessor(driver, slug) {
|
|
1970
|
-
|
|
2157
|
+
const accessor = {
|
|
1971
2158
|
async find(params) {
|
|
1972
2159
|
const orderParsed = parseOrderBy(params?.orderBy);
|
|
1973
2160
|
const entities = await driver.fetchCollection({
|
|
@@ -2061,8 +2248,28 @@
|
|
|
2061
2248
|
onUpdate: (entity) => onUpdate(entity ?? void 0),
|
|
2062
2249
|
onError
|
|
2063
2250
|
});
|
|
2064
|
-
} : void 0
|
|
2251
|
+
} : void 0,
|
|
2252
|
+
// Fluent Query Builder
|
|
2253
|
+
where(column, operator, value) {
|
|
2254
|
+
return new QueryBuilder(accessor).where(column, operator, value);
|
|
2255
|
+
},
|
|
2256
|
+
orderBy(column, ascending) {
|
|
2257
|
+
return new QueryBuilder(accessor).orderBy(column, ascending);
|
|
2258
|
+
},
|
|
2259
|
+
limit(count) {
|
|
2260
|
+
return new QueryBuilder(accessor).limit(count);
|
|
2261
|
+
},
|
|
2262
|
+
offset(count) {
|
|
2263
|
+
return new QueryBuilder(accessor).offset(count);
|
|
2264
|
+
},
|
|
2265
|
+
search(searchString) {
|
|
2266
|
+
return new QueryBuilder(accessor).search(searchString);
|
|
2267
|
+
},
|
|
2268
|
+
include(...relations) {
|
|
2269
|
+
return new QueryBuilder(accessor).include(...relations);
|
|
2270
|
+
}
|
|
2065
2271
|
};
|
|
2272
|
+
return accessor;
|
|
2066
2273
|
}
|
|
2067
2274
|
function buildRebaseData(driver) {
|
|
2068
2275
|
const cache = /* @__PURE__ */ new Map();
|
|
@@ -2091,6 +2298,7 @@
|
|
|
2091
2298
|
exports2.CollectionRegistry = CollectionRegistry;
|
|
2092
2299
|
exports2.DEFAULT_ONE_OF_TYPE = DEFAULT_ONE_OF_TYPE;
|
|
2093
2300
|
exports2.DEFAULT_ONE_OF_VALUE = DEFAULT_ONE_OF_VALUE;
|
|
2301
|
+
exports2.QueryBuilder = QueryBuilder;
|
|
2094
2302
|
exports2.addInitialSlash = addInitialSlash;
|
|
2095
2303
|
exports2.applyPropertyConditions = applyPropertyConditions;
|
|
2096
2304
|
exports2.buildAdditionalFieldDelegate = buildAdditionalFieldDelegate;
|