@rebasepro/server-postgresql 0.3.0 → 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.
Files changed (49) hide show
  1. package/dist/common/src/collections/default-collections.d.ts +5 -8
  2. package/dist/common/src/data/query_builder.d.ts +6 -2
  3. package/dist/index.es.js +301 -500
  4. package/dist/index.es.js.map +1 -1
  5. package/dist/index.umd.js +297 -496
  6. package/dist/index.umd.js.map +1 -1
  7. package/dist/server-postgresql/src/PostgresBackendDriver.d.ts +2 -0
  8. package/dist/server-postgresql/src/auth/ensure-tables.d.ts +7 -4
  9. package/dist/server-postgresql/src/auth/services.d.ts +6 -31
  10. package/dist/server-postgresql/src/schema/auth-schema.d.ts +87 -340
  11. package/dist/server-postgresql/src/services/EntityFetchService.d.ts +2 -1
  12. package/dist/server-postgresql/src/services/EntityPersistService.d.ts +4 -0
  13. package/dist/server-postgresql/src/services/entityService.d.ts +4 -0
  14. package/dist/server-postgresql/src/utils/drizzle-conditions.d.ts +5 -1
  15. package/dist/types/src/controllers/auth.d.ts +2 -2
  16. package/dist/types/src/controllers/client.d.ts +25 -40
  17. package/dist/types/src/controllers/data.d.ts +21 -3
  18. package/dist/types/src/controllers/data_driver.d.ts +5 -0
  19. package/dist/types/src/controllers/email.d.ts +2 -0
  20. package/dist/types/src/types/auth_adapter.d.ts +3 -56
  21. package/dist/types/src/types/backend.d.ts +2 -2
  22. package/dist/types/src/types/backend_hooks.d.ts +2 -17
  23. package/dist/types/src/types/collections.d.ts +9 -5
  24. package/dist/types/src/types/entity_views.d.ts +19 -28
  25. package/dist/types/src/types/properties.d.ts +9 -7
  26. package/dist/types/src/types/user_management_delegate.d.ts +16 -53
  27. package/dist/types/src/users/index.d.ts +0 -1
  28. package/dist/types/src/users/user.d.ts +0 -1
  29. package/package.json +6 -6
  30. package/src/PostgresBackendDriver.ts +10 -0
  31. package/src/PostgresBootstrapper.ts +25 -21
  32. package/src/auth/ensure-tables.ts +82 -129
  33. package/src/auth/services.ts +71 -170
  34. package/src/schema/auth-schema.ts +13 -69
  35. package/src/schema/doctor.ts +44 -3
  36. package/src/schema/generate-drizzle-schema-logic.ts +33 -3
  37. package/src/schema/generate-drizzle-schema.ts +2 -6
  38. package/src/schema/introspect-db-logic.ts +7 -0
  39. package/src/services/EntityFetchService.ts +13 -1
  40. package/src/services/EntityPersistService.ts +9 -0
  41. package/src/services/entityService.ts +7 -0
  42. package/src/utils/drizzle-conditions.ts +40 -5
  43. package/src/websocket.ts +1 -3
  44. package/test/auth-services.test.ts +7 -150
  45. package/test/doctor.test.ts +6 -2
  46. package/test/relation-pipeline-gaps.test.ts +315 -0
  47. package/dist/server-postgresql/src/schema/default-collections.d.ts +0 -2
  48. package/dist/types/src/users/roles.d.ts +0 -14
  49. package/src/schema/default-collections.ts +0 -69
package/dist/index.es.js CHANGED
@@ -1,7 +1,7 @@
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";
4
- import { PgVarchar, PgText, PgChar, pgSchema, pgTable, timestamp, jsonb, boolean, varchar, uuid, primaryKey, unique, getTableConfig } from "drizzle-orm/pg-core";
3
+ import { or, and, sql, inArray, eq, ilike, asc, desc, gt, lt, getTableName as getTableName$1, count, relations, isTable } from "drizzle-orm";
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";
7
7
  import { promises } from "fs";
@@ -2711,114 +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
- properties: {
2723
- id: {
2724
- name: "ID",
2725
- type: "string",
2726
- isId: "uuid"
2727
- },
2728
- email: {
2729
- name: "Email",
2730
- type: "string",
2731
- validation: {
2732
- required: true,
2733
- unique: true
2734
- }
2735
- },
2736
- password_hash: {
2737
- name: "Password Hash",
2738
- type: "string",
2739
- ui: {
2740
- hideFromCollection: true
2741
- }
2742
- },
2743
- display_name: {
2744
- name: "Display Name",
2745
- type: "string"
2746
- },
2747
- photo_url: {
2748
- name: "Photo URL",
2749
- type: "string"
2750
- },
2751
- email_verified: {
2752
- name: "Email Verified",
2753
- type: "boolean",
2754
- defaultValue: false
2755
- },
2756
- email_verification_token: {
2757
- name: "Email Verification Token",
2758
- type: "string",
2759
- ui: {
2760
- hideFromCollection: true
2761
- }
2762
- },
2763
- email_verification_sent_at: {
2764
- name: "Email Verification Sent At",
2765
- type: "date",
2766
- ui: {
2767
- hideFromCollection: true
2768
- }
2769
- },
2770
- metadata: {
2771
- name: "Metadata",
2772
- type: "map",
2773
- defaultValue: {},
2774
- ui: {
2775
- hideFromCollection: true
2776
- }
2777
- },
2778
- created_at: {
2779
- name: "Created At",
2780
- type: "date",
2781
- autoValue: "on_create",
2782
- ui: {
2783
- readOnly: true,
2784
- hideFromCollection: true
2785
- }
2786
- },
2787
- updated_at: {
2788
- name: "Updated At",
2789
- type: "date",
2790
- autoValue: "on_update",
2791
- ui: {
2792
- readOnly: true,
2793
- hideFromCollection: true
2794
- }
2795
- }
2796
- }
2797
- };
2798
- function mapOperator(op) {
2799
- switch (op) {
2800
- case "==":
2801
- return "eq";
2802
- case "!=":
2803
- return "neq";
2804
- case ">":
2805
- return "gt";
2806
- case ">=":
2807
- return "gte";
2808
- case "<":
2809
- return "lt";
2810
- case "<=":
2811
- return "lte";
2812
- case "array-contains":
2813
- return "cs";
2814
- case "array-contains-any":
2815
- return "csa";
2816
- case "not-in":
2817
- return "nin";
2818
- default:
2819
- return op;
2820
- }
2821
- }
2822
2714
  class QueryBuilder {
2823
2715
  constructor(collection) {
2824
2716
  this.collection = collection;
@@ -2826,23 +2718,30 @@ class QueryBuilder {
2826
2718
  params = {
2827
2719
  where: {}
2828
2720
  };
2829
- /**
2830
- * Add a filter condition to your query.
2831
- * @example
2832
- * client.collection('users').where('age', '>=', 18).find()
2833
- */
2834
- 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
+ }
2835
2726
  if (!this.params.where) {
2836
2727
  this.params.where = {};
2837
2728
  }
2838
- const mappedOp = mapOperator(operator);
2839
- let formattedValue = value;
2840
- if (Array.isArray(value) && ["in", "nin", "cs", "csa"].includes(mappedOp)) {
2841
- formattedValue = `(${value.join(",")})`;
2842
- } else if (value === null) {
2843
- 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];
2844
2744
  }
2845
- this.params.where[column] = mappedOp === "eq" ? String(formattedValue) : `${mappedOp}.${formattedValue}`;
2846
2745
  return this;
2847
2746
  }
