@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 +49 -19
- package/dist/index.es.js.map +1 -1
- package/package.json +5 -5
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
|
-
|
|
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
|
|
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 (
|
|
3612
|
+
} else if (dbError && (dbError.code === "42703" || dbError.code === "42P01")) {
|
|
3601
3613
|
code = "SCHEMA_DRIFT";
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
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 =
|
|
3633
|
-
clientMessage = `Schema drift: ${pgErr.code === "42703"
|
|
3634
|
-
} else if (code === "
|
|
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
|
|
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
|