@rebasepro/server 0.25.0-canary.ge06257e → 0.25.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 (37) hide show
  1. package/dist/{GCSStorageController-CAKa_EFW.js → GCSStorageController-C7ARrDAp.js} +3 -3
  2. package/dist/{GCSStorageController-CAKa_EFW.js.map → GCSStorageController-C7ARrDAp.js.map} +1 -1
  3. package/dist/{S3StorageController-Kt-LJpf3.js → S3StorageController-qevJ0pVE.js} +3 -3
  4. package/dist/{S3StorageController-Kt-LJpf3.js.map → S3StorageController-qevJ0pVE.js.map} +1 -1
  5. package/dist/api/errors.d.ts +6 -10
  6. package/dist/{auth-xk37vrHO.js → auth-CteGWxsL.js} +20 -20
  7. package/dist/{auth-xk37vrHO.js.map → auth-CteGWxsL.js.map} +1 -1
  8. package/dist/{backup-CpaJWXSc.js → backup-Dq0lyJ_D.js} +4 -4
  9. package/dist/{backup-CpaJWXSc.js.map → backup-Dq0lyJ_D.js.map} +1 -1
  10. package/dist/boot/boot.d.ts +10 -0
  11. package/dist/{cron-routes-CPIhdasM.js → cron-routes-Dhf82HzT.js} +3 -3
  12. package/dist/{cron-routes-CPIhdasM.js.map → cron-routes-Dhf82HzT.js.map} +1 -1
  13. package/dist/{errors-CHSJYpoy.js → errors-_ytoGi8A.js} +11 -18
  14. package/dist/errors-_ytoGi8A.js.map +1 -0
  15. package/dist/{function-routes-C0emkHtX.js → function-routes-DNV4RUF0.js} +2 -2
  16. package/dist/{function-routes-C0emkHtX.js.map → function-routes-DNV4RUF0.js.map} +1 -1
  17. package/dist/functions/index.js.map +1 -1
  18. package/dist/index.es.js +128 -41
  19. package/dist/index.es.js.map +1 -1
  20. package/dist/{logs-routes-BM2cDory.js → logs-routes-CelTiS7C.js} +2 -2
  21. package/dist/{logs-routes-BM2cDory.js.map → logs-routes-CelTiS7C.js.map} +1 -1
  22. package/dist/{openapi-generator-IKwDAQGO.js → openapi-generator-BamY6-ga.js} +2 -2
  23. package/dist/{openapi-generator-IKwDAQGO.js.map → openapi-generator-BamY6-ga.js.map} +1 -1
  24. package/dist/{query-parser-BnhugNwZ.js → query-parser-CGohjyWI.js} +3 -3
  25. package/dist/{query-parser-BnhugNwZ.js.map → query-parser-CGohjyWI.js.map} +1 -1
  26. package/dist/{request-timeout-LiGpCCP1.js → request-timeout-CRny5KD0.js} +2 -2
  27. package/dist/{request-timeout-LiGpCCP1.js.map → request-timeout-CRny5KD0.js.map} +1 -1
  28. package/dist/{schema-editor-routes-zjVKreVQ.js → schema-editor-routes-CnNGCayU.js} +2 -2
  29. package/dist/{schema-editor-routes-zjVKreVQ.js.map → schema-editor-routes-CnNGCayU.js.map} +1 -1
  30. package/dist/{soft-delete-params-uHxJoETb.js → soft-delete-params-BAgpmBvp.js} +2 -2
  31. package/dist/{soft-delete-params-uHxJoETb.js.map → soft-delete-params-BAgpmBvp.js.map} +1 -1
  32. package/dist/{sql-export-routes-D86u7N9i.js → sql-export-routes-BgTJ7K3S.js} +2 -2
  33. package/dist/{sql-export-routes-D86u7N9i.js.map → sql-export-routes-BgTJ7K3S.js.map} +1 -1
  34. package/dist/{upload-validation-pXcDy_EQ.js → upload-validation-I9cCUBIW.js} +2 -2
  35. package/dist/{upload-validation-pXcDy_EQ.js.map → upload-validation-I9cCUBIW.js.map} +1 -1
  36. package/package.json +5 -5
  37. package/dist/errors-CHSJYpoy.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rebasepro/server",
3
- "version": "0.25.0-canary.ge06257e",
3
+ "version": "0.25.0",
4
4
  "description": "Database-Agnostic Backend Core for Rebase",
5
5
  "keywords": [
6
6
  "rebase",
@@ -54,10 +54,10 @@
54
54
  "jsonwebtoken": "^9.0.3",
55
55
  "ws": "^8.21.1",
56
56
  "zod": "^4.4.3",
57
- "@rebasepro/client": "0.25.0-canary.ge06257e",
58
- "@rebasepro/common": "0.25.0-canary.ge06257e",
59
- "@rebasepro/utils": "0.25.0-canary.ge06257e",
60
- "@rebasepro/types": "0.25.0-canary.ge06257e"
57
+ "@rebasepro/client": "0.25.0",
58
+ "@rebasepro/types": "0.25.0",
59
+ "@rebasepro/common": "0.25.0",
60
+ "@rebasepro/utils": "0.25.0"
61
61
  },