2848
2747
  /**
@@ -2944,10 +2843,13 @@ function convertWhereToFilter(where) {
2944
2843
  filter[field] = ["==", rawValue];
2945
2844
  continue;
2946
2845
  }
2947
- if (Array.isArray(rawValue) && rawValue.length === 2) {
2948
- const [rawOp, val] = rawValue;
2949
- const mappedOp = operatorMap[rawOp] ?? "==";
2950
- 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];
2951
2853
  continue;
2952
2854
  }
2953
2855
  if (typeof rawValue === "string") {
@@ -3041,6 +2943,9 @@ function createDriverAccessor(driver, slug) {
3041
2943
  }
3042
2944
  });
3043
2945
  },
2946
+ deleteAll: driver.deleteAll ? async () => {
2947
+ return driver.deleteAll(slug);
2948
+ } : void 0,
3044
2949
  count: driver.countEntities ? async (params) => {
3045
2950
  return driver.countEntities({
3046
2951
  path: slug,
@@ -3082,8 +2987,12 @@ function createDriverAccessor(driver, slug) {
3082
2987
  });
3083
2988
  } : void 0,
3084
2989
  // Fluent Query Builder
3085
- where(column, operator, value) {
3086
- 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);
3087
2996
  },
3088
2997
  orderBy(column, ascending) {
3089
2998
  return new QueryBuilder(accessor).orderBy(column, ascending);
@@ -3134,7 +3043,6 @@ class DrizzleConditionBuilder {
3134
3043
  const conditions = [];
3135
3044
  for (const [field, filterParam] of Object.entries(filter)) {
3136
3045
  if (!filterParam) continue;
3137
- const [op, value] = filterParam;
3138
3046
  let fieldColumn = table[field];
3139
3047
  if (!fieldColumn) {
3140
3048
  const relationKey = `${field}_id`;
@@ -3146,13 +3054,39 @@ class DrizzleConditionBuilder {
3146
3054
  console.warn(`Filtering by field '${field}', but it does not exist in table for collection '${collectionPath}'`);
3147
3055
  continue;
3148
3056
  }
3149
- const condition = this.buildSingleFilterCondition(fieldColumn, op, value);
3150
- if (condition) {
3151
- 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
+ }
3152
3063
  }
3153
3064
  }
3154
3065
  return conditions;
3155
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
+ }
3156
3090
  /**
3157
3091
  * Build a single filter condition for a specific operator and value
3158
3092
  */
@@ -5549,6 +5483,10 @@ class EntityFetchService {
5549
5483
  const filterConditions = this.buildFilterConditions(options.filter, table, collectionPath);
5550
5484
  if (filterConditions.length > 0) allConditions.push(...filterConditions);
5551
5485
  }
