@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.
@@ -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
+ }
@@ -1,3 +1,4 @@
1
1
  export * from "./util";
2
2
  export * from "./collections";
3
3
  export * from "./data/buildRebaseData";
4
+ export * from "./data/query_builder";
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
- const normalizedRelation = sanitizeRelation(relation, collection);
1355
- const relationKey = normalizedRelation.relationName;
1356
- if (relationKey) {
1357
- relations[relationKey] = normalizedRelation;
1358
- registeredRelationNames.add(relationKey);
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
- const relation = (sourceCollection.relations ?? []).find((rel) => rel.relationName === relProp.relationName);
1407
- if (!relation) {
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 || !mergedRelationsRaw.find((r) => r.relationName === 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$1(op) {
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$1(op);
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,