@rebasepro/server-core 0.2.1 → 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/dist/common/src/data/query_builder.d.ts +51 -0
- package/dist/common/src/index.d.ts +1 -0
- package/dist/index.es.js +174 -162
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +174 -162
- package/dist/index.umd.js.map +1 -1
- package/dist/types/src/controllers/data.d.ts +21 -0
- package/jest.config.cjs +4 -1
- package/package.json +6 -6
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { FindResponse, CollectionAccessor, QueryBuilderInterface, FilterOperator } from "@rebasepro/types";
|
|
2
|
+
export declare class QueryBuilder<M extends Record<string, unknown> = Record<string, unknown>> implements QueryBuilderInterface<M> {
|
|
3
|
+
private collection;
|
|
4
|
+
private params;
|
|
5
|
+
constructor(collection: CollectionAccessor<M>);
|
|
6
|
+
/**
|
|
7
|
+
* Add a filter condition to your query.
|
|
8
|
+
* @example
|
|
9
|
+
* client.collection('users').where('age', '>=', 18).find()
|
|
10
|
+
*/
|
|
11
|
+
where(column: keyof M & string, operator: FilterOperator, value: unknown): this;
|
|
12
|
+
/**
|
|
13
|
+
* Order the results by a specific column.
|
|
14
|
+
* @example
|
|
15
|
+
* client.collection('users').orderBy('createdAt', 'desc').find()
|
|
16
|
+
*/
|
|
17
|
+
orderBy(column: keyof M & string, ascending?: "asc" | "desc"): this;
|
|
18
|
+
/**
|
|
19
|
+
* Limit the number of results returned.
|
|
20
|
+
*/
|
|
21
|
+
limit(count: number): this;
|
|
22
|
+
/**
|
|
23
|
+
* Skip the first N results.
|
|
24
|
+
*/
|
|
25
|
+
offset(count: number): this;
|
|
26
|
+
/**
|
|
27
|
+
* Set a free-text search string if supported by the backend.
|
|
28
|
+
*/
|
|
29
|
+
search(searchString: string): this;
|
|
30
|
+
/**
|
|
31
|
+
* Include related entities in the response.
|
|
32
|
+
* Relations will be populated with full entity data instead of just IDs.
|
|
33
|
+
*
|
|
34
|
+
* @param relations - Relation names to include, or "*" for all.
|
|
35
|
+
* @example
|
|
36
|
+
* // Include specific relations
|
|
37
|
+
* client.data.posts.include("tags", "author").find()
|
|
38
|
+
*
|
|
39
|
+
* // Include all relations
|
|
40
|
+
* client.data.posts.include("*").find()
|
|
41
|
+
*/
|
|
42
|
+
include(...relations: string[]): this;
|
|
43
|
+
/**
|
|
44
|
+
* Execute the find query and return the results.
|
|
45
|
+
*/
|
|
46
|
+
find(): Promise<FindResponse<M>>;
|
|
47
|
+
/**
|
|
48
|
+
* Listen to realtime updates matching this query.
|
|
49
|
+
*/
|
|
50
|
+
listen(onUpdate: (data: FindResponse<M>) => void, onError?: (error: Error) => void): () => void;
|
|
51
|
+
}
|
package/dist/index.es.js
CHANGED
|
@@ -1165,42 +1165,6 @@ function enumToObjectEntries(enumValues) {
|
|
|
1165
1165
|
});
|
|
1166
1166
|
}
|
|
1167
1167
|
}
|
|
1168
|
-
function getSubcollections(collection) {
|
|
1169
|
-
if (collection.childCollections) {
|
|
1170
|
-
return collection.childCollections() ?? [];
|
|
1171
|
-
}
|
|
1172
|
-
if (getDataSourceCapabilities(collection.driver).supportsSubcollections && collection.subcollections) {
|
|
1173
|
-
return collection.subcollections() ?? [];
|
|
1174
|
-
}
|
|
1175
|
-
if (getDataSourceCapabilities(collection.driver).supportsRelations && collection.relations) {
|
|
1176
|
-
const manyRelations = collection.relations.filter((r) => r.cardinality === "many");
|
|
1177
|
-
return manyRelations.map((r) => {
|
|
1178
|
-
const target = r.target();
|
|
1179
|
-
if (!target) return void 0;
|
|
1180
|
-
const relationKey = r.relationName || target.slug;
|
|
1181
|
-
let customName;
|
|
1182
|
-
if (collection.properties) {
|
|
1183
|
-
const prop = Object.entries(collection.properties).find(([_, p]) => p.type === "relation" && p.relationName === relationKey);
|
|
1184
|
-
if (prop && prop[1].name) {
|
|
1185
|
-
customName = prop[1].name;
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
const baseOverrides = {
|
|
1189
|
-
slug: relationKey
|
|
1190
|
-
};
|
|
1191
|
-
if (customName) {
|
|
1192
|
-
baseOverrides.name = customName;
|
|
1193
|
-
baseOverrides.singularName = customName;
|
|
1194
|
-
}
|
|
1195
|
-
const targetWithOverrides = {
|
|
1196
|
-
...target,
|
|
1197
|
-
...baseOverrides
|
|
1198
|
-
};
|
|
1199
|
-
return r.overrides ? mergeDeep(targetWithOverrides, r.overrides) : targetWithOverrides;
|
|
1200
|
-
}).filter((c) => Boolean(c));
|
|
1201
|
-
}
|
|
1202
|
-
return [];
|
|
1203
|
-
}
|
|
1204
1168
|
function sanitizeRelation(relation, sourceCollection, resolveCollection) {
|
|
1205
1169
|
if (!relation.target) {
|
|
1206
1170
|
throw new Error("Relation is missing a `target` collection.");
|
|
@@ -1232,6 +1196,8 @@ function sanitizeRelation(relation, sourceCollection, resolveCollection) {
|
|
|
1232
1196
|
} else {
|
|
1233
1197
|
targetCollection = evaluated;
|
|
1234
1198
|
}
|
|
1199
|
+
} else if (rawTarget && typeof rawTarget === "object") {
|
|
1200
|
+
targetCollection = rawTarget;
|
|
1235
1201
|
}
|
|
1236
1202
|
if (!targetCollection) {
|
|
1237
1203
|
throw new Error("Relation is missing a valid `target` collection.");
|
|
@@ -1351,11 +1317,14 @@ function resolveCollectionRelations(collection) {
|
|
|
1351
1317
|
const registeredRelationNames = /* @__PURE__ */ new Set();
|
|
1352
1318
|
if (relCollection.relations) {
|
|
1353
1319
|
relCollection.relations.forEach((relation) => {
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1320
|
+
try {
|
|
1321
|
+
const normalizedRelation = sanitizeRelation(relation, collection);
|
|
1322
|
+
const relationKey = normalizedRelation.relationName;
|
|
1323
|
+
if (relationKey) {
|
|
1324
|
+
relations[relationKey] = normalizedRelation;
|
|
1325
|
+
registeredRelationNames.add(relationKey);
|
|
1326
|
+
}
|
|
1327
|
+
} catch (e) {
|
|
1359
1328
|
}
|
|
1360
1329
|
});
|
|
1361
1330
|
}
|
|
@@ -1403,12 +1372,8 @@ function resolvePropertyRelation({
|
|
|
1403
1372
|
overrides: relProp.overrides
|
|
1404
1373
|
};
|
|
1405
1374
|
}
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
console.warn(`Unrecognized relation format for property '${propertyKey}' in collection '${sourceCollection.slug}'`);
|
|
1409
|
-
return void 0;
|
|
1410
|
-
}
|
|
1411
|
-
return relation;
|
|
1375
|
+
console.warn(`Unrecognized or missing relation target for property '${propertyKey}' in collection '${sourceCollection.slug}'`);
|
|
1376
|
+
return void 0;
|
|
1412
1377
|
}
|
|
1413
1378
|
function getTableName(collection) {
|
|
1414
1379
|
if (getDataSourceCapabilities(collection.driver).supportsRelations) {
|
|
@@ -1424,6 +1389,43 @@ function findRelation(resolvedRelations, key) {
|
|
|
1424
1389
|
if (snakeKey !== key && resolvedRelations[snakeKey]) return resolvedRelations[snakeKey];
|
|
1425
1390
|
return void 0;
|
|
1426
1391
|
}
|
|
1392
|
+
function getSubcollections(collection) {
|
|
1393
|
+
if (collection.childCollections) {
|
|
1394
|
+
return collection.childCollections() ?? [];
|
|
1395
|
+
}
|
|
1396
|
+
if (getDataSourceCapabilities(collection.driver).supportsSubcollections && collection.subcollections) {
|
|
1397
|
+
return collection.subcollections() ?? [];
|
|
1398
|
+
}
|
|
1399
|
+
if (getDataSourceCapabilities(collection.driver).supportsRelations) {
|
|
1400
|
+
const resolvedRelations = resolveCollectionRelations(collection);
|
|
1401
|
+
const manyRelations = Object.values(resolvedRelations).filter((r) => r.cardinality === "many");
|
|
1402
|
+
return manyRelations.map((r) => {
|
|
1403
|
+
const target = r.target();
|
|
1404
|
+
if (!target) return void 0;
|
|
1405
|
+
const relationKey = r.relationName || target.slug;
|
|
1406
|
+
let customName;
|
|
1407
|
+
if (collection.properties) {
|
|
1408
|
+
const prop = Object.entries(collection.properties).find(([_, p]) => p.type === "relation" && p.relationName === relationKey);
|
|
1409
|
+
if (prop && prop[1].name) {
|
|
1410
|
+
customName = prop[1].name;
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
const baseOverrides = {
|
|
1414
|
+
slug: relationKey
|
|
1415
|
+
};
|
|
1416
|
+
if (customName) {
|
|
1417
|
+
baseOverrides.name = customName;
|
|
1418
|
+
baseOverrides.singularName = customName;
|
|
1419
|
+
}
|
|
1420
|
+
const targetWithOverrides = {
|
|
1421
|
+
...target,
|
|
1422
|
+
...baseOverrides
|
|
1423
|
+
};
|
|
1424
|
+
return r.overrides ? mergeDeep(targetWithOverrides, r.overrides) : targetWithOverrides;
|
|
1425
|
+
}).filter((c) => Boolean(c));
|
|
1426
|
+
}
|
|
1427
|
+
return [];
|
|
1428
|
+
}
|
|
1427
1429
|
var logic = { exports: {} };
|
|
1428
1430
|
(function(module, exports) {
|
|
1429
1431
|
(function(root, factory) {
|
|
@@ -2331,8 +2333,18 @@ class CollectionRegistry {
|
|
|
2331
2333
|
const mergedRelationsRaw = [...extractedRelations];
|
|
2332
2334
|
for (const manual of manualRelations) {
|
|
2333
2335
|
const name2 = manual.relationName;
|
|
2334
|
-
if (!name2
|
|
2336
|
+
if (!name2) {
|
|
2335
2337
|
mergedRelationsRaw.push(manual);
|
|
2338
|
+
} else {
|
|
2339
|
+
const existingIndex = mergedRelationsRaw.findIndex((r) => r.relationName === name2);
|
|
2340
|
+
if (existingIndex === -1) {
|
|
2341
|
+
mergedRelationsRaw.push(manual);
|
|
2342
|
+
} else {
|
|
2343
|
+
mergedRelationsRaw[existingIndex] = {
|
|
2344
|
+
...manual,
|
|
2345
|
+
...mergedRelationsRaw[existingIndex]
|
|
2346
|
+
};
|
|
2347
|
+
}
|
|
2336
2348
|
}
|
|
2337
2349
|
}
|
|
2338
2350
|
let mergedRelations = mergedRelationsRaw;
|
|
@@ -2552,6 +2564,118 @@ class CollectionRegistry {
|
|
|
2552
2564
|
};
|
|
2553
2565
|
}
|
|
2554
2566
|
}
|
|
2567
|
+
function mapOperator$1(op) {
|
|
2568
|
+
switch (op) {
|
|
2569
|
+
case "==":
|
|
2570
|
+
return "eq";
|
|
2571
|
+
case "!=":
|
|
2572
|
+
return "neq";
|
|
2573
|
+
case ">":
|
|
2574
|
+
return "gt";
|
|
2575
|
+
case ">=":
|
|
2576
|
+
return "gte";
|
|
2577
|
+
case "<":
|
|
2578
|
+
return "lt";
|
|
2579
|
+
case "<=":
|
|
2580
|
+
return "lte";
|
|
2581
|
+
case "array-contains":
|
|
2582
|
+
return "cs";
|
|
2583
|
+
case "array-contains-any":
|
|
2584
|
+
return "csa";
|
|
2585
|
+
case "not-in":
|
|
2586
|
+
return "nin";
|
|
2587
|
+
default:
|
|
2588
|
+
return op;
|
|
2589
|
+
}
|
|
2590
|
+
}
|
|
2591
|
+
class QueryBuilder {
|
|
2592
|
+
constructor(collection) {
|
|
2593
|
+
this.collection = collection;
|
|
2594
|
+
}
|
|
2595
|
+
params = {
|
|
2596
|
+
where: {}
|
|
2597
|
+
};
|
|
2598
|
+
/**
|
|
2599
|
+
* Add a filter condition to your query.
|
|
2600
|
+
* @example
|
|
2601
|
+
* client.collection('users').where('age', '>=', 18).find()
|
|
2602
|
+
*/
|
|
2603
|
+
where(column, operator, value) {
|
|
2604
|
+
if (!this.params.where) {
|
|
2605
|
+
this.params.where = {};
|
|
2606
|
+
}
|
|
2607
|
+
const mappedOp = mapOperator$1(operator);
|
|
2608
|
+
let formattedValue = value;
|
|
2609
|
+
if (Array.isArray(value) && ["in", "nin", "cs", "csa"].includes(mappedOp)) {
|
|
2610
|
+
formattedValue = `(${value.join(",")})`;
|
|
2611
|
+
} else if (value === null) {
|
|
2612
|
+
formattedValue = "null";
|
|
2613
|
+
}
|
|
2614
|
+
this.params.where[column] = mappedOp === "eq" ? String(formattedValue) : `${mappedOp}.${formattedValue}`;
|
|
2615
|
+
return this;
|
|
2616
|
+
}
|
|
2617
|
+
/**
|
|
2618
|
+
* Order the results by a specific column.
|
|
2619
|
+
* @example
|
|
2620
|
+
* client.collection('users').orderBy('createdAt', 'desc').find()
|
|
2621
|
+
*/
|
|
2622
|
+
orderBy(column, ascending = "asc") {
|
|
2623
|
+
this.params.orderBy = `${column}:${ascending}`;
|
|
2624
|
+
return this;
|
|
2625
|
+
}
|
|
2626
|
+
/**
|
|
2627
|
+
* Limit the number of results returned.
|
|
2628
|
+
*/
|
|
2629
|
+
limit(count) {
|
|
2630
|
+
this.params.limit = count;
|
|
2631
|
+
return this;
|
|
2632
|
+
}
|
|
2633
|
+
/**
|
|
2634
|
+
* Skip the first N results.
|
|
2635
|
+
*/
|
|
2636
|
+
offset(count) {
|
|
2637
|
+
this.params.offset = count;
|
|
2638
|
+
return this;
|
|
2639
|
+
}
|
|
2640
|
+
/**
|
|
2641
|
+
* Set a free-text search string if supported by the backend.
|
|
2642
|
+
*/
|
|
2643
|
+
search(searchString) {
|
|
2644
|
+
this.params.searchString = searchString;
|
|
2645
|
+
return this;
|
|
2646
|
+
}
|
|
2647
|
+
/**
|
|
2648
|
+
* Include related entities in the response.
|
|
2649
|
+
* Relations will be populated with full entity data instead of just IDs.
|
|
2650
|
+
*
|
|
2651
|
+
* @param relations - Relation names to include, or "*" for all.
|
|
2652
|
+
* @example
|
|
2653
|
+
* // Include specific relations
|
|
2654
|
+
* client.data.posts.include("tags", "author").find()
|
|
2655
|
+
*
|
|
2656
|
+
* // Include all relations
|
|
2657
|
+
* client.data.posts.include("*").find()
|
|
2658
|
+
*/
|
|
2659
|
+
include(...relations) {
|
|
2660
|
+
this.params.include = relations;
|
|
2661
|
+
return this;
|
|
2662
|
+
}
|
|
2663
|
+
/**
|
|
2664
|
+
* Execute the find query and return the results.
|
|
2665
|
+
*/
|
|
2666
|
+
async find() {
|
|
2667
|
+
return this.collection.find(this.params);
|
|
2668
|
+
}
|
|
2669
|
+
/**
|
|
2670
|
+
* Listen to realtime updates matching this query.
|
|
2671
|
+
*/
|
|
2672
|
+
listen(onUpdate, onError) {
|
|
2673
|
+
if (!this.collection.listen) {
|
|
2674
|
+
throw new Error("Listen is only available when RebaseClient is configured with a websocketUrl.");
|
|
2675
|
+
}
|
|
2676
|
+
return this.collection.listen(this.params, onUpdate, onError);
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2555
2679
|
class BackendCollectionRegistry extends CollectionRegistry {
|
|
2556
2680
|
/**
|
|
2557
2681
|
* Get the available relation keys for a given collection path.
|
|
@@ -2779,7 +2903,7 @@ function codeToStatus(code2) {
|
|
|
2779
2903
|
};
|
|
2780
2904
|
return map2[code2];
|
|
2781
2905
|
}
|
|
2782
|
-
function mapOperator
|
|
2906
|
+
function mapOperator(op) {
|
|
2783
2907
|
switch (op) {
|
|
2784
2908
|
case "eq":
|
|
2785
2909
|
return "==";
|
|
@@ -2824,7 +2948,7 @@ function parseQueryOptions(query) {
|
|
|
2824
2948
|
if (parts.length >= 2) {
|
|
2825
2949
|
const op = parts[0];
|
|
2826
2950
|
const val = parts.slice(1).join(".");
|
|
2827
|
-
const rebaseOp = mapOperator
|
|
2951
|
+
const rebaseOp = mapOperator(op);
|
|
2828
2952
|
if (rebaseOp) {
|
|
2829
2953
|
let parsedVal = val;
|
|
2830
2954
|
if (val === "true") parsedVal = true;
|
|
@@ -24998,118 +25122,6 @@ function createCron(transport, options2) {
|
|
|
24998
25122
|
toggleJob
|
|
24999
25123
|
};
|
|
25000
25124
|
}
|
|
25001
|
-
function mapOperator(op) {
|
|
25002
|
-
switch (op) {
|
|
25003
|
-
case "==":
|
|
25004
|
-
return "eq";
|
|
25005
|
-
case "!=":
|
|
25006
|
-
return "neq";
|
|
25007
|
-
case ">":
|
|
25008
|
-
return "gt";
|
|
25009
|
-
case ">=":
|
|
25010
|
-
return "gte";
|
|
25011
|
-
case "<":
|
|
25012
|
-
return "lt";
|
|
25013
|
-
case "<=":
|
|
25014
|
-
return "lte";
|
|
25015
|
-
case "array-contains":
|
|
25016
|
-
return "cs";
|
|
25017
|
-
case "array-contains-any":
|
|
25018
|
-
return "csa";
|
|
25019
|
-
case "not-in":
|
|
25020
|
-
return "nin";
|
|
25021
|
-
default:
|
|
25022
|
-
return op;
|
|
25023
|
-
}
|
|
25024
|
-
}
|
|
25025
|
-
class QueryBuilder {
|
|
25026
|
-
constructor(collection) {
|
|
25027
|
-
this.collection = collection;
|
|
25028
|
-
}
|
|
25029
|
-
params = {
|
|
25030
|
-
where: {}
|
|
25031
|
-
};
|
|
25032
|
-
/**
|
|
25033
|
-
* Add a filter condition to your query.
|
|
25034
|
-
* @example
|
|
25035
|
-
* client.collection('users').where('age', '>=', 18).find()
|
|
25036
|
-
*/
|
|
25037
|
-
where(column, operator, value) {
|
|
25038
|
-
if (!this.params.where) {
|
|
25039
|
-
this.params.where = {};
|
|
25040
|
-
}
|
|
25041
|
-
const mappedOp = mapOperator(operator);
|
|
25042
|
-
let formattedValue = value;
|
|
25043
|
-
if (Array.isArray(value) && ["in", "nin", "cs", "csa"].includes(mappedOp)) {
|
|
25044
|
-
formattedValue = `(${value.join(",")})`;
|
|
25045
|
-
} else if (value === null) {
|
|
25046
|
-
formattedValue = "null";
|
|
25047
|
-
}
|
|
25048
|
-
this.params.where[column] = mappedOp === "eq" ? String(formattedValue) : `${mappedOp}.${formattedValue}`;
|
|
25049
|
-
return this;
|
|
25050
|
-
}
|
|
25051
|
-
/**
|
|
25052
|
-
* Order the results by a specific column.
|
|
25053
|
-
* @example
|
|
25054
|
-
* client.collection('users').orderBy('createdAt', 'desc').find()
|
|
25055
|
-
*/
|
|
25056
|
-
orderBy(column, ascending = "asc") {
|
|
25057
|
-
this.params.orderBy = `${column}:${ascending}`;
|
|
25058
|
-
return this;
|
|
25059
|
-
}
|
|
25060
|
-
/**
|
|
25061
|
-
* Limit the number of results returned.
|
|
25062
|
-
*/
|
|
25063
|
-
limit(count) {
|
|
25064
|
-
this.params.limit = count;
|
|
25065
|
-
return this;
|
|
25066
|
-
}
|
|
25067
|
-
/**
|
|
25068
|
-
* Skip the first N results.
|
|
25069
|
-
*/
|
|
25070
|
-
offset(count) {
|
|
25071
|
-
this.params.offset = count;
|
|
25072
|
-
return this;
|
|
25073
|
-
}
|
|
25074
|
-
/**
|
|
25075
|
-
* Set a free-text search string if supported by the backend.
|
|
25076
|
-
*/
|
|
25077
|
-
search(searchString) {
|
|
25078
|
-
this.params.searchString = searchString;
|
|
25079
|
-
return this;
|
|
25080
|
-
}
|
|
25081
|
-
/**
|
|
25082
|
-
* Include related entities in the response.
|
|
25083
|
-
* Relations will be populated with full entity data instead of just IDs.
|
|
25084
|
-
*
|
|
25085
|
-
* @param relations - Relation names to include, or "*" for all.
|
|
25086
|
-
* @example
|
|
25087
|
-
* // Include specific relations
|
|
25088
|
-
* client.data.posts.include("tags", "author").find()
|
|
25089
|
-
*
|
|
25090
|
-
* // Include all relations
|
|
25091
|
-
* client.data.posts.include("*").find()
|
|
25092
|
-
*/
|
|
25093
|
-
include(...relations) {
|
|
25094
|
-
this.params.include = relations;
|
|
25095
|
-
return this;
|
|
25096
|
-
}
|
|
25097
|
-
/**
|
|
25098
|
-
* Execute the find query and return the results.
|
|
25099
|
-
*/
|
|
25100
|
-
async find() {
|
|
25101
|
-
return this.collection.find(this.params);
|
|
25102
|
-
}
|
|
25103
|
-
/**
|
|
25104
|
-
* Listen to realtime updates matching this query.
|
|
25105
|
-
*/
|
|
25106
|
-
listen(onUpdate, onError) {
|
|
25107
|
-
if (!this.collection.listen) {
|
|
25108
|
-
throw new Error("Listen is only available when RebaseClient is configured with a websocketUrl.");
|
|
25109
|
-
}
|
|
25110
|
-
return this.collection.listen(this.params, onUpdate, onError);
|
|
25111
|
-
}
|
|
25112
|
-
}
|
|
25113
25125
|
function rowToEntity(row, slug) {
|
|
25114
25126
|
return {
|
|
25115
25127
|
id: row.id,
|