@rebasepro/server-postgres 0.9.1-canary.d906fb7 → 0.9.1-canary.e3f810f

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.
@@ -19,6 +19,22 @@ export declare class UserService implements UserRepository {
19
19
  private userIdentitiesTable;
20
20
  constructor(db: NodePgDatabase, tableOrTables?: RebasePgTable | Partial<AuthSchemaTables>);
21
21
  private getQualifiedUsersTableName;
22
+ /**
23
+ * Run a privileged auth write with an explicitly cleared RLS context.
24
+ *
25
+ * The auth services run on the base/owner connection, which by design
26
+ * carries a NULL `app.user_id` so the `auth.uid() IS NULL` server-escape
27
+ * in the default policies applies. That NULL is normally guaranteed by
28
+ * `set_config(..., is_local = true)` resetting at transaction end — but a
29
+ * GUC that survives on a pooled connection (or a connection role that
30
+ * doesn't bypass RLS: FORCE ROW LEVEL SECURITY, or a non-owner role)
31
+ * turns the trusted write into an RLS-scoped one and denies it with
32
+ * SQLSTATE 42501. Clearing the GUCs here, transaction-locally at the
33
+ * single chokepoint, makes the server context deterministic instead of
34
+ * trusting whatever state the pool hands us. `auth.uid()` reads '' as
35
+ * NULL via NULLIF, so '' is the server context.
36
+ */
37
+ private withServerContext;
22
38
  private mapRowToUser;
23
39
  private mapPayload;
24
40
  createUser(data: CreateUserData): Promise<UserData>;
@@ -19,6 +19,27 @@ export interface PostgresPoolConfig {
19
19
  /** Enable TCP keep-alive (default: true) */
20
20
  keepAlive?: boolean;
21
21
  }
22
+ /**
23
+ * Destroy pool clients that are released while still inside a transaction.
24
+ *
25
+ * pg-pool returns a client to the idle list whenever `release()` is called
26
+ * without an error — even if the connection is still mid-transaction (status
27
+ * `T`/`E`). That happens in practice: drizzle's pool transaction releases in
28
+ * a `finally` after attempting ROLLBACK, and if the ROLLBACK itself fails
29
+ * (e.g. it was queued behind a statement that hit the client-side
30
+ * query_timeout), the client goes back dirty. The next checkout then runs
31
+ * its statements inside the zombie transaction — with the previous request's
32
+ * `app.*` RLS GUCs still applied, which turns unrelated queries into
33
+ * RLS-scoped ones (observed in production as registration failing with
34
+ * SQLSTATE 42501 under a leaked anonymous context).
35
+ *
36
+ * pg-pool emits `release` before it consults its private `_expired` set, so
37
+ * marking the client expired here makes `_release()` destroy it instead of
38
+ * pooling it. Both `client._txStatus` (pg ≥ 8.16) and `pool._expired` are
39
+ * private APIs — feature-detect and fall back to loud logging so an upstream
40
+ * change degrades to observability, never to silent corruption.
41
+ */
42
+ export declare function guardPoolAgainstDirtyRelease(pool: Pool, label: string): void;
22
43
  /**
23
44
  * Create a Drizzle-backed Postgres connection with a production-grade
24
45
  * connection pool.
package/dist/index.es.js CHANGED
@@ -71,16 +71,51 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
71
71
  var connection_exports = /* @__PURE__ */ __exportAll({
72
72
  createDirectDatabaseConnection: () => createDirectDatabaseConnection,
73
73
  createPostgresDatabaseConnection: () => createPostgresDatabaseConnection,
74
- createReadReplicaConnection: () => createReadReplicaConnection
74
+ createReadReplicaConnection: () => createReadReplicaConnection,
75
+ guardPoolAgainstDirtyRelease: () => guardPoolAgainstDirtyRelease
75
76
  });
76
77
  var DEFAULT_POOL = {
77
78
  max: 20,
78
79
  idleTimeoutMillis: 3e4,
79
80
  connectionTimeoutMillis: 1e4,
80
- queryTimeout: 3e4,
81
+ queryTimeout: 6e4,
81
82
  statementTimeout: 3e4,
82
83
  keepAlive: true
83
84
  };
85
+ /** ReadyForQuery status byte: `I` idle, `T` in transaction, `E` failed transaction. */
86
+ var TX_IDLE = "I";
87
+ /**
88
+ * Destroy pool clients that are released while still inside a transaction.
89
+ *
90
+ * pg-pool returns a client to the idle list whenever `release()` is called
91
+ * without an error — even if the connection is still mid-transaction (status
92
+ * `T`/`E`). That happens in practice: drizzle's pool transaction releases in
93
+ * a `finally` after attempting ROLLBACK, and if the ROLLBACK itself fails
94
+ * (e.g. it was queued behind a statement that hit the client-side
95
+ * query_timeout), the client goes back dirty. The next checkout then runs
96
+ * its statements inside the zombie transaction — with the previous request's
97
+ * `app.*` RLS GUCs still applied, which turns unrelated queries into
98
+ * RLS-scoped ones (observed in production as registration failing with
99
+ * SQLSTATE 42501 under a leaked anonymous context).
100
+ *
101
+ * pg-pool emits `release` before it consults its private `_expired` set, so
102
+ * marking the client expired here makes `_release()` destroy it instead of
103
+ * pooling it. Both `client._txStatus` (pg ≥ 8.16) and `pool._expired` are
104
+ * private APIs — feature-detect and fall back to loud logging so an upstream
105
+ * change degrades to observability, never to silent corruption.
106
+ */
107
+ function guardPoolAgainstDirtyRelease(pool, label) {
108
+ pool.on("release", (err, client) => {
109
+ if (err) return;
110
+ const txStatus = client?._txStatus;
111
+ if (typeof txStatus !== "string" || txStatus === TX_IDLE) return;
112
+ const expired = pool._expired;
113
+ if (expired && typeof expired.add === "function" && typeof expired.has === "function" && client && typeof client === "object") {
114
+ expired.add(client);
115
+ logger.error(`[${label}] Client released back to the pool while still in a transaction (status '${txStatus}') — destroying it so the open transaction and its session state (RLS GUCs) cannot leak into the next request.`);
116
+ } else logger.error(`[${label}] Client released mid-transaction (status '${txStatus}') but the pool's internal expiry set is unavailable (pg-pool internals changed?). The connection may leak its open transaction into subsequent requests.`);
117
+ });
118
+ }
84
119
  /**
85
120
  * Create a Drizzle-backed Postgres connection with a production-grade
86
121
  * connection pool.
@@ -112,6 +147,7 @@ function createPostgresDatabaseConnection(connectionString, schema, poolConfig)
112
147
  logger.error("[pg-pool] Unexpected pool error", { detail: err.message });
113
148
  if (err.message.includes("ETIMEDOUT")) logger.warn("[pg-pool] Connection timeout detected — pool will auto-retry");
114
149
  });
150
+ guardPoolAgainstDirtyRelease(pool, "pg-pool");
115
151
  return {
116
152
  db: schema ? drizzle(pool, { schema }) : drizzle(pool),
117
153
  pool,
@@ -144,6 +180,7 @@ function createDirectDatabaseConnection(connectionString, schema, poolConfig) {
144
180
  pool.on("error", (err) => {
145
181
  logger.error("[pg-direct-pool] Unexpected pool error", { detail: err.message });
146
182
  });
183
+ guardPoolAgainstDirtyRelease(pool, "pg-direct-pool");
147
184
  return {
148
185
  db: schema ? drizzle(pool, { schema }) : drizzle(pool),
149
186
  pool,
@@ -173,6 +210,7 @@ function createReadReplicaConnection(connectionString, schema, poolConfig) {
173
210
  pool.on("error", (err) => {
174
211
  logger.error("[pg-replica-pool] Unexpected pool error", { detail: err.message });
175
212
  });
213
+ guardPoolAgainstDirtyRelease(pool, "pg-replica-pool");
176
214
  return {
177
215
  db: schema ? drizzle(pool, { schema }) : drizzle(pool),
178
216
  pool,
@@ -6486,8 +6524,27 @@ function coerceDeclaredNumbers(row, collection) {
6486
6524
  return out;
6487
6525
  }
6488
6526
  /** Render one target row in the requested style. */
6527
+ /**
6528
+ * Drop every column the collection marked `excludeFromApi`.
6529
+ *
6530
+ * Password hashes and verification tokens have to be readable server-side but
6531
+ * must never reach a client — and "never" has to mean every exit from this
6532
+ * pipeline, including relation targets, or a secret leaks through whichever
6533
+ * path was overlooked. Keyed by both the property name and its column name,
6534
+ * since a row can arrive keyed either way depending on the caller.
6535
+ */
6536
+ function stripExcluded(row, collection) {
6537
+ const properties = collection.properties;
6538
+ if (!properties) return row;
6539
+ for (const [key, property] of Object.entries(properties)) {
6540
+ if (!property?.excludeFromApi) continue;
6541
+ delete row[key];
6542
+ if (property.columnName) delete row[property.columnName];
6543
+ }
6544
+ return row;
6545
+ }
6489
6546
  function renderTarget(targetRow, targetCollection, style, registry) {
6490
- if (style === "inline") return coerceDeclaredNumbers({ ...targetRow }, targetCollection);
6547
+ if (style === "inline") return stripExcluded(coerceDeclaredNumbers({ ...targetRow }, targetCollection), targetCollection);
6491
6548
  const address = relationTargetAddress(targetRow, targetCollection, registry);
6492
6549
  const path = targetCollection.slug;
6493
6550
  return createRelationRefWithData(address, path, {
@@ -6529,7 +6586,7 @@ function toCmsRow(row, collection, registry) {
6529
6586
  normalized[key] = relData.map((item) => renderTarget(isJunctionRelation(relation) ? unwrapJunctionRow(item) : item, targetCollection, "ref", registry));
6530
6587
  } else if (relation.cardinality === "one" && typeof relData === "object" && !Array.isArray(relData)) normalized[key] = renderTarget(relData, relation.target(), "ref", registry);
6531
6588
  }
6532
- return normalized;
6589
+ return stripExcluded(normalized, collection);
6533
6590
  }
6534
6591
  /**
6535
6592
  * The row REST serves: every column under its own name, with the value Postgres
@@ -6554,7 +6611,7 @@ function toRestRow(row, collection, registry) {
6554
6611
  else if (relation && typeof value === "object" && value !== null) flat[key] = renderTarget(value, relation.target(), "inline", registry);
6555
6612
  else flat[key] = coerceDeclaredNumber(value, collection.properties?.[key]);
6556
6613
  }
6557
- return flat;
6614
+ return stripExcluded(flat, collection);
6558
6615
  }
6559
6616
  //#endregion
6560
6617
  //#region src/services/FetchService.ts
@@ -9196,6 +9253,7 @@ var DatabasePoolManager = class {
9196
9253
  pool.on("error", (err) => {
9197
9254
  logger.error(`[DatabasePoolManager] Unexpected error on idle client for db ${databaseName}`, { error: err });
9198
9255
  });
9256
+ guardPoolAgainstDirtyRelease(pool, `pg-pool:${databaseName}`);
9199
9257
  this.pools.set(databaseName, pool);
9200
9258
  return pool;
9201
9259
  }
@@ -20756,14 +20814,22 @@ async function ensureAuthTablesExist(db, collection) {
20756
20814
  $$ LANGUAGE sql STABLE
20757
20815
  `);
20758
20816
  });
20759
- await db.execute(sql`
20760
- ALTER TABLE ${sql.raw(usersTableName)}
20761
- ADD COLUMN IF NOT EXISTS is_anonymous BOOLEAN DEFAULT FALSE
20762
- `);
20763
- await db.execute(sql`
20764
- ALTER TABLE ${sql.raw(usersTableName)}
20765
- ADD COLUMN IF NOT EXISTS roles TEXT[] DEFAULT '{}' NOT NULL
20766
- `);
20817
+ for (const columnDef of [
20818
+ "display_name VARCHAR(255)",
20819
+ "photo_url VARCHAR(500)",
20820
+ "roles TEXT[] DEFAULT '{}' NOT NULL",
20821
+ "password_hash VARCHAR(255)",
20822
+ "email_verified BOOLEAN DEFAULT FALSE NOT NULL",
20823
+ "email_verification_token VARCHAR(255)",
20824
+ "email_verification_sent_at TIMESTAMP WITH TIME ZONE",
20825
+ "is_anonymous BOOLEAN DEFAULT FALSE NOT NULL",
20826
+ "metadata JSONB DEFAULT '{}' NOT NULL",
20827
+ "created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL",
20828
+ "updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL"
20829
+ ]) await db.execute(sql`
20830
+ ALTER TABLE ${sql.raw(usersTableName)}
20831
+ ADD COLUMN IF NOT EXISTS ${sql.raw(columnDef)}
20832
+ `);
20767
20833
  try {
20768
20834
  if ((await db.execute(sql`
20769
20835
  SELECT EXISTS (
@@ -20834,6 +20900,34 @@ async function ensureAuthTablesExist(db, collection) {
20834
20900
  CREATE INDEX IF NOT EXISTS idx_recovery_codes_user
20835
20901
  ON ${sql.raw(recoveryCodesTableName)}(user_id)
20836
20902
  `);
20903
+ try {
20904
+ const authTablePairs = [
20905
+ [usersSchema, resolvedTable],
20906
+ [authSchema, "user_identities"],
20907
+ [authSchema, "refresh_tokens"],
20908
+ [authSchema, "password_reset_tokens"],
20909
+ [authSchema, "app_config"],
20910
+ [authSchema, "mfa_factors"],
20911
+ [authSchema, "mfa_challenges"],
20912
+ [authSchema, "recovery_codes"]
20913
+ ];
20914
+ for (const [schemaName, tableName] of authTablePairs) if ((await db.execute(sql`
20915
+ SELECT 1
20916
+ FROM pg_class c
20917
+ JOIN pg_namespace n ON n.oid = c.relnamespace
20918
+ WHERE n.nspname = ${schemaName}
20919
+ AND c.relname = ${tableName}
20920
+ AND c.relforcerowsecurity
20921
+ `)).rows.length > 0) {
20922
+ await db.execute(sql`
20923
+ ALTER TABLE ${sql.raw(`"${schemaName}"."${tableName}"`)}
20924
+ NO FORCE ROW LEVEL SECURITY
20925
+ `);
20926
+ logger.warn(`🔧 Cleared stale FORCE ROW LEVEL SECURITY on "${schemaName}"."${tableName}" (legacy RLS model — it binds the owner connection and breaks privileged auth writes)`);
20927
+ }
20928
+ } catch (rlsReconcileError) {
20929
+ logger.warn(`⚠️ Could not reconcile FORCE ROW LEVEL SECURITY on auth tables: ${rlsReconcileError instanceof Error ? rlsReconcileError.message : String(rlsReconcileError)}`);
20930
+ }
20837
20931
  logger.info("✅ Auth tables ready");
20838
20932
  } catch (error) {
20839
20933
  logger.error("❌ Failed to create auth tables", { error });
@@ -20881,6 +20975,31 @@ var UserService = class {
20881
20975
  const name = getTableName(this.usersTable);
20882
20976
  return `"${getTableConfig(this.usersTable).schema || "public"}"."${name}"`;
20883
20977
  }
20978
+ /**
20979
+ * Run a privileged auth write with an explicitly cleared RLS context.
20980
+ *
20981
+ * The auth services run on the base/owner connection, which by design
20982
+ * carries a NULL `app.user_id` so the `auth.uid() IS NULL` server-escape
20983
+ * in the default policies applies. That NULL is normally guaranteed by
20984
+ * `set_config(..., is_local = true)` resetting at transaction end — but a
20985
+ * GUC that survives on a pooled connection (or a connection role that
20986
+ * doesn't bypass RLS: FORCE ROW LEVEL SECURITY, or a non-owner role)
20987
+ * turns the trusted write into an RLS-scoped one and denies it with
20988
+ * SQLSTATE 42501. Clearing the GUCs here, transaction-locally at the
20989
+ * single chokepoint, makes the server context deterministic instead of
20990
+ * trusting whatever state the pool hands us. `auth.uid()` reads '' as
20991
+ * NULL via NULLIF, so '' is the server context.
20992
+ */
20993
+ async withServerContext(fn) {
20994
+ return await this.db.transaction(async (tx) => {
20995
+ await tx.execute(sql`
20996
+ SELECT set_config('app.user_id', '', true),
20997
+ set_config('app.user_roles', '', true),
20998
+ set_config('app.jwt', '', true)
20999
+ `);
21000
+ return await fn(tx);
21001
+ });
21002
+ }
20884
21003
  mapRowToUser(row) {
20885
21004
  if (!row) return row;
20886
21005
  const id = row.id ?? row.uid;
@@ -20978,7 +21097,7 @@ var UserService = class {
20978
21097
  }
20979
21098
  async createUser(data) {
20980
21099
  const payload = this.mapPayload(data);
20981
- const [row] = await this.db.insert(this.usersTable).values(payload).returning();
21100
+ const [row] = await this.withServerContext(async (db) => await db.insert(this.usersTable).values(payload).returning());
20982
21101
  return this.mapRowToUser(row);
20983
21102
  }
20984
21103
  async getUserById(id) {
@@ -21017,12 +21136,12 @@ var UserService = class {
21017
21136
  }));
21018
21137
  }
21019
21138
  async linkUserIdentity(userId, provider, providerId, profileData) {
21020
- await this.db.insert(this.userIdentitiesTable).values({
21139
+ await this.withServerContext(async (db) => db.insert(this.userIdentitiesTable).values({
21021
21140
  userId,
21022
21141
  provider,
21023
21142
  providerId,
21024
21143
  profileData: profileData || null
21025
- }).onConflictDoNothing({ target: [this.userIdentitiesTable.provider, this.userIdentitiesTable.providerId] });
21144
+ }).onConflictDoNothing({ target: [this.userIdentitiesTable.provider, this.userIdentitiesTable.providerId] }));
21026
21145
  }
21027
21146
  async updateUser(id, data) {
21028
21147
  const idCol = getColumn(this.usersTable, "id");
@@ -21030,13 +21149,13 @@ var UserService = class {
21030
21149
  const payload = this.mapPayload(data);
21031
21150
  const updatedAtKey = getColumnKey(this.usersTable, "updatedAt", "updated_at") || "updatedAt";
21032
21151
  payload[updatedAtKey] = /* @__PURE__ */ new Date();
21033
- const [row] = await this.db.update(this.usersTable).set(payload).where(eq(idCol, id)).returning();
21152
+ const [row] = await this.withServerContext(async (db) => await db.update(this.usersTable).set(payload).where(eq(idCol, id)).returning());
21034
21153
  return row ? this.mapRowToUser(row) : null;
21035
21154
  }
21036
21155
  async deleteUser(id) {
21037
21156
  const idCol = getColumn(this.usersTable, "id");
21038
21157
  if (!idCol) return;
21039
- await this.db.delete(this.usersTable).where(eq(idCol, id));
21158
+ await this.withServerContext(async (db) => db.delete(this.usersTable).where(eq(idCol, id)));
21040
21159
  }
21041
21160
  async listUsers() {
21042
21161
  return (await this.db.select().from(this.usersTable)).map((row) => this.mapRowToUser(row));
@@ -21090,10 +21209,10 @@ var UserService = class {
21090
21209
  if (!idCol) return;
21091
21210
  const passwordHashColKey = getColumnKey(this.usersTable, "passwordHash", "password_hash") || "passwordHash";
21092
21211
  const updatedAtColKey = getColumnKey(this.usersTable, "updatedAt", "updated_at") || "updatedAt";
21093
- await this.db.update(this.usersTable).set({
21212
+ await this.withServerContext(async (db) => db.update(this.usersTable).set({
21094
21213
  [passwordHashColKey]: passwordHash,
21095
21214
  [updatedAtColKey]: /* @__PURE__ */ new Date()
21096
- }).where(eq(idCol, id));
21215
+ }).where(eq(idCol, id)));
21097
21216
  }
21098
21217
  /**
21099
21218
  * Set email verification status
@@ -21104,11 +21223,11 @@ var UserService = class {
21104
21223
  const emailVerifiedColKey = getColumnKey(this.usersTable, "emailVerified", "email_verified") || "emailVerified";
21105
21224
  const emailVerificationTokenColKey = getColumnKey(this.usersTable, "emailVerificationToken", "email_verification_token") || "emailVerificationToken";
21106
21225
  const updatedAtColKey = getColumnKey(this.usersTable, "updatedAt", "updated_at") || "updatedAt";
21107
- await this.db.update(this.usersTable).set({
21226
+ await this.withServerContext(async (db) => db.update(this.usersTable).set({
21108
21227
  [emailVerifiedColKey]: verified,
21109
21228
  [emailVerificationTokenColKey]: null,
21110
21229
  [updatedAtColKey]: /* @__PURE__ */ new Date()
21111
- }).where(eq(idCol, id));
21230
+ }).where(eq(idCol, id)));
21112
21231
  }