62
62
  "devDependencies": {
63
63
  "@jest/globals": "^30.4.1",
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors-CHSJYpoy.js","names":[],"sources":["../src/api/errors.ts"],"sourcesContent":["import type { Context, ErrorHandler } from \"hono\";\nimport type { ContentfulStatusCode } from \"hono/utils/http-status\";\nimport type { HonoEnv } from \"./types\";\nimport { logger } from \"../utils/logger\";\nimport { hostEnv } from \"../utils/host\";\n\n/**\n * A stale caller's schema stamp, as the cause of the error it explains.\n *\n * `createSchemaDriftDetector` puts the two stamps on the context when a request\n * carries an `x-rebase-schema` older than this backend's. It lives next door;\n * this half is here because `errors.ts` is in the graph of\n * `@rebasepro/server/functions` and may not reach `@rebasepro/types` at runtime.\n */\nfunction schemaDriftCause(drift: { client: string; server: string } | undefined): {\n code: string;\n message: string;\n clientSchema: string;\n serverSchema: string;\n} | undefined {\n if (!drift) return undefined;\n return {\n code: \"SCHEMA_DRIFT\",\n message:\n `This client was generated against schema ${drift.client}; this backend serves `\n + `${drift.server}. If the field named above was renamed or removed, regenerate the `\n + \"SDK (`rebase generate-sdk`) and rebuild.\",\n clientSchema: drift.client,\n serverSchema: drift.server\n };\n}\n\n/** Tracks whether we've already shown the doctor hint (once per process). */\nlet _schemaDriftHinted = false;\n\n/**\n * The schema-drift remedy, in the words that work on *this* database.\n *\n * The three copies of this message hard-coded `Run \\`pnpm db:push\\``, and on a\n * stock scaffold — where the managed PGlite database is the default — that\n * command answers `✗ rebase db push does not work on the managed development\n * database.` and exits 1. So the one instruction the server gave when a\n * developer's schema had drifted was a command their project refuses.\n *\n * Atlas plans a push by diffing against a second, empty database, and PGlite\n * serves exactly one — which is why it cannot run there, and why the remedy\n * has to know which database is under this run. There, boot applies additive\n * changes, so restarting `rebase dev` *is* the fix.\n *\n * `REBASE_DEV_DATABASE_KIND` is set by the CLI from the database it resolved,\n * the same variable `rebase schema generate`'s closing line already branches\n * on. Absent — a deployed backend, a container, anything not started by\n * `rebase dev` — the answer is the general one.\n */\nexport function schemaDriftRemedy(): { short: string; lines: string[] } {\n if (hostEnv().REBASE_DEV_DATABASE_KIND === \"managed\") {\n return {\n short: \"Restart `rebase dev` — boot applies additive schema changes to the managed database.\",\n lines: [\n \" Quick fixes (managed development database):\",\n \" restart `rebase dev` boot applies additive changes\",\n \" rebase doctor full 3-way drift report\",\n \"\",\n \" `rebase db push` does not run here: Atlas plans against a\",\n \" second, empty database and PGlite serves one. For a change\",\n \" boot leaves alone, use your own Postgres (DATABASE_URL)\",\n \" or `rebase dev --docker`.\"\n ]\n };\n }\n\n return {\n short: \"Run `rebase db push` to sync your schema, or `rebase db migrate` to apply pending migrations.\",\n lines: [\n \" Quick fixes (local dev, against DATABASE_URL):\",\n \" rebase db push sync schema to database (dev)\",\n \" rebase db migrate apply pending migrations (prod)\",\n \" rebase doctor full 3-way drift report\",\n \"\",\n \" Managed cloud: the runtime applies schema + RLS at boot\",\n \" (REBASE_MIGRATE_ON_BOOT); redeploy rather than db push,\",\n \" which cannot reach the tenant database.\"\n ]\n };\n}\n\n/** Shape of Postgres / network errors with diagnostic codes */\ninterface PgLikeError {\n code?: string;\n address?: string;\n port?: number;\n message?: string;\n table?: string;\n column?: string;\n schema?: string;\n detail?: string;\n hint?: string;\n constraint?: string;\n}\n\n/** 5-character SQLSTATE, e.g. `42501`, `23505`. */\nconst SQLSTATE_RE = /^[0-9A-Z]{5}$/;\n\n/**\n * What SQLSTATE 25006 means here, in the only terms that help the author fix it.\n *\n * Every request-scoped read runs `withTransaction(..., { accessMode: \"read\n * only\" })`, so a write attempted anywhere under it — including from a\n * `context.data` call inside an `afterRead` callback — is refused by Postgres\n * rather than by us. The callback name is in the message because that is the\n * file the reader has to open, and nothing else on a read path can raise this.\n */\nconst READ_ONLY_TRANSACTION_MESSAGE =\n \"An `afterRead` callback tried to write. Request-scoped reads run in a READ ONLY \" +\n \"transaction, so neither the callback nor anything it calls (context.data included) \" +\n \"may write. Move the write outside the read: enqueue a background job, or use \" +\n \"`rebase.dataAsAdmin` from a job or a custom function.\";\n\n/**\n * Walk the cause chain for the underlying database error, identified by a\n * 5-char SQLSTATE `code`. Drizzle wraps the pg error in `.cause`, and route\n * code sometimes wraps drizzle again, so the real error may sit several\n * levels down.\n */\nfunction extractDbError(error: unknown, depth = 0): PgLikeError | null {\n if (!error || typeof error !== \"object\" || depth > 8) return null;\n const e = error as PgLikeError & { cause?: unknown };\n if (typeof e.code === \"string\" && SQLSTATE_RE.test(e.code)) return e;\n if (e.cause && typeof e.cause === \"object\") return extractDbError(e.cause, depth + 1);\n return null;\n}\n\n/**\n * Did the database refuse a value as unreadable for its column's type —\n * SQLSTATE class 22, anywhere down the cause chain?\n *\n * For a lookup by an id the caller supplied, that is an answer rather than a\n * failure: `abc` against a UUID key names no row, exactly as a well-formed id\n * nobody has does.\n */\nexport function isDataException(error: unknown): boolean {\n return extractDbError(error)?.code?.startsWith(\"22\") === true;\n}\n\n/**\n * The two ways a request fails before it ever reaches the database, read off\n * the cause chain by pg-pool's own wording (it sets no code on either).\n *\n * - `timeout exceeded when trying to connect` — every pooled connection was\n * busy for the whole of `connectionTimeoutMillis`. pg-pool raises it only\n * from its wait queue, so it means the pool is exhausted: slow queries\n * holding connections, or `DB_POOL_MAX` too small for the load.\n * - `Connection terminated due to connection timeout` — a fresh connection\n * took longer than that to open: the database is slow or far away.\n *\n * Either is load, not a fault. Answered as a 500 they read as a crash and gave\n * a client nothing to back off on; as a 503 with `Retry-After` the SDK's\n * existing retry path takes them (`offline-connectivity.ts`).\n */\nconst POOL_FAILURES: ReadonlyArray<{ message: string; code: \"DB_POOL_EXHAUSTED\" | \"DB_CONNECT_TIMEOUT\" }> = [\n { message: \"timeout exceeded when trying to connect\", code: \"DB_POOL_EXHAUSTED\" },\n { message: \"Connection terminated due to connection timeout\", code: \"DB_CONNECT_TIMEOUT\" }\n];\n\nfunction poolFailureOf(error: unknown, depth = 0): \"DB_POOL_EXHAUSTED\" | \"DB_CONNECT_TIMEOUT\" | undefined {\n if (!error || typeof error !== \"object\" || depth > 8) return undefined;\n const message = Reflect.get(error, \"message\");\n if (typeof message === \"string\") {\n const match = POOL_FAILURES.find(failure => message === failure.message);\n if (match) return match.code;\n }\n return poolFailureOf(Reflect.get(error, \"cause\"), depth + 1);\n}\n\n/** Seconds a client is asked to wait after a pool failure — one pool timeout's worth is too long to make a person wait. */\nconst POOL_RETRY_AFTER_SECONDS = 1;\n\n/**\n * SQLSTATEs that mean the database dropped or refused the connection, not\n * that it judged the statement: class 08 (connection exception), and the\n * three shutdown codes — `57P01` a server shutting down or an administrator\n * ending the session, `57P02` a crash restart, `57P03` a server still\n * starting.\n */\nconst UNREACHABLE_SQLSTATES = new Set([\"57P01\", \"57P02\", \"57P03\"]);\n\n/**\n * What node-postgres says when the socket under a client closed, in the words\n * it sets no code on.\n */\nconst CONNECTION_LOST_MESSAGES = new Set([\n \"Connection terminated unexpectedly\",\n \"Client has encountered a connection error and is not queryable\"\n]);\n\n/** The socket failures Node raises when a host cannot be reached or drops the connection. */\nconst SOCKET_FAILURE_CODES = new Set([\n \"ECONNREFUSED\", \"ECONNRESET\", \"ENOTFOUND\", \"EAI_AGAIN\", \"EHOSTUNREACH\", \"ENETUNREACH\", \"ETIMEDOUT\", \"EPIPE\"\n]);\n\n/**\n * A Node socket failure: one of {@link SOCKET_FAILURE_CODES} on an error the\n * runtime raised for a system call, or the `AggregateError` `net` raises when\n * every address a host resolved to failed that way.\n */\nfunction isSocketFailure(error: object, depth = 0): boolean {\n const code = Reflect.get(error, \"code\");\n if (typeof code === \"string\" && SOCKET_FAILURE_CODES.has(code) && typeof Reflect.get(error, \"syscall\") === \"string\") {\n return true;\n }\n const inner = Reflect.get(error, \"errors\");\n return depth < 2 && Array.isArray(inner) && inner.length > 0 &&\n inner.every(item => typeof item === \"object\" && item !== null && isSocketFailure(item, depth + 1));\n}\n\n/**\n * Did the request fail because the database could not be reached at all — not\n * because it refused the statement, and not because some other service is down?\n *\n * Read off the error the database driver raised, wherever it sits in the cause\n * chain:\n *\n * - a SQLSTATE that means the connection was refused or dropped (class `08`,\n * `57P01`–`57P03`). The database's own word, so it settles the question\n * either way: a SQLSTATE of any other class is the database answering;\n * - node-postgres's own sentence for a socket that closed under a query;\n * - a socket failure (`ECONNREFUSED`, `ENOTFOUND`, …), but only where it can\n * only be the database's: thrown as it is, which is how node-postgres hands\n * back a pool connection it could not open, or as the cause of a failed\n * Drizzle query (the wrapper that carries the statement). Under any other\n * wrapper it belongs to another client: `fetch failed` is a `fetch()` to\n * some other host, and an S3 call that cannot connect carries the AWS SDK's\n * `$metadata`. Answering those \"the database is unreachable\" would send an\n * operator to a database that is fine.\n */\nexport function isDatabaseUnreachable(error: unknown): boolean {\n if (typeof error !== \"object\" || error === null) return false;\n\n const reported = databaseReport(error);\n if (reported !== undefined) return reported;\n\n const socketFailureOf = (candidate: unknown): boolean =>\n typeof candidate === \"object\" && candidate !== null &&\n isSocketFailure(candidate) && Reflect.get(candidate, \"$metadata\") === undefined;\n if (socketFailureOf(error)) return true;\n return typeof Reflect.get(error, \"query\") === \"string\" && socketFailureOf(Reflect.get(error, \"cause\"));\n}\n\n/**\n * What the database itself said about the connection, down the cause chain:\n * `true` for a lost or refused connection, `false` for a SQLSTATE that is the\n * database answering, `undefined` when nothing on the chain is the database's.\n */\nfunction databaseReport(error: object, depth = 0): boolean | undefined {\n const code = Reflect.get(error, \"code\");\n // `EPIPE` has a SQLSTATE's shape, and is a socket's.\n if (typeof code === \"string\" && SQLSTATE_RE.test(code) && !SOCKET_FAILURE_CODES.has(code)) {\n return code.startsWith(\"08\") || UNREACHABLE_SQLSTATES.has(code);\n }\n const message = Reflect.get(error, \"message\");\n if (typeof message === \"string\" && CONNECTION_LOST_MESSAGES.has(message)) return true;\n const cause = Reflect.get(error, \"cause\");\n return depth < 8 && typeof cause === \"object\" && cause !== null ? databaseReport(cause, depth + 1) : undefined;\n}\n\n/**\n * The operator's half of a connection that failed — to the database, or to a\n * service a request called — in a few words: the socket failure and the host\n * it names (`ECONNREFUSED at 127.0.0.1:5432`), or what the database or its\n * client said about the connection.\n */\nfunction describeConnectionFailure(error: unknown, depth = 0): string {\n if (typeof error !== \"object\" || error === null || depth > 8) return \"the connection failed\";\n const code = Reflect.get(error, \"code\");\n const message = Reflect.get(error, \"message\");\n if (typeof code === \"string\" && SOCKET_FAILURE_CODES.has(code)) {\n const address = Reflect.get(error, \"address\");\n const port = Reflect.get(error, \"port\");\n const hostname = Reflect.get(error, \"hostname\");\n if (address !== undefined) return `${code} at ${String(address)}${port !== undefined ? `:${String(port)}` : \"\"}`;\n if (typeof hostname === \"string\") return `${code} for ${hostname}`;\n }\n if (typeof message === \"string\" && (CONNECTION_LOST_MESSAGES.has(message) ||\n (typeof code === \"string\" && (code.startsWith(\"08\") || UNREACHABLE_SQLSTATES.has(code))))) {\n return typeof code === \"string\" ? `${message} (${code})` : message;\n }\n const inner = Reflect.get(error, \"errors\");\n if (Array.isArray(inner) && inner.length > 0) return describeConnectionFailure(inner[0], depth + 1);\n const cause = Reflect.get(error, \"cause\");\n if (typeof cause === \"object\" && cause !== null) return describeConnectionFailure(cause, depth + 1);\n return typeof code === \"string\" ? code : \"the connection failed\";\n}\n\n/**\n * Is there a Node socket failure under this error, anywhere down its cause\n * chain?\n *\n * Asked only once {@link isDatabaseUnreachable} has said no, so what it finds\n * belongs to another service: a `fetch()` to some other host (`fetch failed`,\n * its `cause` the socket's error), an S3 call that cannot connect (the AWS SDK\n * hands back the socket's own error with `$metadata` on it), or anything else\n * wrapping one. An error that only carries a socket code, with no system call\n * behind it, is not one.\n */\nfunction hasUpstreamSocketFailure(error: unknown, depth = 0): boolean {\n if (typeof error !== \"object\" || error === null || depth > 8) return false;\n return isSocketFailure(error) || hasUpstreamSocketFailure(Reflect.get(error, \"cause\"), depth + 1);\n}\n\n/** What a caller is told when a service the request needs cannot be reached. The host stays in the log. */\nconst UPSTREAM_UNAVAILABLE_MESSAGE =\n \"A service this request depends on could not be reached, so it was not served. Retry shortly.\";\n\n/** What a caller is told when the database cannot be reached. Retryable, and not about them. */\nexport const DATABASE_UNAVAILABLE_MESSAGE =\n \"The database could not be reached, so this request was not served. \" +\n \"Retry shortly; if it keeps happening, the server's database is down or unreachable from it.\";\n\n/** Seconds a client is asked to wait before retrying a request the database could not be reached for. */\nconst DATABASE_RETRY_AFTER_SECONDS = 5;\n\n/** The operator's line for an unreachable database, at most once a minute — see {@link warnPoolFailure}. */\nlet lastUnreachableWarningAt = 0;\n\nfunction warnDatabaseUnreachable(detail: string): void {\n const now = Date.now();\n if (now - lastUnreachableWarningAt < POOL_WARNING_INTERVAL_MS) return;\n lastUnreachableWarningAt = now;\n logger.error(\n `❌ The database is unreachable (${detail}), so requests that need it are answered 503 DATABASE_UNAVAILABLE. ` +\n \"Check that it is running and that this server can reach it; requests recover on their own once it is back. \" +\n \"Not repeated for a minute.\"\n );\n}\n\n/**\n * The operator's line, at most once a minute. A pool that is exhausted is\n * exhausted for every request at once, and a line per request buries the one\n * thing to do about it.\n */\nconst POOL_WARNING_INTERVAL_MS = 60_000;\nlet lastPoolWarningAt = 0;\n\nfunction warnPoolFailure(code: \"DB_POOL_EXHAUSTED\" | \"DB_CONNECT_TIMEOUT\"): void {\n const now = Date.now();\n if (now - lastPoolWarningAt < POOL_WARNING_INTERVAL_MS) return;\n lastPoolWarningAt = now;\n logger.warn(code === \"DB_POOL_EXHAUSTED\"\n ? \"⚠️ Database pool exhausted: every connection stayed busy for DB_POOL_CONNECT_TIMEOUT, so requests \" +\n \"are answered 503. Raise DB_POOL_MAX (default 20; DB_POOL_MAX__<KEY> for another data source), or \" +\n \"find the slow queries or long transactions holding connections. Not repeated for a minute.\"\n : \"⚠️ Opening a database connection took longer than DB_POOL_CONNECT_TIMEOUT, so requests are \" +\n \"answered 503. The database is slow to accept connections or far away. Not repeated for a minute.\");\n}\n\n/**\n * Extract the missing table or column name from a PG error.\n * PG 42P01 messages look like: 'relation \"my_table\" does not exist'\n * PG 42703 messages look like: 'column \"my_col\" does not exist' or 'column my_table.my_col does not exist'\n */\nfunction extractMissingIdentifier(pgMessage?: string): string | null {\n if (!pgMessage) return null;\n // Match quoted identifier: relation \"xxx\" / column \"xxx\"\n const quoted = pgMessage.match(/(?:relation|column|table)\\s+\"([^\"]+)\"/i);\n if (quoted) return quoted[1];\n // Match unquoted: column table.col does not exist\n const unquoted = pgMessage.match(/(?:relation|column|table)\\s+([\\w.]+)\\s+does not exist/i);\n if (unquoted) return unquoted[1];\n return null;\n}\n\n/**\n * The sentence a `INVALID_FILTER_VALUE` answer carries.\n *\n * Built from Postgres's own wording rather than passed through, because the\n * driver hands the whole failed statement over as `error.message` and this one\n * goes to the caller in production too. What is quoted back is the type name\n * and the literal the caller themselves sent — never a table, a column list or\n * a statement.\n *\n * The three shapes are the ones a filter actually produces: an unparseable\n * literal (22P02, `?id=eq.abc`), a value outside an enum's labels (22P02 with\n * different wording), and a number past the column type's range (22003). A\n * fourth SQLSTATE in class 22 lands on the general sentence, which still says\n * the useful thing: it is the value that is wrong, not the server.\n */\nfunction describeDataException(dbError?: PgLikeError): string {\n const message = dbError?.message ?? \"\";\n const column = dbError?.column ? ` for column \"${dbError.column}\"` : \"\";\n\n const syntax = message.match(/invalid input syntax for type ([\\w ]+): \"(.*)\"/);\n if (syntax) return `\"${syntax[2]}\" is not a valid ${syntax[1]}${column}.`;\n\n const enumValue = message.match(/invalid input value for enum ([\\w.\"]+): \"(.*)\"/);\n if (enumValue) return `\"${enumValue[2]}\" is not one of the values of ${enumValue[1]}${column}.`;\n\n const range = message.match(/value \"(.*)\" is out of range for type ([\\w ]+)/);\n if (range) return `\"${range[1]}\" is out of range for ${range[2]}${column}.`;\n\n return `A value in this request could not be read as the type of the column it was compared against${column}.`;\n}\n\n/**\n * Standardized API error class.\n * Throw this from any route handler — the errorHandler middleware\n * will format it into `{ error: { message, code, details? } }`.\n */\nexport class ApiError extends Error {\n public readonly statusCode: number;\n public readonly code: string;\n public readonly details?: unknown;\n /**\n * Whether this outcome is a routine part of normal operation rather than\n * something an operator should look at. Expected errors log at debug; every\n * other operational error logs at warn.\n *\n * The motivating case is `POST /auth/refresh` with no session: clients\n * refresh on page load before they know whether one exists, so every\n * anonymous page view is a 401 — correct, and not worth a warning line.\n *\n * The other class is a caller-caused 4xx that never reached the database: a\n * mistyped filter operator, sort direction or limit, a request for a\n * collection that does not exist. Nothing on this server is wrong, and the\n * response body has already told the caller what to fix — while one client\n * holding a stale name would otherwise write a warning per request, forever,\n * until the level means nothing. See `api/rest/query-parser.ts`.\n *\n * What stays at warn is anything that says something about the *server*:\n * a schema that has drifted from the code, a permission the database\n * refused, a dependency that failed. Those are 4xx too, and they are still\n * incidents.\n */\n public readonly expected: boolean;\n\n constructor(statusCode: number, code: string, message: string, details?: unknown, expected = false) {\n super(message);\n this.name = \"ApiError\";\n this.statusCode = statusCode;\n this.code = code;\n this.details = details;\n this.expected = expected;\n }\n\n // ── Factory methods ──────────────────────────────────────────────\n\n static badRequest(message: string, code = \"BAD_REQUEST\", details?: unknown): ApiError {\n return new ApiError(400, code, message, details);\n }\n\n static unauthorized(message: string, code = \"UNAUTHORIZED\"): ApiError {\n return new ApiError(401, code, message);\n }\n\n /**\n * A 401 that is a normal outcome, not an incident — logged at debug.\n * See {@link ApiError.expected}.\n */\n static unauthenticated(message: string, code = \"UNAUTHORIZED\"): ApiError {\n return new ApiError(401, code, message, undefined, true);\n }\n\n static forbidden(message: string, code = \"FORBIDDEN\"): ApiError {\n return new ApiError(403, code, message);\n }\n\n static notFound(message: string, code = \"NOT_FOUND\"): ApiError {\n return new ApiError(404, code, message);\n }\n\n /**\n * `details` because a 409 is usually a `23505`, and the one thing the\n * caller needs is *which field* collided. The column name goes in; the\n * value never does — see `pgFieldViolations`.\n */\n static conflict(message: string, code = \"CONFLICT\", details?: unknown): ApiError {\n return new ApiError(409, code, message, details);\n }\n\n static internal(message: string, code = \"INTERNAL_ERROR\"): ApiError {\n return new ApiError(500, code, message);\n }\n\n static serviceUnavailable(message: string, code = \"SERVICE_UNAVAILABLE\"): ApiError {\n return new ApiError(503, code, message);\n }\n\n /**\n * `503 DATABASE_UNAVAILABLE`: the database could not be reached. What\n * every door answers then — the data API, sign-in, the admin routes and\n * the realtime socket — so a client sees one retryable answer, sent with\n * `Retry-After`, rather than a permission failure or a crash.\n *\n * @see isDatabaseUnreachable\n */\n static databaseUnavailable(message = DATABASE_UNAVAILABLE_MESSAGE): ApiError {\n return new ApiError(503, \"DATABASE_UNAVAILABLE\", message);\n }\n}\n\n/**\n * Canonical error response shape:\n * `{ error: { message: string, code: string, details?: unknown } }`\n */\nexport interface ErrorResponse {\n error: {\n message: string;\n code: string;\n details?: unknown;\n /** Request correlation ID for tracing (echoes X-Request-ID). */\n requestId?: string;\n /**\n * Why this request was going to fail whatever it asked for.\n *\n * Only `SCHEMA_DRIFT` today: the caller's `x-rebase-schema` stamp is\n * older than this backend's, so a 400 naming an unknown field is very\n * likely a rename the client has not regenerated for. The error itself\n * is unchanged — this explains it, it does not cause it.\n */\n cause?: {\n code: string;\n message: string;\n clientSchema: string;\n serverSchema: string;\n };\n };\n}\n\n/**\n * General shape of errors that flow through the API error handler.\n * Extends Error with optional HTTP status, error code, and details.\n */\nexport interface RebaseApiError extends Error {\n statusCode?: number;\n code?: string;\n details?: unknown;\n}\n\n/**\n * The answer an error chose for itself, read the same way at every door.\n *\n * @see declaredErrorAnswer\n */\nexport interface DeclaredErrorAnswer {\n /** The HTTP status the error carries. A socket frame has no slot for it. */\n status: number;\n code: string;\n message: string;\n details?: unknown;\n /** See {@link ApiError.expected}: log it at debug, not warn. */\n expected: boolean;\n}\n\n/**\n * The status, code and message an error carries as its own answer — or\n * `undefined` for an error that carries none, which is a server fault and gets\n * masked.\n *\n * Two classes carry one. The server's `ApiError`, and `RebaseApiError` (or its\n * `RebaseClientError` subclass) from `@rebasepro/types` once it has a status.\n * The second is the browser-safe class: a `config/collections/*.ts` file is\n * bundled into the admin SPA and cannot import this package, so it is what a\n * collection callback throws, and what a callback refusal becomes —\n * `callbackRefusal` returns one, and `toCallbackError` wraps anything thrown\n * that does not already carry a status.\n *\n * One function because several doors turn an error into an answer: the REST\n * error handler, the two WebSocket servers, and the Postgres realtime\n * subscriptions. Each used to list the classes it recognised by hand. The\n * sockets listed only `ApiError`, so a `beforeDelete` veto that REST\n * answered as 400 `CALLBACK_REJECTED` with the author's message reached the\n * admin panel — which writes through the socket — as `INTERNAL_ERROR`, and in\n * production as \"An unexpected error occurred\".\n *\n * Matched by name as well as `instanceof`: a monorepo can resolve two copies of\n * a package, and `instanceof` is false across them. Name matching is also why\n * this file needs no runtime import of `@rebasepro/types`, which it may not\n * have — it is in the graph of `@rebasepro/server/functions`.\n */\nexport function declaredErrorAnswer(error: unknown): DeclaredErrorAnswer | undefined {\n if (error === null || typeof error !== \"object\") return undefined;\n const e = error as { name?: unknown; message?: unknown; code?: unknown; details?: unknown; statusCode?: unknown; status?: unknown };\n\n let status: number | undefined;\n if (error instanceof ApiError || e.name === \"ApiError\") {\n status = typeof e.statusCode === \"number\" ? e.statusCode : undefined;\n } else if (typeof e.name === \"string\" && /^Rebase(Api|Client)Error$/.test(e.name)) {\n // It spells its status `status`; `statusCode` wins when both are set.\n status = typeof e.statusCode === \"number\" ? e.statusCode\n : typeof e.status === \"number\" ? e.status\n : undefined;\n // Without a status it has not chosen an answer — `RebaseClientError`\n // is also raised for plain logic errors — so it is not one here.\n if (status === undefined) return undefined;\n } else {\n return undefined;\n }\n\n return {\n status: status || 500,\n code: typeof e.code === \"string\" && e.code ? e.code : \"INTERNAL_ERROR\",\n message: typeof e.message === \"string\" ? e.message : String(e.message ?? \"\"),\n ...(e.details !== undefined && { details: e.details }),\n expected: error instanceof ApiError && error.expected\n };\n}\n\n// `isRebaseApiError` was here. It read `return error instanceof Error`, so it\n// answered yes to every error while being named and used as though it\n// discriminated — the create and update handlers guarded a \"classify this as\n// BAD_REQUEST\" branch on it, and an unreachable database was therefore reported\n// to callers as a bad request. Deleted rather than repaired: the shape it\n// claimed to test is not decidable from an `Error`, and the layer that does\n// know — the driver, which holds the SQLSTATE — raises a real `ApiError`.\n\n/**\n * Leave the code and the message where the request log will find them.\n *\n * A failed request used to produce two lines, each holding half of it: this\n * handler had the code and the diagnosis, `requestLogger` had the user, the\n * collection, the status and the latency. Correlating them meant matching on\n * the request id — which only one of them printed reliably — and the pair cost\n * twice the volume for less than one line's worth of meaning.\n */\nfunction handOffToRequestLog(c: Context<HonoEnv>, code: string, message: string, expected = false): void {\n if (typeof c.set !== \"function\") return;\n c.set(\"errorSummary\", { code, message, expected });\n}\n\n/**\n * Is a request line coming for this request?\n *\n * `requestLogger` claims it before the handler runs, so by the time an error\n * reaches here the answer is already known. When nothing claimed it — a router\n * a project mounted onto its own Hono app, a test driving `app.fetch`\n * directly — this handler stays the only thing that would report the failure,\n * so it still writes its own line. Silence is the one outcome neither half may\n * produce.\n */\nfunction requestWillBeLogged(c: Context<HonoEnv>): boolean {\n return typeof c.get === \"function\" && c.get(\"requestLogged\") === true;\n}\n\n/**\n * Hono error-handling middleware (`app.onError`).\n * Converts any error into the canonical `{ error: { message, code } }` shape.\n */\nexport const errorHandler: ErrorHandler<HonoEnv> = (err, c) => {\n // Typecast custom error properties\n const error: RebaseApiError = err;\n const reqId = typeof c.get === \"function\" ? c.get(\"requestId\") : undefined;\n\n /* A stale SDK, named on the errors it explains.\n\n 400 and 404 only: those are what a renamed or removed field produces —\n an unknown filter field is a 400, a collection gone from under its slug\n is a 404 — and they are the two a caller can act on by regenerating.\n Attaching it to a 500 would be noise, since a server fault has nothing to\n do with how old the caller's schema is. */\n const driftFor = (status: number) =>\n (status === 400 || status === 404) && typeof c.get === \"function\"\n ? schemaDriftCause(c.get(\"schemaDrift\"))\n : undefined;\n\n // An error that chose its own answer — `ApiError`, or the browser-safe\n // `RebaseApiError` a collection callback throws. The same predicate the\n // WebSocket servers use; see `declaredErrorAnswer`.\n const answer = declaredErrorAnswer(error);\n if (answer) {\n // Operational errors — log at warn, unless the error declares itself a\n // routine outcome (see ApiError.expected), which would otherwise put a\n // warning in the log for every anonymous page view.\n handOffToRequestLog(c, answer.code, answer.message, answer.expected);\n if (answer.code === \"DATABASE_UNAVAILABLE\") c.header(\"Retry-After\", String(DATABASE_RETRY_AFTER_SECONDS));\n if (!requestWillBeLogged(c)) {\n const line = `[API] ${c.req.method} ${c.req.path} → ${answer.status} ${answer.code}: ${answer.message}` +\n (reqId ? ` [${reqId}]` : \"\");\n if (answer.expected) {\n logger.debug(line);\n } else {\n logger.warn(`⚠️ ${line}`);\n }\n }\n const apiErrorDrift = driftFor(answer.status);\n return c.json({\n error: {\n message: answer.message,\n code: answer.code,\n ...(answer.details !== undefined && { details: answer.details }),\n ...(reqId && { requestId: reqId }),\n ...(apiErrorDrift && { cause: apiErrorDrift })\n }\n } satisfies ErrorResponse, answer.status as ContentfulStatusCode);\n }\n\n // A body that is not JSON, read with `c.req.json()` — every auth and admin\n // route reads its body that way. It threw a bare `SyntaxError`, which\n // landed here as a 500 logged with a stack: a server fault for the\n // caller's typo, where the data API (which parses its own body) answered\n // 400. Mapped once, here, rather than at each of forty call sites.\n if (err instanceof SyntaxError && /JSON/i.test(err.message)) {\n handOffToRequestLog(c, \"INVALID_JSON\", \"Invalid JSON body\");\n return c.json({\n error: {\n message: \"Invalid JSON body\",\n code: \"INVALID_JSON\",\n ...(reqId && { requestId: reqId })\n }\n } satisfies ErrorResponse, 400);\n }\n\n let statusCode = error.statusCode || codeToStatus(error.code) || 500;\n let code = error.code || \"INTERNAL_ERROR\";\n\n // Handle DB connection and specific system errors for better logging\n let logMessage = error.message;\n\n // Resolve the actual cause — Node's net module wraps dual-stack failures\n // in an AggregateError whose inner errors carry the real address/port.\n let resolvedCause: PgLikeError | undefined;\n if (error.cause && typeof error.cause === \"object\" && error.cause !== null && \"code\" in error.cause) {\n const cause = error.cause as PgLikeError & { errors?: PgLikeError[] };\n if (cause.code === \"ECONNREFUSED\" && !cause.address && Array.isArray(cause.errors)) {\n // AggregateError — pick the first inner error that has address info\n resolvedCause = cause.errors.find(e => e.address) || cause;\n } else {\n resolvedCause = cause;\n }\n }\n\n // The real database error may sit several levels down the cause chain.\n // Losing it turns a precise failure (e.g. an RLS denial) into an opaque\n // \"Failed query: …\" 500 that is undiagnosable without direct DB access.\n const dbError = extractDbError(error);\n const poolFailure = dbError ? undefined : poolFailureOf(error);\n // Ahead of every SQLSTATE arm below: `57P01` is a SQLSTATE too, and it\n // says the connection is gone, not that the statement was wrong.\n const unreachable = !poolFailure && isDatabaseUnreachable(error);\n // A socket failure that is not the database's is another service's: a\n // `fetch()`, an S3 call. It answered `500` with the errno as the code\n // (`ECONNREFUSED`), or `INTERNAL_ERROR` — a server fault, for a service it\n // calls being down.\n const upstream = !poolFailure && !unreachable && !dbError && hasUpstreamSocketFailure(error);\n\n if (poolFailure) {\n code = poolFailure;\n statusCode = 503;\n logMessage = poolFailure === \"DB_POOL_EXHAUSTED\"\n ? \"Timed out waiting for a database connection: every pooled connection was busy (DB_POOL_MAX).\"\n : \"Timed out opening a database connection (DB_POOL_CONNECT_TIMEOUT).\";\n warnPoolFailure(poolFailure);\n c.header(\"Retry-After\", String(POOL_RETRY_AFTER_SECONDS));\n } else if (unreachable) {\n code = \"DATABASE_UNAVAILABLE\";\n statusCode = 503;\n const reason = describeConnectionFailure(error);\n logMessage = `The database could not be reached: ${reason}.`;\n warnDatabaseUnreachable(reason);\n c.header(\"Retry-After\", String(DATABASE_RETRY_AFTER_SECONDS));\n } else if (upstream) {\n code = \"SERVICE_UNAVAILABLE\";\n statusCode = 503;\n logMessage = `A service this request depends on could not be reached: ${describeConnectionFailure(error)}.`;\n } else if (resolvedCause && (resolvedCause.code === \"ENETUNREACH\" || resolvedCause.code === \"ECONNREFUSED\")) {\n // Some other service: a database that cannot be reached is the arm\n // above, so this one does not say \"database\".\n const cause = resolvedCause;\n logMessage = cause.code === \"ENETUNREACH\"\n ? `Network unreachable: cannot connect to ${cause.address}:${cause.port}.`\n : `Connection refused by ${cause.address}:${cause.port}.`;\n } else if (\"code\" in error && error.code === \"ENETUNREACH\") {\n const netErr = error as PgLikeError;\n logMessage = `Network unreachable. Cannot connect to service at ${netErr.address}:${netErr.port}.`;\n } else if (dbError && (dbError.code === \"42703\" || dbError.code === \"42P01\")) {\n code = \"SCHEMA_DRIFT\";\n const issue = dbError.code === \"42703\" ? \"column\" : \"table\";\n const identifier = dbError.table || dbError.column || extractMissingIdentifier(dbError.message) || \"unknown\";\n logMessage = `Schema drift: ${issue} \"${identifier}\" does not exist in the database. ${schemaDriftRemedy().short}`;\n } else if (dbError) {\n const parts = [`[PG ${dbError.code}] ${dbError.message}`];\n if (dbError.detail) parts.push(`Detail: ${dbError.detail}`);\n if (dbError.hint) parts.push(`Hint: ${dbError.hint}`);\n if (dbError.table) parts.push(`Table: ${dbError.table}`);\n if (dbError.column) parts.push(`Column: ${dbError.column}`);\n if (dbError.constraint) parts.push(`Constraint: ${dbError.constraint}`);\n if (dbError.code === \"42501\") {\n code = \"DB_PERMISSION_DENIED\";\n const ungranted = missingGrant(dbError.message);\n parts.push(ungranted\n ? `The role this request runs as was never granted ${ungranted.kind} \"${ungranted.name}\" — ` +\n \"a missing GRANT, not a row-level security policy.\"\n : \"The database rejected the statement for lack of privilege — usually a row-level \" +\n `security policy${dbError.table ? ` on \"${dbError.table}\"` : \"\"} denying this role, ` +\n \"or a stale FORCE ROW LEVEL SECURITY flag binding the owner connection.\"\n );\n }\n // 25006 read_only_sql_transaction. A request-scoped read opens its\n // transaction `READ ONLY`, so the only way to reach this is user code on\n // a read path attempting a write — which means an `afterRead` callback,\n // or something it called. Left in the generic branch it was a 500\n // \"Internal Server Error\", indistinguishable from the database being\n // down; it is the caller's own code, and it is not a server failure.\n if (dbError.code === \"25006\") {\n code = \"READ_ONLY_TRANSACTION\";\n statusCode = 409;\n parts.push(READ_ONLY_TRANSACTION_MESSAGE);\n }\n // SQLSTATE class 22 — data exception. The caller sent a value the\n // column's type cannot hold: `?id=eq.abc` on an integer key,\n // `?status=eq.nope` on an enum, a timestamp that is not one, a number\n // past the type's range. Every *other* bad query parameter already has\n // a precise 400 (`INVALID_LIMIT`, `UNKNOWN_FILTER_FIELD`,\n // `INVALID_LOGICAL_GROUP`); a bad *value* fell off the end of this\n // chain and answered 500 INTERNAL_ERROR — and in production `dbMessage`\n // is stripped, so the caller got a bare 500 naming nothing and had no\n // way to learn their own typo was the cause.\n if (dbError.code?.startsWith(\"22\")) {\n code = \"INVALID_FILTER_VALUE\";\n statusCode = 400;\n }\n logMessage = parts.join(\". \");\n }\n\n const isDbSchemaMismatch = code === \"SCHEMA_DRIFT\";\n\n // `logMessage`, not the sanitized client message: the request line is a\n // server log, and the whole point of this branch is the diagnosis it built.\n handOffToRequestLog(c, code, logMessage);\n\n if (isDbSchemaMismatch) {\n // Database schema mismatch is logged as a warning instead of a fatal error\n if (!requestWillBeLogged(c)) logger.warn(\n `⚠️ [API] ${c.req.method} ${c.req.path} → ${statusCode} ${code}: ${logMessage}` +\n (reqId ? ` [${reqId}]` : \"\")\n );\n // In dev mode, show a one-time hint to run `rebase doctor`\n if (!_schemaDriftHinted && hostEnv().NODE_ENV !== \"production\") {\n _schemaDriftHinted = true;\n // Drawn rather than hand-aligned: the remedy inside it now varies\n // with the database, and a box whose rows were padded by hand\n // stayed straight only for the text it was written around.\n const WIDTH = 62;\n const row = (text: string) => `│${text.padEnd(WIDTH).slice(0, WIDTH)}│`;\n logger.warn([\n \"\",\n `┌${\"─\".repeat(WIDTH)}┐`,\n // One space short, deliberately: the emoji occupies two columns\n // in a terminal and one in `String.length`.\n `│${\" 💡 TIP: Run `rebase doctor` for full schema diagnostics\".padEnd(WIDTH - 1)}│`,\n row(\"\"),\n ...schemaDriftRemedy().lines.map(row),\n `└${\"─\".repeat(WIDTH)}┘`,\n \"\"\n ].join(\"\\n\"));\n }\n } else if (poolFailure || unreachable) {\n // Said once a minute by `warnPoolFailure` / `warnDatabaseUnreachable`;\n // the request line carries each occurrence.\n if (!requestWillBeLogged(c)) logger.warn(\n `⚠️ [API] ${c.req.method} ${c.req.path} → ${statusCode} ${code}: ${logMessage}` +\n (reqId ? ` [${reqId}]` : \"\")\n );\n } else if (code === \"READ_ONLY_TRANSACTION\" || code === \"INVALID_FILTER_VALUE\") {\n // A 4xx: the application's own callback, refused — or a filter value\n // the caller's own request could not have worked with. Not a server\n // fault, so not an ❌ in the log either — and, like the drift arm above,\n // not a second line when the request log is already going to carry it.\n if (!requestWillBeLogged(c)) logger.warn(\n `⚠️ [API] ${c.req.method} ${c.req.path} → ${statusCode} ${code}: ${logMessage}` +\n (reqId ? ` [${reqId}]` : \"\")\n );\n } else if (!requestWillBeLogged(c)) {\n // Unexpected errors — log at error level\n logger.error(\n `❌ [API] ${c.req.method} ${c.req.path} → ${statusCode} ${code}: ${logMessage}` +\n (reqId ? ` [${reqId}]` : \"\")\n );\n }\n\n // Suppress the huge stack trace for known DB errors: it is noisy, and the\n // extracted [PG …] line above carries the signal. The SQL and the bound\n // params it used to leak are no longer this branch's problem — `logger`\n // strips Drizzle's `Failed query: … / params: …` wrapper out of every\n // message and stack it emits, so the fallbacks below (a connection dropped\n // mid-statement carries no SQLSTATE, so `dbError` is null and the stack is\n // logged) are covered too.\n const suppressStack = isDbSchemaMismatch || dbError !== null || poolFailure !== undefined || unreachable ||\n (statusCode < 500 && code === \"BAD_REQUEST\");\n if (!suppressStack) {\n // The error goes in as a value, not as `String(error.stack)`. A string\n // is a leaf to the logger: `serialiseError` — the `.cause`/\n // `AggregateError` walker the boot path relies on — never runs on one,\n // so the request path used to print the outer wrapper's stack and drop\n // the sentence that says what actually failed (`connect ECONNRESET`,\n // sitting two `.cause` links down). Structured, it walks the chain and\n // redacts each link on the way.\n logger.error(\"unhandled request error\", { error });\n }\n\n // Sanitize the message for the client to prevent leaking sensitive details\n // like SQL queries or internal IP addresses.\n let clientMessage = \"An unexpected error occurred\";\n if (code === \"READ_ONLY_TRANSACTION\") {\n // Ahead of the generic 4xx arm below, which would echo the raw driver\n // message (\"Failed query: insert into …\") back to the caller.\n clientMessage = READ_ONLY_TRANSACTION_MESSAGE;\n } else if (code === \"INVALID_FILTER_VALUE\") {\n // Also ahead of the 4xx arm: `error.message` here is the driver's\n // \"Failed query: select … / params: …\", which is both unhelpful and the\n // one thing this envelope must never carry.\n clientMessage = describeDataException(dbError || (error as PgLikeError));\n } else if (statusCode < 500 && error.message) {\n // If it's a 4xx error (e.g. from validation), it's generally safe to send the message\n clientMessage = error.message;\n } else if (error instanceof ApiError || error.name === \"ApiError\") {\n // We already handled ApiError above, but just in case\n clientMessage = error.message;\n } else if (code === \"SCHEMA_DRIFT\") {\n const pgErr = dbError || (error as PgLikeError);\n const issue = pgErr.code === \"42703\" ? \"column\" : \"table\";\n const identifier = pgErr.table || pgErr.column || extractMissingIdentifier(pgErr.message || error.message) || \"unknown\";\n clientMessage = `Schema drift: ${issue} \"${identifier}\" does not exist. ${schemaDriftRemedy().short}`;\n } else if (poolFailure) {\n clientMessage = \"The database is busy: no connection was free in time. Retry shortly.\";\n } else if (unreachable) {\n clientMessage = DATABASE_UNAVAILABLE_MESSAGE;\n } else if (upstream) {\n clientMessage = UPSTREAM_UNAVAILABLE_MESSAGE;\n } else if (code === \"DB_PERMISSION_DENIED\") {\n const ungranted = missingGrant(dbError?.message);\n clientMessage = ungranted\n ? `Permission denied by the database: the role this request runs as has no privilege on ${ungranted.kind} ` +\n `\"${ungranted.name}\". That is a missing GRANT to that role, not a row-level security policy.`\n : `Permission denied by the database${dbError?.table ? ` on \"${dbError.table}\"` : \"\"} (row-level security). Check the RLS policies for this table.`;\n } else if (code === \"INTERNAL_ERROR\") {\n clientMessage = \"Internal Server Error\";\n }\n\n // Database diagnostics for the envelope: the SQLSTATE is always safe to\n // return; message/detail/hint can reference schema internals, so only\n // outside production.\n const dbDetails = dbError ? {\n dbCode: dbError.code,\n ...(hostEnv().NODE_ENV !== \"production\" && {\n dbMessage: dbError.message,\n ...(dbError.detail && { detail: dbError.detail }),\n ...(dbError.hint && { hint: dbError.hint })\n })\n } : undefined;\n\n const drift = driftFor(statusCode);\n\n return c.json({\n error: {\n message: clientMessage,\n code,\n ...(error.details !== undefined\n ? { details: error.details }\n : dbDetails !== undefined ? { details: dbDetails } : {}),\n ...(reqId && { requestId: reqId }),\n ...(drift && { cause: drift })\n }\n } satisfies ErrorResponse, statusCode as ContentfulStatusCode);\n};\n\n/**\n * The object a `42501` names when no policy was involved.\n *\n * One SQLSTATE covers two different failures. A policy refusing a row says\n * \"… violates row-level security policy …\"; a role that was never granted an\n * object says \"permission denied for schema rebase\" (or table, function, …).\n * Answering the second with \"check the RLS policies\" sent people looking at\n * policies that were fine, for a grant that was missing.\n */\nfunction missingGrant(message: string | undefined): { kind: string; name: string } | undefined {\n const match = message?.match(/^permission denied for (schema|table|relation|view|materialized view|sequence|function|procedure|routine|type|column|database|foreign table|large object|tablespace|language) (.+)$/i);\n return match ? { kind: match[1].toLowerCase(), name: match[2].replace(/^\"|\"$/g, \"\") } : undefined;\n}\n\n/**\n * Map known error codes to HTTP status codes.\n */\nfunction codeToStatus(code?: string): number | undefined {\n if (!code) return undefined;\n const map: Record<string, number> = {\n BAD_REQUEST: 400,\n INVALID_INPUT: 400,\n WEAK_PASSWORD: 400,\n UNAUTHORIZED: 401,\n INVALID_CREDENTIALS: 401,\n INVALID_TOKEN: 401,\n FORBIDDEN: 403,\n NOT_FOUND: 404,\n CONFLICT: 409,\n EMAIL_EXISTS: 409,\n ROLE_EXISTS: 409,\n READ_ONLY_TRANSACTION: 409,\n INVALID_FILTER_VALUE: 400,\n SCHEMA_DRIFT: 500,\n DB_PERMISSION_DENIED: 500,\n INTERNAL_ERROR: 500,\n NOT_CONFIGURED: 503,\n SERVICE_UNAVAILABLE: 503\n };\n return map[code];\n}\n\n\n"],"mappings":"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"}