@rebasepro/server 0.9.1-canary.d906fb7 → 0.9.1-canary.e2fc7b6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.es.js CHANGED
@@ -3502,6 +3502,21 @@ function createRoutedRealtimeService(opts) {
3502
3502
  //#region src/api/errors.ts
3503
3503
  /** Tracks whether we've already shown the doctor hint (once per process). */
3504
3504
  var _schemaDriftHinted = false;
3505
+ /** 5-character SQLSTATE, e.g. `42501`, `23505`. */
3506
+ var SQLSTATE_RE = /^[0-9A-Z]{5}$/;
3507
+ /**
3508
+ * Walk the cause chain for the underlying database error, identified by a
3509
+ * 5-char SQLSTATE `code`. Drizzle wraps the pg error in `.cause`, and route
3510
+ * code sometimes wraps drizzle again, so the real error may sit several
3511
+ * levels down.
3512
+ */
3513
+ function extractDbError(error, depth = 0) {
3514
+ if (!error || typeof error !== "object" || depth > 8) return null;
3515
+ const e = error;
3516
+ if (typeof e.code === "string" && SQLSTATE_RE.test(e.code)) return e;
3517
+ if (e.cause && typeof e.cause === "object") return extractDbError(e.cause, depth + 1);
3518
+ return null;
3519
+ }
3505
3520
  /**
3506
3521
  * Extract the missing table or column name from a PG error.
3507
3522
  * PG 42P01 messages look like: 'relation "my_table" does not exist'
@@ -3586,26 +3601,31 @@ var errorHandler = (err, c) => {
3586
3601
  if (cause.code === "ECONNREFUSED" && !cause.address && Array.isArray(cause.errors)) resolvedCause = cause.errors.find((e) => e.address) || cause;
3587
3602
  else resolvedCause = cause;
3588
3603
  }
3589
- if (resolvedCause) {
3604
+ const dbError = extractDbError(error);
3605
+ if (resolvedCause && (resolvedCause.code === "ENETUNREACH" || resolvedCause.code === "ECONNREFUSED")) {
3590
3606
  const cause = resolvedCause;
3591
3607
  if (cause.code === "ENETUNREACH") logMessage = `Network unreachable. Cannot connect to database at ${cause.address}:${cause.port}.`;
3592
- else if (cause.code === "ECONNREFUSED") logMessage = `Connection refused to database at ${cause.address}:${cause.port}. Is PostgreSQL running?`;
3593
- else if (cause.code === "42703" || cause.code === "42P01") {
3594
- code = "SCHEMA_DRIFT";
3595
- logMessage = `Schema drift: ${cause.code === "42703" ? "column" : "table"} "${cause.table || cause.column || extractMissingIdentifier(cause.message) || "unknown"}" does not exist in the database. Run \`pnpm db:push\` to sync your schema, or \`pnpm db:migrate\` to apply pending migrations.`;
3596
- }
3608
+ else logMessage = `Connection refused to database at ${cause.address}:${cause.port}. Is PostgreSQL running?`;
3597
3609
  } else if ("code" in error && error.code === "ENETUNREACH") {
3598
3610
  const netErr = error;
3599
3611
  logMessage = `Network unreachable. Cannot connect to service at ${netErr.address}:${netErr.port}.`;
3600
- } else if ("code" in error && (error.code === "42703" || error.code === "42P01")) {
3612
+ } else if (dbError && (dbError.code === "42703" || dbError.code === "42P01")) {
3601
3613
  code = "SCHEMA_DRIFT";
3602
- const issue = error.code === "42703" ? "column" : "table";
3603
- const pgErr = error;
3604
- logMessage = `Schema drift: ${issue} "${pgErr.table || pgErr.column || extractMissingIdentifier(error.message) || "unknown"}" does not exist in the database. Run \`pnpm db:push\` to sync your schema, or \`pnpm db:migrate\` to apply pending migrations.`;
3605
- }
3606
- const causePg = error.cause && typeof error.cause === "object" ? error.cause : void 0;
3607
- const pgErrorCode = causePg?.code || error.code;
3608
- const isDbSchemaMismatch = code === "SCHEMA_DRIFT" || pgErrorCode === "42703" || pgErrorCode === "42P01";
3614
+ logMessage = `Schema drift: ${dbError.code === "42703" ? "column" : "table"} "${dbError.table || dbError.column || extractMissingIdentifier(dbError.message) || "unknown"}" does not exist in the database. Run \`pnpm db:push\` to sync your schema, or \`pnpm db:migrate\` to apply pending migrations.`;
3615
+ } else if (dbError) {
3616
+ const parts = [`[PG ${dbError.code}] ${dbError.message}`];
3617
+ if (dbError.detail) parts.push(`Detail: ${dbError.detail}`);
3618
+ if (dbError.hint) parts.push(`Hint: ${dbError.hint}`);
3619
+ if (dbError.table) parts.push(`Table: ${dbError.table}`);
3620
+ if (dbError.column) parts.push(`Column: ${dbError.column}`);
3621
+ if (dbError.constraint) parts.push(`Constraint: ${dbError.constraint}`);
3622
+ if (dbError.code === "42501") {
3623
+ code = "DB_PERMISSION_DENIED";
3624
+ parts.push(`The database rejected the statement for lack of privilege — usually a row-level security policy${dbError.table ? ` on "${dbError.table}"` : ""} denying this role, or a stale FORCE ROW LEVEL SECURITY flag binding the owner connection.`);
3625
+ }
3626
+ logMessage = parts.join(". ");
3627
+ }
3628
+ const isDbSchemaMismatch = code === "SCHEMA_DRIFT";
3609
3629
  if (isDbSchemaMismatch) {
3610
3630
  logger.warn(`⚠️ [API] ${c.req.method} ${c.req.path} → ${statusCode} ${code}: ${logMessage}` + (reqId ? ` [${reqId}]` : ""));
3611
3631
  if (!_schemaDriftHinted && process.env.NODE_ENV !== "production") {
@@ -3624,18 +3644,27 @@ var errorHandler = (err, c) => {
3624
3644
  ].join("\n"));
3625
3645
  }
3626
3646
  } else logger.error(`❌ [API] ${c.req.method} ${c.req.path} → ${statusCode} ${code}: ${logMessage}` + (reqId ? ` [${reqId}]` : ""));
3627
- if (!(isDbSchemaMismatch || statusCode < 500 && code === "BAD_REQUEST")) logger.error(String(error.stack || error));
3647
+ if (!(isDbSchemaMismatch || dbError !== null || statusCode < 500 && code === "BAD_REQUEST")) logger.error(String(error.stack || error));
3628
3648
  let clientMessage = "An unexpected error occurred";
3629
3649
  if (statusCode < 500 && error.message) clientMessage = error.message;
3630
3650
  else if (error instanceof ApiError || error.name === "ApiError") clientMessage = error.message;
3631
3651
  else if (code === "SCHEMA_DRIFT") {
3632
- const pgErr = causePg || error;
3633
- clientMessage = `Schema drift: ${pgErr.code === "42703" || pgErrorCode === "42703" ? "column" : "table"} "${pgErr.table || pgErr.column || extractMissingIdentifier(pgErr.message || error.message) || "unknown"}" does not exist. Run \`pnpm db:push\` to sync your schema.`;
3634
- } else if (code === "INTERNAL_ERROR") clientMessage = "Internal Server Error";
3652
+ const pgErr = dbError || error;
3653
+ clientMessage = `Schema drift: ${pgErr.code === "42703" ? "column" : "table"} "${pgErr.table || pgErr.column || extractMissingIdentifier(pgErr.message || error.message) || "unknown"}" does not exist. Run \`pnpm db:push\` to sync your schema.`;
3654
+ } else if (code === "DB_PERMISSION_DENIED") clientMessage = `Permission denied by the database${dbError?.table ? ` on "${dbError.table}"` : ""} (row-level security). Check the RLS policies for this table.`;
3655
+ else if (code === "INTERNAL_ERROR") clientMessage = "Internal Server Error";
3656
+ const dbDetails = dbError ? {
3657
+ dbCode: dbError.code,
3658
+ ...process.env.NODE_ENV !== "production" && {
3659
+ dbMessage: dbError.message,
3660
+ ...dbError.detail && { detail: dbError.detail },
3661
+ ...dbError.hint && { hint: dbError.hint }
3662
+ }
3663
+ } : void 0;
3635
3664
  return c.json({ error: {
3636
3665
  message: clientMessage,
3637
3666
  code,
3638
- ...error.details !== void 0 && { details: error.details },
3667
+ ...error.details !== void 0 ? { details: error.details } : dbDetails !== void 0 ? { details: dbDetails } : {},
3639
3668
  ...reqId && { requestId: reqId }
3640
3669
  } }, statusCode);
3641
3670
  };
@@ -3657,6 +3686,7 @@ function codeToStatus(code) {
3657
3686
  EMAIL_EXISTS: 409,
3658
3687
  ROLE_EXISTS: 409,
3659
3688
  SCHEMA_DRIFT: 500,
3689
+ DB_PERMISSION_DENIED: 500,
3660
3690
  INTERNAL_ERROR: 500,
3661
3691
  NOT_CONFIGURED: 503,
3662
3692
  SERVICE_UNAVAILABLE: 503