21113
21232
  /**
21114
21233
  * Set email verification token
@@ -21119,11 +21238,11 @@ var UserService = class {
21119
21238
  const emailVerificationTokenColKey = getColumnKey(this.usersTable, "emailVerificationToken", "email_verification_token") || "emailVerificationToken";
21120
21239
  const emailVerificationSentAtColKey = getColumnKey(this.usersTable, "emailVerificationSentAt", "email_verification_sent_at") || "emailVerificationSentAt";
21121
21240
  const updatedAtColKey = getColumnKey(this.usersTable, "updatedAt", "updated_at") || "updatedAt";
21122
- await this.db.update(this.usersTable).set({
21241
+ await this.withServerContext(async (db) => db.update(this.usersTable).set({
21123
21242
  [emailVerificationTokenColKey]: token,
21124
21243
  [emailVerificationSentAtColKey]: token ? /* @__PURE__ */ new Date() : null,
21125
21244
  [updatedAtColKey]: /* @__PURE__ */ new Date()
21126
- }).where(eq(idCol, id));
21245
+ }).where(eq(idCol, id)));
21127
21246
  }
21128
21247
  /**
21129
21248
  * Find user by email verification token
@@ -21168,22 +21287,22 @@ var UserService = class {
21168
21287
  async setUserRoles(userId, roleIds) {
21169
21288
  const usersTableName = this.getQualifiedUsersTableName();
21170
21289
  const rolesArray = `{${roleIds.join(",")}}`;
21171
- await this.db.execute(sql`
21290
+ await this.withServerContext(async (db) => db.execute(sql`
21172
21291
  UPDATE ${sql.raw(usersTableName)}
21173
21292
  SET roles = ${rolesArray}::text[], updated_at = NOW()
21174
21293
  WHERE id = ${userId}
21175
- `);
21294
+ `));
21176
21295
  }
21177
21296
  /**
21178
21297
  * Assign a specific role to new user (appends if not present)
21179
21298
  */
