@rebasepro/server-postgresql 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.
@@ -1,4 +1,7 @@
1
- import { FindResponse, CollectionAccessor, QueryBuilderInterface, FilterOperator } from "@rebasepro/types";
1
+ import { FindResponse, CollectionAccessor, QueryBuilderInterface, FilterOperator, LogicalCondition, WhereValue, FilterCondition } from "@rebasepro/types";
2
+ export declare function or(...conditions: (FilterCondition | LogicalCondition)[]): LogicalCondition;
3
+ export declare function and(...conditions: (FilterCondition | LogicalCondition)[]): LogicalCondition;
4
+ export declare function cond(column: string, operator: FilterOperator, value: unknown): FilterCondition;
2
5
  export declare class QueryBuilder<M extends Record<string, unknown> = Record<string, unknown>> implements QueryBuilderInterface<M> {
3
6
  private collection;
4
7
  private params;
@@ -8,7 +11,8 @@ export declare class QueryBuilder<M extends Record<string, unknown> = Record<str
8
11
  * @example
9
12
  * client.collection('users').where('age', '>=', 18).find()
10
13
  */
11
- where(column: keyof M & string, operator: FilterOperator, value: unknown): this;
14
+ where<K extends keyof M & string>(column: K, operator: FilterOperator, value: WhereValue<M[K]>): this;
15
+ where(logicalCondition: LogicalCondition): this;
12
16
  /**
13
17
  * Order the results by a specific column.
14
18
  * @example
package/dist/index.es.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Pool, Client } from "pg";
2
2
  import { drizzle } from "drizzle-orm/node-postgres";
3
- import { sql, inArray, eq, and, or, ilike, asc, desc, gt, lt, getTableName as getTableName$1, count, relations, isTable } from "drizzle-orm";
3
+ import { or, and, sql, inArray, eq, ilike, asc, desc, gt, lt, getTableName as getTableName$1, count, relations, isTable } from "drizzle-orm";
4
4
  import { PgVarchar, PgText, PgChar, pgSchema, pgTable, timestamp, jsonb, text, boolean, varchar, uuid, unique, getTableConfig } from "drizzle-orm/pg-core";
5
5
  import { createHash, randomUUID } from "crypto";
6
6
  import * as fs from "fs";
@@ -2711,166 +2711,6 @@ class CollectionRegistry {
2711
2711
  };
2712
2712
  }
2713
2713
  }
2714
- const defaultUsersCollection = {
2715
- name: "Users",
2716
- singularName: "User",
2717
- slug: "users",
2718
- table: "users",
2719
- schema: "rebase",
2720
- icon: "Users",
2721
- group: "Settings",
2722
- openEntityMode: "dialog",
2723
- disableDefaultActions: ["copy"],
2724
- sort: ["createdAt", "desc"],
2725
- properties: {
2726
- id: {
2727
- name: "ID",
2728
- type: "string",
2729
- isId: "uuid",
2730
- ui: {
2731
- readOnly: true
2732
- }
2733
- },
2734
- email: {
2735
- name: "Email",
2736
- type: "string",
2737
- validation: {
2738
- required: true,
2739
- unique: true
2740
- }
2741
- },
2742
- displayName: {
2743
- name: "Name",
2744
- type: "string",
2745
- columnName: "display_name",
2746
- validation: {
2747
- required: true
2748
- }
2749
- },
2750
- photoURL: {
2751
- name: "Photo URL",
2752
- type: "string",
2753
- columnName: "photo_url",
2754
- url: "image"
2755
- },
2756
- roles: {
2757
- name: "Roles",
2758
- type: "array",
2759
- columnType: "text[]",
2760
- of: {
2761
- name: "Role",
2762
- type: "string",
2763
- enum: {
2764
- admin: "Admin",
2765
- editor: "Editor",
2766
- viewer: "Viewer"
2767
- }
2768
- }
2769
- },
2770
- passwordHash: {
2771
- name: "Password Hash",
2772
- type: "string",
2773
- columnName: "password_hash",
2774
- ui: {
2775
- hideFromCollection: true,
2776
- disabled: {
2777
- hidden: true
2778
- }
2779
- }
2780
- },
2781
- emailVerified: {
2782
- name: "Email Verified",
2783
- type: "boolean",
2784
- columnName: "email_verified",
2785
- defaultValue: false,
2786
- ui: {
2787
- hideFromCollection: true,
2788
- disabled: {
2789
- hidden: true
2790
- }
2791
- }
2792
- },
2793
- emailVerificationToken: {
2794
- name: "Email Verification Token",
2795
- type: "string",
2796
- columnName: "email_verification_token",
2797
- ui: {
2798
- hideFromCollection: true,
2799
- disabled: {
2800
- hidden: true
2801
- }
2802
- }
2803
- },
2804
- emailVerificationSentAt: {
2805
- name: "Email Verification Sent At",
2806
- type: "date",
2807
- columnName: "email_verification_sent_at",
2808
- ui: {
2809
- hideFromCollection: true,
2810
- disabled: {
2811
- hidden: true
2812
- }
2813
- }
2814
- },
2815
- metadata: {
2816
- name: "Metadata",
2817
- type: "map",
2818
- defaultValue: {},
2819
- ui: {
2820
- hideFromCollection: true,
2821
- disabled: {
2822
- hidden: true
2823
- }
2824
- }
2825
- },
2826
- createdAt: {
2827
- name: "Created At",
2828
- type: "date",
2829
- columnName: "created_at",
2830
- ui: {
2831
- readOnly: true
2832
- }
2833
- },
2834
- updatedAt: {
2835
- name: "Updated At",
2836
- type: "date",
2837
- columnName: "updated_at",
2838
- autoValue: "on_update",
2839
- ui: {
2840
- hideFromCollection: true,
2841
- disabled: {
2842
- hidden: true
2843
- }
2844
- }
2845
- }
2846
- },
2847
- listProperties: ["displayName", "email", "roles", "createdAt"],
2848
- propertiesOrder: ["id", "email", "displayName", "roles", "createdAt"]
2849
- };
2850
- function mapOperator(op) {
2851
- switch (op) {
2852
- case "==":
2853
- return "eq";
2854
- case "!=":
2855
- return "neq";
2856
- case ">":
2857
- return "gt";
2858
- case ">=":
2859
- return "gte";
2860
- case "<":
2861
- return "lt";
2862
- case "<=":
2863
- return "lte";
2864
- case "array-contains":
2865
- return "cs";
2866
- case "array-contains-any":
2867
- return "csa";
2868
- case "not-in":
2869
- return "nin";
2870
- default:
2871
- return op;
2872
- }
2873
- }
2874
2714
  class QueryBuilder {
2875
2715
  constructor(collection) {
2876
2716
  this.collection = collection;
@@ -2878,23 +2718,30 @@ class QueryBuilder {
2878
2718
  params = {
2879
2719
  where: {}
2880
2720
  };
2881
- /**
2882
- * Add a filter condition to your query.
2883
- * @example
2884
- * client.collection('users').where('age', '>=', 18).find()
2885
- */
2886
- where(column, operator, value) {
2721
+ where(columnOrCondition, operator, value) {
2722
+ if (typeof columnOrCondition === "object" && columnOrCondition !== null && "type" in columnOrCondition) {
2723
+ this.params.logical = columnOrCondition;
2724
+ return this;
2725
+ }
2887
2726
  if (!this.params.where) {
2888
2727
  this.params.where = {};
2889
2728
  }
2890
- const mappedOp = mapOperator(operator);
2891
- let formattedValue = value;
2892
- if (Array.isArray(value) && ["in", "nin", "cs", "csa"].includes(mappedOp)) {
2893
- formattedValue = `(${value.join(",")})`;
2894
- } else if (value === null) {
2895
- formattedValue = "null";
2729
+ const column = columnOrCondition;
2730
+ const condition = [operator, value];
2731
+ const existing = this.params.where[column];
2732
+ if (existing === void 0) {
2733
+ this.params.where[column] = condition;
2734
+ } else if (Array.isArray(existing) && existing.length > 0 && Array.isArray(existing[0])) {
2735
+ this.params.where[column].push(condition);
2736
+ } else {
2737
+ let firstCondition;
2738
+ if (Array.isArray(existing) && existing.length === 2 && typeof existing[0] === "string") {
2739
+ firstCondition = existing;
2740
+ } else {
2741
+ firstCondition = ["==", existing];
2742
+ }
2743
+ this.params.where[column] = [firstCondition, condition];
2896
2744
  }
2897
- this.params.where[column] = mappedOp === "eq" ? String(formattedValue) : `${mappedOp}.${formattedValue}`;
2898
2745
  return this;
2899
2746
  }
2900
2747
  /**
@@ -2996,10 +2843,13 @@ function convertWhereToFilter(where) {
2996
2843
  filter[field] = ["==", rawValue];
2997
2844
  continue;
2998
2845
  }
2999
- if (Array.isArray(rawValue) && rawValue.length === 2) {
3000
- const [rawOp, val] = rawValue;
3001
- const mappedOp = operatorMap[rawOp] ?? "==";
3002
- filter[field] = [mappedOp, val];
2846
+ if (Array.isArray(rawValue)) {
2847
+ const conditions = Array.isArray(rawValue[0]) ? rawValue : [rawValue];
2848
+ const mappedConditions = conditions.map(([rawOp, val]) => {
2849
+ const mappedOp = operatorMap[rawOp] ?? "==";
2850
+ return [mappedOp, val];
2851
+ });
2852
+ filter[field] = Array.isArray(rawValue[0]) ? mappedConditions : mappedConditions[0];
3003
2853
  continue;
3004
2854
  }
3005
2855
  if (typeof rawValue === "string") {
@@ -3137,8 +2987,12 @@ function createDriverAccessor(driver, slug) {
3137
2987
  });
3138
2988
  } : void 0,
3139
2989
  // Fluent Query Builder
3140
- where(column, operator, value) {
3141
- return new QueryBuilder(accessor).where(column, operator, value);
2990
+ where(columnOrCondition, operator, value) {
2991
+ const builder = new QueryBuilder(accessor);
2992
+ if (typeof columnOrCondition === "object") {
2993
+ return builder.where(columnOrCondition);
2994
+ }
2995
+ return builder.where(columnOrCondition, operator, value);
3142
2996
  },
3143
2997
  orderBy(column, ascending) {
3144
2998
  return new QueryBuilder(accessor).orderBy(column, ascending);
@@ -3189,7 +3043,6 @@ class DrizzleConditionBuilder {
3189
3043
  const conditions = [];
3190
3044
  for (const [field, filterParam] of Object.entries(filter)) {
3191
3045
  if (!filterParam) continue;
3192
- const [op, value] = filterParam;
3193
3046
  let fieldColumn = table[field];
3194
3047
  if (!fieldColumn) {
3195
3048
  const relationKey = `${field}_id`;
@@ -3201,13 +3054,39 @@ class DrizzleConditionBuilder {
3201
3054
  console.warn(`Filtering by field '${field}', but it does not exist in table for collection '${collectionPath}'`);
3202
3055
  continue;
3203
3056
  }
3204
- const condition = this.buildSingleFilterCondition(fieldColumn, op, value);
3205
- if (condition) {
3206
- conditions.push(condition);
3057
+ const paramsList = Array.isArray(filterParam) && filterParam.length > 0 && Array.isArray(filterParam[0]) ? filterParam : [filterParam];
3058
+ for (const [op, value] of paramsList) {
3059
+ const condition = this.buildSingleFilterCondition(fieldColumn, op, value);
3060
+ if (condition) {
3061
+ conditions.push(condition);
3062
+ }
3207
3063
  }
3208
3064
  }
3209
3065
  return conditions;
3210
3066
  }
3067
+ /**
3068
+ * Build logical conditions recursively from LogicalCondition or FilterCondition
3069
+ */
3070
+ static buildLogicalConditions(cond, table, collectionPath) {
3071
+ if ("type" in cond) {
3072
+ const subSQLs = cond.conditions.map((c) => this.buildLogicalConditions(c, table, collectionPath)).filter((sql2) => sql2 !== null);
3073
+ if (subSQLs.length === 0) return null;
3074
+ return (cond.type === "or" ? or(...subSQLs) : and(...subSQLs)) ?? null;
3075
+ } else {
3076
+ let fieldColumn = table[cond.column];
3077
+ if (!fieldColumn) {
3078
+ const relationKey = `${cond.column}_id`;
3079
+ if (relationKey in table) {
3080
+ fieldColumn = table[relationKey];
3081
+ }
3082
+ }
3083
+ if (!fieldColumn) {
3084
+ console.warn(`Filtering by field '${cond.column}', but it does not exist in table for collection '${collectionPath}'`);
3085
+ return null;
3086
+ }
3087
+ return this.buildSingleFilterCondition(fieldColumn, cond.operator, cond.value);
3088
+ }
3089
+ }
3211
3090
  /**
3212
3091
  * Build a single filter condition for a specific operator and value
3213
3092
  */
@@ -5604,6 +5483,10 @@ class EntityFetchService {
5604
5483
  const filterConditions = this.buildFilterConditions(options.filter, table, collectionPath);
5605
5484
  if (filterConditions.length > 0) allConditions.push(...filterConditions);
5606
5485
  }
5486
+ if (options.logical) {
5487
+ const logicalCondition = DrizzleConditionBuilder.buildLogicalConditions(options.logical, table, collectionPath);
5488
+ if (logicalCondition) allConditions.push(logicalCondition);
5489
+ }
5607
5490
  if (options.startAfter) {
5608
5491
  const cursorConditions = this.buildCursorConditions(table, idField, idInfo, options, collectionPath);
5609
5492
  if (cursorConditions.length > 0) allConditions.push(...cursorConditions);
@@ -5775,6 +5658,10 @@ class EntityFetchService {
5775
5658
  const filterConditions = this.buildFilterConditions(options.filter, table, collectionPath);
5776
5659
  if (filterConditions.length > 0) allConditions.push(...filterConditions);
5777
5660
  }
5661
+ if (options.logical) {
5662
+ const logicalCondition = DrizzleConditionBuilder.buildLogicalConditions(options.logical, table, collectionPath);
5663
+ if (logicalCondition) allConditions.push(logicalCondition);
5664
+ }
5778
5665
  if (vectorMeta?.filter) {
5779
5666
  allConditions.push(vectorMeta.filter);
5780
5667
  }
@@ -8733,7 +8620,6 @@ const runGeneration = async (collectionsFilePath, outputPath) => {
8733
8620
  if (!collections || !Array.isArray(collections)) {
8734
8621
  collections = [];
8735
8622
  }
8736
- collections = Array.from(new Map([defaultUsersCollection, ...collections].map((c) => [c.slug, c])).values());
8737
8623
  collections.sort((a, b) => a.slug.localeCompare(b.slug));
8738
8624
  const schemaContent = await generateSchema(collections);
8739
8625
  if (outputPath) {
@@ -10341,31 +10227,23 @@ class PostgresCollectionRegistry extends CollectionRegistry {
10341
10227
  return collection.relations.map((r) => r.relationName || r.localKey || "").filter(Boolean);
10342
10228
  }
10343
10229
  }
10344
- async function ensureAuthTablesExist(db, registry) {
10230
+ async function ensureAuthTablesExist(db, collection) {
10345
10231
  logger.info("🔍 Checking auth tables...");
10346
10232
  try {
10347
- let usersTableName = '"users"';
10233
+ let usersTableName = '"rebase"."users"';
10348
10234
  let userIdType = "TEXT";
10349
- let usersSchema2 = "public";
10350
- if (registry) {
10351
- const usersTable = registry.getTable("users");
10352
- if (usersTable) {
10353
- const {
10354
- getTableName: getTableName2
10355
- } = await import("drizzle-orm");
10356
- usersSchema2 = getTableConfig(usersTable).schema || "public";
10357
- usersTableName = usersSchema2 === "public" ? `"${getTableName2(usersTable)}"` : `"${usersSchema2}"."${getTableName2(usersTable)}"`;
10358
- if (usersTable.id) {
10359
- const col = usersTable.id;
10360
- const meta = getColumnMeta(col);
10361
- const columnType = meta.columnType;
10362
- if (columnType === "PgUUID") {
10363
- userIdType = "UUID";
10364
- } else if (columnType === "PgSerial" || columnType === "PgInteger") {
10365
- userIdType = "INTEGER";
10366
- } else if (columnType === "PgBigInt" || columnType === "PgBigSerial") {
10367
- userIdType = "BIGINT";
10368
- }
10235
+ let usersSchema2 = "rebase";
10236
+ if (collection) {
10237
+ const rawTable = "table" in collection && typeof collection.table === "string" ? collection.table : collection.slug;
10238
+ usersSchema2 = "schema" in collection && typeof collection.schema === "string" ? collection.schema : "public";
10239
+ usersTableName = usersSchema2 === "public" ? `"${rawTable}"` : `"${usersSchema2}"."${rawTable}"`;
10240
+ const idProp = collection.properties?.id;
10241
+ if (idProp) {
10242
+ const isId = "isId" in idProp ? idProp.isId : void 0;
10243
+ if (isId === "uuid") {
10244
+ userIdType = "UUID";
10245
+ } else if (isId === "autoincrement") {
10246
+ userIdType = "INTEGER";
10369
10247
  }
10370
10248
  }
10371
10249
  }
@@ -11758,19 +11636,21 @@ function createPostgresBootstrapper(pgConfig) {
11758
11636
  const internals = driverResult.internals;
11759
11637
  const db = internals.db;
11760
11638
  const registry = internals.registry;
11761
- await ensureAuthTablesExist(db, registry);
11639
+ const authCollection = authConfig.collection;
11640
+ await ensureAuthTablesExist(db, authCollection);
11762
11641
  let emailService;
11763
11642
  if (authConfig.email) {
11764
11643
  emailService = createEmailService(authConfig.email);
11765
11644
  }
11766
- const customUsersTable = registry?.getTable("users");
11645
+ const tableName = authCollection ? "table" in authCollection && typeof authCollection.table === "string" ? authCollection.table : authCollection.slug : void 0;
11646
+ const usersTable = tableName ? registry.getTable(tableName) : void 0;
11767
11647
  let usersSchemaName = "rebase";
11768
- if (customUsersTable) {
11769
- usersSchemaName = getTableConfig(customUsersTable).schema || "public";
11648
+ if (authCollection && "schema" in authCollection && typeof authCollection.schema === "string") {
11649
+ usersSchemaName = authCollection.schema;
11770
11650
  }
11771
11651
  const authTables = createAuthSchema(usersSchemaName);
11772
- if (customUsersTable) {
11773
- authTables.users = customUsersTable;
11652
+ if (usersTable) {
11653
+ authTables.users = usersTable;
11774
11654
  }
11775
11655
  const userService = new UserService(db, authTables);
11776
11656
  const authRepository = new PostgresAuthRepository(db, authTables);