@rebasepro/common 0.2.5 → 0.4.0

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rebasepro/common",
3
3
  "type": "module",
4
- "version": "0.2.5",
4
+ "version": "0.4.0",
5
5
  "description": "Awesome Firebase/Firestore-based headless open-source CMS",
6
6
  "funding": {
7
7
  "url": "https://github.com/sponsors/rebaseco"
@@ -41,8 +41,8 @@
41
41
  "dependencies": {
42
42
  "fast-equals": "6.0.0",
43
43
  "json-logic-js": "^2.0.5",
44
- "@rebasepro/types": "0.2.5",
45
- "@rebasepro/utils": "0.2.5"
44
+ "@rebasepro/types": "0.4.0",
45
+ "@rebasepro/utils": "0.4.0"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@jest/globals": "^29.7.0",
@@ -17,6 +17,10 @@ export const defaultUsersCollection: PostgresCollection = {
17
17
  group: "Settings",
18
18
  openEntityMode: "dialog",
19
19
  disableDefaultActions: ["copy"],
20
+ securityRules: [
21
+ { operation: "select", roles: ["admin"] },
22
+ { operations: ["insert", "update", "delete"], roles: ["admin"] }
23
+ ],
20
24
  sort: ["createdAt", "desc"],
21
25
  properties: {
22
26
  id: {
@@ -91,6 +95,7 @@ export const defaultUsersCollection: PostgresCollection = {
91
95
  name: "Created At",
92
96
  type: "date",
93
97
  columnName: "created_at",
98
+ autoValue: "on_create",
94
99
  ui: { readOnly: true }
95
100
  },
96
101
  updatedAt: {
@@ -8,7 +8,10 @@ import {
8
8
  EntityValues,
9
9
  FilterValues,
10
10
  WhereFilterOp,
11
- WhereFieldValue
11
+ WhereFieldValue,
12
+ WhereFilterOpShort,
13
+ LogicalCondition,
14
+ WhereValue
12
15
  } from "@rebasepro/types";
13
16
  import { toSnakeCase } from "@rebasepro/utils";
14
17
  import { QueryBuilder } from "./query_builder";
@@ -69,11 +72,18 @@ function convertWhereToFilter(where?: Record<string, WhereFieldValue>): FilterVa
69
72
  continue;
70
73
  }
71
74
 
72
- // Handle tuple: [operator, value]
73
- if (Array.isArray(rawValue) && rawValue.length === 2) {
74
- const [rawOp, val] = rawValue;
75
- const mappedOp = operatorMap[rawOp] ?? "==";
76
- filter[field] = [mappedOp, val];
75
+ // Handle tuple or array of tuples
76
+ if (Array.isArray(rawValue)) {
77
+ const conditions: [WhereFilterOpShort, unknown][] = Array.isArray(rawValue[0])
78
+ ? (rawValue as [WhereFilterOpShort, unknown][])
79
+ : [rawValue as [WhereFilterOpShort, unknown]];
80
+
81
+ const mappedConditions: [WhereFilterOp, unknown][] = conditions.map(([rawOp, val]) => {
82
+ const mappedOp = operatorMap[rawOp] ?? "==";
83
+ return [mappedOp, val];
84
+ });
85
+
86
+ filter[field] = Array.isArray(rawValue[0]) ? mappedConditions : mappedConditions[0];
77
87
  continue;
78
88
  }
79
89
 
@@ -244,8 +254,12 @@ values: {} as Record<string, unknown> }
244
254
  } : undefined,
245
255
 
246
256
  // Fluent Query Builder
247
- where(column: keyof M & string, operator: WhereFilterOp, value: unknown) {
248
- return new QueryBuilder<M>(accessor).where(column, operator, value);
257
+ where(columnOrCondition: string | LogicalCondition, operator?: WhereFilterOpShort, value?: unknown) {
258
+ const builder = new QueryBuilder<M>(accessor);
259
+ if (typeof columnOrCondition === "object") {
260
+ return builder.where(columnOrCondition);
261
+ }
262
+ return builder.where(columnOrCondition as keyof M & string, operator!, value as WhereValue<M[keyof M & string]>);
249
263
  },
250
264
  orderBy(column: keyof M & string, ascending?: "asc" | "desc") {
251
265
  return new QueryBuilder<M>(accessor).orderBy(column, ascending);
@@ -1,21 +1,15 @@
1
- import { FindParams, Entity, FindResponse, CollectionAccessor, QueryBuilderInterface, FilterOperator } from "@rebasepro/types";
2
-
3
- /**
4
- * Maps standard operators to Rebase backend's string-based operators
5
- */
6
- function mapOperator(op: FilterOperator): string {
7
- switch (op) {
8
- case "==": return "eq";
9
- case "!=": return "neq";
10
- case ">": return "gt";
11
- case ">=": return "gte";
12
- case "<": return "lt";
13
- case "<=": return "lte";
14
- case "array-contains": return "cs";
15
- case "array-contains-any": return "csa";
16
- case "not-in": return "nin";
17
- default: return op;
18
- }
1
+ import { FindParams, Entity, FindResponse, CollectionAccessor, QueryBuilderInterface, FilterOperator, LogicalCondition, WhereValue, FilterCondition } from "@rebasepro/types";
2
+
3
+ export function or(...conditions: (FilterCondition | LogicalCondition)[]): LogicalCondition {
4
+ return { type: "or", conditions };
5
+ }
6
+
7
+ export function and(...conditions: (FilterCondition | LogicalCondition)[]): LogicalCondition {
8
+ return { type: "and", conditions };
9
+ }
10
+
11
+ export function cond(column: string, operator: FilterOperator, value: unknown): FilterCondition {
12
+ return { column, operator, value };
19
13
  }
20
14
 
21
15
  export class QueryBuilder<M extends Record<string, unknown> = Record<string, unknown>> implements QueryBuilderInterface<M> {
@@ -28,22 +22,38 @@ export class QueryBuilder<M extends Record<string, unknown> = Record<string, unk
28
22
  * @example
29
23
  * client.collection('users').where('age', '>=', 18).find()
30
24
  */
31
- where(column: keyof M & string, operator: FilterOperator, value: unknown): this {
25
+ where<K extends keyof M & string>(column: K, operator: FilterOperator, value: WhereValue<M[K]>): this;
26
+ where(logicalCondition: LogicalCondition): this;
27
+ where(columnOrCondition: string | LogicalCondition, operator?: FilterOperator, value?: unknown): this {
28
+ // Handle LogicalCondition signature
29
+ if (typeof columnOrCondition === "object" && columnOrCondition !== null && "type" in columnOrCondition) {
30
+ this.params.logical = columnOrCondition as LogicalCondition;
31
+ return this;
32
+ }
33
+
32
34
  if (!this.params.where) {
33
35
  this.params.where = {};
34
36
  }
35
37
 
36
- const mappedOp = mapOperator(operator);
37
- let formattedValue = value;
38
+ const column = columnOrCondition as string;
39
+ const condition: [FilterOperator, unknown] = [operator!, value];
40
+ const existing = this.params.where[column];
38
41
 
39
- // Handle arrays for in, nin, cs, csa
40
- if (Array.isArray(value) && ["in", "nin", "cs", "csa"].includes(mappedOp)) {
41
- formattedValue = `(${value.join(",")})`;
42
- } else if (value === null) {
43
- formattedValue = "null";
42
+ if (existing === undefined) {
43
+ this.params.where[column] = condition;
44
+ } else if (Array.isArray(existing) && existing.length > 0 && Array.isArray(existing[0])) {
45
+ (this.params.where[column] as [FilterOperator, unknown][]).push(condition);
46
+ } else {
47
+ // Convert existing single tuple/value into array of tuples
48
+ let firstCondition: [FilterOperator, unknown];
49
+ if (Array.isArray(existing) && existing.length === 2 && typeof existing[0] === "string") {
50
+ firstCondition = existing as [FilterOperator, unknown];
51
+ } else {
52
+ firstCondition = ["==", existing];
53
+ }
54
+ this.params.where[column] = [firstCondition, condition];
44
55
  }
45
56
 
46
- this.params.where[column] = mappedOp === "eq" ? String(formattedValue) : `${mappedOp}.${formattedValue}`;
47
57
  return this;
48
58
  }
49
59