@rebasepro/server-postgres 0.21.0 → 0.21.1

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 (32) hide show
  1. package/README.md +15 -13
  2. package/dist/{BranchService-W3DMfcZZ.js → BranchService-CucnFcSE.js} +2 -2
  3. package/dist/{BranchService-W3DMfcZZ.js.map → BranchService-CucnFcSE.js.map} +1 -1
  4. package/dist/PostgresBackendDriver.d.ts +26 -10
  5. package/dist/backup/backup-cron.d.ts +28 -1
  6. package/dist/{backup-cli-Bp-ou6o2.js → backup-cli-CkjsJEcu.js} +3 -2
  7. package/dist/backup-cli-CkjsJEcu.js.map +1 -0
  8. package/dist/{cli-errors-C3g_kHBw.js → cli-errors-Dka89exj.js} +19 -2
  9. package/dist/cli-errors-Dka89exj.js.map +1 -0
  10. package/dist/cli.js +15 -8
  11. package/dist/cli.js.map +1 -1
  12. package/dist/{doctor-ByFWL_ET.js → doctor-Gzzd8wTq.js} +4 -4
  13. package/dist/{doctor-ByFWL_ET.js.map → doctor-Gzzd8wTq.js.map} +1 -1
  14. package/dist/{ensure-collection-policies-CQGHln-y.js → ensure-collection-policies-B4IDFtQ-.js} +2 -2
  15. package/dist/{ensure-collection-policies-CQGHln-y.js.map → ensure-collection-policies-B4IDFtQ-.js.map} +1 -1
  16. package/dist/{ensure-collection-tables-Bkvehxdm.js → ensure-collection-tables-Cr2ye5JC.js} +2 -2
  17. package/dist/{ensure-collection-tables-Bkvehxdm.js.map → ensure-collection-tables-Cr2ye5JC.js.map} +1 -1
  18. package/dist/{generate-drizzle-schema-vSK-VdYT.js → generate-drizzle-schema-D2MDdJt-.js} +2 -2
  19. package/dist/{generate-drizzle-schema-vSK-VdYT.js.map → generate-drizzle-schema-D2MDdJt-.js.map} +1 -1
  20. package/dist/{generate-drizzle-schema-logic-Dfyf_MRu.js → generate-drizzle-schema-logic-DFa9qy9u.js} +4 -2
  21. package/dist/{generate-drizzle-schema-logic-Dfyf_MRu.js.map → generate-drizzle-schema-logic-DFa9qy9u.js.map} +1 -1
  22. package/dist/{generated-schema-staleness-DRQe2BpC.js → generated-schema-staleness-Di9c4ZxN.js} +9 -5
  23. package/dist/{generated-schema-staleness-DRQe2BpC.js.map → generated-schema-staleness-Di9c4ZxN.js.map} +1 -1
  24. package/dist/index.es.js +264 -180
  25. package/dist/index.es.js.map +1 -1
  26. package/dist/schema/doctor-cli.js +2 -2
  27. package/dist/schema/generate-drizzle-schema.js +1 -1
  28. package/dist/schema/generated-schema-staleness.d.ts +22 -0
  29. package/dist/services/realtimeService.d.ts +75 -12
  30. package/package.json +7 -7
  31. package/dist/backup-cli-Bp-ou6o2.js.map +0 -1
  32. package/dist/cli-errors-C3g_kHBw.js.map +0 -1
package/dist/index.es.js CHANGED
@@ -3,22 +3,22 @@ __createRequire(import.meta.url);
3
3
  import { r as __exportAll } from "./__vite-browser-external-BnuHet1e.js";
4
4
  import { a as createDirectDatabaseConnection, c as guardPoolAgainstDirtyRelease, l as pinSearchPath, o as createPostgresDatabaseConnection, r as cappedPoolMax, s as createReadReplicaConnection, t as DatabasePoolManager, u as poolMaxCeiling } from "./databasePoolManager-Bj5FbeAs.js";
5
5
  import { f as hiddenColumnsOption, l as SEARCH_UNACCENT_FN, m as searchColumnNames, o as sharedRelationName, u as buildSearchColumnSpec, x as visibleColumnProjection } from "./plan-schema-Hgl62S-w.js";
6
- import { d as extractPgError, f as isRoleSwitchingPermissionError, g as sanitizeErrorForClient, h as reachedDatabase, i as diagnoseDbError, l as classifyConnectFailure, m as pgErrorToFriendlyMessage, n as deepestErrorMessage, o as isEconnrefused, p as isRowLevelSecurityDenial, s as parseHostInfo, u as extractCauseMessage } from "./cli-errors-C3g_kHBw.js";
7
- import { r as BranchingUnsupportedError, t as BranchService } from "./BranchService-W3DMfcZZ.js";
6
+ import { d as extractPgError, f as isRoleSwitchingPermissionError, g as sanitizeErrorForClient, h as reachedDatabase, i as diagnoseDbError, l as classifyConnectFailure, m as pgErrorToFriendlyMessage, n as deepestErrorMessage, o as isEconnrefused, p as isRowLevelSecurityDenial, s as parseHostInfo, u as extractCauseMessage } from "./cli-errors-Dka89exj.js";
7
+ import { r as BranchingUnsupportedError, t as BranchService } from "./BranchService-CucnFcSE.js";
8
8
  import { c as warnOnLegacyRlsFunctions, i as ensureAppRole, l as warnOnRoleSchemaCollision, n as applyAuthContext, o as validatePolicyPgRoles, r as detectConnectionPosture, s as warnOnAnonymousGrants, t as REBASE_USER_ROLE } from "./rls-enforcement-C6Xk0lA6.js";
9
9
  import { l as resolveColumnName } from "./column-plan-helpers-CpILzHJS.js";
10
10
  import { i as isAuthCollection, t as AUTH_USERS_COLUMNS } from "./auth-users-columns-D2LBFrMH.js";
11
11
  import { a as generatePostgresVectorDdl, i as generatePostgresTriggersDdl, n as generatePostgresPoliciesDdl, r as generatePostgresSearchDdl, t as generatePostgresDdl } from "./generate-postgres-ddl-logic-CR2xcS7e.js";