5486
+ if (options.logical) {
5487
+ const logicalCondition = DrizzleConditionBuilder.buildLogicalConditions(options.logical, table, collectionPath);
5488
+ if (logicalCondition) allConditions.push(logicalCondition);
5489
+ }
5552
5490
  if (options.startAfter) {
5553
5491
  const cursorConditions = this.buildCursorConditions(table, idField, idInfo, options, collectionPath);
5554
5492
  if (cursorConditions.length > 0) allConditions.push(...cursorConditions);
@@ -5720,6 +5658,10 @@ class EntityFetchService {
5720
5658
  const filterConditions = this.buildFilterConditions(options.filter, table, collectionPath);
5721
5659
  if (filterConditions.length > 0) allConditions.push(...filterConditions);
5722
5660
  }
5661
+ if (options.logical) {
5662
+ const logicalCondition = DrizzleConditionBuilder.buildLogicalConditions(options.logical, table, collectionPath);
5663
+ if (logicalCondition) allConditions.push(logicalCondition);
5664
+ }
5723
5665
  if (vectorMeta?.filter) {
5724
5666
  allConditions.push(vectorMeta.filter);
5725
5667
  }
@@ -6305,6 +6247,14 @@ class EntityPersistService {
6305
6247
  const parsedId = parsedIdObj[idInfo.fieldName];
6306
6248
  await this.db.delete(table).where(eq(idField, parsedId));
6307
6249
  }
6250
+ /**
6251
+ * Delete all entities from a collection
6252
+ */
6253
+ async deleteAll(collectionPath, _databaseId) {
6254
+ const collection = getCollectionByPath(collectionPath, this.registry);
6255
+ const table = getTableForCollection(collection, this.registry);
6256
+ await this.db.delete(table);
6257
+ }
6308
6258
  /**
6309
6259
  * Save an entity (create or update)
6310
6260
  */
@@ -6636,6 +6586,12 @@ class EntityService {
6636
6586
  async deleteEntity(collectionPath, entityId, databaseId) {
6637
6587
  return this.persistService.deleteEntity(collectionPath, entityId, databaseId);
6638
6588
  }
6589
+ /**
6590
+ * Delete all entities from a collection
6591
+ */
6592
+ async deleteAll(collectionPath, databaseId) {
6593
+ return this.persistService.deleteAll(collectionPath, databaseId);
6594
+ }
6639
6595
  /**
6640
6596
  * Execute raw SQL
6641
6597
  */
@@ -7336,6 +7292,10 @@ class PostgresBackendDriver {
7336
7292
  await this.realtimeService.notifyEntityUpdate(entity.path, entity.id.toString(), null, entity.databaseId || resolvedCollection?.databaseId);
7337
7293
  }
7338
7294
  }
7295
+ async deleteAll(path2) {
7296
+ await this.entityService.deleteAll(path2);
7297
+ await this.realtimeService.notifyEntityUpdate(path2, "*", null);
7298
+ }
7339
7299
  async checkUniqueField(path2, name, value, entityId, collection) {
7340
7300
  return this.entityService.checkUniqueField(path2, name, value, entityId, collection?.databaseId);
7341
7301
  }
@@ -7605,11 +7565,11 @@ class AuthenticatedPostgresBackendDriver {
7605
7565
  console.warn("[DataDriver] User ID (uid) is missing for authenticated delegate. Using 'anonymous'. User object:", this.user);
7606
7566
  userId = "anonymous";
7607
7567
  }
7608
- const userRoles2 = this.user?.roles ?? [];
7568
+ const userRoles = this.user?.roles ?? [];
7609
7569
  if (!this.user?.roles) {
7610
7570
  console.warn("[DataDriver] User roles are missing for authenticated delegate. Using empty array. User object:", this.user);
7611
7571
  }
7612
- const normalizedRoles = userRoles2.map((r) => typeof r === "string" ? r : r?.id ?? String(r));
7572
+ const normalizedRoles = userRoles.map((r) => typeof r === "string" ? r : r?.id ?? String(r));
7613
7573
  const rolesString = normalizedRoles.join(",");
7614
7574
  await tx.execute(sql`
7615
7575
  SELECT
@@ -7617,7 +7577,7 @@ class AuthenticatedPostgresBackendDriver {
7617
7577
  set_config('app.user_roles', ${rolesString}, true),
7618
7578
  set_config('app.jwt', ${JSON.stringify({
7619
7579
  sub: userId,
7620
- roles: userRoles2
7580
+ roles: userRoles
7621
7581
  })}, true)
7622
7582
  `);
7623
7583
  const txEntityService = new EntityService(tx, this.delegate.registry);
@@ -7672,6 +7632,9 @@ class AuthenticatedPostgresBackendDriver {
7672
7632
  async deleteEntity(props) {
7673
7633
  return this.withTransaction((delegate) => delegate.deleteEntity(props));
7674
7634
  }
7635
+ async deleteAll(path2) {
7636
+ return this.delegate.deleteAll(path2);
7637
+ }
7675
7638
  async checkUniqueField(path2, name, value, entityId, collection) {
7676
7639
  return this.withTransaction((delegate) => delegate.checkUniqueField(path2, name, value, entityId, collection));
7677
7640
  }
@@ -7751,11 +7714,10 @@ class DatabasePoolManager {
7751
7714
  this.pools.clear();
7752
7715
  }
7753
7716
  }
7754
- function createAuthSchema(rolesSchemaName = "rebase", usersSchemaName = "rebase") {
7755
- const rolesSchema = rolesSchemaName === "public" ? null : pgSchema(rolesSchemaName);
7717
+ function createAuthSchema(usersSchemaName = "rebase") {
7756
7718
  const usersSchema2 = usersSchemaName === "public" ? null : pgSchema(usersSchemaName);
7757
- const rolesTableCreator = rolesSchema ? rolesSchema.table.bind(rolesSchema) : pgTable;
7758
- const usersTableCreator = usersSchema2 ? usersSchema2.table.bind(usersSchema2) : pgTable;
7719
+ const tableCreator = usersSchema2 ? usersSchema2.table.bind(usersSchema2) : pgTable;
7720
+ const usersTableCreator = tableCreator;
7759
7721
  const users2 = usersTableCreator("users", {
7760
7722
  id: uuid("id").defaultRandom().primaryKey(),
7761
7723
  email: varchar("email", {
@@ -7777,37 +7739,12 @@ function createAuthSchema(rolesSchemaName = "rebase", usersSchemaName = "rebase"
7777
7739
  }),
7778
7740
  emailVerificationSentAt: timestamp("email_verification_sent_at"),
7779
7741
  isAnonymous: boolean("is_anonymous").default(false).notNull(),
7742
+ roles: text("roles").array().default([]).notNull(),
7780
7743
  metadata: jsonb("metadata").$type().default({}).notNull(),
7781
7744
  createdAt: timestamp("created_at").defaultNow().notNull(),
7782
7745
  updatedAt: timestamp("updated_at").defaultNow().notNull()
7783
7746
  });
7784
- const roles2 = rolesTableCreator("roles", {
7785
- id: varchar("id", {
7786
- length: 50
7787
- }).primaryKey(),
7788
- // 'admin', 'editor', 'viewer'
7789
- name: varchar("name", {
7790
- length: 100
7791
- }).notNull(),
7792
- isAdmin: boolean("is_admin").default(false).notNull(),
7793
- defaultPermissions: jsonb("default_permissions").$type(),
7794
- collectionPermissions: jsonb("collection_permissions").$type()
7795
- });
7796
- const userRoles2 = rolesTableCreator("user_roles", {
7797
- userId: uuid("user_id").notNull().references(() => users2.id, {
7798
- onDelete: "cascade"
7799
- }),
7800
- roleId: varchar("role_id", {
7801
- length: 50
7802
- }).notNull().references(() => roles2.id, {
7803
- onDelete: "cascade"
7804
- })
7805
- }, (table) => ({
7806
- pk: primaryKey({
7807
- columns: [table.userId, table.roleId]
7808
- })
7809
- }));
7810
- const refreshTokens2 = rolesTableCreator("refresh_tokens", {
7747
+ const refreshTokens2 = tableCreator("refresh_tokens", {
7811
7748
  id: uuid("id").defaultRandom().primaryKey(),
7812
7749
  userId: uuid("user_id").notNull().references(() => users2.id, {
7813
7750
  onDelete: "cascade"
@@ -7826,7 +7763,7 @@ function createAuthSchema(rolesSchemaName = "rebase", usersSchemaName = "rebase"
7826
7763
  }, (table) => ({
7827
7764
  uniqueDeviceSession: unique("unique_device_session").on(table.userId, table.userAgent, table.ipAddress)
7828
7765
  }));
7829
- const passwordResetTokens2 = rolesTableCreator("password_reset_tokens", {
7766
+ const passwordResetTokens2 = tableCreator("password_reset_tokens", {
7830
7767
  id: uuid("id").defaultRandom().primaryKey(),
7831
7768
  userId: uuid("user_id").notNull().references(() => users2.id, {
7832
7769
  onDelete: "cascade"
@@ -7838,14 +7775,14 @@ function createAuthSchema(rolesSchemaName = "rebase", usersSchemaName = "rebase"
7838
7775
  usedAt: timestamp("used_at"),
7839
7776
  createdAt: timestamp("created_at").defaultNow().notNull()
7840
7777
  });
7841
- const appConfig2 = rolesTableCreator("app_config", {
7778
+ const appConfig2 = tableCreator("app_config", {
7842
7779
  key: varchar("key", {
7843
7780
  length: 100
7844
7781
  }).primaryKey(),
7845
7782
  value: jsonb("value").notNull(),
7846
7783
  updatedAt: timestamp("updated_at").defaultNow().notNull()
7847
7784
  });
7848
- const userIdentities2 = rolesTableCreator("user_identities", {
7785
+ const userIdentities2 = tableCreator("user_identities", {
7849
7786
  id: uuid("id").defaultRandom().primaryKey(),
7850
7787
  userId: uuid("user_id").notNull().references(() => users2.id, {
7851
7788
  onDelete: "cascade"
@@ -7863,7 +7800,7 @@ function createAuthSchema(rolesSchemaName = "rebase", usersSchemaName = "rebase"
7863
7800
  }, (table) => ({
7864
7801
  uniqueProviderId: unique("unique_provider_id").on(table.provider, table.providerId)
7865
7802
  }));
7866
- const mfaFactors2 = rolesTableCreator("mfa_factors", {
7803
+ const mfaFactors2 = tableCreator("mfa_factors", {
7867
7804
  id: uuid("id").defaultRandom().primaryKey(),
7868
7805
  userId: uuid("user_id").notNull().references(() => users2.id, {
7869
7806
  onDelete: "cascade"
@@ -7882,7 +7819,7 @@ function createAuthSchema(rolesSchemaName = "rebase", usersSchemaName = "rebase"
7882
7819
  createdAt: timestamp("created_at").defaultNow().notNull(),
7883
7820
  updatedAt: timestamp("updated_at").defaultNow().notNull()
7884
7821
  });
7885
- const mfaChallenges2 = rolesTableCreator("mfa_challenges", {
7822
+ const mfaChallenges2 = tableCreator("mfa_challenges", {
7886
7823
  id: uuid("id").defaultRandom().primaryKey(),
7887
7824
  factorId: uuid("factor_id").notNull().references(() => mfaFactors2.id, {
7888
7825
  onDelete: "cascade"
@@ -7894,7 +7831,7 @@ function createAuthSchema(rolesSchemaName = "rebase", usersSchemaName = "rebase"
7894
7831
  }),
7895
7832
  expiresAt: timestamp("expires_at").notNull()
7896
7833
  });
7897
- const recoveryCodes2 = rolesTableCreator("recovery_codes", {
7834
+ const recoveryCodes2 = tableCreator("recovery_codes", {
7898
7835
  id: uuid("id").defaultRandom().primaryKey(),
7899
7836
  userId: uuid("user_id").notNull().references(() => users2.id, {
7900
7837
  onDelete: "cascade"
@@ -7906,11 +7843,8 @@ function createAuthSchema(rolesSchemaName = "rebase", usersSchemaName = "rebase"
7906
7843
  createdAt: timestamp("created_at").defaultNow().notNull()
7907
7844
  });
7908
7845
  return {
7909
- rolesSchema,
7910
7846
  usersSchema: usersSchema2,
7911
7847
  users: users2,
7912
- roles: roles2,
7913
- userRoles: userRoles2,
7914
7848
  refreshTokens: refreshTokens2,
7915
7849
  passwordResetTokens: passwordResetTokens2,
7916
7850
  appConfig: appConfig2,
@@ -7920,12 +7854,9 @@ function createAuthSchema(rolesSchemaName = "rebase", usersSchemaName = "rebase"
7920
7854
  recoveryCodes: recoveryCodes2
7921
7855
  };
7922
7856
  }
7923
- const defaultAuthSchema = createAuthSchema("rebase", "rebase");
7924
- const rebaseSchema = defaultAuthSchema.rolesSchema;
7857
+ const defaultAuthSchema = createAuthSchema("rebase");
7925
7858
  const usersSchema = defaultAuthSchema.usersSchema;
7926
7859
  const users = defaultAuthSchema.users;
7927
- const roles = defaultAuthSchema.roles;
7928
- const userRoles = defaultAuthSchema.userRoles;
7929
7860
  const refreshTokens = defaultAuthSchema.refreshTokens;
7930
7861
  const passwordResetTokens = defaultAuthSchema.passwordResetTokens;
7931
7862
  const appConfig = defaultAuthSchema.appConfig;
@@ -7936,30 +7867,12 @@ const recoveryCodes = defaultAuthSchema.recoveryCodes;
7936
7867
  const usersRelations = relations(users, ({
7937
7868
  many
7938
7869
  }) => ({
7939
- userRoles: many(userRoles),
7940
7870
  refreshTokens: many(refreshTokens),
7941
7871
  passwordResetTokens: many(passwordResetTokens),
7942
7872
  userIdentities: many(userIdentities),
7943
7873
  mfaFactors: many(mfaFactors),
7944
7874
  recoveryCodes: many(recoveryCodes)
7945
7875
  }));
7946
- const rolesRelations = relations(roles, ({
7947
- many
7948
- }) => ({
7949
- userRoles: many(userRoles)
7950
- }));
7951
- const userRolesRelations = relations(userRoles, ({
7952
- one
7953
- }) => ({
7954
- user: one(users, {
7955
- fields: [userRoles.userId],
7956
- references: [users.id]
7957
- }),
7958
- role: one(roles, {
7959
- fields: [userRoles.roleId],
7960
- references: [roles.id]
7961
- })
7962
- }));
7963
7876
  const refreshTokensRelations = relations(refreshTokens, ({
7964
7877
  one
7965
7878
  }) => ({
@@ -8066,6 +7979,8 @@ const getDrizzleColumn = (propName, prop, collection, collections) => {
8066
7979
  columnDefinition = `${enumName}("${colName}")`;
8067
7980
  } else if ("isId" in stringProp && stringProp.isId === "uuid") {
8068
7981
  columnDefinition = `uuid("${colName}")`;
7982
+ } else if (stringProp.columnType === "uuid") {
7983
+ columnDefinition = `uuid("${colName}")`;
8069
7984
  } else if (stringProp.columnType === "text") {
8070
7985
  columnDefinition = `text("${colName}")`;
8071
7986
  } else if (stringProp.columnType === "char") {
@@ -8133,11 +8048,38 @@ const getDrizzleColumn = (propName, prop, collection, collections) => {
8133
8048
  }
8134
8049
  break;
8135
8050
  }
8136
- case "map":
8051
+ case "map": {
8052
+ const mapProp = prop;
8053
+ if (mapProp.columnType === "json") {
8054
+ columnDefinition = `json("${colName}")`;
8055
+ } else {
8056
+ columnDefinition = `jsonb("${colName}")`;
8057
+ }
8058
+ break;
8059
+ }
8137
8060
  case "array": {
8138
- const arrayOrMapProp = prop;
8139
- if (arrayOrMapProp.columnType === "json") {
8061
+ const arrayProp = prop;
8062
+ let colType = arrayProp.columnType;
8063
+ if (!colType && arrayProp.of && !Array.isArray(arrayProp.of)) {
8064
+ const ofProp = arrayProp.of;
8065
+ if (ofProp.type === "string") {
8066
+ colType = "text[]";
8067
+ } else if (ofProp.type === "number") {
8068
+ colType = ofProp.validation?.integer ? "integer[]" : "numeric[]";
8069
+ } else if (ofProp.type === "boolean") {
8070
+ colType = "boolean[]";
8071
+ }
8072
+ }
8073
+ if (colType === "json") {
8140
8074
  columnDefinition = `json("${colName}")`;
8075
+ } else if (colType === "text[]") {
8076
+ columnDefinition = `text("${colName}").array()`;
8077
+ } else if (colType === "integer[]") {
8078
+ columnDefinition = `integer("${colName}").array()`;
8079
+ } else if (colType === "boolean[]") {
8080
+ columnDefinition = `boolean("${colName}").array()`;
8081
+ } else if (colType === "numeric[]") {
8082
+ columnDefinition = `numeric("${colName}").array()`;
8141
8083
  } else {
8142
8084
  columnDefinition = `jsonb("${colName}")`;
8143
8085
  }
@@ -8221,8 +8163,8 @@ const resolveRawSql = (expression) => {
8221
8163
  const resolved = expression.replace(/\{(\w+)\}/g, (_, col) => col);
8222
8164
  return `sql\`${resolved}\``;
8223
8165
  };
8224
- const wrapWithRoleCheck = (clause, roles2) => {
8225
- const rolesArrayString = `ARRAY[${roles2.map((r) => `'${r}'`).join(",")}]`;
8166
+ const wrapWithRoleCheck = (clause, roles) => {
8167
+ const rolesArrayString = `ARRAY[${roles.map((r) => `'${r}'`).join(",")}]`;
8226
8168
  const roleCondition = `string_to_array(auth.roles(), ',') @> ${rolesArrayString}`;
8227
8169
  return `sql\`(${unwrapSql(clause)}) AND (${roleCondition})\``;
8228
8170
  };
@@ -8275,22 +8217,22 @@ const generatePolicyCode = (collection, rule, index) => {
8275
8217
  };
8276
8218
  const generateSinglePolicyCode = (collection, rule, operation, policyName) => {
8277
8219
  const mode = rule.mode ?? "permissive";
8278
- const roles2 = rule.roles ? [...rule.roles].sort() : void 0;
8220
+ const roles = rule.roles ? [...rule.roles].sort() : void 0;
8279
8221
  const needsUsing = operation !== "insert";
8280
8222
  const needsWithCheck = operation !== "select" && operation !== "delete";
8281
8223
  let usingClause = needsUsing ? buildUsingClause(rule, collection) : null;
8282
8224
  let withCheckClause = needsWithCheck ? buildWithCheckClause(rule, collection) : null;
8283
- if (roles2 && roles2.length > 0) {
8225
+ if (roles && roles.length > 0) {
8284
8226
  if (usingClause) {
8285
- usingClause = wrapWithRoleCheck(usingClause, roles2);
8227
+ usingClause = wrapWithRoleCheck(usingClause, roles);
8286
8228
  } else if (needsUsing) {
8287
- const rolesArrayString = `ARRAY[${roles2.map((r) => `'${r}'`).join(",")}]`;
8229
+ const rolesArrayString = `ARRAY[${roles.map((r) => `'${r}'`).join(",")}]`;
8288
8230
  usingClause = `sql\`string_to_array(auth.roles(), ',') @> ${rolesArrayString}\``;
8289
8231
  }
8290
8232
  if (withCheckClause) {
8291
- withCheckClause = wrapWithRoleCheck(withCheckClause, roles2);
8233
+ withCheckClause = wrapWithRoleCheck(withCheckClause, roles);
8292
8234
  } else if (needsWithCheck) {
8293
- const rolesArrayString = `ARRAY[${roles2.map((r) => `'${r}'`).join(",")}]`;
8235
+ const rolesArrayString = `ARRAY[${roles.map((r) => `'${r}'`).join(",")}]`;
8294
8236
  withCheckClause = `sql\`string_to_array(auth.roles(), ',') @> ${rolesArrayString}\``;
8295
8237
  }
8296
8238
  }
@@ -8613,7 +8555,7 @@ ${tableRelations.join(",\n")}
8613
8555
  schemaContent += tablesExport + enumsExport + relationsExport;
8614
8556
  return schemaContent;
8615
8557
  };
8616
- const formatTerminalText = (text, options = {}) => {
8558
+ const formatTerminalText = (text2, options = {}) => {
8617
8559
  let codes = "";
8618
8560
  if (options.bold) codes += "\x1B[1m";
8619
8561
  if (options.backgroundColor) {
@@ -8640,7 +8582,7 @@ const formatTerminalText = (text, options = {}) => {
8640
8582
  };
8641
8583
  codes += textColors[options.textColor];
8642
8584
  }
8643
- return `${codes}${text}\x1B[0m`;
8585
+ return `${codes}${text2}\x1B[0m`;
8644
8586
  };
8645
8587
  const runGeneration = async (collectionsFilePath, outputPath) => {
8646
8588
  try {
@@ -8678,7 +8620,6 @@ const runGeneration = async (collectionsFilePath, outputPath) => {
8678
8620
  if (!collections || !Array.isArray(collections)) {
8679
8621
  collections = [];
8680
8622
  }
8681
- collections = Array.from(new Map([defaultUsersCollection, ...collections].map((c) => [c.slug, c])).values());
8682
8623
  collections.sort((a, b) => a.slug.localeCompare(b.slug));
8683
8624
  const schemaContent = await generateSchema(collections);
8684
8625
  if (outputPath) {
@@ -10024,15 +9965,15 @@ function createPostgresWebSocket(server, realtimeService, driver, authConfig, au
10024
9965
  wsDebug("👤 [WebSocket Server] Processing FETCH_ROLES request");
10025
9966
  const delegate = await getScopedDelegate();
10026
9967
  const admin = delegate.admin;
10027
- let roles2 = [];
9968
+ let roles = [];
10028
9969
  if (isSQLAdmin(admin) && admin.fetchAvailableRoles) {
10029
- roles2 = await admin.fetchAvailableRoles();
9970
+ roles = await admin.fetchAvailableRoles();
10030
9971
  }
10031
- wsDebug(`👤 [WebSocket Server] Fetched ${roles2.length} roles.`);
9972
+ wsDebug(`👤 [WebSocket Server] Fetched ${roles.length} roles.`);
10032
9973
  const response = {
10033
9974
  type: "FETCH_ROLES_SUCCESS",
10034
9975
  payload: {
10035
- roles: roles2
9976
+ roles
10036
9977
  },
10037
9978
  requestId
10038
9979
  };
@@ -10286,85 +10227,35 @@ class PostgresCollectionRegistry extends CollectionRegistry {
10286
10227
  return collection.relations.map((r) => r.relationName || r.localKey || "").filter(Boolean);
10287
10228
  }
10288
10229
  }
10289
- const DEFAULT_ROLES = [{
10290
- id: "admin",
10291
- name: "Admin",
10292
- is_admin: true,
10293
- default_permissions: {
10294
- read: true,
10295
- create: true,
10296
- edit: true,
10297
- delete: true
10298
- }
10299
- }, {
10300
- id: "editor",
10301
- name: "Editor",
10302
- is_admin: false,
10303
- default_permissions: {
10304
- read: true,
10305
- create: true,
10306
- edit: true,
10307
- delete: true
10308
- }
10309
- }, {
10310
- id: "viewer",
10311
- name: "Viewer",
10312
- is_admin: false,
10313
- default_permissions: {
10314
- read: true,
10315
- create: false,
10316
- edit: false,
10317
- delete: false
10318
- }
10319
- }];
10320
- async function ensureAuthTablesExist(db, registry) {
10230
+ async function ensureAuthTablesExist(db, collection) {
10321
10231
  logger.info("🔍 Checking auth tables...");
10322
10232
  try {
10323
- let usersTableName = '"users"';
10233
+ let usersTableName = '"rebase"."users"';
10324
10234
  let userIdType = "TEXT";
10325
- let usersSchema2 = "public";
10326
- if (registry) {
10327
- const usersTable = registry.getTable("users");
10328
- if (usersTable) {
10329
- const {
10330
- getTableName: getTableName2
10331
- } = await import("drizzle-orm");
10332
- usersSchema2 = getTableConfig(usersTable).schema || "public";
10333
- usersTableName = usersSchema2 === "public" ? `"${getTableName2(usersTable)}"` : `"${usersSchema2}"."${getTableName2(usersTable)}"`;
10334
- if (usersTable.id) {
10335
- const col = usersTable.id;
10336
- const meta = getColumnMeta(col);
10337
- const columnType = meta.columnType;
10338
- if (columnType === "PgUUID") {
10339
- userIdType = "UUID";
10340
- } else if (columnType === "PgSerial" || columnType === "PgInteger") {
10341
- userIdType = "INTEGER";
10342
- } else if (columnType === "PgBigInt" || columnType === "PgBigSerial") {
10343
- userIdType = "BIGINT";
10344
- }
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";
10345
10247
  }
10346
10248
  }
10347
10249
  }
10348
- let rolesSchema = "rebase";
10349
- if (registry) {
10350
- const rolesTable = registry.getTable("roles");
10351
- if (rolesTable) {
10352
- rolesSchema = getTableConfig(rolesTable).schema || "public";
10353
- }
10354
- }
10355
10250
  if (usersSchema2 !== "public") {
10356
10251
  await db.execute(sql`CREATE SCHEMA IF NOT EXISTS ${sql.raw(usersSchema2)}`);
10357
10252
  }
10358
- if (rolesSchema !== "public" && rolesSchema !== usersSchema2) {
10359
- await db.execute(sql`CREATE SCHEMA IF NOT EXISTS ${sql.raw(rolesSchema)}`);
10360
- }
10361
10253
  await db.execute(sql`CREATE SCHEMA IF NOT EXISTS rebase`);
10362
- const userIdentitiesTable = `"${rolesSchema}"."user_identities"`;
10363
- const rolesTableName = `"${rolesSchema}"."roles"`;
10364
- const userRolesTableName = `"${rolesSchema}"."user_roles"`;
10365
- const refreshTokensTableName = `"${rolesSchema}"."refresh_tokens"`;
10366
- const passwordResetTokensTableName = `"${rolesSchema}"."password_reset_tokens"`;
10367
- const appConfigTableName = `"${rolesSchema}"."app_config"`;
10254
+ const authSchema = usersSchema2 === "public" ? "rebase" : usersSchema2;
10255
+ const userIdentitiesTable = `"${authSchema}"."user_identities"`;
10256
+ const refreshTokensTableName = `"${authSchema}"."refresh_tokens"`;
10257
+ const passwordResetTokensTableName = `"${authSchema}"."password_reset_tokens"`;
10258
+ const appConfigTableName = `"${authSchema}"."app_config"`;
10368
10259
  await db.execute(sql`
10369
10260
  CREATE TABLE IF NOT EXISTS ${sql.raw(userIdentitiesTable)} (
10370
10261
  id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
@@ -10381,27 +10272,6 @@ async function ensureAuthTablesExist(db, registry) {
10381
10272
  CREATE INDEX IF NOT EXISTS idx_user_identities_user
10382
10273
  ON ${sql.raw(userIdentitiesTable)}(user_id)
10383
10274
  `);
10384
- await db.execute(sql`
10385
- CREATE TABLE IF NOT EXISTS ${sql.raw(rolesTableName)} (
10386
- id TEXT PRIMARY KEY,
10387
- name TEXT NOT NULL,
10388
- is_admin BOOLEAN DEFAULT FALSE,
10389
- default_permissions JSONB,
10390
- collection_permissions JSONB,
10391
- created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
10392
- )
10393
- `);
10394
- await db.execute(sql`
10395
- CREATE TABLE IF NOT EXISTS ${sql.raw(userRolesTableName)} (
10396
- user_id ${sql.raw(userIdType)} NOT NULL REFERENCES ${sql.raw(usersTableName)}(id) ON DELETE CASCADE,
10397
- role_id TEXT NOT NULL REFERENCES ${sql.raw(rolesTableName)}(id) ON DELETE CASCADE,
10398
- PRIMARY KEY (user_id, role_id)
10399
- )
10400
- `);
10401
- await db.execute(sql`
10402
- CREATE INDEX IF NOT EXISTS idx_user_roles_user
10403
- ON ${sql.raw(userRolesTableName)}(user_id)
10404
- `);
10405
10275
  await db.execute(sql`
10406
10276
  CREATE TABLE IF NOT EXISTS ${sql.raw(refreshTokensTableName)} (
10407
10277
  id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
@@ -10469,14 +10339,43 @@ async function ensureAuthTablesExist(db, registry) {
10469
10339
  $$ LANGUAGE sql STABLE
10470
10340
  `);
10471
10341
  });
10472
- await seedDefaultRoles(db, rolesTableName);
10473
10342
  await db.execute(sql`
10474
10343
  ALTER TABLE ${sql.raw(usersTableName)}
10475
10344
  ADD COLUMN IF NOT EXISTS is_anonymous BOOLEAN DEFAULT FALSE
10476
10345
  `);
10477
- const mfaFactorsTableName = `"${rolesSchema}"."mfa_factors"`;
10478
- const mfaChallengesTableName = `"${rolesSchema}"."mfa_challenges"`;
10479
- const recoveryCodesTableName = `"${rolesSchema}"."recovery_codes"`;
10346
+ await db.execute(sql`
10347
+ ALTER TABLE ${sql.raw(usersTableName)}
10348
+ ADD COLUMN IF NOT EXISTS roles TEXT[] DEFAULT '{}' NOT NULL
10349
+ `);
10350
+ try {
10351
+ const legacyCheck = await db.execute(sql`
10352
+ SELECT EXISTS (
10353
+ SELECT 1 FROM information_schema.tables
10354
+ WHERE table_schema = 'rebase' AND table_name = 'user_roles'
10355
+ ) AS has_user_roles
10356
+ `);
10357
+ const hasLegacyTables = legacyCheck.rows[0].has_user_roles;
10358
+ if (hasLegacyTables) {
10359
+ logger.info("🔄 Migrating roles from legacy user_roles table...");
10360
+ await db.execute(sql`
10361
+ UPDATE ${sql.raw(usersTableName)} u
10362
+ SET roles = COALESCE((
10363
+ SELECT array_agg(ur.role_id)
10364
+ FROM "rebase"."user_roles" ur
10365
+ WHERE ur.user_id = u.id
10366
+ ), '{}')
10367
+ WHERE u.roles = '{}' OR u.roles IS NULL
10368
+ `);
10369
+ await db.execute(sql`DROP TABLE IF EXISTS "rebase"."user_roles" CASCADE`);
10370
+ await db.execute(sql`DROP TABLE IF EXISTS "rebase"."roles" CASCADE`);
10371
+ logger.info("✅ Legacy roles tables migrated and dropped");
10372
+ }
10373
+ } catch (migrationError) {
10374
+ logger.warn(`⚠️ Legacy roles migration skipped: ${migrationError instanceof Error ? migrationError.message : String(migrationError)}`);
10375
+ }
10376
+ const mfaFactorsTableName = `"${authSchema}"."mfa_factors"`;
10377
+ const mfaChallengesTableName = `"${authSchema}"."mfa_challenges"`;
10378
+ const recoveryCodesTableName = `"${authSchema}"."recovery_codes"`;
10480
10379
  await db.execute(sql`
10481
10380
  CREATE TABLE IF NOT EXISTS ${sql.raw(mfaFactorsTableName)} (
10482
10381
  id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
@@ -10528,28 +10427,6 @@ async function ensureAuthTablesExist(db, registry) {
10528
10427
  logger.warn("⚠️ Continuing without creating auth tables.");
10529
10428
  }
10530
10429
  }
10531
- async function seedDefaultRoles(db, rolesTableName) {
10532
- const result = await db.execute(sql`SELECT COUNT(*) as count FROM ${sql.raw(rolesTableName)}`);
10533
- const count2 = parseInt(result.rows[0]?.count || "0", 10);
10534
- if (count2 > 0) {
10535
- logger.info(`📋 Found ${count2} existing roles`);
10536
- return;
10537
- }
10538
- logger.info("🌱 Seeding default roles...");
10539
- for (const role of DEFAULT_ROLES) {
10540
- await db.execute(sql`
10541
- INSERT INTO ${sql.raw(rolesTableName)} (id, name, is_admin, default_permissions)
10542
- VALUES (
10543
- ${role.id},
10544
- ${role.name},
10545
- ${role.is_admin},
10546
- ${JSON.stringify(role.default_permissions)}::jsonb
10547
- )
10548
- ON CONFLICT (id) DO NOTHING
10549
- `);
10550
- }
10551
- logger.info("✅ Default roles created: admin, editor, viewer");
10552
- }
10553
10430
  function getColumnKey(table, ...keys2) {
10554
10431
  if (!table) return void 0;
10555
10432
  for (const key of keys2) {
@@ -10569,24 +10446,18 @@ function getColumn(table, ...keys2) {
10569
10446
  class UserService {
10570
10447
  constructor(db, tableOrTables) {
10571
10448
  this.db = db;
10572
- if (tableOrTables && (tableOrTables.users || tableOrTables.roles)) {
10449
+ if (tableOrTables && tableOrTables.users) {
10573
10450
  const tables = tableOrTables;
10574
10451
  this.usersTable = tables.users || users;
10575
10452
  this.userIdentitiesTable = tables.userIdentities || userIdentities;
10576
- this.userRolesTable = tables.userRoles || userRoles;
10577
- this.rolesTable = tables.roles || roles;
10578
10453
  } else {
10579
10454
  const table = tableOrTables;
10580
10455
  this.usersTable = table || users;
10581
10456
  this.userIdentitiesTable = userIdentities;
10582
- this.userRolesTable = userRoles;
10583
- this.rolesTable = roles;
10584
10457
  }
10585
10458
  }
10586
10459
  usersTable;
10587
10460
  userIdentitiesTable;
10588
- userRolesTable;
10589
- rolesTable;
10590
10461
  getQualifiedUsersTableName() {
10591
10462
  const name = getTableName$1(this.usersTable);
10592
10463
  const schema = getTableConfig(this.usersTable).schema || "public";
@@ -10608,7 +10479,7 @@ class UserService {
10608
10479
  const metadata = {
10609
10480
  ...row.metadata || {}
10610
10481
  };
10611
- const knownKeys = /* @__PURE__ */ new Set(["id", "uid", "email", "password_hash", "passwordHash", "display_name", "displayName", "photo_url", "photoUrl", "photoURL", "email_verified", "emailVerified", "email_verification_token", "emailVerificationToken", "email_verification_sent_at", "emailVerificationSentAt", "is_anonymous", "isAnonymous", "created_at", "createdAt", "updated_at", "updatedAt", "metadata"]);
10482
+ const knownKeys = /* @__PURE__ */ new Set(["id", "uid", "email", "password_hash", "passwordHash", "display_name", "displayName", "photo_url", "photoUrl", "photoURL", "email_verified", "emailVerified", "email_verification_token", "emailVerificationToken", "email_verification_sent_at", "emailVerificationSentAt", "is_anonymous", "isAnonymous", "roles", "created_at", "createdAt", "updated_at", "updatedAt", "metadata"]);
10612
10483
  for (const [key, val] of Object.entries(row)) {
10613
10484
  if (!knownKeys.has(key)) {
10614
10485
  const camelKey = camelCase(key);
@@ -10759,19 +10630,18 @@ class UserService {
10759
10630
  const displayNameCol = getColumn(this.usersTable, "displayName", "display_name");
10760
10631
  const displayNameColumn = displayNameCol ? displayNameCol.name : "display_name";
10761
10632
  const idCol = getColumn(this.usersTable, "id");
10762
- const idColumn = idCol ? idCol.name : "id";
10633
+ idCol ? idCol.name : "id";
10763
10634
  const usersTableName = this.getQualifiedUsersTableName();
10764
- const rolesSchema = getTableConfig(this.userRolesTable).schema || "public";
10765
10635
  const conditions = [];
10766
10636
  if (roleId) {
10767
- conditions.push(sql`EXISTS (SELECT 1 FROM ${sql.raw(`"${rolesSchema}"."user_roles"`)} ur WHERE ur.user_id = ${sql.raw(usersTableName)}.${sql.raw(idColumn)} AND ur.role_id = ${roleId})`);
10637
+ conditions.push(sql`${roleId} = ANY(${sql.raw(usersTableName)}.roles)`);
10768
10638
  }
10769
10639
  if (search) {
10770
10640
  const pattern = `%${search}%`;
10771
10641
  conditions.push(sql`(${sql.raw(usersTableName)}.${sql.raw(emailColumn)} ILIKE ${pattern} OR ${sql.raw(usersTableName)}.${sql.raw(displayNameColumn)} ILIKE ${pattern})`);
10772
10642
  }
10773
10643
  const whereClause = conditions.length > 0 ? sql`WHERE ${sql.join(conditions, sql` AND `)}` : sql``;
10774
- const orderByClause = roleId ? sql`ORDER BY ${sql.raw(usersTableName)}.${sql.raw(orderColumn)} ${direction}` : sql`ORDER BY (SELECT count(*) FROM ${sql.raw(`"${rolesSchema}"."user_roles"`)} ur WHERE ur.user_id = ${sql.raw(usersTableName)}.${sql.raw(idColumn)}) DESC, ${sql.raw(usersTableName)}.${sql.raw(orderColumn)} ${direction}`;
10644
+ const orderByClause = roleId ? sql`ORDER BY ${sql.raw(usersTableName)}.${sql.raw(orderColumn)} ${direction}` : sql`ORDER BY array_length(${sql.raw(usersTableName)}.roles, 1) DESC NULLS LAST, ${sql.raw(usersTableName)}.${sql.raw(orderColumn)} ${direction}`;
10775
10645
  const countResult = await this.db.execute(sql`
10776
10646
  SELECT count(*)::int as total FROM ${sql.raw(usersTableName)}
10777
10647
  ${whereClause}
@@ -10845,54 +10715,57 @@ class UserService {
10845
10715
  return row ? this.mapRowToUser(row) : null;
10846
10716
  }
10847
10717
  /**
10848
- * Get roles for a user from database
10718
+ * Get roles for a user from database (inline TEXT[] column)
10849
10719
  */
10850
10720
  async getUserRoles(userId) {
10851
- const rolesSchema = getTableConfig(this.rolesTable).schema || "public";
10721
+ const usersTableName = this.getQualifiedUsersTableName();
10852
10722
  const result = await this.db.execute(sql`
10853
- SELECT r.id, r.name, r.is_admin, r.default_permissions, r.collection_permissions
10854
- FROM ${sql.raw(`"${rolesSchema}"."roles"`)} r
10855
- INNER JOIN ${sql.raw(`"${rolesSchema}"."user_roles"`)} ur ON r.id = ur.role_id
10856
- WHERE ur.user_id = ${userId}
10723
+ SELECT roles FROM ${sql.raw(usersTableName)} WHERE id = ${userId}
10857
10724
  `);
10858
- return result.rows.map((row) => ({
10859
- id: row.id,
10860
- name: row.name,
10861
- isAdmin: row.is_admin,
10862
- defaultPermissions: row.default_permissions,
10863
- collectionPermissions: row.collection_permissions
10725
+ if (result.rows.length === 0) return [];
10726
+ const row = result.rows[0];
10727
+ const roleIds = row.roles ?? [];
10728
+ return roleIds.map((id) => ({
10729
+ id,
10730
+ name: id,
10731
+ isAdmin: id === "admin",
10732
+ defaultPermissions: null,
10733
+ collectionPermissions: null
10864
10734
  }));
10865
10735
  }
10866
10736
  /**
10867
10737
  * Get role IDs for a user
10868
10738
  */
10869
10739
  async getUserRoleIds(userId) {
10870
- const roles2 = await this.getUserRoles(userId);
10871
- return roles2.map((r) => r.id);
10740
+ const usersTableName = this.getQualifiedUsersTableName();
10741
+ const result = await this.db.execute(sql`
10742
+ SELECT roles FROM ${sql.raw(usersTableName)} WHERE id = ${userId}
10743
+ `);
10744
+ if (result.rows.length === 0) return [];
10745
+ const row = result.rows[0];
10746
+ return row.roles ?? [];
10872
10747
  }
10873
10748
  /**
10874
- * Set roles for a user
10749
+ * Set roles for a user (replaces existing roles)
10875
10750
  */
10876
10751
  async setUserRoles(userId, roleIds) {
10877
- const rolesSchema = getTableConfig(this.userRolesTable).schema || "public";
10878
- await this.db.execute(sql`DELETE FROM ${sql.raw(`"${rolesSchema}"."user_roles"`)} WHERE user_id = ${userId}`);
10879
- for (const roleId of roleIds) {
10880
- await this.db.execute(sql`
10881
- INSERT INTO ${sql.raw(`"${rolesSchema}"."user_roles"`)} (user_id, role_id)
10882
- VALUES (${userId}, ${roleId})
10883
- ON CONFLICT DO NOTHING
10884
- `);
10885
- }
10752
+ const usersTableName = this.getQualifiedUsersTableName();
10753
+ const rolesArray = `{${roleIds.join(",")}}`;
10754
+ await this.db.execute(sql`
10755
+ UPDATE ${sql.raw(usersTableName)}
10756
+ SET roles = ${rolesArray}::text[], updated_at = NOW()
10757
+ WHERE id = ${userId}
10758
+ `);
10886
10759
  }
10887
10760
  /**
10888
- * Assign a specific role to new user
10761
+ * Assign a specific role to new user (appends if not present)
10889
10762
  */
10890
10763
  async assignDefaultRole(userId, roleId) {
10891
- const rolesSchema = getTableConfig(this.userRolesTable).schema || "public";
10764
+ const usersTableName = this.getQualifiedUsersTableName();
10892
10765
  await this.db.execute(sql`
10893
- INSERT INTO ${sql.raw(`"${rolesSchema}"."user_roles"`)} (user_id, role_id)
10894
- VALUES (${userId}, ${roleId})
10895
- ON CONFLICT DO NOTHING
10766
+ UPDATE ${sql.raw(usersTableName)}
10767
+ SET roles = array_append(roles, ${roleId}), updated_at = NOW()
10768
+ WHERE id = ${userId} AND NOT (${roleId} = ANY(roles))
10896
10769
  `);
10897
10770
  }
10898
10771
  /**
@@ -10901,101 +10774,12 @@ class UserService {
10901
10774
  async getUserWithRoles(userId) {
10902
10775
  const user = await this.getUserById(userId);
10903
10776
  if (!user) return null;
10904
- const roles2 = await this.getUserRoles(userId);
10777
+ const roles = await this.getUserRoles(userId);
10905
10778
  return {
10906
10779
  user,
10907
- roles: roles2
10908
- };
10909
- }
10910
- }
10911
- class RoleService {
10912
- constructor(db, tableOrTables) {
10913
- this.db = db;
10914
- if (tableOrTables && (tableOrTables.roles || tableOrTables.users)) {
10915
- this.rolesTable = tableOrTables.roles || roles;
10916
- } else {
10917
- this.rolesTable = tableOrTables || roles;
10918
- }
10919
- }
10920
- rolesTable;
10921
- getQualifiedRolesTableName() {
10922
- const name = getTableName$1(this.rolesTable);
10923
- const schema = getTableConfig(this.rolesTable).schema || "public";
10924
- return `"${schema}"."${name}"`;
10925
- }
10926
- async getRoleById(id) {
10927
- const tableName = this.getQualifiedRolesTableName();
10928
- const result = await this.db.execute(sql`
10929
- SELECT id, name, is_admin, default_permissions, collection_permissions
10930
- FROM ${sql.raw(tableName)}
10931
- WHERE id = ${id}
10932
- `);
10933
- if (result.rows.length === 0) return null;
10934
- const row = result.rows[0];
10935
- return {
10936
- id: row.id,
10937
- name: row.name,
10938
- isAdmin: row.is_admin,
10939
- defaultPermissions: row.default_permissions,
10940
- collectionPermissions: row.collection_permissions
10941
- };
10942
- }
10943
- async listRoles() {
10944
- const tableName = this.getQualifiedRolesTableName();
10945
- const result = await this.db.execute(sql`
10946
- SELECT id, name, is_admin, default_permissions, collection_permissions
10947
- FROM ${sql.raw(tableName)}
10948
- ORDER BY name
10949
- `);
10950
- return result.rows.map((row) => ({
10951
- id: row.id,
10952
- name: row.name,
10953
- isAdmin: row.is_admin,
10954
- defaultPermissions: row.default_permissions,
10955
- collectionPermissions: row.collection_permissions
10956
- }));
10957
- }
10958
- async createRole(data) {
10959
- const tableName = this.getQualifiedRolesTableName();
10960
- const result = await this.db.execute(sql`
10961
- INSERT INTO ${sql.raw(tableName)} (id, name, is_admin, default_permissions, collection_permissions)
10962
- VALUES (
10963
- ${data.id},
10964
- ${data.name},
10965
- ${data.isAdmin ?? false},
10966
- ${data.defaultPermissions ? JSON.stringify(data.defaultPermissions) : null}::jsonb,
10967
- ${data.collectionPermissions ? JSON.stringify(data.collectionPermissions) : null}::jsonb
10968
- )
10969
- RETURNING id, name, is_admin, default_permissions, collection_permissions
10970
- `);
10971
- const row = result.rows[0];
10972
- return {
10973
- id: row.id,
10974
- name: row.name,
10975
- isAdmin: row.is_admin,
10976
- defaultPermissions: row.default_permissions,
10977
- collectionPermissions: row.collection_permissions
10780
+ roles
10978
10781
  };
10979
10782
  }
10980
- async updateRole(id, data) {
10981
- const existing = await this.getRoleById(id);
10982
- if (!existing) return null;
10983
- const tableName = this.getQualifiedRolesTableName();
10984
- await this.db.execute(sql`
10985
- UPDATE ${sql.raw(tableName)}
10986
- SET
10987
- name = ${data.name ?? existing.name},
10988
- is_admin = ${data.isAdmin ?? existing.isAdmin},
10989
- default_permissions = ${data.defaultPermissions ? JSON.stringify(data.defaultPermissions) : JSON.stringify(existing.defaultPermissions)}::jsonb,
10990
- collection_permissions = ${data.collectionPermissions !== void 0 ? data.collectionPermissions ? JSON.stringify(data.collectionPermissions) : null : existing.collectionPermissions ? JSON.stringify(existing.collectionPermissions) : null}::jsonb
10991
- WHERE id = ${id}
10992
- `);
10993
- return this.getRoleById(id);
10994
- }
10995
- async deleteRole(id) {
10996
- const tableName = this.getQualifiedRolesTableName();
10997
- await this.db.execute(sql`DELETE FROM ${sql.raw(tableName)} WHERE id = ${id}`);
10998
- }
10999
10783
  }
11000
10784
  class RefreshTokenService {
11001
10785
  constructor(db, tableOrTables) {
@@ -11191,11 +10975,9 @@ class PostgresAuthRepository {
11191
10975
  constructor(db, tableOrTables) {
11192
10976
  this.db = db;
11193
10977
  this.userService = new UserService(db, tableOrTables);
11194
- this.roleService = new RoleService(db, tableOrTables);
11195
10978
  this.tokenRepository = new PostgresTokenRepository(db, tableOrTables);
11196
10979
  }
11197
10980
  userService;
11198
- roleService;
11199
10981
  tokenRepository;
11200
10982
  // User operations (delegate to UserService)
11201
10983
  async createUser(data) {
@@ -11255,25 +11037,56 @@ class PostgresAuthRepository {
11255
11037
  async getUserWithRoles(userId) {
11256
11038
  return this.userService.getUserWithRoles(userId);
11257
11039
  }
11258
- // Role operations (delegate to RoleService)
11040
+ // Role operations (roles are inline on users, synthesized from string IDs)
11259
11041
  async getRoleById(id) {
11260
- return this.roleService.getRoleById(id);
11042
+ return {
11043
+ id,
11044
+ name: id,
11045
+ isAdmin: id === "admin",
11046
+ defaultPermissions: null,
11047
+ collectionPermissions: null
11048
+ };
11261
11049
  }
11262
11050
  async listRoles() {
11263
- return this.roleService.listRoles();
11051
+ return [{
11052
+ id: "admin",
11053
+ name: "Admin",
11054
+ isAdmin: true,
11055
+ defaultPermissions: null,
11056
+ collectionPermissions: null
11057
+ }, {
11058
+ id: "editor",
11059
+ name: "Editor",
11060
+ isAdmin: false,
11061
+ defaultPermissions: null,
11062
+ collectionPermissions: null
11063
+ }, {
11064
+ id: "viewer",
11065
+ name: "Viewer",
11066
+ isAdmin: false,
11067
+ defaultPermissions: null,
11068
+ collectionPermissions: null
11069
+ }];
11070
+ }
11071
+ async createRole(_data) {
11072
+ return {
11073
+ id: _data.id,
11074
+ name: _data.name,
11075
+ isAdmin: _data.isAdmin ?? false,
11076
+ defaultPermissions: _data.defaultPermissions ?? null,
11077
+ collectionPermissions: _data.collectionPermissions ?? null
11078
+ };
11264
11079
  }
11265
- async createRole(data) {
11266
- return this.roleService.createRole({
11267
- ...data,
11080
+ async updateRole(id, data) {
11081
+ return {
11082
+ id,
11083
+ name: data.name ?? id,
11084
+ isAdmin: data.isAdmin ?? id === "admin",
11268
11085
  defaultPermissions: data.defaultPermissions ?? null,
11269
11086
  collectionPermissions: data.collectionPermissions ?? null
11270
- });
11271
- }
11272
- async updateRole(id, data) {
11273
- return this.roleService.updateRole(id, data);
11087
+ };
11274
11088
  }
11275
- async deleteRole(id) {
11276
- await this.roleService.deleteRole(id);
11089
+ async deleteRole(_id) {
11277
11090
  }
11278
11091
  // Token operations (delegate to PostgresTokenRepository)
11279
11092
  async createRefreshToken(userId, tokenHash, expiresAt, userAgent, ipAddress) {
@@ -11823,34 +11636,27 @@ function createPostgresBootstrapper(pgConfig) {
11823
11636
  const internals = driverResult.internals;
11824
11637
  const db = internals.db;
11825
11638
  const registry = internals.registry;
11826
- await ensureAuthTablesExist(db, registry);
11639
+ const authCollection = authConfig.collection;
11640
+ await ensureAuthTablesExist(db, authCollection);
11827
11641
  let emailService;
11828
11642
  if (authConfig.email) {
11829
11643
  emailService = createEmailService(authConfig.email);
11830
11644
  }
11831
- const customUsersTable = registry?.getTable("users");
11832
- const customRolesTable = registry?.getTable("roles");
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;
11833
11647
  let usersSchemaName = "rebase";
11834
- let rolesSchemaName = "rebase";
11835
- if (customUsersTable) {
11836
- usersSchemaName = getTableConfig(customUsersTable).schema || "public";
11648
+ if (authCollection && "schema" in authCollection && typeof authCollection.schema === "string") {
11649
+ usersSchemaName = authCollection.schema;
11837
11650
  }
11838
- if (customRolesTable) {
11839
- rolesSchemaName = getTableConfig(customRolesTable).schema || "public";
11840
- }
11841
- const authTables = createAuthSchema(rolesSchemaName, usersSchemaName);
11842
- if (customUsersTable) {
11843
- authTables.users = customUsersTable;
11844
- }
11845
- if (customRolesTable) {
11846
- authTables.roles = customRolesTable;
11651
+ const authTables = createAuthSchema(usersSchemaName);
11652
+ if (usersTable) {
11653
+ authTables.users = usersTable;
11847
11654
  }
11848
11655
  const userService = new UserService(db, authTables);
11849
- const roleService = new RoleService(db, authTables);
11850
11656
  const authRepository = new PostgresAuthRepository(db, authTables);
11851
11657
  return {
11852
11658
  userService,
11853
- roleService,
11659
+ roleService: userService,
11854
11660
  emailService,
11855
11661
  authRepository
11856
11662
  };
@@ -11955,17 +11761,12 @@ export {
11955
11761
  mfaFactorsRelations,
11956
11762
  passwordResetTokens,
11957
11763
  passwordResetTokensRelations,
11958
- rebaseSchema,
11959
11764
  recoveryCodes,
11960
11765
  recoveryCodesRelations,
11961
11766
  refreshTokens,
11962
11767
  refreshTokensRelations,
11963
- roles,
11964
- rolesRelations,
11965
11768
  userIdentities,
11966
11769
  userIdentitiesRelations,
11967
- userRoles,
11968
- userRolesRelations,
11969
11770
  users,
11970
11771
  usersRelations,
11971
11772
  usersSchema