@rebasepro/server-postgres 0.22.0 → 0.23.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 (81) hide show
  1. package/dist/{BranchService-CucnFcSE.js → BranchService-DGPL6G_C.js} +2 -2
  2. package/dist/{BranchService-CucnFcSE.js.map → BranchService-DGPL6G_C.js.map} +1 -1
  3. package/dist/PostgresBackendDriver.d.ts +41 -1
  4. package/dist/auth/services.d.ts +27 -8
  5. package/dist/backup/backup-cli.d.ts +22 -0
  6. package/dist/{backup-cli-DqBakiMO.js → backup-cli-24v4OlSp.js} +17 -14
  7. package/dist/backup-cli-24v4OlSp.js.map +1 -0
  8. package/dist/{backup-service-CaMOS76G.js → backup-service-HQ9GC4tN.js} +2 -2
  9. package/dist/{backup-service-CaMOS76G.js.map → backup-service-HQ9GC4tN.js.map} +1 -1
  10. package/dist/{cli-errors-Dka89exj.js → cli-errors-B8qHg02P.js} +6 -1
  11. package/dist/cli-errors-B8qHg02P.js.map +1 -0
  12. package/dist/cli-flags-BglvpjHv.js +138 -0
  13. package/dist/cli-flags-BglvpjHv.js.map +1 -0
  14. package/dist/cli-flags.d.ts +28 -0
  15. package/dist/cli-helpers.d.ts +6 -0
  16. package/dist/cli-scratch-database.d.ts +34 -0
  17. package/dist/cli.js +97 -261
  18. package/dist/cli.js.map +1 -1
  19. package/dist/{column-plan-helpers-CpILzHJS.js → column-plan-helpers-DF-8dTVa.js} +16 -20
  20. package/dist/column-plan-helpers-DF-8dTVa.js.map +1 -0
  21. package/dist/{doctor-CU9IogdL.js → doctor-Cb2thZ8s.js} +8 -7
  22. package/dist/doctor-Cb2thZ8s.js.map +1 -0
  23. package/dist/{ensure-collection-policies-Dzd-2S81.js → ensure-collection-policies-RHUcEp4v.js} +5 -5
  24. package/dist/{ensure-collection-policies-Dzd-2S81.js.map → ensure-collection-policies-RHUcEp4v.js.map} +1 -1
  25. package/dist/{ensure-collection-tables-CpAgy51F.js → ensure-collection-tables-BEEjn5cn.js} +53 -22
  26. package/dist/ensure-collection-tables-BEEjn5cn.js.map +1 -0
  27. package/dist/{ensure-tables-Cr5B4UmH.js → ensure-tables-Dhn9KM3B.js} +2 -2
  28. package/dist/{ensure-tables-Cr5B4UmH.js.map → ensure-tables-Dhn9KM3B.js.map} +1 -1
  29. package/dist/{generate-drizzle-schema-logic-BcMl7VSy.js → generate-drizzle-schema-logic-BfzK7UQd.js} +2 -2
  30. package/dist/{generate-drizzle-schema-logic-BcMl7VSy.js.map → generate-drizzle-schema-logic-BfzK7UQd.js.map} +1 -1
  31. package/dist/{generate-drizzle-schema-B537GIvz.js → generate-drizzle-schema-yzY_BLhr.js} +2 -2
  32. package/dist/{generate-drizzle-schema-B537GIvz.js.map → generate-drizzle-schema-yzY_BLhr.js.map} +1 -1
  33. package/dist/{generate-postgres-ddl-logic-D7imhYV8.js → generate-postgres-ddl-logic-Bt2d2mRH.js} +2 -2
  34. package/dist/{generate-postgres-ddl-logic-D7imhYV8.js.map → generate-postgres-ddl-logic-Bt2d2mRH.js.map} +1 -1
  35. package/dist/index.es.js +1508 -355
  36. package/dist/index.es.js.map +1 -1
  37. package/dist/{introspect-db-logic-C6LQdTxj.js → introspect-db-logic-WMuAfxvw.js} +323 -4
  38. package/dist/introspect-db-logic-WMuAfxvw.js.map +1 -0
  39. package/dist/{plan-schema-CboAIwLN.js → plan-schema-C0fxM8dY.js} +158 -31
  40. package/dist/plan-schema-C0fxM8dY.js.map +1 -0
  41. package/dist/{policy-drift-xJfy9xG7.js → policy-drift-DljYdrpW.js} +2 -2
  42. package/dist/{policy-drift-xJfy9xG7.js.map → policy-drift-DljYdrpW.js.map} +1 -1
  43. package/dist/{rls-bootstrap-sql-H3rCFi3F.js → rls-bootstrap-sql-B8EclDyM.js} +320 -17
  44. package/dist/rls-bootstrap-sql-B8EclDyM.js.map +1 -0
  45. package/dist/schema/atlas-argv.d.ts +15 -0
  46. package/dist/schema/column-plan-helpers.d.ts +23 -4
  47. package/dist/schema/destructive-sql.d.ts +52 -1
  48. package/dist/schema/doctor-cli.js +3 -3
  49. package/dist/schema/generate-drizzle-schema.js +1 -1
  50. package/dist/schema/generate-postgres-ddl.js +1 -1
  51. package/dist/schema/introspect-db.js +1 -312
  52. package/dist/schema/introspect-db.js.map +1 -1
  53. package/dist/schema/plan/diff-plan.d.ts +3 -2
  54. package/dist/schema/plan/plan-schema.d.ts +10 -3
  55. package/dist/schema/plan/types.d.ts +6 -1
  56. package/dist/services/FetchService.d.ts +97 -28
  57. package/dist/services/PersistService.d.ts +41 -5
  58. package/dist/services/RelationService.d.ts +11 -0
  59. package/dist/services/RelationWriteService.d.ts +6 -0
  60. package/dist/services/cdc/CdcListener.d.ts +6 -1
  61. package/dist/services/collection-helpers.d.ts +11 -0
  62. package/dist/services/dataService.d.ts +3 -20
  63. package/dist/services/field-op-sql.d.ts +71 -0
  64. package/dist/services/junction-writes.d.ts +19 -3
  65. package/dist/services/pg-notify-listener.d.ts +7 -0
  66. package/dist/services/read-field-access.d.ts +19 -0
  67. package/dist/services/realtimeService.d.ts +77 -3
  68. package/dist/services/row-pipeline.d.ts +5 -0
  69. package/dist/services/soft-delete.d.ts +0 -2
  70. package/dist/services/write-transaction-scope.d.ts +39 -0
  71. package/dist/utils/drizzle-conditions.d.ts +7 -2
  72. package/dist/websocket.d.ts +10 -3
  73. package/package.json +9 -9
  74. package/dist/backup-cli-DqBakiMO.js.map +0 -1
  75. package/dist/cli-errors-Dka89exj.js.map +0 -1
  76. package/dist/column-plan-helpers-CpILzHJS.js.map +0 -1
  77. package/dist/doctor-CU9IogdL.js.map +0 -1
  78. package/dist/ensure-collection-tables-CpAgy51F.js.map +0 -1
  79. package/dist/introspect-db-logic-C6LQdTxj.js.map +0 -1
  80. package/dist/plan-schema-CboAIwLN.js.map +0 -1
  81. package/dist/rls-bootstrap-sql-H3rCFi3F.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"cli-errors-Dka89exj.js","names":[],"sources":["../src/utils/pg-error-utils.ts","../src/cli-errors.ts"],"sourcesContent":["/**\n * Shared PostgreSQL error extraction and user-friendly message formatting.\n *\n * Drizzle wraps native PG errors in a `.cause` chain. These utilities\n * unwrap that chain to get the real PostgreSQL error (identified by a\n * 5-character alphanumeric `code` such as `42P01`) and translate it into\n * a message that is safe and helpful to show to end-users.\n */\n\nimport { declaredErrorAnswer, logger } from \"@rebasepro/server\";\nimport type { CollectionConfig } from \"@rebasepro/types\";\nimport { fieldKeyForColumn, getTableName } from \"@rebasepro/common\";\n\n/**\n * Shape of a deliberate client-facing error — `ApiError` from\n * `@rebasepro/server`, `RebaseApiError` from `@rebasepro/types`, or anything\n * else carrying a 4xx `statusCode`.\n *\n * Matched structurally rather than with `instanceof`: `@rebasepro/server` can\n * be loaded twice (published dist vs. workspace source), which breaks class\n * identity — `PersistService` hedges against the same thing by also accepting\n * `name === \"ApiError\"`.\n */\ninterface ClientFacingError extends Error {\n statusCode?: number;\n code?: string;\n /** See `ApiError.expected` — routine outcomes log at debug, not warn. */\n expected?: boolean;\n}\n\n/**\n * Return the error when it is a deliberate 4xx, otherwise null.\n *\n * A thrown `ApiError` is a decision the server made about the request, not a\n * database failure: its message and code are already written for the client.\n *\n * So is a `RebaseApiError` — what a collection callback throws, since a\n * collection file cannot import the server package. It spells its status\n * `status`, and reading only `statusCode` here turned an `afterRead` that\n * refused with a 403 into \"Could not load data … Check server logs\" on a\n * subscription, where REST and the socket's own requests answer the 403.\n * Recognised by the predicate those doors use, `declaredErrorAnswer`.\n */\nfunction asClientFacingError(error: unknown): Pick<ClientFacingError, \"statusCode\" | \"code\" | \"message\" | \"expected\"> | null {\n const answer = declaredErrorAnswer(error);\n if (answer) {\n if (answer.status < 400 || answer.status >= 500) return null;\n return { statusCode: answer.status, code: answer.code, message: answer.message, expected: answer.expected };\n }\n if (!(error instanceof Error)) return null;\n const e = error as ClientFacingError;\n if (typeof e.statusCode !== \"number\" || e.statusCode < 400 || e.statusCode >= 500) return null;\n return e;\n}\n\n/** Shape of PostgreSQL errors with diagnostic metadata. */\nexport interface PostgresError extends Error {\n code?: string;\n detail?: string;\n hint?: string;\n constraint?: string;\n column?: string;\n table?: string;\n dataType?: string;\n cause?: unknown;\n}\n\n/**\n * Extract the underlying PostgreSQL error from a Drizzle wrapper.\n * Drizzle wraps PG errors in a `cause` property — this function\n * recursively walks the chain until it finds an object with a PG\n * error code (5-char alphanumeric, e.g. `42P01`).\n */\nexport function extractPgError(error: unknown): PostgresError | null {\n if (!error || typeof error !== \"object\") return null;\n if (!(error instanceof Error)) {\n // Check non-Error objects for a cause chain (Drizzle sometimes wraps oddly)\n if (\"cause\" in error && (error as Record<string, unknown>).cause && typeof (error as Record<string, unknown>).cause === \"object\") {\n return extractPgError((error as Record<string, unknown>).cause);\n }\n return null;\n }\n\n // Check if the error itself has a PG error code\n if (\"code\" in error && typeof (error as PostgresError).code === \"string\" && /^[0-9A-Z]{5}$/.test((error as PostgresError).code!)) {\n return error as PostgresError;\n }\n\n // Check the cause chain (Drizzle wraps PG errors)\n if (error.cause && typeof error.cause === \"object\") {\n return extractPgError(error.cause);\n }\n\n return null;\n}\n\n/**\n * Whether the failure came back from Postgres rather than from building the\n * query — which decides whether a fallback query is worth issuing.\n *\n * Reads here run inside a transaction (that is where `SET LOCAL ROLE` binds\n * RLS). Once a statement raises, that transaction is aborted, and every later\n * statement on it returns `25P02` — \"current transaction is aborted, commands\n * ignored until end of transaction block\". So a retry after a database error\n * cannot succeed, and it replaces a precise diagnosis (\"invalid input syntax\n * for type uuid\") with a generic one. Rethrow instead.\n *\n * A query the driver could not even build — a missing reciprocal relation, say\n * — never reached Postgres, leaves the transaction usable, and is exactly what\n * the fallback paths exist for.\n */\nexport function reachedDatabase(error: unknown): boolean {\n return extractPgError(error) !== null;\n}\n\n/**\n * Walk the error cause chain and return the deepest meaningful message.\n */\nexport function extractCauseMessage(error: unknown): string | null {\n if (!error || typeof error !== \"object\") return null;\n if (!(error instanceof Error)) return null;\n\n if (error.cause && typeof error.cause === \"object\") {\n const deeper = extractCauseMessage(error.cause);\n if (deeper) return deeper;\n // The cause itself has a message\n if (error.cause instanceof Error && error.cause.message) {\n return error.cause.message;\n }\n }\n return null;\n}\n\n/**\n * Codes that mean \"this connection will never work as configured\".\n *\n * A wrong password or a database that does not exist is a settled fact about\n * the connection string, not a transient fault — retrying produces the same\n * answer forever.\n */\nconst UNRECOVERABLE_CONNECT_CODES = new Set([\n \"28P01\", // invalid_password\n \"28000\", // invalid_authorization_specification\n \"3D000\", // invalid_catalog_name — the database does not exist\n \"42501\" // insufficient_privilege\n]);\n\nexport interface ConnectFailure {\n /** True when retrying cannot help: the connection string itself is wrong. */\n fatal: boolean;\n /** The deepest message available — the Postgres one where there is one. */\n reason: string;\n /** The `SQLSTATE`, when the failure came from Postgres rather than the socket. */\n code?: string;\n}\n\n/**\n * Describe a failed connection attempt in terms a developer can act on.\n *\n * The error a caller catches is Drizzle's wrapper: its message is\n * `Failed query: SELECT 1` and its stack runs through drizzle internals, while\n * the sentence that says what is actually wrong — \"password authentication\n * failed for user …\", \"database … does not exist\" — sits in `.cause`. Logging\n * the wrapper, as the bootstrapper used to, tells a developer with a typo in\n * their `DATABASE_URL` nothing at all.\n */\nexport function classifyConnectFailure(error: unknown): ConnectFailure {\n const pgError = extractPgError(error);\n const reason =\n pgError?.message ??\n extractCauseMessage(error) ??\n (error instanceof Error ? error.message : String(error));\n return {\n fatal: Boolean(pgError?.code && UNRECOVERABLE_CONNECT_CODES.has(pgError.code)),\n reason,\n code: pgError?.code\n };\n}\n\n/**\n * Detect whether an error is specifically a role-switching permission failure\n * (e.g. \"permission denied to set role\" or \"must be member of role\"),\n * as opposed to a table-level permission denial.\n *\n * This is used by the backend driver to auto-disable role switching when the\n * connection user lacks SET ROLE privileges, rather than surfacing a confusing\n * error to the Studio SQL Editor user.\n */\nexport function isRoleSwitchingPermissionError(error: unknown): boolean {\n const pgError = extractPgError(error);\n if (!pgError || pgError.code !== \"42501\") return false;\n const msg = pgError.message.toLowerCase();\n return msg.includes(\"set role\") || msg.includes(\"member of role\");\n}\n\n/**\n * Was this `42501` the *caller* being refused by a policy, rather than the\n * server lacking a privilege?\n *\n * Both arrive as `insufficient_privilege`, and they are opposite kinds of\n * problem. A row-level-security refusal is a working access-control system\n * doing its job: the caller asked for something their policies do not permit,\n * which is a 403 and nobody's bug. A missing `GRANT` is the deployment being\n * wrong — the connection role cannot touch the table at all, no policy is\n * involved, and nothing the caller changes about the request will help.\n *\n * Postgres distinguishes them in the message, so this does too:\n *\n * new row violates row-level security policy for table \"notes\" → the caller\n * permission denied for table notes → the server\n *\n * Only writes reach this. A read that RLS excludes is not an error — the rows\n * are filtered and the caller gets an empty page — so the erroring case is\n * specifically an `INSERT`/`UPDATE` whose row fails a policy's `WITH CHECK`.\n *\n * Matched on the message because that is the only thing carrying the\n * distinction; the SQLSTATE is identical either way. Narrow by design: anything\n * not naming row-level security stays the server's problem, since reporting a\n * genuine privilege misconfiguration as \"forbidden\" would send an operator\n * hunting for a policy bug that does not exist.\n */\nexport function isRowLevelSecurityDenial(error: unknown): boolean {\n const pgError = extractPgError(error);\n if (!pgError || pgError.code !== \"42501\") return false;\n return pgError.message.toLowerCase().includes(\"row-level security policy\");\n}\n\n/**\n * One constraint the database refused, named in the caller's vocabulary.\n *\n * `field` is the **wire** name, derived from the physical column: `author_id`\n * on the table is `authorId` on the row and in the request body, and telling a\n * caller their `author_id` is missing sends them looking for a key their client\n * has never seen. `code` is the SQLSTATE, which is stable, documented by\n * Postgres and the same in every environment — unlike the message, which is\n * deliberately thinner in production.\n *\n * Deliberately no `value`. The column name is safe to publish: it is already in\n * the OpenAPI, the generated SDK and the caller's own request. The *value* is\n * not — `23505`'s detail reads `Key (email)=(a@b.c) already exists.`, which\n * answers \"is this person registered?\" for any address on a table whose rows\n * RLS is otherwise hiding completely.\n */\nexport interface PgFieldViolation {\n field: string;\n code: string;\n}\n\n/**\n * The physical column names a Postgres error is about.\n *\n * Postgres puts one in `column` for `23502` and nowhere else, so the rest are\n * read out of the two places that do carry them:\n *\n * - `detail` — `Key (author_id)=(9) is not present in table \"authors\".` and\n * `Key (email, tenant_id)=(…) already exists.` Only the parenthesised column\n * list is read; the values after `=` are never touched.\n * - `constraint` — `users_email_key`, `posts_author_id_fkey`. The table prefix\n * and the constraint suffix are stripped, leaving the columns joined by `_`,\n * which is ambiguous for multi-column constraints and is therefore only used\n * when `detail` gave nothing.\n *\n * `22P02` carries neither, because an invalid literal fails during parse rather\n * than against a constraint. Its message names the *type*, and an enum type is\n * generated as `<table>_<column>` — which is the one case worth decoding,\n * because a value outside an enum's labels is the most common 22P02 anyone\n * hits and the least legible one.\n */\nfunction offendingColumns(pgError: PostgresError, collection?: CollectionConfig): string[] {\n if (pgError.column) return [pgError.column];\n\n const detail = pgError.detail;\n if (detail) {\n const keyed = /Key \\(([^)]+)\\)=/.exec(detail);\n if (keyed) {\n return keyed[1].split(\",\").map(part => part.trim().replace(/^\"|\"$/g, \"\")).filter(Boolean);\n }\n }\n\n if (pgError.code === \"22P02\") {\n // `invalid input value for enum posts_status: \"archived\"`\n const enumType = /invalid input value for enum ([^:\\s]+)/i.exec(pgError.message ?? \"\");\n if (enumType && collection) {\n const typeName = enumType[1].replace(/^.*\\./, \"\").replace(/\"/g, \"\");\n const table = getTableName(collection);\n if (typeName.startsWith(`${table}_`)) return [typeName.slice(table.length + 1)];\n }\n return [];\n }\n\n const constraint = pgError.constraint;\n if (constraint && collection) {\n const table = getTableName(collection);\n const stripped = constraint\n .replace(new RegExp(`^${table}_`), \"\")\n .replace(/_(pkey|key|fkey|unique|check|excl|idx)$/, \"\");\n // Only when it names something the collection actually has: a\n // hand-written constraint name is not a column list, and guessing at one\n // would put a field in the response that does not exist.\n if (stripped && stripped !== constraint) {\n const columns = new Set<string>();\n for (const [key, prop] of Object.entries(collection.properties ?? {})) {\n columns.add((prop as { columnName?: string })?.columnName ?? key);\n }\n if (columns.has(stripped)) return [stripped];\n const wire = fieldKeyForColumn(collection, stripped);\n if (collection.properties && wire in collection.properties) return [stripped];\n }\n }\n\n return [];\n}\n\n/**\n * The fields a Postgres error is about, for `details.violations`.\n *\n * Only the four SQLSTATEs that describe *the caller's data*: a missing value, a\n * duplicate, a bad literal, a foreign key pointing at nothing. Everything else\n * — a dropped connection, a missing table, a privilege problem — is the\n * deployment's, and naming a field for it would send the reader to the wrong\n * place.\n *\n * Empty when the collection is unknown or the column cannot be recovered. An\n * empty list is the honest answer; inventing a field is worse than saying\n * nothing, because a form will mark the wrong input red.\n */\nexport function pgFieldViolations(\n pgError: PostgresError,\n collection?: CollectionConfig\n): PgFieldViolation[] {\n const code = pgError.code;\n if (code !== \"23502\" && code !== \"23505\" && code !== \"22P02\" && code !== \"23503\") return [];\n // Without the collection there is no mapping, only a guess. `toWireKey`\n // would get the default case right and silently invent a field for any\n // property carrying an explicit `columnName` — and a form pointed at an\n // input that does not exist is worse off than one shown the raw column.\n if (!collection) return [];\n const columns = offendingColumns(pgError, collection);\n if (columns.length === 0) return [];\n return columns.map(column => ({\n field: fieldKeyForColumn(collection, column),\n code\n }));\n}\n\n/**\n * Translate a raw PostgreSQL error into a user-friendly message.\n *\n * @param pgError - The extracted PostgreSQL error (from {@link extractPgError})\n * @param context - A human-readable context string (e.g. collection slug or path)\n * @returns An object with a `message` safe for the client and the PG `code`.\n */\nexport function pgErrorToFriendlyMessage(\n pgError: PostgresError,\n context: string,\n options: { verbose?: boolean; collection?: CollectionConfig } = {}\n): { message: string; code: string; violations: PgFieldViolation[] } {\n // Postgres's own `DETAIL` and `HINT`, and the raw driver message, are what\n // make these errors useful while you are building — and what makes them an\n // oracle once the server is answering strangers.\n //\n // The sharp one is `23505`: its detail reads `Key (email)=(a@b.c) already\n // exists.`, which answers \"is this person registered?\" for any address, on\n // a table whose rows RLS is otherwise hiding completely. The rest leak\n // physical column and constraint names, which are the map for the next\n // question. So in production the shape of the failure is served — what\n // rule was broken, and where — and the values are not.\n //\n // The full text is not lost: the caller logs the original error. This\n // decides what crosses the wire, not what is recorded.\n const verbose = options.verbose ?? process.env.NODE_ENV !== \"production\";\n\n const detail = verbose ? (pgError.detail as string | undefined) : undefined;\n const hint = verbose ? (pgError.hint as string | undefined) : undefined;\n const constraint = pgError.constraint as string | undefined;\n const column = pgError.column as string | undefined;\n const table = pgError.table as string | undefined;\n const dataType = verbose ? (pgError.dataType as string | undefined) : undefined;\n const rawMessage = pgError.message || \"Unknown database error\";\n const pgMessage = verbose ? rawMessage : \"\";\n const code = pgError.code || \"UNKNOWN\";\n\n // Which *field* the caller has to fix, derived from the column. Safe in\n // production — a column name is already published in the OpenAPI and the\n // generated SDK — where the value that broke the rule is not, and is never\n // included. See `pgFieldViolations`.\n const violations = pgFieldViolations(pgError, options.collection);\n\n const suffix = hint ? ` Hint: ${hint}` : \"\";\n const tableRef = table ?? context;\n\n /** `\": <postgres said>\"`, or nothing when the details are withheld. */\n const said = pgMessage ? `: ${pgMessage}` : \"\";\n\n switch (pgError.code) {\n case \"23503\": // foreign_key_violation\n return {\n message: detail\n ? `Foreign key constraint violated: ${detail}${suffix}`\n : `Cannot complete operation: a foreign key constraint${constraint ? ` (${constraint})` : \"\"} was violated in \"${context}\".${suffix}`,\n code,\n violations\n };\n case \"23505\": // unique_violation\n return {\n message: detail\n ? `Duplicate value: ${detail}${suffix}`\n : `Cannot complete operation: a unique constraint${constraint ? ` (${constraint})` : \"\"} was violated in \"${context}\".${suffix}`,\n code,\n violations\n };\n case \"23502\": {\n // not_null_violation. The message names the *field* when the\n // collection is known, because that is the key the caller sent and\n // the only one they can act on: told `author_id` is missing, they go\n // looking for a key their generated client has never mentioned.\n const named = violations[0]?.field ?? column ?? \"unknown\";\n return {\n message: `Missing required field: \"${named}\" in \"${tableRef}\" cannot be empty.${suffix}`,\n code,\n violations\n };\n }\n case \"23514\": // check_violation\n return {\n message: `Validation failed: a check constraint${constraint ? ` (${constraint})` : \"\"} was violated in \"${context}\".${suffix}`,\n code,\n violations\n };\n case \"22P02\": // invalid_text_representation (e.g. invalid UUID, wrong enum value)\n return {\n message: `Invalid data format in \"${context}\"${said}.${suffix}`,\n code,\n violations\n };\n case \"22001\": // string_data_right_truncation (value too long)\n return {\n message: `Value too long for column \"${column ?? \"unknown\"}\" in \"${tableRef}\"${said}.${suffix}`,\n code,\n violations\n };\n case \"22003\": // numeric_value_out_of_range\n return {\n message: `Numeric value out of range for column \"${column ?? \"unknown\"}\" in \"${tableRef}\"${said}.${suffix}`,\n code,\n violations\n };\n case \"42703\": // undefined_column\n return {\n message: `Unknown column in \"${tableRef}\"${said}. Check if your schema is up to date (run migrations).${suffix}`,\n code,\n violations\n };\n case \"42P01\": // undefined_table\n return {\n message: `Table not found for \"${context}\"${said}. Check if your schema is up to date (run migrations).${suffix}`,\n code,\n violations\n };\n case \"42501\": // insufficient_privilege\n // Two unrelated failures share this SQLSTATE, and the old message\n // named both causes because it could not tell them apart — which\n // meant it was half wrong whichever one had happened, and sent the\n // reader to check the other. Postgres says which in its own message.\n return rawMessage.toLowerCase().includes(\"row-level security policy\")\n ? {\n // The caller. Their policies do not permit this row; the\n // deployment is working exactly as configured.\n message: `Not permitted to write this row in \"${tableRef}\": it does not satisfy the row-level security policy.${suffix}`,\n code,\n violations\n }\n : {\n // The deployment. No policy is involved — the connecting\n // role cannot touch the table at all.\n message: `Permission denied on \"${tableRef}\": the database role this server connects as lacks privileges on it.${suffix}`,\n code,\n violations\n };\n case \"28000\": // invalid_authorization_specification\n return {\n message: `Authorization failed for \"${context}\". Check your database credentials.${suffix}`,\n code,\n violations\n };\n default: {\n // Unhandled PG code — still surface the actual database message\n const parts = [`Database error in \"${context}\" [${code}]${said}`];\n if (detail) parts.push(`Detail: ${detail}`);\n if (column) parts.push(`Column: ${column}`);\n if (dataType) parts.push(`Data type: ${dataType}`);\n if (constraint) parts.push(`Constraint: ${constraint}`);\n if (hint) parts.push(`Hint: ${hint}`);\n return { message: parts.join(\". \"), code, violations };\n }\n }\n}\n\n/**\n * Sanitize any error into a message safe and helpful for the client.\n *\n * A deliberate 4xx (`ApiError`) passes through untouched — the server already\n * decided what the client should read. Otherwise the PG error is extracted\n * from the Drizzle cause chain, falling back to a generic message that\n * doesn't leak SQL.\n *\n * @param error - The raw caught error\n * @param context - A human-readable context string (e.g. collection path)\n * @returns An object with `message` (user-friendly) and optional `code`\n * (the `ApiError` code, or the PG SQLSTATE).\n */\nexport function sanitizeErrorForClient(\n error: unknown,\n context: string,\n options: { collection?: CollectionConfig } = {}\n): { message: string; code?: string; violations?: PgFieldViolation[] } {\n // ── A deliberate 4xx is not a database failure ──────────────────\n // Its message and code are written for the client; replacing them with\n // \"Check server logs\" would discard the only diagnosis the caller gets\n // (e.g. the offending filter field and the collection's valid ones).\n // Log level follows the same convention as the HTTP error handler in\n // @rebasepro/server: routine outcomes at debug, everything else at warn.\n const clientError = asClientFacingError(error);\n if (clientError) {\n const line = `[API ${clientError.statusCode} ${clientError.code ?? \"BAD_REQUEST\"}] in \"${context}\": ${clientError.message}`;\n if (clientError.expected) {\n logger.debug(line);\n } else {\n logger.warn(`⚠️ ${line}`);\n }\n return { message: clientError.message, ...(clientError.code && { code: clientError.code }) };\n }\n\n // ── Always log the full, unsanitized error server-side ──────────\n const pgError = extractPgError(error);\n\n if (pgError) {\n logger.error(`[PG ${pgError.code}] Error in \"${context}\"`, {\n code: pgError.code,\n message: pgError.message,\n detail: pgError.detail,\n hint: pgError.hint,\n column: pgError.column,\n table: pgError.table,\n constraint: pgError.constraint,\n dataType: pgError.dataType\n // The outer Drizzle wrapper message used to be logged here \"for\n // full context\": it is `Failed query: <sql>\\nparams: <values>`, so\n // it published the statement and every bound value (an email, a\n // password hash) on every realtime data failure. The SQLSTATE,\n // detail, table, column and constraint above are the diagnostic\n // value; the wrapper added only the leak. `logger` strips the\n // wrapper as well, but the field itself carried nothing else.\n });\n const { message, code, violations } = pgErrorToFriendlyMessage(pgError, context, { collection: options.collection });\n return { message, code, ...(violations.length > 0 && { violations }) };\n }\n\n // No PG error found — log the raw error as-is\n logger.error(`Database error in \"${context}\" (no PG error extracted)`, {\n error: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n cause: error instanceof Error && error.cause\n ? (error.cause instanceof Error ? error.cause.message : String(error.cause))\n : undefined\n });\n\n // Try to get the deepest cause message\n const causeMessage = extractCauseMessage(error);\n if (causeMessage) {\n return { message: `Database error in \"${context}\": ${causeMessage}` };\n }\n\n // Last resort — generic message, never leak raw SQL\n return { message: `Could not load data for \"${context}\". Check server logs for details.` };\n}\n","import chalk from \"chalk\";\nimport { outWarn, outError } from \"./cli-output\";\nimport { extractCauseMessage } from \"./utils/pg-error-utils\";\n\n/**\n * Detect whether an error (or AggregateError wrapping multiple attempts)\n * represents an ECONNREFUSED — i.e. the database is simply not running.\n *\n * Handles:\n * - Direct `{ code: \"ECONNREFUSED\" }` errors from Node `net`\n * - `AggregateError` from dual-stack IPv4+IPv6 connection attempts\n * - Drizzle's `cause`-wrapped pg errors\n */\nexport function isEconnrefused(err: unknown): boolean {\n if (!err || typeof err !== \"object\") return false;\n const e = err as { code?: string; cause?: unknown; errors?: unknown[] };\n if (e.code === \"ECONNREFUSED\") return true;\n // AggregateError from Node net (dual-stack IPv4 + IPv6)\n if (Array.isArray(e.errors)) {\n return e.errors.some(inner =>\n inner && typeof inner === \"object\" && (inner as { code?: string }).code === \"ECONNREFUSED\"\n );\n }\n // Drizzle wraps the pg error in `cause`\n if (e.cause && typeof e.cause === \"object\") {\n return isEconnrefused(e.cause);\n }\n return false;\n}\n\n/**\n * Detect PostgreSQL authentication failures.\n * PG error codes: 28P01 (invalid_password), 28000 (invalid_authorization_specification)\n */\nexport function isAuthFailure(err: unknown): boolean {\n if (!err || typeof err !== \"object\") return false;\n const e = err as { code?: string; cause?: unknown };\n if (e.code === \"28P01\" || e.code === \"28000\") return true;\n if (e.cause && typeof e.cause === \"object\") {\n return isAuthFailure(e.cause);\n }\n // Also check the message for common pg auth failure text\n if (\"message\" in e && typeof (e as { message?: string }).message === \"string\") {\n const msg = (e as { message: string }).message.toLowerCase();\n if (msg.includes(\"password authentication failed\") || msg.includes(\"no pg_hba.conf entry\")) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Detect the \"SSL is not enabled on the server\" failure — the client attempted\n * an SSL handshake against a Postgres server that doesn't support it (common\n * with a plain local dev database). The fix is `?sslmode=disable` on the URL.\n */\nexport function isSslNotEnabled(err: unknown): boolean {\n if (!err || typeof err !== \"object\") return false;\n const e = err as { message?: string; cause?: unknown };\n if (typeof e.message === \"string\" && e.message.toLowerCase().includes(\"ssl is not enabled on the server\")) {\n return true;\n }\n if (e.cause && typeof e.cause === \"object\") {\n return isSslNotEnabled(e.cause);\n }\n return false;\n}\n\n/**\n * Detect PostgreSQL \"cannot drop ... because other objects depend on it\"\n * (error code 2BP01, dependent_objects_still_exist). This is the failure that\n * strands a declarative `db push` half-applied when a collection is removed but\n * an enum type it defined is still referenced by another object.\n */\nexport function isDependencyDropError(err: unknown): boolean {\n if (!err || typeof err !== \"object\") return false;\n const e = err as { code?: string; message?: string; cause?: unknown };\n if (e.code === \"2BP01\") return true;\n if (typeof e.message === \"string\") {\n const msg = e.message.toLowerCase();\n if (msg.includes(\"other objects depend on it\") || msg.includes(\"cannot drop type\")) {\n return true;\n }\n }\n if (e.cause && typeof e.cause === \"object\") {\n return isDependencyDropError(e.cause);\n }\n return false;\n}\n\n/**\n * Parse host:port from a DATABASE_URL for display purposes.\n *\n * Exported because every message about a connection has to name the thing it\n * could not reach, and the boot path needs the same rendering the CLI banners\n * use — including the same refusal to print the URL itself, which carries the\n * password.\n */\nexport function parseHostInfo(databaseUrl: string): string {\n try {\n const parsed = new URL(databaseUrl);\n return `${parsed.hostname}:${parsed.port || 5432}`;\n } catch {\n return \"unknown\";\n }\n}\n\n/**\n * The sentence the operating system actually produced, dug out of the wrappers.\n *\n * `connect ECONNREFUSED 127.0.0.1:5432` is written by `net`, then wrapped by\n * `pg`, then wrapped again by Drizzle as `Failed query: …` — and on a\n * dual-stack host it is not in `.cause` at all but inside the\n * `AggregateError.errors` array of one attempt per resolved address. Printing\n * the banner without it loses the one token every search engine, runbook and\n * colleague recognises.\n */\nexport function deepestErrorMessage(err: unknown): string | null {\n if (!err || typeof err !== \"object\") return null;\n const e = err as { message?: string; code?: string; cause?: unknown; errors?: unknown[] };\n\n if (Array.isArray(e.errors)) {\n for (const inner of e.errors) {\n const deeper = deepestErrorMessage(inner);\n if (deeper) return deeper;\n }\n }\n if (e.cause) {\n const deeper = deepestErrorMessage(e.cause);\n if (deeper) return deeper;\n }\n if (typeof e.message === \"string\" && e.message && !e.message.startsWith(\"Failed query:\")) {\n return e.code ? `${e.message} (${e.code})` : e.message;\n }\n return null;\n}\n\n/** One indented line naming the driver's own reason, or nothing. */\nfunction driverReasonLine(err: unknown): string {\n const reason = err === undefined ? null : deepestErrorMessage(err);\n return reason ? ` The driver said: ${reason}\\n\\n` : \"\";\n}\n\n/**\n * Format a diagnostic banner for ECONNREFUSED errors.\n */\nfunction formatConnectionRefusedBanner(databaseUrl: string, err?: unknown): string {\n const hostInfo = parseHostInfo(databaseUrl);\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ Cannot connect to PostgreSQL at ${hostInfo}\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n driverReasonLine(err) +\n ` The database server is not running or is not accepting\\n` +\n ` connections. Common fixes:\\n` +\n `\\n` +\n ` • docker compose up -d db (the service a Rebase scaffold ships)\\n` +\n ` • brew services start postgresql@18\\n` +\n ` • Verify DATABASE_URL in your .env file\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/**\n * Format a diagnostic banner for authentication failures.\n */\nfunction formatAuthFailureBanner(databaseUrl: string, err?: unknown): string {\n const hostInfo = parseHostInfo(databaseUrl);\n let username = \"unknown\";\n try {\n username = new URL(databaseUrl).username || \"unknown\";\n } catch { /* ignore */ }\n\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ Authentication failed for user \"${username}\" at ${hostInfo}\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n driverReasonLine(err) +\n ` PostgreSQL rejected the credentials. Common fixes:\\n` +\n `\\n` +\n ` • Check the username and password in DATABASE_URL\\n` +\n ` • Verify the user exists: psql -c \"\\\\du\"\\n` +\n ` • Reset the password: ALTER USER ${username} PASSWORD 'new_password';\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/**\n * Format a diagnostic banner for \"SSL is not enabled on the server\".\n */\nfunction formatSslNotEnabledBanner(databaseUrl: string): string {\n const hostInfo = parseHostInfo(databaseUrl);\n const suggestion = databaseUrl.includes(\"?\") ? \"&sslmode=disable\" : \"?sslmode=disable\";\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ SSL is not enabled on the PostgreSQL server at ${hostInfo}\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n ` The client tried to connect over SSL, but the server does not\\n` +\n ` support it. This is normal for a plain local dev database.\\n` +\n `\\n` +\n ` Fix: append ${chalk.bold(\"sslmode=disable\")} to DATABASE_URL, e.g.\\n` +\n `\\n` +\n ` DATABASE_URL=...${suggestion}\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/**\n * Format a diagnostic banner for a dependency-drop failure during `db push`.\n * Explains that the database may be left partially migrated and how to recover.\n */\nfunction formatDependencyDropBanner(): string {\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ Schema push failed: a type/table could not be dropped\\n` +\n ` because other objects still depend on it.\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n ` ${chalk.yellow(\"The database may now be partially migrated.\")} Atlas applies\\n` +\n ` statements individually, so earlier changes in this push may\\n` +\n ` already be committed while later ones failed.\\n` +\n `\\n` +\n ` This commonly happens when a collection is removed but an enum\\n` +\n ` type it defined is still referenced. To recover:\\n` +\n `\\n` +\n ` 1. Inspect the leftover object named in the error above.\\n` +\n ` 2. Drop it with CASCADE, e.g.:\\n` +\n ` psql \"$DATABASE_URL\" -c 'DROP TYPE \"<name>\" CASCADE;'\\n` +\n ` 3. Re-run: ${chalk.bold.green(\"rebase db push\")}\\n` +\n `\\n` +\n ` Prefer a safe, versioned workflow? Use ${chalk.bold(\"rebase db generate\")}\\n` +\n ` + ${chalk.bold(\"rebase db migrate\")} instead of push for destructive changes.\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/**\n * Pre-flight check: verify that the database is reachable before running\n * a heavy subprocess (Atlas, migrations, etc.).\n *\n * Exits with code 1 and a friendly banner on known failure modes.\n * On unknown errors, logs a warning and allows the caller to proceed.\n */\nexport async function checkDatabaseConnectivity(databaseUrl: string): Promise<void> {\n let client: import(\"pg\").Client | undefined;\n try {\n const { Client } = await import(\"pg\");\n client = new Client({\n connectionString: databaseUrl,\n connectionTimeoutMillis: 5000\n });\n await client.connect();\n await client.query(\"SELECT 1\");\n } catch (err: unknown) {\n if (isEconnrefused(err)) {\n outError(formatConnectionRefusedBanner(databaseUrl, err));\n process.exit(1);\n }\n if (isAuthFailure(err)) {\n outError(formatAuthFailureBanner(databaseUrl, err));\n process.exit(1);\n }\n if (isSslNotEnabled(err)) {\n outError(formatSslNotEnabledBanner(databaseUrl));\n process.exit(1);\n }\n // Unknown error — warn but don't block; let the downstream tool surface details\n outWarn(chalk.yellow(` ⚠ Could not verify database connectivity: ${err instanceof Error ? err.message : String(err)}`));\n outWarn(chalk.gray(\" Proceeding anyway — the command may fail if the database is unreachable.\"));\n } finally {\n try {\n await client?.end();\n } catch {\n // ignore cleanup errors\n }\n }\n}\n\n/**\n * Post-hoc error diagnosis for direct database operations (e.g. applyPolicies).\n * Returns a formatted diagnostic string if the error matches a known pattern,\n * or null if unrecognized.\n */\nexport function diagnoseDbError(err: unknown, databaseUrl?: string): string | null {\n if (isEconnrefused(err)) {\n return formatConnectionRefusedBanner(databaseUrl || \"\", err);\n }\n if (isAuthFailure(err)) {\n return formatAuthFailureBanner(databaseUrl || \"\", err);\n }\n if (isSslNotEnabled(err)) {\n return formatSslNotEnabledBanner(databaseUrl || \"\");\n }\n if (isDependencyDropError(err)) {\n return formatDependencyDropBanner();\n }\n return null;\n}\n\n/**\n * Say why the command failed, on the way out.\n *\n * The entry point below used to be `.catch(() => process.exit(1))`, which threw\n * the error away. Every message this file and its services raise — \"Branch\n * \\\"x\\\" already exists.\", \"the source database has active connections\", \"Branch\n * name is too long\" — was written, wrapped in the right PG error code, and then\n * discarded one frame before it reached a terminal. What a developer saw was a\n * header line, no error, and exit 1.\n *\n * Two shapes are deliberately kept quiet:\n *\n * - **A child process that already spoke.** Atlas, `pg_dump` and `psql` run\n * with inherited stdio, so their diagnosis is on the terminal already and\n * execa's wrapper adds only `Command failed with exit code 1: atlas …`.\n * `packages/cli` filters exactly these two phrasings one level up\n * (`runDbCommand`), and this is that filter, for the process that is actually\n * throwing.\n *\n * - **A message that is only a query.** Drizzle reports failures as\n * `Failed query: <sql> params:` and hides the real PostgreSQL error in\n * `cause`, so the wrapper alone tells a reader nothing they can act on. The\n * cause is appended when it says something the message does not.\n *\n * - **An error that has already printed its own diagnosis**, marked\n * `alreadyReported`. `CollectionsPathMissing` is one: it prints the path, what\n * it resolved to and the cwd it resolved against, and then throws so the entry\n * point owns the exit code. Repeating its one-line summary underneath would\n * undo the \"printed once\" this whole path exists for.\n */\nexport function reportCommandFailure(error: unknown): void {\n const message = error instanceof Error ? error.message : String(error ?? \"\");\n\n if ((error as { alreadyReported?: boolean } | null)?.alreadyReported) return;\n if (!message || /Command failed|exited with code/i.test(message)) return;\n\n outError(\"\");\n outError(chalk.red(` ✗ ${message}`));\n\n const cause = extractCauseMessage(error);\n if (cause && !message.includes(cause)) {\n outError(chalk.gray(` ${cause}`));\n }\n outError(\"\");\n}\n\n/* ------------------------------------------------------------------------- *\n * Atlas failures that have a remedy\n *\n * Atlas runs with a teed stderr, so what reaches these is its output as text,\n * not a `pg` error object carrying a `code`. They are deliberately NOT part of\n * `diagnoseDbError`: that one is shared with the boot path, and \"record a\n * baseline with `rebase db migrate`\" is advice for somebody standing at a\n * terminal, not for a container that has just failed to start.\n * ------------------------------------------------------------------------- */\n\n/**\n * `migrate apply` refusing because the database already has the schema.\n *\n * `42710` (duplicate_object) and `42P07` (duplicate_table) are what a migration\n * hits when boot-ensure — or a `db push` — has already provisioned the objects\n * it was going to create. Since boot-ensure provisions *every* production\n * database, this is the normal case rather than the exotic one, and the raw\n * failure (`pq: type \"posts_status\" already exists (42710)`, then `sql/migrate:\n * write revision: … current transaction is aborted`) names no way forward.\n */\nexport function parseAlreadyProvisioned(text: string): { object: string; code: string } | null {\n const withCode = /(?:type|relation|constraint|schema) \"([^\"]+)\" already exists \\((42710|42P07)\\)/.exec(text);\n if (withCode) return { object: withCode[1], code: withCode[2] };\n // Some Atlas builds keep the SQLSTATE on a following line instead.\n const bare = /(?:type|relation|constraint|schema) \"([^\"]+)\" already exists/.exec(text);\n if (bare && /42710|42P07/.test(text)) return { object: bare[1], code: /42P07/.test(text) ? \"42P07\" : \"42710\" };\n return null;\n}\n\n/**\n * What to do about it: tell Atlas the database is already at a version.\n *\n * Atlas's own mechanism (`migrate apply --baseline <version>`), not a ledger of\n * ours — it writes the revision row Atlas reads, so every later `rebase db\n * migrate` is an ordinary one.\n */\nexport function formatBaselineRemedy(version: string | null, object?: string): string {\n const versionToken = version ?? \"<version>\";\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ This database already has the schema this migration creates\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n (object ? ` ${chalk.gray(`\"${object}\" is already there, so the migration cannot create it.`)}\\n\\n` : \"\") +\n ` Nothing is wrong with the migration — the database was provisioned\\n` +\n ` another way. Every Rebase boot ensures the schema, and ${chalk.bold(\"rebase db push\")}\\n` +\n ` applies it directly, so a database that has ever run either one is\\n` +\n ` ahead of a migration history that was never recorded.\\n` +\n `\\n` +\n ` Record where it already is, then migrate normally:\\n` +\n `\\n` +\n ` ${chalk.bold.green(`rebase db migrate --baseline ${versionToken}`)}\\n` +\n ` ${chalk.bold.green(\"rebase db migrate\")}\\n` +\n `\\n` +\n ` The baseline is the version already applied — the numeric prefix of\\n` +\n ` the migration file describing what is in the database now. Migrations\\n` +\n ` after it run; it and everything before it are marked done.\\n` +\n `\\n` +\n ` On a database nothing has ever booted against, none of this is needed.\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/** `ALTER TABLE … SET NOT NULL` on a table that already holds rows (23502). */\nexport function parseNotNullViolation(text: string): { table: string; column: string } | null {\n const match = /column \"([^\"]+)\" of relation \"([^\"]+)\" contains null values/.exec(text);\n return match ? { table: match[2], column: match[1] } : null;\n}\n\n/**\n * The three ways out, because there is no fourth.\n *\n * Boot-ensure handles this case — it adds the column nullable and sets NOT NULL\n * only when the table is empty — so a push that dies here is strictly worse\n * than the boot that would have run instead, and the developer deserves to be\n * told which of the three they want rather than left with `pq: … contains null\n * values (23502)` and no next step.\n */\nexport function formatNotNullViolationBanner(\n violation: { table: string; column: string },\n rowCount: number | null\n): string {\n const { table, column } = violation;\n const rows = rowCount === null\n ? ` \"${table}\" already holds rows, and none of them has a value for \"${column}\".\\n`\n : ` \"${table}\" already holds ${rowCount} row${rowCount === 1 ? \"\" : \"s\"}, and they have no value for \"${column}\".\\n`;\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ Cannot make \"${table}\".\"${column}\" required: existing rows are null\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n rows +\n ` PostgreSQL will not add the NOT NULL until every one of them does.\\n` +\n `\\n` +\n ` Three ways forward:\\n` +\n `\\n` +\n ` 1. Give the property a default, so the push can backfill:\\n` +\n ` ${chalk.gray(`{ type: \"string\", defaultValue: \"…\", validation: { required: true } }`)}\\n` +\n ` 2. Backfill by hand first, then push again:\\n` +\n ` ${chalk.gray(`psql \"$DATABASE_URL\" -c 'UPDATE \"${table}\" SET \"${column}\" = … WHERE \"${column}\" IS NULL;'`)}\\n` +\n ` 3. Leave it optional — take \\`validation.required\\` off the property.\\n` +\n `\\n` +\n ` ${chalk.gray(\"Nothing was applied for this column. `rebase dev` would have added it\")}\\n` +\n ` ${chalk.gray(\"nullable instead: boot sets NOT NULL only when it is safe.\")}\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/** Atlas refusing to remove a label from an enum — a renamed `enum` option id. */\nexport function parseEnumLabelDrop(text: string): { label: string; enumType: string } | null {\n const match = /dropping (?:enum )?value \"([^\"]+)\" from enum \"([^\"]+)\" is not supported/.exec(text);\n return match ? { label: match[1], enumType: match[2] } : null;\n}\n\n/**\n * Why the two paths disagree, and what retiring an option id actually costs.\n *\n * Boot-ensure adds enum labels and never removes one, so `rebase dev` accepts\n * the very edit that stops `db push` dead. PostgreSQL has no `ALTER TYPE …\n * DROP VALUE`: a label goes only by rewriting the type, which is a data\n * migration and not a schema push.\n */\nexport function formatEnumLabelDropBanner(drop: { label: string; enumType: string }): string {\n const { label, enumType } = drop;\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ \"${label}\" cannot be removed from the enum \"${enumType}\"\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n ` PostgreSQL has no ALTER TYPE … DROP VALUE. An option id can be added\\n` +\n ` to an enum but never taken out of one, so renaming an id reads as a\\n` +\n ` removal and a push cannot carry it.\\n` +\n `\\n` +\n ` ${chalk.yellow(\"Nothing was applied.\")} ${chalk.bold(\"rebase dev\")} accepts this same edit — boot adds\\n` +\n ` new labels and never removes one — so the two paths disagree by\\n` +\n ` design, and the old label would simply have stayed behind, unused.\\n` +\n `\\n` +\n ` To keep the id: put \"${label}\" back and change only its label text.\\n` +\n `\\n` +\n ` To retire it for real, it is a data migration:\\n` +\n `\\n` +\n ` 1. Add the new id alongside the old one, and push.\\n` +\n ` 2. Move the rows: ${chalk.gray(`UPDATE … SET … = '<new>' WHERE … = '${label}';`)}\\n` +\n ` 3. Rewrite the type, which is what actually drops the label:\\n` +\n ` ${chalk.gray(\"rebase db generate retire_option && rebase db migrate\")}\\n` +\n ` ${chalk.gray(\"(a migration can CREATE TYPE …, ALTER TABLE … TYPE … USING, DROP TYPE)\")}\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/** How many rows a table holds, or `null` when we cannot say. */\nasync function countRows(databaseUrl: string | undefined, table: string): Promise<number | null> {\n if (!databaseUrl) return null;\n try {\n const { Client } = await import(\"pg\");\n const client = new Client({ connectionString: databaseUrl, connectionTimeoutMillis: 5000 });\n await client.connect();\n try {\n // Identifier interpolation, and it has to be: the name comes from\n // PostgreSQL's own error text, and `count(*)` takes no parameter in\n // that position. Quoted, with any embedded quote doubled.\n const res = await client.query(`SELECT count(*)::int AS n FROM \"${table.replace(/\"/g, \"\\\"\\\"\")}\"`);\n return (res.rows[0] as { n: number } | undefined)?.n ?? null;\n } finally {\n await client.end();\n }\n } catch {\n return null;\n }\n}\n\n/**\n * A hand-written generated column reading a column the plan wants to retype or\n * drop.\n *\n * Rebase's own generated columns are dropped and rebuilt around the apply — it\n * holds their definition in `search.sql`. This one it does not: PostgreSQL\n * stores a generated expression only in the catalogue, so dropping it to let\n * the apply through would destroy the only copy. Refuse instead, and say what\n * the operator can do about it.\n */\nexport function formatForeignGeneratedColumnBanner(\n conflicts: { schema: string; table: string; column: string; blocking: { column: string; kind: string }[] }[]\n): string {\n const lines = conflicts.map(c => {\n const reads = c.blocking.map(b => `\"${b.column}\"`).join(\", \");\n return ` ${chalk.bold(`${c.schema}.${c.table}.${c.column}`)} reads ${reads}`;\n });\n const first = conflicts[0];\n return (\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n ` ❌ A generated column depends on a column this push changes\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n` +\n `\\n` +\n lines.join(\"\\n\") + `\\n` +\n `\\n` +\n ` PostgreSQL refuses to retype or drop a column while a GENERATED\\n` +\n ` ALWAYS AS … STORED expression reads it, and an Atlas apply is one\\n` +\n ` transaction — so this refusal would roll back every other statement\\n` +\n ` in the push with it.\\n` +\n `\\n` +\n ` ${chalk.yellow(\"Nothing was applied.\")} Rebase rebuilds the generated columns it made\\n` +\n ` itself (a collection's ${chalk.bold(\"search\")} block) around the apply. It has no\\n` +\n ` definition for this one, and a dropped generated expression is not\\n` +\n ` recoverable from anywhere but your own migration.\\n` +\n `\\n` +\n ` Drop it, push, and put it back:\\n` +\n `\\n` +\n ` ${chalk.gray(`ALTER TABLE \"${first.schema}\".\"${first.table}\" DROP COLUMN \"${first.column}\";`)}\\n` +\n ` ${chalk.gray(\"rebase db push\")}\\n` +\n ` ${chalk.gray(\"-- then re-create the column with its original expression\")}\\n` +\n `\\n` +\n ` Or declare it through a ${chalk.bold(\"search\")} block, and Rebase will own it.\\n` +\n `\\n` +\n `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n`\n );\n}\n\n/**\n * The remedy for an Atlas invocation that failed, or `null`.\n *\n * Scoped by the invocation, because the same database state means different\n * things to different subcommands: \"already exists\" under `migrate apply` wants\n * a baseline, while under `schema apply` it is a genuine conflict.\n */\nexport async function diagnoseAtlasFailure(context: {\n domain: string;\n args: string[];\n stderr: string;\n databaseUrl?: string;\n /** The newest migration version on disk, named in the baseline remedy. */\n latestMigrationVersion?: string | null;\n}): Promise<string | null> {\n const { domain, args, stderr, databaseUrl } = context;\n\n if (domain === \"migrate\" && args.includes(\"apply\")) {\n const provisioned = parseAlreadyProvisioned(stderr);\n if (provisioned) {\n return formatBaselineRemedy(context.latestMigrationVersion ?? null, provisioned.object);\n }\n }\n\n if (domain === \"schema\" && args.includes(\"apply\")) {\n const enumDrop = parseEnumLabelDrop(stderr);\n if (enumDrop) return formatEnumLabelDropBanner(enumDrop);\n\n const notNull = parseNotNullViolation(stderr);\n if (notNull) return formatNotNullViolationBanner(notNull, await countRows(databaseUrl, notNull.table));\n }\n\n return null;\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"column-plan-helpers-CpILzHJS.js","names":[],"sources":["../src/schema/column-plan-helpers.ts"],"sourcesContent":["/**\n * The questions about a property that the schema planner asks first.\n *\n * What is the primary key, what is this column called, what does an id default\n * to, what labels does this enum have. `schema/plan/plan-schema.ts` is the only\n * caller — it is the only reader of a `Property` — and these live beside it\n * rather than inside it because they are also where the *refusals* are, and a\n * refusal wants to be reachable and testable on its own.\n *\n * They were extracted while there were still three emitters, each carrying its\n * own verbatim copy of all four rules until one of them was edited: the audit\n * that produced this module counted twelve open disagreements and found that a\n * third of the commits touching those files were re-synchronisations. The\n * emitters are renderers now and there is nothing left to keep in step, but the\n * questions are still questions about a property rather than about a plan, so\n * this is where they belong.\n *\n * Everything here is pure and synchronous, and it throws rather than guesses:\n * an enum with no values, a `cuid` id on Postgres and a second `isId` are all\n * configurations that produce a broken database in silence, so they are refused\n * where every path passes.\n */\nimport type {\n CollectionConfig,\n NumberProperty,\n Property,\n StringProperty\n} from \"@rebasepro/types\";\nimport { toSnakeCase } from \"@rebasepro/utils\";\n\n/**\n * Resolve the SQL column name for a property.\n *\n * Uses the explicit `columnName` when set (e.g. from introspection), falling\n * back to `toSnakeCase(propName)` for manually-authored collections.\n */\nexport const resolveColumnName = (propName: string, prop?: Property | null): string => {\n if (prop && \"columnName\" in prop && typeof prop.columnName === \"string\") {\n return prop.columnName;\n }\n return toSnakeCase(propName);\n};\n\n/** Every property that carries a truthy `isId`, in declaration order. */\nconst idPropertyEntries = (collection: CollectionConfig): [string, Property][] =>\n Object.entries(collection.properties ?? {}).filter(\n ([, prop]) => prop && typeof prop === \"object\" && \"isId\" in prop && Boolean((prop as { isId?: unknown }).isId)\n ) as [string, Property][];\n\n/**\n * Refuse a collection that declares two primary keys.\n *\n * Postgres allows a composite primary key; Rebase does not model one, and the\n * three emitters each invented a different wrong answer for it. The Drizzle\n * generator appended `.primaryKey()` to both columns (two primary keys, which\n * drizzle-kit turns into an error at push time), the DDL generator wrote two\n * inline `PRIMARY KEY` clauses in one `CREATE TABLE` (Postgres: \"multiple\n * primary keys for table are not allowed\"), and boot-ensure created the table\n * with the *first* id column and silently never added the second — so a managed\n * tenant got a table missing a column its own collection reads.\n *\n * None of those is recoverable at runtime, and two of them fail long after the\n * config was written. Refused here instead, where the collection has a name.\n */\nexport const assertSinglePrimaryKey = (collection: CollectionConfig): void => {\n const ids = idPropertyEntries(collection);\n if (ids.length < 2) return;\n throw new Error(\n `Collection \"${collection.slug ?? collection.name ?? \"(unnamed)\"}\" marks ${ids.length} properties ` +\n `with \\`isId\\` (${ids.map(([name]) => `\"${name}\"`).join(\", \")}). ` +\n \"Composite primary keys are not supported: give exactly one property `isId`, and express the \" +\n \"second key with `indexes: [{ on: [...], unique: true, reason: \\\"…\\\" }]`.\"\n );\n};\n\nexport const getPrimaryKeyProp = (collection: CollectionConfig): { name: string, type: \"string\" | \"number\", isUuid: boolean } => {\n if (collection.properties) {\n const idPropEntry = idPropertyEntries(collection)[0];\n if (idPropEntry) {\n const prop = idPropEntry[1];\n const isUuid = prop.type === \"string\" && \"isId\" in prop && (prop as StringProperty).isId === \"uuid\";\n return { name: idPropEntry[0], type: prop.type === \"number\" ? \"number\" : \"string\", isUuid };\n }\n }\n // Fallback: a collection that declares no `isId` gets an implicit `id`.\n const idProp = collection.properties?.[\"id\"] as Property | undefined;\n if (idProp?.type === \"number\") {\n return { name: \"id\", type: \"number\", isUuid: false };\n }\n const isUuid = idProp?.type === \"string\" && \"isId\" in idProp && (idProp as StringProperty).isId === \"uuid\";\n return { name: \"id\", type: \"string\", isUuid: isUuid ?? false };\n};\n\nexport const getPrimaryKeyName = (collection: CollectionConfig): string =>\n getPrimaryKeyProp(collection).name;\n\nexport const isIdProperty = (propName: string, prop: Property, collection: CollectionConfig): boolean => {\n if (\"isId\" in prop && Boolean(prop.isId)) return true;\n\n // Only fall back to \"id\" when NO property is explicitly marked.\n return idPropertyEntries(collection).length === 0 && propName === \"id\";\n};\n\n/**\n * The labels an `enum` declares, in order, whichever form it was written in.\n *\n * `EnumValues` is `EnumValueConfig[] | Record<id, label>` and the docs recommend\n * the record. Boot-ensure read only the array form — `(p.enum as unknown[]).map`\n * — so a record-form enum threw `p.enum.map is not a function` on a managed\n * tenant, at boot, with no developer in the loop. The other two emitters each\n * carried their own copy of the both-forms walk.\n *\n * An empty list throws rather than returning `[]`. `CREATE TYPE … AS ENUM ()`\n * is not valid SQL, so every emitter \"handled\" it by skipping the type and then\n * typing the column with it anyway: Drizzle referenced an enum variable it never\n * declared (the generated file does not compile), the DDL named a type nothing\n * creates, and boot-ensure's `ADD COLUMN` failed — and add-column is not a\n * survivable action, so the boot died. There is no useful column at the end of\n * any of those paths.\n */\nexport const enumLabelsOf = (propName: string, prop: Property, collection: CollectionConfig): string[] => {\n const values = (prop as { enum?: unknown }).enum;\n if (values === undefined || values === null) return [];\n\n const labels = Array.isArray(values)\n ? values.map((entry: unknown) =>\n String(entry !== null && typeof entry === \"object\" && \"id\" in (entry as Record<string, unknown>)\n ? (entry as Record<string, unknown>).id\n : entry))\n : Object.keys(values as Record<string, unknown>).map(String);\n\n const usable = labels.filter(label => label.length > 0);\n if (usable.length === 0) {\n throw new Error(\n `Property \"${propName}\" of collection \"${collection.slug ?? collection.name ?? \"(unnamed)\"}\" ` +\n \"declares an empty `enum`. A Postgres enum type needs at least one label — \" +\n \"`CREATE TYPE … AS ENUM ()` is not valid SQL — so the column would reference a type \" +\n \"nothing creates. List the values, or drop the `enum` for a plain column.\"\n );\n }\n return usable;\n};\n\n/**\n * Does this property compile to a Postgres enum *type*?\n *\n * Strings only. A `number` property with an `enum` used to create a type on all\n * three paths and then declare the column `NUMERIC`/`INTEGER`, so the type was\n * orphaned the moment it was created and the values it listed were enforced by\n * nothing. The dropdown in the panel is driven by the config, not the column, so\n * nothing is lost by not creating it — and a type no column uses is one more\n * object `db push` has to plan a DROP for.\n */\nexport const declaresEnumType = (prop: Property): boolean =>\n prop.type === \"string\" && Boolean((prop as StringProperty).enum);\n\n/**\n * The `DEFAULT` an id column carries, as the thing itself rather than a\n * rendered string — each emitter spells it differently.\n *\n * The three of them disagreed on all four strategies. The DDL generator wrote\n * `DEFAULT ${prop.isId}` verbatim, so the documented `` isId: \"sql`gen_id()`\" ``\n * reached Postgres with the template wrapper still on it — a syntax error in\n * `schema.sql`. Boot-ensure gave a default to `uuid` and to nothing else, so on\n * a managed tenant every other strategy produced a column with no default at\n * all, and the first insert that trusted the config failed on a NULL primary\n * key. Only the Drizzle generator stripped the wrapper.\n */\nexport type IdColumnDefault =\n /** `gen_random_uuid()` / `.defaultRandom()`. */\n | { kind: \"uuid\" }\n /** `GENERATED BY DEFAULT AS IDENTITY` / `.generatedByDefaultAsIdentity()` — part of the type, not a DEFAULT. */\n | { kind: \"identity\" }\n /** A raw SQL expression, wrapper already stripped. */\n | { kind: \"sql\"; expression: string };\n\n/**\n * Refuses `isId: \"cuid\"`.\n *\n * `DEFAULT cuid()` has been emitted since the option existed and no `cuid()`\n * function has ever been created — not by the generators, not by boot, not by\n * any migration. So the column has never had a working default on Postgres: the\n * first insert that relied on it failed with `function cuid() does not exist`.\n * There is nothing to keep compatible.\n *\n * It is refused rather than implemented. Shipping a real `rebase.cuid()` means a\n * new SQL function in the same carved-out class as the search helpers — excluded\n * from Atlas by pattern, applied by boot, re-applied by push, checked by the\n * doctor, and versioned across the fleet forever — to reproduce a monotonic id\n * that `uuid` already provides and that a project wanting cuid's exact shape can\n * write itself in one line of SQL.\n */\nexport const idColumnDefault = (propName: string, prop: Property, collection: CollectionConfig): IdColumnDefault | undefined => {\n if (!(\"isId\" in prop)) return undefined;\n const isId = (prop as { isId?: unknown }).isId;\n // `true` and `\"manual\"` both mean \"the application supplies it\".\n if (isId === undefined || isId === null || isId === true || isId === \"manual\" || isId === false) return undefined;\n if (typeof isId !== \"string\") return undefined;\n\n if (prop.type === \"string\" && (isId as StringProperty[\"isId\"]) === \"uuid\") return { kind: \"uuid\" };\n if (prop.type === \"number\" && (isId as NumberProperty[\"isId\"]) === \"increment\") return { kind: \"identity\" };\n\n if (isId === \"cuid\") {\n throw new Error(\n `Property \"${propName}\" of collection \"${collection.slug ?? collection.name ?? \"(unnamed)\"}\" ` +\n \"uses `isId: \\\"cuid\\\"`, which Postgres cannot honour: the generated default calls a `cuid()` \" +\n \"function that Rebase has never created, so every insert relying on it fails. \" +\n \"Use `isId: \\\"uuid\\\"`, or give the strategy as SQL — ``isId: \\\"sql`my_id()`\\\"`` — and create that \" +\n \"function in a migration.\"\n );\n }\n\n // Any other string is a raw SQL default. Written either bare\n // (`gen_random_uuid()::text`) or in the documented template form\n // (``sql`gen_random_uuid()::text` ``); the wrapper is markup, not SQL.\n const expression = isId.startsWith(\"sql`\") && isId.endsWith(\"`\")\n ? isId.substring(4, isId.length - 1)\n : isId;\n return { kind: \"sql\", expression };\n};\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"doctor-CU9IogdL.js","names":[],"sources":["../../codegen/dist/index.es.js","../src/schema/doctor.ts"],"sourcesContent":["import { getDeclaredSubcollections } from \"@rebasepro/types\";\nimport { effectiveAccess, fieldKeyForColumn, findRelation, isRelationRequired, resolveCollectionRelations, sortCollectionsBySlug } from \"@rebasepro/common\";\n//#region src/utils.ts\n/**\n* Utility functions for the SDK generator\n*/\n/**\n* Convert a slug/snake_case string to PascalCase\n* e.g. \"private_notes\" → \"PrivateNotes\"\n*\n* Capitals already inside a word are meaningful and are kept: lowercasing the\n* tail of every chunk turned \"TestEntities\" into \"Testentities\", which is what\n* ended up in the generated type names. SHOUTING_CASE is the one shape where\n* the tail is not meaningful, so it is folded down.\n*/\nfunction toPascalCase(str) {\n\treturn str.split(/[_\\-\\s]+/).filter(Boolean).map((word) => {\n\t\tconst rest = /^[A-Z0-9]+$/.test(word) ? word.slice(1).toLowerCase() : word.slice(1);\n\t\treturn word.charAt(0).toUpperCase() + rest;\n\t}).join(\"\");\n}\n/**\n* Convert a slug/snake_case string to camelCase\n* e.g. \"private_notes\" → \"privateNotes\"\n*/\nfunction toCamelCase(str) {\n\tif (!/[_\\-\\s]/.test(str)) return str.charAt(0).toLowerCase() + str.slice(1);\n\tconst pascal = toPascalCase(str);\n\treturn pascal.charAt(0).toLowerCase() + pascal.slice(1);\n}\n/**\n* Convert a slug to a safe JS identifier\n* e.g. \"private-notes\" → \"privateNotes\"\n*/\nfunction toSafeIdentifier(str) {\n\treturn toCamelCase(str.replace(/[^a-zA-Z0-9_]/g, \"_\"));\n}\n/**\n* Indent a block of text by a given number of spaces\n*/\nfunction indent(text, spaces) {\n\tconst pad = \" \".repeat(spaces);\n\treturn text.split(\"\\n\").map((line) => line.trim() ? pad + line : line).join(\"\\n\");\n}\n//#endregion\n//#region src/generate-types.ts\n/**\n* A schema that cannot be expressed as a valid TypeScript file.\n*\n* Thrown rather than emitted. The generator used to concatenate whatever it was\n* given, so a slug that collided with another one, or that was not an\n* identifier, produced a file that either failed to compile or — worse —\n* compiled while quietly routing one collection to another's slug.\n*/\nvar CodegenError = class extends Error {\n\tconstructor(message) {\n\t\tsuper(message);\n\t\tthis.name = \"CodegenError\";\n\t}\n};\nvar IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\n/**\n* A property name for the emitted TypeScript: verbatim when it is a valid\n* identifier, quoted otherwise.\n*\n* Every key that reaches the output goes through here. Column names are not\n* required to be identifiers — `\"order\"`, `\"user id\"`, a quoted Postgres\n* identifier — and the previous behaviour of camel-casing them into shape\n* renamed the column in the type while the wire kept the original, so the\n* generated `Row` described fields that did not exist.\n*/\nfunction emitKey(key) {\n\treturn IDENTIFIER.test(key) ? key : JSON.stringify(key);\n}\n/**\n* A string literal, escaped.\n*\n* `\"${value}\"` was the previous form. A value containing a quote closed the\n* literal early, which at best broke the file and at worst let a slug from a\n* remote contract inject top-level statements into a file the developer\n* compiles and bundles.\n*/\nfunction emitString(value) {\n\treturn JSON.stringify(value);\n}\n/** The `id`s of an enum declared as an array, an array of `{ id }`, or an object map. */\nfunction enumIds(raw) {\n\tif (Array.isArray(raw)) return raw.map((entry) => entry && typeof entry === \"object\" ? entry.id : entry);\n\tif (raw && typeof raw === \"object\") return Object.keys(raw);\n\treturn [];\n}\nfunction propertyToTypeScriptType(prop) {\n\tswitch (prop.type) {\n\t\tcase \"string\": {\n\t\t\tconst sp = prop;\n\t\t\tif (sp.enum) {\n\t\t\t\tconst ids = enumIds(sp.enum);\n\t\t\t\tif (ids.length === 0) return \"string\";\n\t\t\t\treturn ids.map((v) => emitString(String(v))).join(\" | \");\n\t\t\t}\n\t\t\treturn \"string\";\n\t\t}\n\t\tcase \"number\": {\n\t\t\tconst np = prop;\n\t\t\tif (np.enum) {\n\t\t\t\tconst ids = enumIds(np.enum);\n\t\t\t\tconst numbers = ids.map(Number);\n\t\t\t\tif (ids.length === 0 || numbers.some((n) => !Number.isFinite(n))) return \"number\";\n\t\t\t\treturn numbers.map((n) => String(n)).join(\" | \");\n\t\t\t}\n\t\t\treturn \"number\";\n\t\t}\n\t\tcase \"boolean\": return \"boolean\";\n\t\tcase \"date\": return \"string\";\n\t\tcase \"geopoint\": return \"{ latitude: number; longitude: number; }\";\n\t\tcase \"reference\": return \"string | number\";\n\t\tcase \"relation\": return \"string | number\";\n\t\tcase \"map\": {\n\t\t\tconst mapProp = prop;\n\t\t\tif (mapProp.properties) return `{ ${Object.entries(mapProp.properties).map(([k, v]) => {\n\t\t\t\tconst child = v;\n\t\t\t\tconst optional = !child.validation?.required;\n\t\t\t\tconst type = propertyToTypeScriptType(child);\n\t\t\t\treturn `${emitKey(k)}${optional ? \"?\" : \"\"}: ${optional ? `${type} | null` : type};`;\n\t\t\t}).join(\" \")} }`;\n\t\t\treturn \"Record<string, unknown>\";\n\t\t}\n\t\tcase \"array\": {\n\t\t\tconst arrProp = prop;\n\t\t\tif (arrProp.of) return `Array<${propertyToTypeScriptType(arrProp.of)}>`;\n\t\t\treturn \"Array<unknown>\";\n\t\t}\n\t\tcase \"vector\": return \"number[]\";\n\t\tcase \"binary\": return \"string\";\n\t\tdefault: return \"unknown\";\n\t}\n}\n/**\n* Unwrap a relation target that arrived as a module namespace rather than the\n* collection itself — `target: () => import(\"./authors\")` is a common slip.\n*/\nfunction resolveTargetCollection(relation) {\n\ttry {\n\t\tlet target = relation.target();\n\t\tif (target && (target.default || target.__esModule)) target = target.default ?? target;\n\t\treturn target;\n\t} catch {\n\t\treturn;\n\t}\n}\n/** The TypeScript type of a foreign key: whatever the target's primary key is. */\nfunction foreignKeyType(relation) {\n\tconst target = resolveTargetCollection(relation);\n\tif (!target?.properties) return \"string | number\";\n\tconst idProp = Object.entries(target.properties).find(([_, p]) => p.isId);\n\tif (!idProp) return \"string | number\";\n\treturn idProp[1].type === \"number\" ? \"number\" : \"string\";\n}\n/**\n* The property's `isId`, on the two property kinds that declare one.\n*\n* `Property` is a union and only `StringProperty` and `NumberProperty` carry\n* `isId`, so it cannot be read off the union directly — which is what\n* `(prop as unknown as Record<string, unknown>).isId` was for, twice. The union\n* discriminates on `type`, so narrowing gets there with no claim at all, and\n* with the declared type (`boolean | \"manual\" | \"uuid\" | string`) intact\n* instead of flattened to `unknown`.\n*/\nfunction declaredIsId(prop) {\n\treturn prop.type === \"string\" || prop.type === \"number\" ? prop.isId : void 0;\n}\n/** Whether a property is the collection's primary key. */\nfunction isPrimaryKey(prop) {\n\treturn Boolean(declaredIsId(prop));\n}\n/**\n* Whether the server assigns this primary key, so a write does not have to.\n* `true` and `\"manual\"` both mean the caller supplies it.\n*/\nfunction isAutoAssignedId(prop) {\n\tconst isId = declaredIsId(prop);\n\treturn Boolean(isId) && isId !== \"manual\" && isId !== true;\n}\n/**\n* The type an *included* relation arrives as: the target's own row, inlined.\n*\n* This is what the read pipeline actually serves — `toRestRow` puts the\n* target's flat columns where the relation was, and the SDK and the HTTP API\n* both go through it. It is deliberately *not* a `{ __type: \"relation\" }`\n* envelope: that shape is the admin's view-model and never reaches a\n* developer's `find()`.\n*\n* Falls back to an open record when the target is not part of this generation\n* run, since there is no `Row` to point at.\n*/\nfunction includedRelationType(relation, accessors) {\n\tconst slug = resolveTargetCollection(relation)?.slug ?? relation.targetSlug;\n\tconst accessor = slug ? accessors.get(slug) : void 0;\n\tconst rowType = accessor ? `Database[${emitString(accessor)}][\"Row\"]` : \"Record<string, unknown>\";\n\treturn relation.cardinality === \"many\" ? `Array<${rowType}>` : rowType;\n}\n/**\n* Map every slug to the property name it is reachable under on `client.data`.\n*\n* The accessor is a safe identifier because `client.data.myNotes` is the point\n* of generating this at all, and `collectionsDictionary` maps it back to the\n* slug the wire uses. Two slugs that safe down to the same identifier cannot\n* both have it: the interface would not compile, and the dictionary — an object\n* literal — would silently keep only the last, routing one collection's reads\n* to the other's table. There is no defensible way to pick, so this refuses.\n*/\nfunction buildAccessors(collections) {\n\tconst accessors = /* @__PURE__ */ new Map();\n\tconst bySafeName = /* @__PURE__ */ new Map();\n\tfor (const collection of collections) {\n\t\tconst slug = collection.slug;\n\t\tif (typeof slug !== \"string\" || slug.length === 0) throw new CodegenError(\"A collection has no slug, so it has no name to generate a type for. Every collection needs a unique `slug`.\");\n\t\tconst safe = toSafeIdentifier(slug);\n\t\tif (safe.length === 0) throw new CodegenError(`The slug ${emitString(slug)} has no characters that can form a property name, so it cannot be reached as \\`client.data.<name>\\`. Use a slug containing letters, digits, underscores or dashes.`);\n\t\tconst existing = bySafeName.get(safe);\n\t\tif (existing !== void 0) throw new CodegenError(`The collections ${emitString(existing)} and ${emitString(slug)} both generate the accessor \"${safe}\", so only one of them could be reached from the generated client and the other's reads would silently go to the wrong table. Rename one of the slugs.`);\n\t\tbySafeName.set(safe, slug);\n\t\taccessors.set(slug, safe);\n\t}\n\treturn accessors;\n}\n/** One emitted `key: type;` line, already indented. */\nfunction line(key, type, optional) {\n\treturn ` ${emitKey(key)}${optional ? \"?\" : \"\"}: ${type};`;\n}\n/**\n* The keys nobody can reach take off the API surface — in *both* directions.\n*\n* `excludeFromApi` means one thing: the API surface does not mention this\n* property. `Row` already honoured that; `Insert` and `Update` deliberately did\n* not, on the reading that the column is stripped from responses rather than\n* from writes. That left the generated types as the one place a password hash\n* was still named, and it invited a client to send one. The server still\n* *accepts* such a field on a write — this describes the surface, it does not\n* add an enforcement point — but nothing generated advertises it.\n*\n* Read through `effectiveAccess`, so the flag and its longhand\n* `access: { read: [], write: [] }` produce the same file. A *role* rule is\n* deliberately not honoured here and `Row` is unchanged by one: a generated type\n* is one shape for every caller, and there is no `Row` that is right for both a\n* reader who holds `hr` and one who does not. The server is the enforcement\n* point; the types describe the surface a caller may name.\n*\n* Keyed by the property name *and* by its column name, the same pair the\n* server's `stripUnreadable` deletes, so a foreign key or a relation addressed\n* under the column name cannot put the property back.\n*/\nfunction excludedApiKeys(properties) {\n\tconst excluded = /* @__PURE__ */ new Set();\n\tfor (const [key, rawProp] of Object.entries(properties)) {\n\t\tconst prop = rawProp;\n\t\tconst access = effectiveAccess(prop);\n\t\tif (access?.read?.length !== 0 || access?.write?.length !== 0) continue;\n\t\texcluded.add(key);\n\t\tif (prop.columnName) excluded.add(prop.columnName);\n\t}\n\treturn excluded;\n}\nfunction generateTypedefs(input) {\n\tconst collections = sortCollectionsBySlug(input);\n\tconst accessors = buildAccessors(collections);\n\tconst lines = [\n\t\t\"/**\",\n\t\t\" * This file was auto-generated by Rebase.\",\n\t\t\" * Do not make direct changes to the file.\",\n\t\t\" */\",\n\t\t\"\",\n\t\t\"export interface Database {\"\n\t];\n\tfor (const collection of collections) {\n\t\tconst properties = collection.properties ?? {};\n\t\tlet resolvedRelations = {};\n\t\ttry {\n\t\t\tresolvedRelations = resolveCollectionRelations(collection);\n\t\t} catch (e) {\n\t\t\tconsole.warn(`[rebase] Could not resolve the relations of \"${collection.slug}\", so its generated type has no relation fields and none of their foreign-key columns. This is usually a circular import in the collection files — make sure the target is \\`() => otherCollection\\` and not evaluated at module load.\\n ${e instanceof Error ? e.message : String(e)}`);\n\t\t}\n\t\tconst declared = getDeclaredSubcollections(collection);\n\t\tlet subcollectionCount = 0;\n\t\tif (declared) try {\n\t\t\tsubcollectionCount = declared().length;\n\t\t} catch {\n\t\t\tsubcollectionCount = -1;\n\t\t}\n\t\tif (subcollectionCount !== 0) console.warn(`[rebase] \"${collection.slug}\" declares ${subcollectionCount < 0 ? \"\" : `${subcollectionCount} `}subcollection(s), which are not part of the generated Database: they are reached over a nested path (\\`data/${collection.slug}/<id>/<relation>\\`), not as a top-level accessor. Register a subcollection as a collection of its own if you want a typed accessor for it.`);\n\t\tlines.push(` ${emitKey(accessors.get(collection.slug))}: {`);\n\t\tlines.push(\" Row: {\");\n\t\tconst emittedKeys = /* @__PURE__ */ new Set();\n\t\tconst excluded = excludedApiKeys(properties);\n\t\tfor (const key of excluded) emittedKeys.add(key);\n\t\tfor (const [key, rawProp] of Object.entries(properties)) {\n\t\t\tconst prop = rawProp;\n\t\t\tif (prop.type === \"relation\") continue;\n\t\t\tif (excluded.has(key)) continue;\n\t\t\tconst tsType = propertyToTypeScriptType(prop);\n\t\t\tconst isRequired = Boolean(prop.validation?.required) || isPrimaryKey(prop);\n\t\t\tlines.push(line(key, isRequired ? tsType : `${tsType} | null`, !isRequired));\n\t\t\temittedKeys.add(key);\n\t\t}\n\t\tfor (const [relKey, relation] of Object.entries(resolvedRelations)) if (relation.kind === \"belongsTo\" && relation.localKey) {\n\t\t\tconst fkKey = fieldKeyForColumn(collection, relation.localKey);\n\t\t\tif (emittedKeys.has(fkKey)) continue;\n\t\t\tconst fkType = foreignKeyType(relation);\n\t\t\tconst shadowedByInclude = relKey === fkKey;\n\t\t\tconst tsType = shadowedByInclude ? `${fkType} | ${includedRelationType(relation, accessors)}` : fkType;\n\t\t\tconst isRequired = isRelationRequired(collection, relation) && !shadowedByInclude;\n\t\t\tlines.push(line(fkKey, isRequired ? tsType : `${tsType} | null`, !isRequired));\n\t\t\temittedKeys.add(fkKey);\n\t\t}\n\t\tfor (const [key, relation] of Object.entries(resolvedRelations)) {\n\t\t\tif (emittedKeys.has(key)) continue;\n\t\t\tlines.push(line(key, includedRelationType(relation, accessors), true));\n\t\t\temittedKeys.add(key);\n\t\t}\n\t\tfor (const [key, rawProp] of Object.entries(properties)) {\n\t\t\tif (rawProp.type !== \"relation\") continue;\n\t\t\tif (emittedKeys.has(key)) continue;\n\t\t\tlines.push(line(key, \"Record<string, unknown>\", true));\n\t\t\temittedKeys.add(key);\n\t\t}\n\t\tlines.push(\" };\");\n\t\tlines.push(\" Insert: {\");\n\t\temittedKeys.clear();\n\t\tfor (const key of excluded) emittedKeys.add(key);\n\t\tfor (const [key, rawProp] of Object.entries(properties)) {\n\t\t\tconst prop = rawProp;\n\t\t\tif (prop.type === \"relation\") continue;\n\t\t\tif (excluded.has(key)) continue;\n\t\t\tconst tsType = propertyToTypeScriptType(prop);\n\t\t\tconst isOptional = !prop.validation?.required || isAutoAssignedId(prop);\n\t\t\tlines.push(line(key, tsType, isOptional));\n\t\t\temittedKeys.add(key);\n\t\t}\n\t\temitWritableRelations(lines, collection, properties, resolvedRelations, emittedKeys, false);\n\t\tlines.push(\" };\");\n\t\tlines.push(\" Update: {\");\n\t\temittedKeys.clear();\n\t\tfor (const key of excluded) emittedKeys.add(key);\n\t\tfor (const [key, rawProp] of Object.entries(properties)) {\n\t\t\tconst prop = rawProp;\n\t\t\tif (prop.type === \"relation\") continue;\n\t\t\tif (isPrimaryKey(prop)) continue;\n\t\t\tif (excluded.has(key)) continue;\n\t\t\tlines.push(line(key, propertyToTypeScriptType(prop), true));\n\t\t\temittedKeys.add(key);\n\t\t}\n\t\temitWritableRelations(lines, collection, properties, resolvedRelations, emittedKeys, true);\n\t\tlines.push(\" };\");\n\t\tlines.push(\" Relations: {\");\n\t\tfor (const [key, relation] of Object.entries(resolvedRelations)) {\n\t\t\tconst slug = resolveTargetCollection(relation)?.slug ?? relation.targetSlug;\n\t\t\tconst accessor = slug ? accessors.get(slug) : void 0;\n\t\t\tlines.push(` ${emitKey(key)}: ${accessor ? emitString(accessor) : \"never\"};`);\n\t\t}\n\t\tlines.push(\" };\");\n\t\tlines.push(\" };\");\n\t}\n\tlines.push(\"}\");\n\tlines.push(\"\");\n\tlines.push(\"export type CollectionName = keyof Database;\");\n\tlines.push(\"\");\n\tlines.push(\"export const collectionsDictionary = {\");\n\tfor (const collection of collections) lines.push(` ${emitKey(accessors.get(collection.slug))}: ${emitString(collection.slug)},`);\n\tlines.push(\"} as const;\");\n\tlines.push(\"\");\n\tlines.push(\"export type CollectionsDictionary = typeof collectionsDictionary;\");\n\tlines.push(\"\");\n\tlines.push(...includeHelperLines());\n\treturn lines.join(\"\\n\");\n}\n/**\n* The `include` type machinery, emitted into the generated file.\n*\n* It lives here rather than in `@rebasepro/types` because it is the *graph*\n* that makes it work, and the graph only exists once a project's collections\n* have been generated. `IncludeSpec` in `@rebasepro/types` is deliberately\n* unconstrained — a hand-written row type has no relations in it to check\n* against — and these narrow it for a project that does have them.\n*\n* Two things a caller gets:\n*\n* - `IncludeFor<\"posts\">` — an include whose keys are relations that exist,\n* recursively, so `include: { comments: { include: { authr: true } } }` is a\n* compile error rather than a 400 at runtime.\n* - `RowWith<\"posts\", I>` — the row a read with that include returns, where\n* every included relation is **present** rather than optional. `Row` types a\n* relation as optional because it is absent from every read that did not ask\n* for it; once a read has asked, `row.author.name` should not need a `?.`.\n*\n* The depth bound matches the server's (`MAX_INCLUDE_DEPTH`), and is spelled as\n* a decrementing tuple because TypeScript has no arithmetic — `Prev[3]` is `2`.\n* Without a bound, a self-referencing relation makes the type infinite and the\n* compiler gives up with \"type instantiation is excessively deep\".\n*/\nfunction includeHelperLines() {\n\treturn [\n\t\t\"/** Which collection each of `A`'s relations reaches. */\",\n\t\t\"export type RelationsOf<A extends CollectionName> =\",\n\t\t\" Database[A] extends { Relations: infer R } ? R : Record<string, never>;\",\n\t\t\"\",\n\t\t\"/** The names of `A`'s relations. */\",\n\t\t\"export type RelationKeys<A extends CollectionName> = keyof RelationsOf<A> & string;\",\n\t\t\"\",\n\t\t\"/** The collection a relation reaches, or `never` when it left this project. */\",\n\t\t\"export type RelationTarget<A extends CollectionName, K extends RelationKeys<A>> =\",\n\t\t\" RelationsOf<A>[K] extends CollectionName ? RelationsOf<A>[K] : never;\",\n\t\t\"\",\n\t\t\"/** Counts `IncludeFor` down; TypeScript has no arithmetic. */\",\n\t\t\"type Prev = [never, 0, 1, 2, 3];\",\n\t\t\"\",\n\t\t\"/**\",\n\t\t\" * Per-relation options, minus `include` — which `IncludeFor` supplies at\",\n\t\t\" * the next depth so the nested keys are checked against the *target's*\",\n\t\t\" * relations rather than this collection's.\",\n\t\t\" */\",\n\t\t\"export interface IncludeOptionsFor<A extends CollectionName, K extends RelationKeys<A>, D extends number> {\",\n\t\t\" limit?: number;\",\n\t\t\" where?: Record<string, unknown>;\",\n\t\t\" logical?: unknown;\",\n\t\t\" orderBy?: unknown;\",\n\t\t\" fields?: string[];\",\n\t\t\" include?: RelationTarget<A, K> extends CollectionName\",\n\t\t\" ? IncludeFor<RelationTarget<A, K>, Prev[D]>\",\n\t\t\" : never;\",\n\t\t\"}\",\n\t\t\"\",\n\t\t\"/**\",\n\t\t\" * An `include` for collection `A`: its relation names, dotted paths, or the\",\n\t\t\" * parametrised tree — with every key checked against the relations that\",\n\t\t\" * actually exist, at every level.\",\n\t\t\" */\",\n\t\t\"export type IncludeFor<A extends CollectionName, D extends number = 3> =\",\n\t\t\" D extends 0\",\n\t\t\" ? never\",\n\t\t\" : | readonly (RelationKeys<A> | \\\"*\\\")[]\",\n\t\t\" | { [K in RelationKeys<A>]?: true | IncludeOptionsFor<A, K, D> };\",\n\t\t\"\",\n\t\t\"/** The relation names an include asks for at the top level. */\",\n\t\t\"type IncludedKeys<A extends CollectionName, I> =\",\n\t\t\" I extends readonly (infer K)[]\",\n\t\t\" ? Extract<K, RelationKeys<A>>\",\n\t\t\" : Extract<keyof I, RelationKeys<A>>;\",\n\t\t\"\",\n\t\t\"/**\",\n\t\t\" * The row a read with include `I` returns: `Row`, with every included\",\n\t\t\" * relation made **required**.\",\n\t\t\" *\",\n\t\t\" * `Row` types a relation as optional because it is absent from every read\",\n\t\t\" * that did not name it. Once a read has named it, it is there — and having\",\n\t\t\" * to write `row.author?.name` after asking for the author is the type\",\n\t\t\" * describing a possibility the query already ruled out.\",\n\t\t\" */\",\n\t\t\"export type RowWith<A extends CollectionName, I> =\",\n\t\t\" Database[A][\\\"Row\\\"] & Required<Pick<Database[A][\\\"Row\\\"], IncludedKeys<A, I> & keyof Database[A][\\\"Row\\\"]>>;\",\n\t\t\"\"\n\t];\n}\n/**\n* The two ways a write can name a `belongsTo` target, both of which the server\n* accepts: the foreign key under its own wire name (`{ authorId: 5 }`, which\n* passes through to the `author_id` column untouched) and the relation\n* *property* (`{ author: 5 }`, which the write transformer maps onto that\n* column).\n*\n* Only the first was generated, so the documented and idiomatic write shape was\n* a type error.\n*\n* The second form is emitted under the **property key**, not the resolved\n* relation name, because that is what the transformer keys off: it looks the\n* payload key up in `properties` and only treats it as a relation if what it\n* finds there is one. A relation whose `relationName` differs from its property\n* key is reachable as the property and not as the name, so emitting the name\n* would have offered a key that writes to a column that does not exist.\n*/\nfunction emitWritableRelations(lines, collection, properties, resolvedRelations, emittedKeys, allOptional) {\n\tconst emit = (key, relation) => {\n\t\tif (emittedKeys.has(key)) return;\n\t\tconst optional = allOptional || !isRelationRequired(collection, relation);\n\t\tlines.push(line(key, foreignKeyType(relation), optional));\n\t\temittedKeys.add(key);\n\t};\n\tfor (const relation of Object.values(resolvedRelations)) if (relation.kind === \"belongsTo\" && relation.localKey) emit(fieldKeyForColumn(collection, relation.localKey), relation);\n\tfor (const [key, rawProp] of Object.entries(properties)) {\n\t\tif (rawProp.type !== \"relation\") continue;\n\t\tconst relation = findRelation(resolvedRelations, key);\n\t\tif (relation?.kind === \"belongsTo\" && relation.localKey) emit(key, relation);\n\t}\n}\n//#endregion\n//#region src/index.ts\nfunction generateSDK(collections, options = {}) {\n\tconst files = [];\n\tfiles.push({\n\t\tpath: \"database.types.ts\",\n\t\tcontent: generateTypedefs(collections)\n\t});\n\tif (options.includeReadme !== false) files.push({\n\t\tpath: \"README.md\",\n\t\tcontent: `# Rebase SDK\n\n> Auto-generated by \\`rebase generate-sdk\\`. Do not edit manually.\n\n## Usage\n\n1. Install the client package:\n \\`\\`\\`bash\n npm install @rebasepro/client\n \\`\\`\\`\n\n2. Initialize with your generated types:\n \\`\\`\\`typescript\n import { createRebaseClient } from '@rebasepro/client';\n import { collectionsDictionary, type Database } from './database.types';\n\n const rebase = createRebaseClient<Database>({\n baseUrl: 'http://localhost:3001',\n // Maps each accessor back to the slug the wire uses. Without it a\n // hyphenated slug is not resolvable from the property name alone.\n collections: collectionsDictionary,\n });\n\n // Property access is the typed surface: rows, filters and sorts are all\n // checked against the generated Database.\n const { data: users } = await rebase.data.users.find();\n console.log(users[0].email); // flat access — no .values wrapper\n \\`\\`\\`\n\n## Field names are the ones the API serves\n\nThe generated \\`Row\\` uses each field's **wire** name — the key it arrives under in\nJSON — and nothing here renames anything.\n\n- **A declared property is its key in the collection.** A property keyed\n \\`createdAt\\` is \\`row.createdAt\\`, whatever \\`columnName\\` says. A column name is\n the name of a different thing: where the value lives, not what the API calls it.\n- **A foreign key derived from a relation is camelCase**, because that is what\n the wire carries. A \\`belongsTo\\` named \\`author\\` gives you \\`row.authorId\\`, not\n the column spelling.\n- **A collection accessor is camelCase too** (\\`my-notes\\` → \\`rebase.data.myNotes\\`),\n which is what \\`collectionsDictionary\\` maps back to the slug.\n\n\\`where\\` and \\`orderBy\\` are keyed off the same type, so what compiles is what the\nbackend answers to.\n\n## \\`Row\\` vs \\`Insert\\` vs \\`Update\\`\n\n| Type | What it describes |\n|---|---|\n| \\`Row\\` | What a read serves. Nullable columns are \\`T \\\\| null\\`; relations appear only when \\`include\\` names them. |\n| \\`Insert\\` | What \\`create()\\` accepts. Server-assigned ids are optional; a \\`belongsTo\\` target may be named either way (\\`{ author: 5 }\\` or \\`{ authorId: 5 }\\`). |\n| \\`Update\\` | What \\`update()\\` accepts. Everything optional, and the primary key is not settable. |\n\nA property marked \\`excludeFromApi\\` is absent from all three: the API surface\ndoes not mention it, in either direction. The server holds the same line — a\nread never serves the column and a write naming it is refused — so this is a\nguarantee rather than a description, and nothing generated names a password\nhash.\n\nIf you need an untyped escape hatch, \\`rebase.data.collection(slug)\\` still works —\nbut it is generic over \\`Record<string, unknown>\\` and gives up everything above.\n`\n\t});\n\treturn files;\n}\n//#endregion\nexport { CodegenError, generateSDK, generateTypedefs, indent, toCamelCase, toPascalCase, toSafeIdentifier };\n\n//# sourceMappingURL=index.es.js.map","/**\n * Rebase Schema Doctor — Three-way schema drift detection.\n *\n * Compares:\n * 1. Collection definitions → Generated Drizzle schema (staleness check)\n * 2. Collection definitions → Live PostgreSQL database (structural drift)\n *\n * Run via: rebase doctor\n */\nimport { promises as fsPromises } from \"fs\";\nimport * as fs from \"fs\";\nimport path from \"path\";\nimport { pathToFileURL } from \"url\";\nimport chalk from \"chalk\";\nimport { computeSchemaVersion, CollectionConfig, isPostgresCollectionConfig, Property, NumberProperty, StringProperty, DateProperty, ArrayProperty, MapProperty, RelationProperty, type ResolvedManyToMany, type ResolvedBelongsTo, isManyToMany } from \"@rebasepro/types\";\nimport { generateSchema } from \"./generate-drizzle-schema-logic\";\nimport { compareGeneratedDeclarations, describeDeclarationDifferences } from \"./generated-schema-staleness\";\nimport { columnPgType } from \"./plan/plan-schema\";\nimport type { PgType } from \"./plan/types\";\nimport { generateTypedefs } from \"@rebasepro/codegen\";\nimport { getTableName, resolveCollectionRelations, findRelation, relationalCollections } from \"@rebasepro/common\";\nimport { toSnakeCase } from \"@rebasepro/utils\";\nimport { loadCollectionsFromDirectory } from \"@rebasepro/server\";\n// The report is CLI output, not application logging — see cli-output.ts.\nimport { out, outError } from \"../cli-output\";\nimport { deepestErrorMessage, diagnoseDbError, parseHostInfo } from \"../cli-errors\";\n\n/**\n * Resolve the SQL column name for a property.\n * Uses the explicit `columnName` when set (e.g. from introspection),\n * falling back to `toSnakeCase(propName)` for manually-authored collections.\n */\nconst resolveColumnName = (propName: string, prop?: Property | null): string => {\n if (prop && \"columnName\" in prop && typeof prop.columnName === \"string\") {\n return prop.columnName;\n }\n return toSnakeCase(propName);\n};\n\n// ── Types ────────────────────────────────────────────────────────────────\n\nexport type IssueSeverity = \"error\" | \"warning\" | \"info\";\n\nexport interface DoctorIssue {\n severity: IssueSeverity;\n category: \"missing_table\" | \"missing_column\" | \"type_mismatch\" | \"missing_constraint\" | \"schema_stale\" | \"missing_enum\" | \"enum_value_mismatch\" | \"missing_foreign_key\" | \"sdk_stale\" | \"sdk_not_generated\" | \"sdk_ungeneratable\";\n table?: string;\n column?: string;\n expected?: string;\n actual?: string;\n message: string;\n fix: string;\n}\n\nexport interface DoctorPhase {\n passed: boolean;\n issues: DoctorIssue[];\n /**\n * Why this phase never ran, when it did not.\n *\n * A check that did not happen is a third state, not a passing one. While a\n * skipped phase initialised to `{ passed: true, issues: [] }` it rendered as\n * `✅ Collections → Database: In sync` and counted towards\n * `✓ All schemas are in sync!` — so a project whose connection string was\n * spelled `POSTGRES_URL`, or a CI job that never exported one, got two green\n * ticks and exit 0 against a database with no tables in it.\n */\n skipped?: string;\n /**\n * Why this phase had nothing to compare against, when it had nothing.\n *\n * Distinct from `skipped`, which means \"the check could not run, and that\n * is probably worth fixing\". This one means \"the artifact is optional and\n * you have not asked for it\": no drift is possible, so the run is still a\n * clean bill of health.\n *\n * It exists because the alternative was a contradiction. The typed-SDK\n * phase returned `{ passed: true }` when `generated/sdk/database.types.ts`\n * did not exist, so a fresh project's report read\n * `✅ Collections → SDK Types: In sync` directly above\n * `ℹ Typed SDK not generated (optional).` — one line calling a file\n * synchronised and the next saying it is absent. \"In sync\" is a claim about\n * a comparison, and no comparison happened.\n */\n notApplicable?: string;\n /**\n * The phase was skipped by a fault rather than by a choice.\n *\n * `skipped` covers both: \"no DATABASE_URL, so nothing to compare against\"\n * is a local situation a developer may be perfectly happy with, and \"the\n * DATABASE_URL you set refuses connections\" is not. The report renders them\n * the same way — a check that did not run is a check that did not run — but\n * the exit code must not, or `rebase doctor` in CI goes green against a\n * database it never reached.\n */\n blocked?: boolean;\n}\n\nexport interface DoctorReport {\n collectionsToSchema: DoctorPhase;\n collectionsToSdk: DoctorPhase;\n schemaToDatabase: DoctorPhase;\n summary: { passed: number; skipped: number; notApplicable: number; warnings: number; errors: number };\n}\n\n// ── Column type mapping ──────────────────────────────────────────────────\n\n/**\n * How `information_schema.columns.data_type` spells a planned column type.\n *\n * The catalogue's names are its own — `character varying`, not `varchar`;\n * `ARRAY` for every array whatever its element; `USER-DEFINED` for an enum, a\n * `vector`, and anything else identified by name rather than by shape. So the\n * translation has to exist somewhere. What must *not* exist twice is the step\n * before it: which column a property compiles to.\n *\n * This function was that second step — a fourth copy of the property switch,\n * kept \"in sync\" with the generators by a comment, and already out of step\n * (`geopoint` answered `null` here, so the one type that was genuinely\n * unpersistable was the one the doctor never checked). It reads the schema plan\n * now.\n */\nconst INFORMATION_SCHEMA_NAMES: Record<PgType[\"kind\"], string> = {\n text: \"text\",\n varchar: \"character varying\",\n char: \"character\",\n uuid: \"uuid\",\n // A `CREATE TYPE … AS ENUM` is a name, not a shape.\n enum: \"USER-DEFINED\",\n smallint: \"smallint\",\n integer: \"integer\",\n bigint: \"bigint\",\n // A serial is an integer with a sequence default; the catalogue reports the\n // underlying width and nothing about the sequence.\n smallserial: \"smallint\",\n serial: \"integer\",\n bigserial: \"bigint\",\n real: \"real\",\n doublePrecision: \"double precision\",\n numeric: \"numeric\",\n boolean: \"boolean\",\n timestamptz: \"timestamp with time zone\",\n date: \"date\",\n time: \"time without time zone\",\n json: \"json\",\n jsonb: \"jsonb\",\n // pgvector's type, like every extension type.\n vector: \"USER-DEFINED\",\n bytea: \"bytea\",\n tsvector: \"USER-DEFINED\",\n array: \"ARRAY\"\n};\n\n/**\n * The `data_type` the database should report for a property's column, or `null`\n * when there is nothing to compare.\n *\n * `null` covers the two honest cases: a `relation`, whose column is derived from\n * the relation rather than from the property, and a configuration the planner\n * refuses outright — which is a problem the doctor's other phases report with a\n * message, not a type mismatch to invent one for.\n */\nexport function getExpectedColumnType(prop: Property): string | null {\n // The FK column a relation owns is planned from the relation, and its type\n // is the *target's* key — which a bare property cannot resolve.\n if (prop.type === \"relation\") return null;\n\n // A one-property collection, because the planner asks the questions a\n // column needs in context: is this the primary key, what is the enum type\n // called, what does the link point at. A `reference` whose target is not\n // here resolves to `text`, exactly as it does in a real bundle.\n const collection = {\n slug: \"__doctor__\",\n table: \"__doctor__\",\n properties: { __column__: prop }\n } as unknown as CollectionConfig;\n\n try {\n const type = columnPgType(\"__column__\", prop, collection, () => undefined);\n return INFORMATION_SCHEMA_NAMES[type.kind];\n } catch {\n return null;\n }\n}\n\n// ── Collection loading ───────────────────────────────────────────────────\n\n/**\n * Re-exported so callers keep importing it from here, but the implementation is\n * now shared with the runtime and the generators — a doctor that disagreed with\n * the policy generator about which files are collections would compare the\n * wrong thing.\n */\nexport async function loadCollections(collectionsPath: string): Promise<CollectionConfig[]> {\n return loadCollectionsFromDirectory(collectionsPath);\n}\n\n// ── Phase 1: Collections ↔ Generated Schema ─────────────────────────────\n\nexport async function checkCollectionsVsSchema(\n collections: CollectionConfig[],\n schemaFilePath: string\n): Promise<DoctorPhase> {\n const issues: DoctorIssue[] = [];\n\n // Check if schema file exists\n if (!fs.existsSync(schemaFilePath)) {\n issues.push({\n severity: \"error\",\n category: \"schema_stale\",\n message: \"Generated schema file does not exist.\",\n fix: \"Run `rebase schema generate`\"\n });\n return { passed: false,\nissues };\n }\n\n // Re-generate schema in-memory and compare with file on disk. Only the\n // collections the generator itself will emit — a Firestore collection has\n // no table in the generated file and must not be reported as missing one.\n const postgresCollections = relationalCollections(collections);\n if (postgresCollections.length === 0) {\n return { passed: true,\nissues };\n }\n\n try {\n // Declaration by declaration, not byte by byte. The generated file is a\n // flat list of `export const`s, each the rendering of one piece of the\n // schema plan, so comparing them says *which* table or enum moved —\n // which is what the reader is about to go looking for — and ignores the\n // formatting and commentary the renderer is free to vary on its own.\n const differences = compareGeneratedDeclarations(\n generateSchema(postgresCollections),\n await fsPromises.readFile(schemaFilePath, \"utf-8\")\n );\n\n if (differences.length > 0) {\n issues.push({\n severity: \"warning\",\n category: \"schema_stale\",\n message: \"Generated schema is out of date — collection definitions have changed since last generation. \"\n + `Differs in: ${describeDeclarationDifferences(differences)}.`,\n fix: \"Run `rebase schema generate`\"\n });\n }\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err);\n issues.push({\n severity: \"warning\",\n category: \"schema_stale\",\n message: `Could not regenerate schema for comparison: ${message}`,\n fix: \"Run `rebase schema generate` to verify\"\n });\n }\n\n return { passed: issues.length === 0,\nissues };\n}\n\nexport async function checkCollectionsVsSdk(\n collections: CollectionConfig[],\n sdkFilePath: string\n): Promise<DoctorPhase> {\n const issues: DoctorIssue[] = [];\n\n // The typed SDK is opt-in — nothing in a scaffolded project imports it until\n // you choose to. A project that never generated one isn't drifting, so report\n // it as information rather than a warning; otherwise `doctor` can never come\n // back clean on a fresh project and users learn to ignore its output.\n if (!fs.existsSync(sdkFilePath)) {\n issues.push({\n severity: \"info\",\n category: \"sdk_not_generated\",\n message: \"Typed SDK not generated (optional).\",\n fix: \"Run `rebase generate-sdk` if you want typed collection access\"\n });\n return { passed: true,\nissues,\nnotApplicable: \"not generated (optional)\" };\n }\n\n try {\n const expectedSdk = generateTypedefs(collections);\n const actualSdk = await fsPromises.readFile(sdkFilePath, \"utf-8\");\n\n // Normalize whitespace for comparison\n const normalize = (s: string) =>\n s\n .replace(/\\/\\/.*$/gm, \"\") // strip single-line comments\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\") // strip multi-line comments\n .replace(/\\s+/g, \" \")\n .trim();\n\n if (normalize(expectedSdk) !== normalize(actualSdk)) {\n issues.push({\n severity: \"warning\",\n category: \"sdk_stale\",\n message: \"Generated SDK types are out of date — collection definitions have changed since last SDK generation.\",\n fix: \"Run `rebase generate-sdk`\"\n });\n }\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err);\n\n // A `CodegenError` is not \"the comparison did not run\" — it is the\n // schema saying it cannot produce a valid typed client at all, most\n // often two slugs collapsing onto one accessor. Reporting that as a\n // warning about staleness described the wrong problem and pointed at a\n // command that would fail the same way.\n if (err instanceof Error && err.name === \"CodegenError\") {\n issues.push({\n severity: \"error\",\n category: \"sdk_ungeneratable\",\n message: `The typed SDK cannot be generated from these collections: ${message}`,\n fix: \"Fix the collection definitions named above, then run `rebase generate-sdk`\"\n });\n } else {\n issues.push({\n severity: \"warning\",\n category: \"sdk_stale\",\n message: `Could not regenerate SDK types for comparison: ${message}`,\n fix: \"Run `rebase generate-sdk` to verify\"\n });\n }\n }\n\n return { passed: issues.length === 0,\nissues };\n}\n\n// ── Phase 2: Collections ↔ Database ──────────────────────────────────────\n\ninterface DbColumn {\n table_schema: string;\n table_name: string;\n column_name: string;\n data_type: string;\n is_nullable: string;\n udt_name: string;\n}\n\n\ninterface DbEnumValue {\n enum_name: string;\n enum_value: string;\n}\n\n/**\n * Extensions the collections need, that the database does not have installed.\n *\n * Only `vector` today, and only because a `{ type: \"vector\" }` property plans a\n * column of a type that will not exist. Rebase installs it only where a project\n * has said it may (`database({ extensions: [\"vector\"] })`), so the absence is\n * often correct configuration and a wrong deployment — which is exactly why the\n * report has to name it rather than the first failing INSERT.\n *\n * Exported for its test.\n */\nexport function missingExtensionIssues(\n collections: CollectionConfig[],\n installed: ReadonlySet<string>\n): DoctorIssue[] {\n const usesVector = collections.some(collection =>\n Object.values(collection.properties ?? {})\n .some(property => (property as { type?: string } | null)?.type === \"vector\"));\n\n if (!usesVector || installed.has(\"vector\")) return [];\n\n return [{\n severity: \"error\",\n category: \"missing_table\",\n message: 'A collection declares a `{ type: \"vector\" }` property and the pgvector extension is not installed on this database.',\n fix: 'Declare it — `database({ extensions: [\"vector\"] })` in config/resources.ts — and use an image that ships the library '\n + \"(the scaffold's pgvector/pgvector:pg18; a stock postgres:18 does not). Or install it once by hand: CREATE EXTENSION vector;\"\n }];\n}\n\n/**\n * The stamp the runtime wrote, against the collections on disk.\n *\n * A stamp that disagrees means this database was provisioned from a different\n * set of collections than the ones in this checkout — a colleague's branch, an\n * older deploy, a tenant somebody else migrated. Everything else in this report\n * is then describing a schema somebody else created, which is worth knowing\n * before acting on any of it.\n *\n * A warning, not an error, and never a direction: the stamp is a hash, so it\n * can say the two disagree and never which is ahead. `null` — never stamped —\n * is not a finding: every database provisioned before the stamp existed reads\n * that way, and so does every fresh one.\n */\nexport async function schemaStampIssues(\n pool: { query<T>(text: string): Promise<{ rows: T[] }> },\n collections: CollectionConfig[]\n): Promise<DoctorIssue[]> {\n let stamped: string | null = null;\n try {\n // `to_regclass` so a missing schema or table is a null rather than a\n // thrown 42P01 — the commonest state on a fresh database is neither.\n const present = await pool.query<{ present: boolean }>(\n `SELECT to_regclass('\"rebase\".\"schema_meta\"') IS NOT NULL AS present`\n );\n if (!present.rows[0]?.present) return [];\n const row = await pool.query<{ value: string }>(\n `SELECT value FROM \"rebase\".\"schema_meta\" WHERE key = 'collections_schema_version' LIMIT 1`\n );\n stamped = row.rows[0]?.value ?? null;\n } catch {\n // The stamp is a diagnostic, not a gate. A database that will not\n // answer this question has bigger problems, and they are reported by\n // the checks around this one.\n return [];\n }\n\n if (!stamped) return [];\n const expected = computeSchemaVersion(collections);\n if (stamped === expected) return [];\n\n return [{\n severity: \"warning\",\n category: \"schema_stale\",\n message: `This database was provisioned from a different set of collections (stamp ${stamped.slice(0, 12)}…, this checkout computes ${expected.slice(0, 12)}…).`,\n fix: \"A hash says the two disagree, never which is ahead. Check you are pointed at the database you think you are; then `rebase db push` to bring it to these collections.\"\n }];\n}\n\nexport async function checkCollectionsVsDatabase(\n collections: CollectionConfig[],\n databaseUrl: string\n): Promise<DoctorPhase> {\n const issues: DoctorIssue[] = [];\n\n // Dynamic import to avoid loading pg when not needed\n const pgModule = await import(\"pg\");\n const { Pool } = pgModule.default ?? pgModule;\n const pool = new Pool({ connectionString: databaseUrl });\n\n // Determine all schemas defined by the collections, plus public and rebase\n const schemas = Array.from(new Set([\n \"public\",\n \"rebase\",\n ...relationalCollections(collections)\n .filter(isPostgresCollectionConfig)\n .map(c => c.schema)\n .filter((s): s is string => !!s)\n ]));\n\n try {\n // Fetch all tables in the defined schemas\n const tablesResult = await pool.query<{ table_schema: string; table_name: string }>(\n `SELECT table_schema, table_name \n FROM information_schema.tables \n WHERE table_schema = ANY($1) AND table_type = 'BASE TABLE'`,\n [schemas]\n );\n const existingTables = new Set(tablesResult.rows.map((r) =>\n r.table_schema === \"public\" ? r.table_name : `${r.table_schema}.${r.table_name}`\n ));\n\n // An extension the collections need and the database does not have.\n //\n // The symptom without this is `type \"vector\" does not exist` on the\n // first write to a table that looks perfectly well-formed in the\n // report: the column is planned, the extension is not, and the two\n // facts live in different places. Extensions are opt-in by design —\n // installing one needs an image that ships the library and a role\n // allowed to install it — so this reports, it does not install.\n const extensionsResult = await pool.query<{ extname: string }>(\"SELECT extname FROM pg_extension\");\n const installedExtensions = new Set(extensionsResult.rows.map(r => r.extname));\n issues.push(...missingExtensionIssues(collections, installedExtensions));\n\n // A database provisioned from a different set of collections than the\n // ones on disk.\n //\n // The runtime stamps what it applied; nothing compared it outside boot,\n // and a stamp that disagrees is exactly the state where the rest of\n // this report is describing a schema somebody else's deploy created.\n issues.push(...await schemaStampIssues(pool, collections));\n\n // Fetch all columns in the defined schemas\n const columnsResult = await pool.query<DbColumn>(\n `SELECT table_schema, table_name, column_name, data_type, is_nullable, udt_name\n FROM information_schema.columns\n WHERE table_schema = ANY($1)\n ORDER BY table_schema, table_name, ordinal_position`,\n [schemas]\n );\n const columnsByTable = new Map<string, DbColumn[]>();\n for (const row of columnsResult.rows) {\n const tableSchema = row.table_schema;\n const tableName = row.table_name;\n const key = tableSchema === \"public\" ? tableName : `${tableSchema}.${tableName}`;\n if (!columnsByTable.has(key)) {\n columnsByTable.set(key, []);\n }\n columnsByTable.get(key)!.push(row);\n }\n\n // Fetch enums\n const enumsResult = await pool.query<DbEnumValue>(\n `SELECT t.typname as enum_name, e.enumlabel as enum_value\n FROM pg_type t\n JOIN pg_enum e ON t.oid = e.enumtypid\n ORDER BY t.typname, e.enumsortorder`\n );\n const enumsByName = new Map<string, string[]>();\n for (const row of enumsResult.rows) {\n if (!enumsByName.has(row.enum_name)) {\n enumsByName.set(row.enum_name, []);\n }\n enumsByName.get(row.enum_name)!.push(row.enum_value);\n }\n\n // Fetch foreign key constraints in the defined schemas\n const fksResult = await pool.query<{\n constraint_name: string;\n table_schema: string;\n table_name: string;\n column_name: string;\n foreign_table_schema: string;\n foreign_table_name: string;\n foreign_column_name: string;\n }>(\n `SELECT\n tc.constraint_name,\n tc.table_schema,\n tc.table_name,\n kcu.column_name,\n ccu.table_schema AS foreign_table_schema,\n ccu.table_name AS foreign_table_name,\n ccu.column_name AS foreign_column_name\n FROM information_schema.table_constraints AS tc\n JOIN information_schema.key_column_usage AS kcu\n ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema\n JOIN information_schema.constraint_column_usage AS ccu\n ON ccu.constraint_name = tc.constraint_name AND ccu.table_schema = tc.table_schema\n WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_schema = ANY($1)`,\n [schemas]\n );\n const fksByTable = new Map<string, typeof fksResult.rows>();\n for (const row of fksResult.rows) {\n const tableSchema = row.table_schema;\n const tableName = row.table_name;\n const key = tableSchema === \"public\" ? tableName : `${tableSchema}.${tableName}`;\n if (!fksByTable.has(key)) {\n fksByTable.set(key, []);\n }\n fksByTable.get(key)!.push(row);\n }\n\n // ── Compare each collection against the database ─────────────────\n\n const postgresCollections = relationalCollections(collections);\n\n for (const collection of postgresCollections) {\n const tableName = getTableName(collection);\n const schemaName = (collection as { schema?: string }).schema || \"public\";\n const fullTableName = schemaName === \"public\" ? tableName : `${schemaName}.${tableName}`;\n\n // Check table existence\n if (!existingTables.has(fullTableName)) {\n issues.push({\n severity: \"error\",\n category: \"missing_table\",\n table: fullTableName,\n message: `Table \"${fullTableName}\" does not exist in the database.`,\n fix: \"Run `rebase db push` or `rebase db generate && rebase db migrate`\"\n });\n continue; // Skip column checks for missing tables\n }\n\n const dbColumns = columnsByTable.get(fullTableName) ?? [];\n const dbColumnMap = new Map(dbColumns.map((c) => [c.column_name, c]));\n\n // System columns that Rebase always creates\n const systemColumns = new Set([\"id\", \"created_on\", \"updated_on\"]);\n\n // Check properties → columns\n for (const [propName, prop] of Object.entries(collection.properties ?? {})) {\n if (prop.type === \"relation\") {\n // ResolvedRelation columns are derived from localKey\n const resolvedRelations = resolveCollectionRelations(collection);\n const relation = findRelation(resolvedRelations, (prop as RelationProperty).relation?.relationName ?? propName);\n if (relation?.kind === \"belongsTo\") {\n const fkColName = relation.localKey;\n if (!dbColumnMap.has(fkColName)) {\n issues.push({\n severity: \"error\",\n category: \"missing_column\",\n table: fullTableName,\n column: fkColName,\n message: `Foreign key column \"${fkColName}\" for relation \"${propName}\" is missing from table \"${fullTableName}\".`,\n fix: \"Run `rebase db push` or `rebase db generate && rebase db migrate`\"\n });\n }\n\n // Check FK constraint exists\n const tableFks = fksByTable.get(fullTableName) ?? [];\n let targetTableName = \"unknown\";\n let targetSchemaName = \"public\";\n try {\n const targetColl = relation.target();\n targetTableName = getTableName(targetColl);\n targetSchemaName = (targetColl as { schema?: string }).schema || \"public\";\n } catch { /* ignore */ }\n\n const hasFk = tableFks.some((fk) =>\n fk.column_name === fkColName &&\n fk.foreign_table_name === targetTableName &&\n fk.foreign_table_schema === targetSchemaName\n );\n\n if (dbColumnMap.has(fkColName) && !hasFk) {\n issues.push({\n severity: \"warning\",\n category: \"missing_foreign_key\",\n table: fullTableName,\n column: fkColName,\n message: `Column \"${fkColName}\" exists but has no FOREIGN KEY constraint referencing \"${targetSchemaName === \"public\" ? targetTableName : `${targetSchemaName}.${targetTableName}`}\".`,\n fix: \"Run `rebase db push` or add the constraint manually\"\n });\n }\n }\n continue;\n }\n\n const colName = resolveColumnName(propName, prop);\n\n // Skip system columns — they're handled automatically\n if (systemColumns.has(colName)) continue;\n\n const dbCol = dbColumnMap.get(colName);\n if (!dbCol) {\n issues.push({\n severity: \"error\",\n category: \"missing_column\",\n table: fullTableName,\n column: colName,\n message: `Column \"${colName}\" is defined in collection \"${collection.slug}\" but missing from table \"${fullTableName}\".`,\n fix: \"Run `rebase db push` or `rebase db generate && rebase db migrate`\"\n });\n continue;\n }\n\n // Type check\n const expectedType = getExpectedColumnType(prop);\n if (expectedType) {\n const actualType = dbCol.data_type;\n let isMismatch = actualType !== expectedType;\n if (prop.type === \"vector\" && dbCol.udt_name !== \"vector\") {\n isMismatch = true;\n }\n if (prop.type === \"array\") {\n const ap = prop as ArrayProperty;\n let expectedColType = ap.columnType;\n if (!expectedColType && ap.of && !Array.isArray(ap.of)) {\n const ofProp = ap.of as Property;\n if (ofProp.type === \"string\") expectedColType = \"text[]\";\n else if (ofProp.type === \"number\") expectedColType = ofProp.validation?.integer ? \"integer[]\" : \"numeric[]\";\n else if (ofProp.type === \"boolean\") expectedColType = \"boolean[]\";\n }\n if (expectedColType && expectedColType.endsWith(\"[]\")) {\n if (actualType !== \"ARRAY\") {\n isMismatch = true;\n } else {\n const expectedUdt = expectedColType === \"text[]\" ? \"_text\" :\n expectedColType === \"integer[]\" ? \"_int4\" :\n expectedColType === \"boolean[]\" ? \"_bool\" :\n expectedColType === \"numeric[]\" ? \"_numeric\" : \"\";\n if (expectedUdt && dbCol.udt_name !== expectedUdt) {\n isMismatch = true;\n }\n }\n }\n }\n if (isMismatch) {\n issues.push({\n severity: \"warning\",\n category: \"type_mismatch\",\n table: fullTableName,\n column: colName,\n expected: prop.type === \"vector\" ? \"vector\" : expectedType,\n actual: dbCol.udt_name === \"vector\" ? \"vector\" : actualType,\n message: `Column \"${colName}\" in table \"${fullTableName}\": expected type \"${prop.type === \"vector\" ? \"vector\" : expectedType}\" but found \"${dbCol.udt_name === \"vector\" ? \"vector\" : actualType}\".`,\n fix: \"Review collection property type or run a migration\"\n });\n }\n }\n\n // Enum value check\n if (prop.type === \"string\" && (prop as StringProperty).enum) {\n const enumValues = (prop as StringProperty).enum;\n if (enumValues) {\n const enumName = `${tableName}_${colName}`;\n const dbEnumValues = enumsByName.get(enumName);\n if (!dbEnumValues) {\n issues.push({\n severity: \"warning\",\n category: \"missing_enum\",\n table: fullTableName,\n column: colName,\n expected: enumName,\n message: `Enum type \"${enumName}\" is defined in collection but not found in the database.`,\n fix: \"Run `rebase db push` or `rebase db generate && rebase db migrate`\"\n });\n } else {\n // Compare enum values\n const expectedValues = Array.isArray(enumValues)\n ? enumValues.map((v) => (typeof v === \"string\" ? v : String(v.id)))\n : Object.keys(enumValues);\n\n const missing = expectedValues.filter((v) => !dbEnumValues.includes(v));\n const extra = dbEnumValues.filter((v) => !expectedValues.includes(v));\n\n if (missing.length > 0 || extra.length > 0) {\n const parts: string[] = [];\n if (missing.length > 0) parts.push(`missing: ${missing.join(\", \")}`);\n if (extra.length > 0) parts.push(`extra in DB: ${extra.join(\", \")}`);\n issues.push({\n severity: \"warning\",\n category: \"enum_value_mismatch\",\n table: fullTableName,\n column: colName,\n expected: expectedValues.join(\", \"),\n actual: dbEnumValues.join(\", \"),\n message: `Enum values for \"${colName}\" in table \"${fullTableName}\" are out of sync (${parts.join(\"; \")}).`,\n fix: \"Run `rebase db push` to update the enum\"\n });\n }\n }\n }\n }\n }\n\n // Also check junction tables for many-to-many relations\n const resolvedRelations = resolveCollectionRelations(collection);\n for (const relation of Object.values(resolvedRelations)) {\n if (isManyToMany(relation)) {\n const junctionTable = relation.through.table;\n const junctionSchema = (collection as { schema?: string }).schema || \"public\";\n const fullJunctionTable = junctionSchema === \"public\" ? junctionTable : `${junctionSchema}.${junctionTable}`;\n if (!existingTables.has(fullJunctionTable)) {\n issues.push({\n severity: \"error\",\n category: \"missing_table\",\n table: fullJunctionTable,\n message: `Junction table \"${fullJunctionTable}\" for many-to-many relation \"${relation.relationName}\" is missing.`,\n fix: \"Run `rebase db push` or `rebase db generate && rebase db migrate`\"\n });\n }\n }\n }\n }\n } finally {\n await pool.end();\n }\n\n return { passed: issues.length === 0,\nissues };\n}\n\n// ── Report Rendering ─────────────────────────────────────────────────────\n\nexport function renderReport(report: DoctorReport): void {\n out();\n out(chalk.bold(\" 🩺 Rebase Schema Doctor\"));\n out(chalk.gray(\" ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"));\n out();\n\n // Phase 1\n renderPhase(\"Collections → Generated Schema\", report.collectionsToSchema);\n\n // Phase 2\n renderPhase(\"Collections → Database\", report.schemaToDatabase);\n\n // Phase 3\n renderPhase(\"Collections → SDK Types\", report.collectionsToSdk);\n\n // Summary\n out(chalk.gray(\" ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"));\n const { passed, skipped, notApplicable, warnings, errors } = report.summary;\n\n const parts: string[] = [];\n parts.push(chalk.green(`${passed} passed`));\n if (skipped > 0) parts.push(chalk.yellow(`${skipped} skipped`));\n if (notApplicable > 0) parts.push(chalk.gray(`${notApplicable} not applicable`));\n if (warnings > 0) parts.push(chalk.yellow(`${warnings} warnings`));\n if (errors > 0) parts.push(chalk.red(`${errors} errors`));\n\n out(` Summary: ${parts.join(\", \")}`);\n out();\n\n if (errors > 0) {\n out(chalk.red.bold(\" ✗ Schema drift detected. Run the suggested fixes above.\"));\n } else if (warnings > 0) {\n out(chalk.yellow.bold(\" ⚠ Minor issues detected. Consider running the suggested fixes.\"));\n } else if (skipped > 0) {\n // Never \"all schemas are in sync\" off the back of a check that did not\n // run: a clean bill of health has to come from measurement.\n out(chalk.yellow.bold(` ⚠ Nothing wrong in the checks that ran, but ${skipped} did not run — this is not a clean bill of health.`));\n } else {\n out(chalk.green.bold(\" ✓ All schemas are in sync!\"));\n }\n out();\n}\n\nfunction renderPhase(label: string, phase: DoctorPhase): void {\n // A phase that never ran gets its own marker and its reason. It is neither\n // \"In sync\" nor a drift report; conflating it with the former is what let\n // doctor certify a database it had not opened.\n if (phase.skipped) {\n out(` ${chalk.yellow(\"⏭\")} ${label}: ${chalk.yellow(`skipped (${phase.skipped})`)}`);\n out();\n return;\n }\n\n // Nothing to compare against. Its own marker, and only the remedy under it\n // — the phase header already carries the reason, so re-printing the note\n // would say the same thing twice. See DoctorPhase.notApplicable.\n if (phase.notApplicable) {\n out(` ${chalk.gray(\"➖\")} ${label}: ${chalk.gray(phase.notApplicable)}`);\n for (const issue of phase.issues.filter((i) => i.severity === \"info\")) {\n out(` ${chalk.gray(issue.fix)}`);\n }\n out();\n return;\n }\n\n const issues = phase.issues;\n const errorCount = issues.filter((i) => i.severity === \"error\").length;\n const warnCount = issues.filter((i) => i.severity === \"warning\").length;\n const infoIssues = issues.filter((i) => i.severity === \"info\");\n\n // Informational notes don't make a phase unhealthy, so key the header off\n // real problems rather than `phase.passed` alone.\n if (errorCount === 0 && warnCount === 0) {\n out(` ${chalk.green(\"✅\")} ${label}: ${chalk.green(\"In sync\")}`);\n } else {\n const parts: string[] = [];\n if (errorCount > 0) parts.push(`${errorCount} error${errorCount > 1 ? \"s\" : \"\"}`);\n if (warnCount > 0) parts.push(`${warnCount} warning${warnCount > 1 ? \"s\" : \"\"}`);\n out(` ${chalk.yellow(\"⚠️\")} ${label}: ${chalk.yellow(parts.join(\", \"))}`);\n }\n out();\n\n // Notes render as a quiet one-liner, not a full drift box.\n for (const issue of infoIssues) {\n const fixPart = issue.fix ? chalk.gray(` — ${issue.fix}`) : \"\";\n out(` ${chalk.gray(\"ℹ\")} ${chalk.gray(issue.message)}${fixPart}`);\n out();\n }\n\n for (const issue of issues.filter((i) => i.severity !== \"info\")) {\n const severityIcon = issue.severity === \"error\" ? chalk.red(\"✗\") : chalk.yellow(\"⚠\");\n const categoryLabel = formatCategory(issue.category);\n out(` ${chalk.gray(\"┌─\")} ${severityIcon} ${chalk.bold(categoryLabel)} ${chalk.gray(\"─\".repeat(Math.max(0, 42 - categoryLabel.length)))}`);\n\n if (issue.table) {\n const colPart = issue.column ? ` │ Column: ${chalk.cyan(issue.column)}` : \"\";\n out(` ${chalk.gray(\"│\")} Table: ${chalk.cyan(issue.table)}${colPart}`);\n }\n\n if (issue.expected && issue.actual) {\n out(` ${chalk.gray(\"│\")} Expected: ${chalk.green(issue.expected)} │ Actual: ${chalk.red(issue.actual)}`);\n }\n\n out(` ${chalk.gray(\"│\")} ${issue.message}`);\n out(` ${chalk.gray(\"│\")} Fix: ${chalk.blue(issue.fix)}`);\n out(` ${chalk.gray(\"└\" + \"─\".repeat(48))}`);\n out();\n }\n}\n\nfunction formatCategory(cat: DoctorIssue[\"category\"]): string {\n const labels: Record<DoctorIssue[\"category\"], string> = {\n missing_table: \"Missing Table\",\n missing_column: \"Missing Column\",\n type_mismatch: \"Type Mismatch\",\n missing_constraint: \"Missing Constraint\",\n schema_stale: \"Stale Schema\",\n missing_enum: \"Missing Enum\",\n enum_value_mismatch: \"Enum Value Mismatch\",\n missing_foreign_key: \"Missing Foreign Key\",\n sdk_stale: \"Stale SDK Types\",\n sdk_not_generated: \"SDK Types Not Generated\",\n sdk_ungeneratable: \"SDK Cannot Be Generated\"\n };\n return labels[cat];\n}\n\n// ── Main entry point ─────────────────────────────────────────────────────\n\nexport async function runDoctor(options: {\n collectionsPath: string;\n schemaPath: string;\n sdkPath: string;\n databaseUrl?: string;\n}): Promise<DoctorReport> {\n out();\n out(chalk.bold(\" 🩺 Loading collections...\"));\n const collections = await loadCollections(options.collectionsPath);\n if (collections.length === 0) {\n outError(chalk.red(\" ✗ No collections found.\"));\n process.exit(1);\n }\n out(chalk.gray(` Found ${collections.length} collection(s)`));\n out();\n\n // Phase 1: Collections ↔ Generated Schema\n out(chalk.gray(\" Checking Collections → Generated Schema...\"));\n const collectionsToSchema = await checkCollectionsVsSchema(collections, options.schemaPath);\n\n // Phase 2: Collections ↔ Database (only if we have a DATABASE_URL)\n let schemaToDatabase: DoctorPhase;\n if (options.databaseUrl) {\n out(chalk.gray(\" Checking Collections → Database...\"));\n try {\n schemaToDatabase = await checkCollectionsVsDatabase(collections, options.databaseUrl);\n } catch (err: unknown) {\n // A database that cannot be reached used to end the whole run: the\n // error escaped `runDoctor` and the CLI's last-resort catch printed\n // a stack. So the two phases that need no database — the generated\n // schema and the SDK types, both of which are read off disk — never\n // ran, and the one command whose job is to say what is wrong said\n // only \"Doctor failed\" over a drizzle stack trace.\n const banner = diagnoseDbError(err, options.databaseUrl);\n if (banner) outError(banner);\n const reason = deepestErrorMessage(err)\n ?? (err instanceof Error ? err.message : String(err));\n schemaToDatabase = {\n passed: false,\n issues: [],\n skipped: `could not reach ${parseHostInfo(options.databaseUrl)} — ${reason}`,\n blocked: true\n };\n }\n } else {\n // Not `{ passed: true }`: see DoctorPhase.skipped.\n schemaToDatabase = { passed: false,\nissues: [],\nskipped: \"DATABASE_URL not set\" };\n out(chalk.yellow(\" ⚠ DATABASE_URL not set — skipping database comparison.\"));\n out(chalk.gray(\" Set DATABASE_URL in your .env to enable full drift detection.\"));\n }\n\n // Phase 3: Collections ↔ SDK Types\n out(chalk.gray(\" Checking Collections → SDK Types...\"));\n const collectionsToSdk = await checkCollectionsVsSdk(collections, options.sdkPath);\n\n const phases = [collectionsToSchema, schemaToDatabase, collectionsToSdk];\n const allIssues = phases.flatMap((p) => p.issues);\n const summary = {\n // A skipped phase is not a passing one, however few issues it collected.\n // Nor is one with nothing to compare against: \"passed\" would put a\n // never-generated SDK in the same column as a verified one.\n passed: phases.filter((p) => p.passed && !p.skipped && !p.notApplicable).length,\n skipped: phases.filter((p) => p.skipped).length,\n notApplicable: phases.filter((p) => p.notApplicable && !p.skipped).length,\n warnings: allIssues.filter((i) => i.severity === \"warning\").length,\n errors: allIssues.filter((i) => i.severity === \"error\").length\n };\n\n const report: DoctorReport = { collectionsToSchema,\ncollectionsToSdk,\nschemaToDatabase,\nsummary };\n renderReport(report);\n\n return report;\n}\n"],"mappings":"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"}