12
- import { t as generateSchema } from "./generate-drizzle-schema-logic-Dfyf_MRu.js";
13
- import { a as readSchemaFactsFor, r as planCollectionSchemaEnsure } from "./ensure-collection-tables-Bkvehxdm.js";
14
- import { t as main } from "./generate-drizzle-schema-vSK-VdYT.js";
12
+ import { t as generateSchema } from "./generate-drizzle-schema-logic-DFa9qy9u.js";
13
+ import { a as readSchemaFactsFor, r as planCollectionSchemaEnsure } from "./ensure-collection-tables-Cr2ye5JC.js";
14
+ import { t as main } from "./generate-drizzle-schema-D2MDdJt-.js";
15
15
  import { A as parsePgToolMajor, C as checkToolServerCompatibility, D as parseBackupDestination, E as joinStorageKey, M as serverVersionNumToMajor, N as splitGlobalsStatements, O as parseBackupTimestamp, P as withDatabaseName, S as buildRowSecurityPgOptions, T as globalsFileForDump, _ as buildBackupFilename, a as detectToolMajor, b as buildPgRestoreArgs, c as getServerVersionMajor, d as pruneBackups, f as resolvePgBinary, g as selectBackupsToPrune, h as validateDump, i as createDump, j as resolveConnectionString, k as parseDbNameFromUrl, l as listBackups, m as uploadBackup, n as applyGlobals, o as discardPartialDump, p as restoreDump, s as ensureDatabaseExists, t as BackupToolError, u as preflight, v as buildPgDumpArgs, w as diagnoseRowSecurityDumpFailure, x as buildPgRestoreListArgs, y as buildPgDumpallGlobalsArgs } from "./backup-service-BNLwvxuy.js";
16
16
  import { i as resolveAuthSchema, r as probeAuthSchema, t as ensureAuthTablesExist } from "./ensure-tables-BnEvEJPr.js";
17
17
  import { c as identifyJoinTables, d as singularize, h as humanize, i as buildTablesMap, m as mapPgType, r as buildEnumMap, s as getIconForTable } from "./introspect-db-logic-C6LQdTxj.js";
18
18
  import { Client } from "pg";
19
- import { ApiError, assertFieldOpsValid, assertWriteRequestValid, createDdlBootstrapper, createEmailService, extractUserFromToken, logger, rawQueryLoggingEnabled, resolveBatchRefs, resolveRequireAuth, safeCompare, splitFieldOps } from "@rebasepro/server";
19
+ import { ApiError, assertFieldOpsValid, assertWriteRequestValid, createDdlBootstrapper, createEmailService, declaredErrorAnswer, extractUserFromToken, logger, rawQueryLoggingEnabled, resolveBatchRefs, resolveRequireAuth, safeCompare, splitFieldOps } from "@rebasepro/server";
20
20
  import { and, asc, count, desc, eq, getTableColumns, getTableName, gt, ilike, inArray, isNotNull, isNull, isTable, lt, not, notInArray, or, relations, sql } from "drizzle-orm";
21
- import { ALL_WHERE_FILTER_OPS, ANONYMOUS_USER_ID, DEFAULT_COMMIT_PATHS, DEFAULT_DATA_SOURCE_KEY, JUNCTION_PIVOT_KEY, ListLimitError, MAX_INCLUDE_DEPTH, Vector, declaredDatabaseExtensions, encodeRelationAggregateSort, hasForeignKeyOnTarget, isChannelBusInstance, isChannelBusInstance as isChannelBusInstance$1, isManyToMany, isRelationalCollectionConfig, isSQLAdmin, isSchemaAdmin, parseRelationAggregateSort, resolveClientListLimit } from "@rebasepro/types";
21
+ import { ALL_WHERE_FILTER_OPS, ANONYMOUS_USER_ID, DEFAULT_COMMIT_PATHS, DEFAULT_DATA_SOURCE_KEY, JUNCTION_PIVOT_KEY, ListLimitError, MAX_INCLUDE_DEPTH, Vector, declaredDatabaseExtensions, encodeRelationAggregateSort, hasForeignKeyOnTarget, isChannelBusInstance, isChannelBusInstance as isChannelBusInstance$1, isManyToMany, isRelationalCollectionConfig, isSQLAdmin, isSchemaAdmin, parseEnvBoolean, parseRelationAggregateSort, resolveClientListLimit } from "@rebasepro/types";
22
22
  import { COMPOSITE_ID_SEPARATOR, COMPOSITE_ID_SEPARATOR as COMPOSITE_ID_SEPARATOR$1, CollectionRegistry, DEFAULT_ONE_OF_TYPE, DEFAULT_ONE_OF_VALUE, OrderBySpecError, REBASE_USER_ROLE as REBASE_USER_ROLE$1, applyDefaultValuesOnCreate, buildCompositeId, buildCompositeId as buildCompositeId$1, buildPropertyCallbacks, buildSdkData, callbackRefusal, canReadField, classifyTable, createRelationRef, createRelationRefWithData, detectJunctionTables, encodeCursor, fieldKeyForColumn, findRelation, firstSqlRow, getColumnName, getDeclaredPrimaryKeys, getJunctionCollectionConfig, getJunctionConfigForRelation, getTableName as getTableName$1, getTenantConfig, isAddressableId, normalizeDriverOrderBy, normalizeEmail, normalizeInclude, normalizeToEntityRelation, parseIdValues, parseOrderBySpecStrict, requireCallbackClient, requireCallbackCollection, resolveCollectionRelations, resolveJunctionSpecs, resolveTenantWrite, revokeInternalTableSql, sqlRows, tenantBypassRoles, toCallbackError, toFilterTuples, updateDateAutoValues, updateUserAutoValues } from "@rebasepro/common";
23
23
  import { camelCase, firstFreeKey, generateForeignKeyName, isPrototypePollutingKey, legacyForeignKeyName, mergeDeep, toSnakeCase, toWireKey, unref } from "@rebasepro/utils";
24
24
  import { AsyncLocalStorage } from "node:async_hooks";