21180
21299
  async assignDefaultRole(userId, roleId) {
21181
21300
  const usersTableName = this.getQualifiedUsersTableName();
21182
- await this.db.execute(sql`
21301
+ await this.withServerContext(async (db) => db.execute(sql`
21183
21302
  UPDATE ${sql.raw(usersTableName)}
21184
21303
  SET roles = array_append(roles, ${roleId}), updated_at = NOW()
21185
21304
  WHERE id = ${userId} AND NOT (${roleId} = ANY(roles))
21186
- `);
21305
+ `));
21187
21306
  }
21188
21307
  /**
21189
21308
  * Get user with their roles
@@ -22833,6 +22952,6 @@ function createPostgresAdapter(pgConfig) {
22833
22952
  };
22834
22953
  }
22835
22954
  //#endregion
22836
- export { AuthenticatedPostgresBackendDriver, BackupToolError, BranchService, DatabasePoolManager, DrizzleConditionBuilder, PostgresBackendDriver, PostgresCollectionRegistry, PostgresConditionBuilder, PostgresRealtimeProvider, RealtimeService, appConfig, backupCronConfigFromEnv, buildBackupFilename, buildPgDumpArgs, buildPgRestoreArgs, checkToolServerCompatibility, createAuthSchema, createBackupCron, createDirectDatabaseConnection, createDump, createPostgresAdapter, createPostgresBootstrapper, createPostgresDatabaseConnection, createPostgresWebSocket, createReadReplicaConnection, detectToolMajor, ensureDatabaseExists, generateSchema, getServerVersionMajor, joinStorageKey, listBackups, magicLinkTokens, magicLinkTokensRelations, mfaChallenges, mfaChallengesRelations, mfaFactors, mfaFactorsRelations, parseBackupDestination, parseBackupTimestamp, parseDbNameFromUrl, parsePgToolMajor, passwordResetTokens, passwordResetTokensRelations, preflight, pruneBackups, recoveryCodes, recoveryCodesRelations, refreshTokens, refreshTokensRelations, resolveConnectionString, resolvePgBinary, restoreDump, selectBackupsToPrune, serverVersionNumToMajor, uploadBackup, userIdentities, userIdentitiesRelations, users, usersRelations, usersSchema, withDatabaseName };
22955
+ export { AuthenticatedPostgresBackendDriver, BackupToolError, BranchService, DatabasePoolManager, DrizzleConditionBuilder, PostgresBackendDriver, PostgresCollectionRegistry, PostgresConditionBuilder, PostgresRealtimeProvider, RealtimeService, appConfig, backupCronConfigFromEnv, buildBackupFilename, buildPgDumpArgs, buildPgRestoreArgs, checkToolServerCompatibility, createAuthSchema, createBackupCron, createDirectDatabaseConnection, createDump, createPostgresAdapter, createPostgresBootstrapper, createPostgresDatabaseConnection, createPostgresWebSocket, createReadReplicaConnection, detectToolMajor, ensureDatabaseExists, generateSchema, getServerVersionMajor, guardPoolAgainstDirtyRelease, joinStorageKey, listBackups, magicLinkTokens, magicLinkTokensRelations, mfaChallenges, mfaChallengesRelations, mfaFactors, mfaFactorsRelations, parseBackupDestination, parseBackupTimestamp, parseDbNameFromUrl, parsePgToolMajor, passwordResetTokens, passwordResetTokensRelations, preflight, pruneBackups, recoveryCodes, recoveryCodesRelations, refreshTokens, refreshTokensRelations, resolveConnectionString, resolvePgBinary, restoreDump, selectBackupsToPrune, serverVersionNumToMajor, uploadBackup, userIdentities, userIdentitiesRelations, users, usersRelations, usersSchema, withDatabaseName };
22837
22956
 
22838
22957
  //# sourceMappingURL=index.es.js.map