@@ -6021,13 +6021,12 @@ async function generateSchemaCommit(input) {
6021
6021
  * them. It is the only sanctioned way a statement that named a role runs
6022
6022
  * without it — every other route now refuses.
6023
6023
  *
6024
- * Exact `"true"` on purpose, matching the check this replaced. `=1` and `=yes`
6025
- * silently do nothing, which `docs/audits/80-config-and-env.md` already records
6026
- * as a finding across the env surface; fixing it here alone would make this one
6027
- * variable disagree with the rest.
6024
+ * Any spelling of yes, through the platform's one parser. It was an exact
6025
+ * `"true"` until that parser existed, deliberately, so that this variable would
6026
+ * not be fixed alone and left disagreeing with the rest.
6028
6027
  */
6029
6028
  function isRoleSwitchingOptedOut() {
6030
- return process.env.DISABLE_DB_ROLE_SWITCHING === "true";
6029
+ return parseEnvBoolean(process.env.DISABLE_DB_ROLE_SWITCHING) === true;
6031
6030
  }
6032
6031
  /**
6033
6032
  * The role a statement will actually have run as, given the role it asked for.
@@ -6093,6 +6092,35 @@ function assertActingUserForAutoValues(properties, status, uid, path) {
6093
6092
  });
6094
6093
  }
6095
6094
  }
6095
+ /**
6096
+ * A listener's `onError`, in the shape the realtime service calls it with.
6097
+ *
6098
+ * Registered with the subscription so a failed refetch reaches it. Before, only
6099
+ * the initial fetch did: a refetch after a change failed into a log line, and
6100
+ * the listener kept its last rows as if they were current. The error arrives as
6101
+ * thrown, not masked the way a socket frame is: this is trusted server code.
6102
+ */
6103
+ function fetchErrorListener(onError) {
6104
+ if (!onError) return void 0;
6105
+ return (error) => onError(error instanceof Error ? error : new Error(String(error)));
6106
+ }
6107
+ /**
6108
+ * The principal a user's reads run as, for the transaction a request opens and
6109
+ * for a subscription alike.
6110
+ *
6111
+ * One definition because there were two. The subscription's copy was built by
6112
+ * hand after the fact and left out `isAnonymous`, so a guest's refetches ran as
6113
+ * an account: a policy that excludes guests filtered the first read and not the
6114
+ * ones after a change.
6115
+ */
6116
+ function authContextOf(user) {
6117
+ return {
6118
+ uid: user?.uid || ANONYMOUS_USER_ID,
6119
+ roles: user?.roles ?? [],
6120
+ isAnonymous: user?.isAnonymous === true,
6121
+ ...user?.claims ? { claims: user.claims } : {}
6122
+ };
6123
+ }
6096
6124
  var PostgresBackendDriver = class PostgresBackendDriver {
6097
6125
  db;
6098
6126
  registry;
@@ -6411,46 +6439,31 @@ var PostgresBackendDriver = class PostgresBackendDriver {
6411
6439
  }
6412
6440
  return rows;
6413
6441
  }
6414
- listenCollection({ path, collection, filter, limit, offset, startAfter, orderBy, searchString, order, onUpdate, onError }) {
6442
+ /**
6443
+ * Listen to a collection: the rows now, then again after every change.
6444
+ *
6445
+ * The realtime service runs every read, the first one included, as
6446
+ * `authContext` — which {@link AuthenticatedPostgresBackendDriver} supplies,
6447
+ * and which defaults to this driver's own user (a driver bound to a
6448
+ * request's transaction has one). With neither, the reads run as the
6449
+ * anonymous user, as the refetches always did.
6450
+ */
6451
+ listenCollection({ path, collection, startAfter, onUpdate, onError, vectorSearch: _vectorSearch, page: _page, withDeleted: _withDeleted, ...query }, authContext = this.user ? authContextOf(this.user) : void 0) {
6415
6452
  const subscriptionId = this.generateSubscriptionId();
6416
- const callbackWrapper = (rows) => {
6417
- onUpdate(rows);
6418
- };
6419
- this.realtimeService.registerDataDriverSubscription(subscriptionId, {
6420
- clientId: "driver",
6453
+ this.realtimeService.startDataDriverSubscription(subscriptionId, {
6421
6454
  type: "collection",
6422
6455
  path,
6423
6456
  collectionRequest: {
6424
- filter,
6425
- orderBy,
6426
- order,
6427
- limit,
6428
- offset,
6457
+ ...query,
6429
6458
  startAfter,
6430
- databaseId: collection?.databaseId,
6431
- searchString
6432
- }
6433
- });
6434
- this.realtimeService.addSubscriptionCallback(subscriptionId, callbackWrapper);
6435
- this.fetchCollection({
6436
- path,
6437
- collection,
6438
- filter,
6439
- limit,
6440
- offset,
6441
- startAfter,
6442
- orderBy,
6443
- searchString,
6444
- order
6445
- }).then((rows) => {
6446
- callbackWrapper(rows);
6447
- }).catch((error) => {
6448
- if (onError) onError(error);
6459
+ databaseId: collection?.databaseId
6460
+ },
6461
+ authContext,
6462
+ onError: fetchErrorListener(onError)
6463
+ }, (rows) => {
6464
+ if (Array.isArray(rows)) onUpdate(rows);
6449
6465
  });
6450
- return () => {
6451
- this.realtimeService.removeSubscriptionCallback(subscriptionId);
6452
- this.realtimeService.subscriptions.delete(subscriptionId);
6453
- };
6466
+ return () => this.realtimeService.unsubscribe(subscriptionId);
6454
6467
  }
6455
6468
  async fetchOne({ path, id, databaseId, collection, withDeleted }) {
6456
6469
  let row = await this.dataService.fetchOne(path, id, databaseId || collection?.databaseId, withDeleted);
@@ -6479,31 +6492,23 @@ var PostgresBackendDriver = class PostgresBackendDriver {
6479
6492
  }
6480
6493
  return row;
6481
6494
  }
6482
- listenOne({ path, id, collection, onUpdate, onError }) {
6495
+ /**
6496
+ * Listen to one row: the row now, then again after every change, and
6497
+ * `null` when it is not there — deleted, never created, or not readable by
6498
+ * the subscriber. Reads as {@link listenCollection} does.
6499
+ */
6500
+ listenOne({ path, id, onUpdate, onError }, authContext = this.user ? authContextOf(this.user) : void 0) {
6483
6501
  const subscriptionId = this.generateSubscriptionId();
6484
- const callbackWrapper = (row) => {
6485
- if (row) onUpdate(row);
6486
- };
6487
- this.realtimeService.registerDataDriverSubscription(subscriptionId, {
6488
- clientId: "driver",
6502
+ this.realtimeService.startDataDriverSubscription(subscriptionId, {
6489
6503
  type: "single",
6490
- path,
6491
- id
6492
- });
6493
- this.realtimeService.addSubscriptionCallback(subscriptionId, callbackWrapper);
6494
- this.fetchOne({
6495
6504
  path,
6496
6505
  id,
6497
- collection
6498
- }).then((row) => {
6499
- if (row) onUpdate(row);
6500
- }).catch((error) => {
6501
- if (onError) onError(error);
6506
+ authContext,
6507
+ onError: fetchErrorListener(onError)
6508
+ }, (row) => {
6509
+ if (!Array.isArray(row)) onUpdate(row);
6502
6510
  });
6503
- return () => {
6504
- this.realtimeService.removeSubscriptionCallback(subscriptionId);
6505
- this.realtimeService.subscriptions.delete(subscriptionId);
6506
- };
6511
+ return () => this.realtimeService.unsubscribe(subscriptionId);
6507
6512
  }
6508
6513
  /**
6509
6514
  * How many membership rows a single write will read to answer "which
@@ -6917,20 +6922,10 @@ var PostgresBackendDriver = class PostgresBackendDriver {
6917
6922
  for (let i = 0; i < ids.length; i++) {
6918
6923
  const id = ids[i];
6919
6924
  try {
6920
- const existing = await txDriver.fetchOne({
6921
- path,
6922
- id: String(id),
6923
- collection
6924
- });
6925
- if (!existing) throw Object.assign(/* @__PURE__ */ new Error(`No row with id ${JSON.stringify(id)}`), {
6926
- statusCode: 404,
6927
- code: "NOT_FOUND"
6928
- });
6929
6925
  await txDriver.delete({
6930
6926
  row: {
6931
6927
  id: String(id),
6932
- path,
6933
- values: existing
6928
+ path
6934
6929
  },
6935
6930
  collection,
6936
6931
  hard
@@ -7010,27 +7005,16 @@ var PostgresBackendDriver = class PostgresBackendDriver {
7010
7005
  status: "existing"
7011
7006
  });
7012
7007
  break;
7013
- case "delete": {
7014
- const existing = await txDriver.fetchOne({
7015
- path: operation.path,
7016
- id,
7017
- collection: operation.collection
7018
- });
7019
- if (!existing) throw Object.assign(/* @__PURE__ */ new Error(`No row with id ${JSON.stringify(id)}`), {
7020
- statusCode: 404,
7021
- code: "NOT_FOUND"
7022
- });
7008
+ case "delete":
7023
7009
  await txDriver.delete({
7024
7010
  row: {
7025
7011
  id,
7026
- path: operation.path,
7027
- values: existing
7012
+ path: operation.path
7028
7013
  },
7029
7014
  collection: operation.collection
7030
7015
  });
7031
7016
  row = null;
7032
7017
  break;
7033
- }
7034
7018
  }
7035
7019
  if (operation.ref && row) named.set(operation.ref, row);
7036
7020
  results.push(row);
@@ -7047,8 +7031,15 @@ var PostgresBackendDriver = class PostgresBackendDriver {
7047
7031
  }
7048
7032
  async delete({ row, collection, hard }) {
7049
7033
  const targetPath = row.path;
7050
- const targetRow = { ...row.values ?? {} };
7051
7034
  const { collection: resolvedCollection, callbacks, globalCallbacks, propertyCallbacks } = this.resolveCollectionCallbacks(collection, targetPath);
7035
+ const stored = await this.fetchOne({
7036
+ path: targetPath,
7037
+ id: row.id,
7038
+ collection: resolvedCollection,
7039
+ withDeleted: hard ? true : void 0
7040
+ });
7041
+ if (!stored) throw ApiError.notFound(`No row "${row.id}" in "${targetPath}" to delete.`);
7042
+ const targetRow = { ...stored };
7052
7043
  const contextForCallback = this.buildCallContext();
7053
7044
  try {
7054
7045
  if (globalCallbacks?.beforeDelete || callbacks?.beforeDelete || propertyCallbacks?.beforeDelete) {
@@ -7121,7 +7112,7 @@ var PostgresBackendDriver = class PostgresBackendDriver {
7121
7112
  tableName: targetPath,
7122
7113
  id: row.id.toString(),
7123
7114
  action: "delete",
7124
- values: row.values ?? {},
7115
+ values: stored,
7125
7116
  updatedBy: this.user?.uid
7126
7117
  });
7127
7118
  if (this._deferNotifications) this._pendingNotifications.push({
@@ -7417,19 +7408,9 @@ var AuthenticatedPostgresBackendDriver = class {
7417
7408
  const pendingNotifications = [];
7418
7409
  const writeScope = options?.accessMode === "read only" ? void 0 : new WriteTransactionScope();
7419
7410
  const result = await withFieldViewer({ roles: this.user?.roles ?? [] }, async () => await runInWriteScope(writeScope, () => this.delegate.db.transaction(async (tx) => {
7420
- let uid = this.user?.uid;
7421
- if (!uid) {
7422
- logger.warn("[DataDriver] User ID (uid) is missing for authenticated delegate. Using 'anonymous'. User object", { detail: this.user });
7423
- uid = "anonymous";
7424
- }
7425
- const userRoles = this.user?.roles ?? [];
7411
+ if (!this.user?.uid) logger.warn("[DataDriver] User ID (uid) is missing for authenticated delegate. Using 'anonymous'. User object", { detail: this.user });
7426
7412
  if (!this.user?.roles) logger.warn("[DataDriver] User roles are missing for authenticated delegate. Using empty array. User object", { detail: this.user });
7427
- await applyAuthContext(tx, {
7428
- uid,
7429
- roles: userRoles,
7430
- isAnonymous: this.user?.isAnonymous === true,
7431
- claims: this.user?.claims
7432
- }, this.delegate.rlsUserRole);
7413
+ await applyAuthContext(tx, authContextOf(this.user), this.delegate.rlsUserRole);
7433
7414
  const txEntityService = new DataService(tx, this.delegate.registry);
7434
7415
  const txDelegate = new PostgresBackendDriver(tx, this.delegate.realtimeService, this.delegate.registry, this.user, this.delegate.poolManager, this.delegate.historyService);
7435
7416
  txDelegate.dataService = txEntityService;
@@ -7459,28 +7440,21 @@ var AuthenticatedPostgresBackendDriver = class {
7459
7440
  return this.withTransaction((delegate) => delegate.fetchCollection(props), { accessMode: "read only" });
7460
7441
  }
7461
7442
  /**
7462
- * Injects the authenticated user's context into the most recently
7463
- * registered realtime subscription so RLS-aware polling can apply.
7443
+ * The delegate is the base driver, on the owner connection. The user's
7444
+ * identity goes in with the subscription, so the first read runs as them
7445
+ * like every refetch after it. It used to be stamped onto the last
7446
+ * registered subscription once the delegate had returned — by which time
7447
+ * the first read had been issued unscoped, and a listener was handed rows
7448
+ * its policies deny.
7464
7449
  */
7465
- injectAuthContext(unsubscribe) {
7466
- const authContext = {
7467
- uid: this.user?.uid || "anonymous",
7468
- roles: this.user?.roles ?? [],
7469
- ...this.user?.claims ? { claims: this.user.claims } : {}
7470
- };
7471
- const entries = Array.from(this.delegate.realtimeService.subscriptions.entries());
7472
- const lastSub = entries[entries.length - 1]?.[1];
7473
- if (lastSub && lastSub.clientId === "driver") lastSub.authContext = authContext;
7474
- return unsubscribe;
7475
- }
7476
7450
  listenCollection(props) {
7477
- return this.injectAuthContext(this.delegate.listenCollection(props));
7451
+ return this.delegate.listenCollection(props, authContextOf(this.user));
7478
7452
  }
7479
7453
  async fetchOne(props) {
7480
7454
  return this.withTransaction((delegate) => delegate.fetchOne(props), { accessMode: "read only" });
7481
7455
  }
7482
7456
  listenOne(props) {
7483
- return this.injectAuthContext(this.delegate.listenOne(props));
7457
+ return this.delegate.listenOne(props, authContextOf(this.user));
7484
7458
  }
7485
7459
  async save(props) {
7486
7460
  return this.withTransaction((delegate) => delegate.save(props));
@@ -9115,36 +9089,67 @@ var RealtimeService = class RealtimeService extends EventEmitter {
9115
9089
  *
9116
9090
  * The last two are identity, not presence: the map has to still hold *this
9117
9091
  * exact object*, not merely something under this id.
9092
+ *
9093
+ * A failure goes through the same slot, by `mayReportFailure()`: an error
9094
+ * frame from a stale, cancelled or replaced fetch would mark a view that
9095
+ * already shows newer rows as failed, or fail another subscription's view.
9096
+ * It also answers yes to the delivery that already claimed the slot, since
9097
+ * that is the send itself failing (a row that will not serialise) after the
9098
+ * check passed and before anything reached the subscriber.
9118
9099
  */
9119
9100
  beginDelivery(subscriptionId, subscription) {
9120
9101
  const seq = ++subscription.started;
9121
- return () => {
9102
+ const canDeliver = () => {
9122
9103
  if (this._subscriptions.get(subscriptionId) !== subscription) return false;
9123
9104
  if (seq <= subscription.delivered) return false;
9124
9105
  subscription.delivered = seq;
9125
9106
  return true;
9126
9107
  };
9108
+ const mayReportFailure = () => canDeliver() || this._subscriptions.get(subscriptionId) === subscription && subscription.delivered === seq;
9109
+ return Object.assign(canDeliver, { mayReportFailure });
9127
9110
  }
9128
- registerDataDriverSubscription(subscriptionId, subscription) {
9129
- this.debugLog("📋 [RealtimeService] Registering DataDriver subscription:", subscriptionId, subscription.authContext ? "(with auth)" : "(no auth)");
9130
- this._subscriptions.set(subscriptionId, {
9131
- ...subscription,
9111
+ /**
9112
+ * Start an in-process listener: register it, then deliver its first rows.
9113
+ *
9114
+ * The first fetch runs here, as the subscriber and through a delivery slot,
9115
+ * the way every refetch after it does. `PostgresBackendDriver.listen*` used
9116
+ * to run it themselves, outside this service, and got all three wrong: a
9117
+ * listener on `withAuth(user)` read its first rows on the owner connection,
9118
+ * so it was handed rows its policies deny; a slow first fetch landed over a
9119
+ * newer refetch, and after an unsubscribe; and a row listener never heard a
9120
+ * `null`. The subscriber's identity is part of the request because a
9121
+ * subscription registered first and stamped with it afterwards has a first
9122
+ * fetch that ran before the stamp.
9123
+ *
9124
+ * No `authContext` reads as the anonymous user, first fetch and refetches
9125
+ * alike — see {@link fetchCollectionWithAuth}.
9126
+ */
9127
+ startDataDriverSubscription(subscriptionId, request, callback) {
9128
+ this.debugLog("📋 [RealtimeService] Starting DataDriver subscription:", subscriptionId, request.authContext ? "(with auth)" : "(no auth)");
9129
+ const subscription = {
9130
+ clientId: "driver",
9131
+ ...request,
9132
9132
  started: 0,
9133
9133
  delivered: 0
9134
- });
9135
- }
9136
- addSubscriptionCallback(subscriptionId, callback) {
9137
- this.debugLog("📋 [RealtimeService] Adding callback for subscription:", subscriptionId);
9134
+ };
9135
+ this._subscriptions.set(subscriptionId, subscription);
9138
9136
  this.subscriptionCallbacks.set(subscriptionId, callback);
9137
+ this.deliverFirstToDataDriver(subscriptionId, subscription, callback);
9139
9138
  }
9140
- removeSubscriptionCallback(subscriptionId) {
9141
- this.debugLog("📋 [RealtimeService] Removing callback for subscription:", subscriptionId);
9142
- this.subscriptionCallbacks.delete(subscriptionId);
9139
+ async deliverFirstToDataDriver(subscriptionId, subscription, callback) {
9140
+ const canDeliver = this.beginDelivery(subscriptionId, subscription);
9141
+ try {
9142
+ const data = subscription.type === "collection" ? await this.fetchCollectionWithAuth(subscription.path, subscription.collectionRequest ?? {}, subscription.authContext) : await this.fetchEntityWithAuth(subscription.path, String(subscription.id), subscription.authContext) ?? null;
9143
+ if (canDeliver()) callback(data);
9144
+ } catch (error) {
9145
+ logger.error(`❌ [RealtimeService] Error in the first fetch for DataDriver subscription ${subscriptionId}`, { error });
9146
+ this.reportDriverFetchFailure(subscriptionId, subscription, canDeliver, error);
9147
+ }
9143
9148
  }
9144
9149
  /**
9145
9150
  * Subscribe to collection changes (RealtimeProvider interface)
9146
9151
  */
9147
- subscribeToCollection(subscriptionId, config, callback) {
9152
+ subscribeToCollection(subscriptionId, config, callback, onError) {
9148
9153
  this._subscriptions.set(subscriptionId, {
9149
9154
  clientId: config.clientId,
9150
9155
  type: "collection",
@@ -9159,6 +9164,7 @@ var RealtimeService = class RealtimeService extends EventEmitter {
9159
9164
  searchString: config.searchString,
9160
9165
  searchExplain: config.searchExplain
9161
9166
  },
9167
+ onError,
9162
9168
  started: 0,
9163
9169
  delivered: 0
9164
9170
  });
@@ -9167,12 +9173,13 @@ var RealtimeService = class RealtimeService extends EventEmitter {
9167
9173
  /**
9168
9174
  * Subscribe to single row changes (RealtimeProvider interface)
9169
9175
  */
9170
- subscribeToOne(subscriptionId, config, callback) {
9176
+ subscribeToOne(subscriptionId, config, callback, onError) {
9171
9177
  this._subscriptions.set(subscriptionId, {
9172
9178
  clientId: config.clientId,
9173
9179
  type: "single",
9174
9180
  path: config.path,
9175
9181
  id: config.id,
9182
+ onError,
9176
9183
  started: 0,
9177
9184
  delivered: 0
9178
9185
  });
@@ -9254,6 +9261,7 @@ var RealtimeService = class RealtimeService extends EventEmitter {
9254
9261
  }
9255
9262
  async handleCollectionSubscription(clientId, request, authContext) {
9256
9263
  const subscriptionId = request.subscriptionId;
9264
+ let canDeliver;
9257
9265
  try {
9258
9266
  if (!this.registry.getCollectionByPath(request.path)) {
9259
9267
  const registered = this.registry.getCollections().map((c) => c.slug).join(", ");
@@ -9310,17 +9318,17 @@ var RealtimeService = class RealtimeService extends EventEmitter {
9310
9318
  delivered: 0
9311
9319
  };
9312
9320
  this._subscriptions.set(subscriptionId, subscription);
9313
- const canDeliver = this.beginDelivery(subscriptionId, subscription);
9321
+ canDeliver = this.beginDelivery(subscriptionId, subscription);
9314
9322
  const rows = await this.fetchCollectionWithAuth(request.path, subscription.collectionRequest, authContext);
9315
9323
  const meta = await this.collectionMetaWithAuth(request.path, subscription.collectionRequest, rows, authContext);
9316
9324
  if (canDeliver()) this.sendCollectionUpdate(clientId, subscriptionId, rows, request.path, meta);
9317
9325
  } catch (error) {
9318
- const sanitized = sanitizeErrorForClient(error, request.path);
9319
- this.sendError(clientId, sanitized.message, subscriptionId, sanitized.code);
9326
+ this.reportSocketFetchFailure(clientId, subscriptionId, request.path, error, canDeliver);
9320
9327
  }
9321
9328
  }
9322
9329
  async handleEntitySubscription(clientId, request, authContext) {
9323
9330
  const subscriptionId = request.subscriptionId;
9331
+ let canDeliver;
9324
9332
  try {
9325
9333
  if (!this.registry.getCollectionByPath(request.path)) {
9326
9334
  const registered = this.registry.getCollections().map((c) => c.slug).join(", ");
@@ -9339,14 +9347,31 @@ var RealtimeService = class RealtimeService extends EventEmitter {
9339
9347
  delivered: 0
9340
9348
  };
9341
9349
  this._subscriptions.set(subscriptionId, subscription);
9342
- const canDeliver = this.beginDelivery(subscriptionId, subscription);
9350
+ canDeliver = this.beginDelivery(subscriptionId, subscription);
9343
9351
  const row = await this.fetchEntityWithAuth(request.path, String(request.id), authContext);
9344
9352
  if (canDeliver()) this.sendSingleUpdate(clientId, subscriptionId, row || null);
9345
9353
  } catch (error) {
9346
- const sanitized = sanitizeErrorForClient(error, request.path);
9347
- this.sendError(clientId, sanitized.message, subscriptionId, sanitized.code);
9354
+ this.reportSocketFetchFailure(clientId, subscriptionId, request.path, error, canDeliver);
9348
9355
  }
9349
9356
  }
9357
+ /**
9358
+ * Answer a subscriber's failed fetch with an error frame, through the slot
9359
+ * its rows would have used.
9360
+ *
9361
+ * The error is always logged (`sanitizeErrorForClient` does that), but the
9362
+ * frame goes out only while this fetch holds the newest slot. A failed
9363
+ * straggler used to send one regardless. The client routes errors by
9364
+ * `subscriptionId`, so it called `onError` for a view already showing newer
9365
+ * rows, or for the subscription that replaced this one under the same id.
9366
+ *
9367
+ * With no slot, the subscribe failed before its fetch began. That part runs
9368
+ * synchronously, so nothing else can have answered the request yet.
9369
+ */
9370
+ reportSocketFetchFailure(clientId, subscriptionId, path, error, canDeliver) {
9371
+ const sanitized = sanitizeErrorForClient(error, path);
9372
+ if (canDeliver && !canDeliver.mayReportFailure()) return;
9373
+ this.sendError(clientId, sanitized.message, subscriptionId, sanitized.code);
9374
+ }
9350
9375
  async handleUnsubscribe(_clientId, subscriptionId) {
9351
9376
  this._subscriptions.delete(subscriptionId);
9352
9377
  this.subscriptionCallbacks.delete(subscriptionId);
@@ -9479,8 +9504,7 @@ var RealtimeService = class RealtimeService extends EventEmitter {
9479
9504
  const meta = await this.collectionMetaWithAuth(notifyPath, subscription.collectionRequest, rows, subscription.authContext);
9480
9505
  if (canDeliver()) this.sendCollectionUpdate(subscription.clientId, subscriptionId, rows, notifyPath, meta);
9481
9506
  } catch (error) {
9482
- const sanitized = sanitizeErrorForClient(error, notifyPath);
9483
- this.sendError(subscription.clientId, sanitized.message, subscriptionId, sanitized.code);
9507
+ this.reportSocketFetchFailure(subscription.clientId, subscriptionId, notifyPath, error, canDeliver);
9484
9508
  }
9485
9509
  }, RealtimeService.REFETCH_DEBOUNCE_MS));
9486
9510
  }
@@ -9500,10 +9524,30 @@ var RealtimeService = class RealtimeService extends EventEmitter {
9500
9524
  if (canDeliver()) callback(rows);
9501
9525
  } catch (error) {
9502
9526
  logger.error(`❌ [RealtimeService] Error in debounced driver refetch for ${subscriptionId}`, { error });
9527
+ this.reportDriverFetchFailure(subscriptionId, subscription, canDeliver, error);
9503
9528
  }
9504
9529
  }, RealtimeService.REFETCH_DEBOUNCE_MS));
9505
9530
  }
9506
9531
  /**
9532
+ * Tell an in-process listener its refetch failed, through the slot the
9533
+ * rows would have used.
9534
+ *
9535
+ * The two driver-refetch `catch` blocks used to log and stop, so the
9536
+ * listener heard nothing and kept its last rows as if they were current.
9537
+ * The error goes to it as thrown: this is trusted server code, and the
9538
+ * masking on the socket path is for clients. The slot keeps a failed
9539
+ * straggler from reporting over newer rows, and keeps a failure away from
9540
+ * a subscription that was cancelled or replaced while it ran.
9541
+ */
9542
+ reportDriverFetchFailure(subscriptionId, subscription, canDeliver, error) {
9543
+ if (!subscription.onError || !canDeliver()) return;
9544
+ try {
9545
+ subscription.onError(error);
9546
+ } catch (listenerError) {
9547
+ logger.error(`❌ [RealtimeService] onError threw for DataDriver subscription ${subscriptionId}`, { error: listenerError });
9548
+ }
9549
+ }
9550
+ /**
9507
9551
  * Fetch a collection with optional RLS auth context.
9508
9552
  * When authContext is provided, the fetch runs inside a transaction
9509
9553
  * with set_config calls so PostgreSQL RLS policies are enforced.
@@ -9659,8 +9703,7 @@ var RealtimeService = class RealtimeService extends EventEmitter {
9659
9703
  const row = await this.fetchEntityWithAuth(notifyPath, id, subscription.authContext);
9660
9704
  if (canDeliver()) this.sendSingleUpdate(subscription.clientId, subscriptionId, row || null);
9661
9705
  } catch (error) {
9662
- const sanitized = sanitizeErrorForClient(error, notifyPath);
9663
- this.sendError(subscription.clientId, sanitized.message, subscriptionId, sanitized.code);
9706
+ this.reportSocketFetchFailure(subscription.clientId, subscriptionId, notifyPath, error, canDeliver);
9664
9707
  }
9665
9708
  }, RealtimeService.REFETCH_DEBOUNCE_MS));
9666
9709
  }
@@ -9680,6 +9723,7 @@ var RealtimeService = class RealtimeService extends EventEmitter {
9680
9723
  if (canDeliver()) callback(row || null);
9681
9724
  } catch (error) {
9682
9725
  logger.error(`❌ [RealtimeService] Error in debounced row driver refetch for ${subscriptionId}`, { error });
9726
+ this.reportDriverFetchFailure(subscriptionId, subscription, canDeliver, error);
9683
9727
  }
9684
9728
  }, RealtimeService.REFETCH_DEBOUNCE_MS));
9685
9729
  }
@@ -10802,6 +10846,31 @@ function extractErrorMessage(error) {
10802
10846
  /**
10803
10847
  * Check if the current session belongs to an admin user.
10804
10848
  */
10849
+ /**
10850
+ * Who a socket reads and writes as, for its request frames and its
10851
+ * subscriptions alike.
10852
+ *
10853
+ * One answer because there were two, and they disagreed about guests. The
10854
+ * subscriptions carried the session's `isAnonymous`; the request frames wrote
10855
+ * `false` for every signed-in session, so a guest read and wrote as an account
10856
+ * through `FETCH_*`, `SAVE` and `DELETE` while its subscriptions did not. And a
10857
+ * socket with no session was a guest on the request frames (`true`) but not on
10858
+ * its subscriptions or over REST: an unauthenticated caller is not a guest
10859
+ * session.
10860
+ */
10861
+ function sessionAuthContext(session) {
10862
+ if (!session?.user) return {
10863
+ uid: ANONYMOUS_USER_ID,
10864
+ roles: ["anon"],
10865
+ isAnonymous: false
10866
+ };
10867
+ return {
10868
+ uid: session.user.uid,
10869
+ roles: session.user.roles ?? [],
10870
+ isAnonymous: session.user.isAnonymous === true,
10871
+ ...session.user.claims ? { claims: session.user.claims } : {}
10872
+ };
10873
+ }
10805
10874
  function isAdminSession(session) {
10806
10875
  if (!session?.user) return false;
10807
10876
  if (session.user.isAdmin) return true;
@@ -10887,13 +10956,15 @@ function createPostgresWebSocket(server, realtimeService, driver, authConfig, au
10887
10956
  if (adapterUser) verifiedUser = {
10888
10957
  uid: adapterUser.uid,
10889
10958
  roles: adapterUser.roles,
10890
- isAdmin: adapterUser.isAdmin
10959
+ isAdmin: adapterUser.isAdmin,
10960
+ isAnonymous: adapterUser.isAnonymous === true
10891
10961
  };
10892
10962
  } catch {}
10893
10963
  else if (authConfig?.serviceKey && safeCompare(token, authConfig.serviceKey)) verifiedUser = {
10894
10964
  uid: "service",
10895
10965
  roles: ["admin"],
10896
- isAdmin: true
10966
+ isAdmin: true,
10967
+ isAnonymous: false
10897
10968
  };
10898
10969
  else {
10899
10970
  const jwtPayload = await extractUserFromToken(token);
@@ -10981,25 +11052,17 @@ function createPostgresWebSocket(server, realtimeService, driver, authConfig, au
10981
11052
  assertFieldOpsValid(values, collection);
10982
11053
  };
10983
11054
  const getScopedDelegate = async () => {
10984
- const session = clientSessions.get(clientId);
10985
11055
  if (typeof driver.withAuth === "function") try {
10986
- const userForAuth = session?.user ? {
10987
- uid: session.user.uid,
11056
+ const who = sessionAuthContext(clientSessions.get(clientId));
11057
+ const userForAuth = {
11058
+ uid: who.uid,
10988
11059
  displayName: null,
10989
11060
  email: null,
10990
11061
  photoURL: null,
10991
11062
  providerId: "websocket",
10992
- isAnonymous: false,
10993
- roles: session.user.roles ?? [],
10994
- claims: session.user.claims
10995
- } : {
10996
- uid: ANONYMOUS_USER_ID,
10997
- displayName: null,
10998
- email: null,
10999
- photoURL: null,
11000
- providerId: "websocket",
11001
- isAnonymous: true,
11002
- roles: ["anon"]
11063
+ isAnonymous: who.isAnonymous === true,
11064
+ roles: who.roles,
11065
+ ...who.claims ? { claims: who.claims } : {}
11003
11066
  };
11004
11067
  return await driver.withAuth(userForAuth);
11005
11068
  } catch (e) {
@@ -11070,7 +11133,13 @@ function createPostgresWebSocket(server, realtimeService, driver, authConfig, au
11070
11133
  wsDebug("🗑️ [WebSocket Server] Processing DELETE_ENTITY request");
11071
11134
  const request = payload;
11072
11135
  wsDebug("🗑️ [WebSocket Server] Deleting row:", request.row);
11073
- await (await getScopedDelegate()).delete(request);
11136
+ await (await getScopedDelegate()).delete({
11137
+ row: {
11138
+ id: request.row.id,
11139
+ path: request.row.path
11140
+ },
11141
+ hard: request.hard
11142
+ });
11074
11143
  wsDebug("🗑️ [WebSocket Server] DELETE_ENTITY completed successfully");
11075
11144
  const response = {
11076
11145
  type: "DELETE_SUCCESS",
@@ -11296,17 +11365,7 @@ function createPostgresWebSocket(server, realtimeService, driver, authConfig, au
11296
11365
  case "presence_state":
11297
11366
  case "channel_history": {
11298
11367
  wsDebug("🔄 [WebSocket Server] Routing realtime message to RealtimeService:", type);
11299
- const session = clientSessions.get(clientId);
11300
- const authContext = session?.user ? {
11301
- uid: session.user.uid,
11302
- roles: session.user.roles ?? [],
11303
- isAnonymous: session.user.isAnonymous === true,
11304
- claims: session.user.claims
11305
- } : {
11306
- uid: ANONYMOUS_USER_ID,
11307
- roles: ["anon"],
11308
- isAnonymous: false
11309
- };
11368
+ const authContext = sessionAuthContext(clientSessions.get(clientId));
11310
11369
  await realtimeService.handleClientMessage(clientId, {
11311
11370
  type,
11312
11371
  payload,
@@ -11342,15 +11401,18 @@ function createPostgresWebSocket(server, realtimeService, driver, authConfig, au
11342
11401
  }));
11343
11402
  return;
11344
11403
  }
11345
- if (error instanceof ApiError || error?.name === "ApiError") {
11346
- const apiError = error;
11347
- logger.warn(`[WebSocket Server] Refused a write: ${apiError.message}`);
11404
+ const answer = declaredErrorAnswer(error);
11405
+ if (answer) {
11406
+ const line = `[WebSocket Server] Refused: ${answer.code} ${answer.message}`;
11407
+ if (answer.expected) logger.debug(line);
11408
+ else logger.warn(line);
11348
11409
  ws.send(JSON.stringify({
11349
11410
  type: "ERROR",
11350
11411
  requestId,
11351
11412
  payload: { error: {
11352
- message: apiError.message,
11353
- code: apiError.code
11413
+ message: answer.message,
11414
+ code: answer.code,
11415
+ ...answer.details !== void 0 && { details: answer.details }
11354
11416
  } }
11355
11417
  }));
11356
11418
  return;
@@ -11497,10 +11559,32 @@ function parseOptionalInt(value) {
11497
11559
  * Create a {@link CronJobDefinition} that dumps the database, uploads the
11498
11560
  * result to the configured destination, and prunes old backups. Object
11499
11561
  * destinations require {@link BackupCronConfig.storage}.
11562
+ *
11563
+ * The dump excludes nothing unless `excludeSchemas` says otherwise, and that
11564
+ * default is not this function's to set: the option goes to `createDump`
11565
+ * untouched, so `buildPgDumpArgs` decides for the cron and `rebase db backup`
11566
+ * alike. The cron used to carry its own default of `["rebase"]`, meant to
11567
+ * leave out Atlas's revision table, and it left out a lot more. `rebase` is
11568
+ * where the framework keeps
11569
+ * everything that is not a collection: the auth tables (`users`, identities,
11570
+ * refresh tokens, MFA factors, recovery codes, `app_config`, `schema_meta`),
11571
+ * API keys, record history, the job queue, cron logs, channel history,
11572
+ * branches and idempotency keys. It also holds the functions that every
11573
+ * generated RLS policy and CDC trigger calls (`rebase.uid()`,
11574
+ * `rebase.roles()`, `rebase.rebase_cdc_notify()`).
11575
+ *
11576
+ * None of that can be left for boot to rebuild. Boot recreates the tables
11577
+ * and functions, but never the rows in them. And a dump that has a policy
11578
+ * or trigger on a `public` table without the function it calls cannot be
11579
+ * restored into an empty database: `pg_restore --exit-on-error` stops at
11580
+ * the first such statement, and every collection's generated policies call
11581
+ * `rebase.uid()`. So every scheduled backup lost every user account, and
11582
+ * could not be restored anyway, while `rebase db backup` took a complete
11583
+ * one. The revision table belongs in a backup too: without it, a restored
11584
+ * database has tables but no record of the migrations that made them.
11500
11585
  */
11501
11586
  function createBackupCron(config) {
11502
11587
  const dbName = parseDbNameFromUrl(config.connectionString) ?? "database";
11503
- const excludeSchemas = config.excludeSchemas ?? ["rebase"];
11504
11588
  return {
11505
11589
  name: config.name ?? "Scheduled database backup",
11506
11590
  schedule: config.schedule,
@@ -11517,7 +11601,7 @@ function createBackupCron(config) {
11517
11601
  connectionString: config.connectionString,
11518
11602
  dbName,
11519
11603
  outDir,
11520
- excludeSchemas
11604
+ excludeSchemas: config.excludeSchemas
11521
11605
  });
11522
11606
  log(`Dump created: ${dump.fileName} (${formatBytes(dump.sizeBytes)})`);
11523
11607
  const check = await validateDump(dump.localFile);
@@ -15055,7 +15139,7 @@ function createPostgresBootstrapper(pgConfig) {
15055
15139
  * proved it can bootstrap with.
15056
15140
  */
15057
15141
  async ensureCollectionSchema(collections, driverResult, log) {
15058
- const { ensureCollectionTables } = await import("./ensure-collection-tables-Bkvehxdm.js").then((n) => n.n);
15142
+ const { ensureCollectionTables } = await import("./ensure-collection-tables-Cr2ye5JC.js").then((n) => n.n);
15059
15143
  const plan = await ensureCollectionTables(provisioningQueryable(driverResult), collections, log);
15060
15144
  for (const failure of plan.failures) logger.warn(describeEnsureFailure(failure));
15061
15145
  return { applied: plan.actions.length - plan.failures.length };
@@ -15089,7 +15173,7 @@ function createPostgresBootstrapper(pgConfig) {
15089
15173
  await provisionRlsRuntime(provisioningQueryable(driverResult));
15090
15174
  },
15091
15175
  async ensureCollectionPolicies(collections, driverResult, log) {
15092
- const { ensureCollectionPolicies } = await import("./ensure-collection-policies-CQGHln-y.js");
15176
+ const { ensureCollectionPolicies } = await import("./ensure-collection-policies-B4IDFtQ-.js");
15093
15177
  const queryable = provisioningQueryable(driverResult);
15094
15178
  const { rows } = await queryable.query("SELECT to_regprocedure('rebase.roles()') IS NOT NULL AS present");
15095
15179
  if (!rows[0]?.present) await provisionRlsRuntime(queryable);