@rebasepro/server-postgres 0.24.1-canary.gcb918fc → 0.25.0-canary.ge06257e
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{BranchService-Bsn2wfaj.js → BranchService-DNg4txyf.js} +14 -4
- package/dist/BranchService-DNg4txyf.js.map +1 -0
- package/dist/PostgresBackendDriver.d.ts +13 -0
- package/dist/PostgresBootstrapper.d.ts +10 -0
- package/dist/backup/backup-cli.d.ts +88 -2
- package/dist/backup/backup-service.d.ts +11 -1
- package/dist/backup/pg-tools.d.ts +18 -0
- package/dist/{backup-cli-DnpJi6I7.js → backup-cli-HCEkPcQj.js} +215 -79
- package/dist/backup-cli-HCEkPcQj.js.map +1 -0
- package/dist/{backup-service-BTBB7RXn.js → backup-service-D-UpT7DH.js} +121 -11
- package/dist/backup-service-D-UpT7DH.js.map +1 -0
- package/dist/{boot-ddl-DVpsFN6r.js → boot-ddl-Dnvpmm_T.js} +3 -3
- package/dist/{boot-ddl-DVpsFN6r.js.map → boot-ddl-Dnvpmm_T.js.map} +1 -1
- package/dist/branch-argv.d.ts +18 -6
- package/dist/cli-console-C_ISGwJo.js +255 -0
- package/dist/cli-console-C_ISGwJo.js.map +1 -0
- package/dist/cli-console.d.ts +10 -0
- package/dist/{cli-errors-CaI4ntyD.js → cli-errors-mx4sSjB8.js} +477 -103
- package/dist/cli-errors-mx4sSjB8.js.map +1 -0
- package/dist/cli-errors.d.ts +40 -4
- package/dist/{cli-flags-JNI-pwSf.js → cli-flags-DUozBFyK.js} +70 -6
- package/dist/cli-flags-DUozBFyK.js.map +1 -0
- package/dist/cli-flags.d.ts +21 -0
- package/dist/cli-helpers.d.ts +53 -24
- package/dist/{arg-Dni7MzLB.js → cli-prompt-egtNKkZl.js} +49 -3
- package/dist/cli-prompt-egtNKkZl.js.map +1 -0
- package/dist/cli-prompt.d.ts +19 -0
- package/dist/cli-scratch-database.d.ts +47 -8
- package/dist/cli.js +548 -94
- package/dist/cli.js.map +1 -1
- package/dist/{column-plan-helpers-drZIsSNf.js → column-plan-helpers-D_V4oaZh.js} +2 -2
- package/dist/{column-plan-helpers-drZIsSNf.js.map → column-plan-helpers-D_V4oaZh.js.map} +1 -1
- package/dist/{connection-C9mT8bgf.js → connection-C938nR3E.js} +2 -2
- package/dist/{connection-C9mT8bgf.js.map → connection-C938nR3E.js.map} +1 -1
- package/dist/{databasePoolManager-BIuewidb.js → databasePoolManager-Yqdw14wY.js} +4 -4
- package/dist/{databasePoolManager-BIuewidb.js.map → databasePoolManager-Yqdw14wY.js.map} +1 -1
- package/dist/{deferred-index-build-DP1_SNFo.js → deferred-index-build-Ca6OqKyR.js} +8 -8
- package/dist/{deferred-index-build-DP1_SNFo.js.map → deferred-index-build-Ca6OqKyR.js.map} +1 -1
- package/dist/{doctor-C4WiftHm.js → doctor-CJyuhV1n.js} +8 -8
- package/dist/{doctor-C4WiftHm.js.map → doctor-CJyuhV1n.js.map} +1 -1
- package/dist/{ensure-collection-policies-CXpbeNag.js → ensure-collection-policies-C1ux3V_z.js} +79 -18
- package/dist/ensure-collection-policies-C1ux3V_z.js.map +1 -0
- package/dist/{ensure-collection-tables-BhsU6qUE.js → ensure-collection-tables-Do-Bvb64.js} +10 -10
- package/dist/{ensure-collection-tables-BhsU6qUE.js.map → ensure-collection-tables-Do-Bvb64.js.map} +1 -1
- package/dist/{ensure-tables-CVm_yvzb.js → ensure-tables-Dl_Eh9ow.js} +5 -5
- package/dist/{ensure-tables-CVm_yvzb.js.map → ensure-tables-Dl_Eh9ow.js.map} +1 -1
- package/dist/{enum-labels-kept-D8fI1MoX.js → enum-labels-kept-D9eTIWCz.js} +3 -3
- package/dist/{enum-labels-kept-D8fI1MoX.js.map → enum-labels-kept-D9eTIWCz.js.map} +1 -1
- package/dist/{generate-drizzle-schema-Bz52jH2h.js → generate-drizzle-schema-Bf8canaR.js} +3 -3
- package/dist/{generate-drizzle-schema-Bz52jH2h.js.map → generate-drizzle-schema-Bf8canaR.js.map} +1 -1
- package/dist/{generate-drizzle-schema-logic-B74WN37S.js → generate-drizzle-schema-logic-BaT0H7Gq.js} +4 -4
- package/dist/{generate-drizzle-schema-logic-B74WN37S.js.map → generate-drizzle-schema-logic-BaT0H7Gq.js.map} +1 -1
- package/dist/{generate-files-CdEckX6c.js → generate-files-D50Pxw1G.js} +6 -47
- package/dist/generate-files-D50Pxw1G.js.map +1 -0
- package/dist/generate-next-step-DCd7yiJ2.js +66 -0
- package/dist/generate-next-step-DCd7yiJ2.js.map +1 -0
- package/dist/{generate-postgres-ddl-logic-B64O2oZF.js → generate-postgres-ddl-logic-B4k9djAV.js} +4 -4
- package/dist/{generate-postgres-ddl-logic-B64O2oZF.js.map → generate-postgres-ddl-logic-B4k9djAV.js.map} +1 -1
- package/dist/{generated-schema-staleness-Di9c4ZxN.js → generated-schema-staleness-FlxM2i4i.js} +102 -12
- package/dist/{generated-schema-staleness-Di9c4ZxN.js.map → generated-schema-staleness-FlxM2i4i.js.map} +1 -1
- package/dist/index.es.js +11 -11
- package/dist/{introspect-db-logic-C-Di8dA9.js → introspect-db-logic-j2vYhmdP.js} +145 -9
- package/dist/introspect-db-logic-j2vYhmdP.js.map +1 -0
- package/dist/{introspect-db-queries-D6kAqReI.js → introspect-db-queries-DevUrzll.js} +41 -6
- package/dist/introspect-db-queries-DevUrzll.js.map +1 -0
- package/dist/{open-rebase-CbOur0TU.js → open-rebase-D8rytTP0.js} +95 -45
- package/dist/open-rebase-D8rytTP0.js.map +1 -0
- package/dist/open-rebase.d.ts +12 -4
- package/dist/{pg-error-utils-vJezlXdI.js → pg-error-utils-CdsGYMTm.js} +10 -3
- package/dist/pg-error-utils-CdsGYMTm.js.map +1 -0
- package/dist/{plan-schema-B1YF_tYq.js → plan-schema-CsNgK0qi.js} +4 -4
- package/dist/{plan-schema-B1YF_tYq.js.map → plan-schema-CsNgK0qi.js.map} +1 -1
- package/dist/{policy-drift-Dm8i-s1J.js → policy-drift-CY5P1ZRu.js} +28 -14
- package/dist/{policy-drift-Dm8i-s1J.js.map → policy-drift-CY5P1ZRu.js.map} +1 -1
- package/dist/{push-index-prebuild-Db4AQF4i.js → push-index-prebuild-BDFcfRPo.js} +4 -4
- package/dist/{push-index-prebuild-Db4AQF4i.js.map → push-index-prebuild-BDFcfRPo.js.map} +1 -1
- package/dist/{push-legacy-renames-BK6ilQq2.js → push-legacy-renames-D4bvMWAb.js} +4 -4
- package/dist/{push-legacy-renames-BK6ilQq2.js.map → push-legacy-renames-D4bvMWAb.js.map} +1 -1
- package/dist/push-policy-plan-DTXE6aVa.js +111 -0
- package/dist/push-policy-plan-DTXE6aVa.js.map +1 -0
- package/dist/{render-ddl-CtC3y9Ol.js → render-ddl-CrfEe__B.js} +10 -9
- package/dist/render-ddl-CrfEe__B.js.map +1 -0
- package/dist/{rls-bootstrap-sql-RQUljnJ1.js → rls-bootstrap-sql-Bd-hJk08.js} +3 -58
- package/dist/rls-bootstrap-sql-Bd-hJk08.js.map +1 -0
- package/dist/{rls-enforcement-DvzqlaL1.js → rls-enforcement-D7P4-ZYX.js} +2 -2
- package/dist/{rls-enforcement-DvzqlaL1.js.map → rls-enforcement-D7P4-ZYX.js.map} +1 -1
- package/dist/{__vite-browser-external-BnuHet1e.js → rolldown-runtime-V7rFbMRe.js} +1 -8
- package/dist/schema/atlas-apply-report.d.ts +23 -0
- package/dist/schema/atlas-argv.d.ts +22 -1
- package/dist/schema/atlas-exclude.d.ts +42 -0
- package/dist/schema/atlas-revisions.d.ts +4 -0
- package/dist/schema/carved-out-migration.d.ts +5 -4
- package/dist/schema/destructive-sql.d.ts +23 -1
- package/dist/schema/doctor-cli.js +5 -5
- package/dist/schema/ensure-collection-policies.d.ts +50 -0
- package/dist/schema/generate-drizzle-schema.js +1 -1
- package/dist/schema/generate-next-step.d.ts +11 -0
- package/dist/schema/generate-postgres-ddl.js +2 -2
- package/dist/schema/generated-schema-diff.d.ts +7 -0
- package/dist/schema/generated-schema-staleness.d.ts +7 -1
- package/dist/schema/introspect-db-logic.d.ts +32 -1
- package/dist/schema/introspect-db-queries.d.ts +18 -5
- package/dist/schema/introspect-db.js +53 -52
- package/dist/schema/introspect-db.js.map +1 -1
- package/dist/schema/migrate-baseline.d.ts +51 -0
- package/dist/schema/plan/render-ddl.d.ts +2 -1
- package/dist/schema/push-policy-plan.d.ts +67 -0
- package/dist/schema/undeclarable-constraints.d.ts +59 -0
- package/dist/security/policy-drift.d.ts +5 -0
- package/dist/services/BranchService.d.ts +7 -0
- package/dist/{source-DBDbBTL4.js → source-DB6xJv3p.js} +65 -3
- package/dist/source-DB6xJv3p.js.map +1 -0
- package/dist/utils/connection-string.d.ts +47 -0
- package/dist/{vector-index-CMJKb3C9.js → vector-index-BFhyEaiz.js} +2 -2
- package/dist/{vector-index-CMJKb3C9.js.map → vector-index-BFhyEaiz.js.map} +1 -1
- package/dist/{websocket-BLGc5yhs.js → websocket-DMeXOoyv.js} +41 -13
- package/dist/websocket-DMeXOoyv.js.map +1 -0
- package/package.json +7 -7
- package/dist/BranchService-Bsn2wfaj.js.map +0 -1
- package/dist/__vite-browser-external-BnuHet1e.js.map +0 -1
- package/dist/arg-Dni7MzLB.js.map +0 -1
- package/dist/backup-cli-DnpJi6I7.js.map +0 -1
- package/dist/backup-service-BTBB7RXn.js.map +0 -1
- package/dist/cli-console-Ca6ZlAVU.js +0 -154
- package/dist/cli-console-Ca6ZlAVU.js.map +0 -1
- package/dist/cli-errors-CaI4ntyD.js.map +0 -1
- package/dist/cli-flags-JNI-pwSf.js.map +0 -1
- package/dist/ensure-collection-policies-CXpbeNag.js.map +0 -1
- package/dist/generate-files-CdEckX6c.js.map +0 -1
- package/dist/introspect-db-logic-C-Di8dA9.js.map +0 -1
- package/dist/introspect-db-queries-D6kAqReI.js.map +0 -1
- package/dist/open-rebase-CbOur0TU.js.map +0 -1
- package/dist/pg-error-utils-vJezlXdI.js.map +0 -1
- package/dist/render-ddl-CtC3y9Ol.js.map +0 -1
- package/dist/rls-bootstrap-sql-RQUljnJ1.js.map +0 -1
- package/dist/source-DBDbBTL4.js.map +0 -1
- package/dist/websocket-BLGc5yhs.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pg-error-utils-vJezlXdI.js","names":[],"sources":["../src/utils/pg-error-utils.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\";\nimport { extractCauseMessage } from \"./error-cause\";\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// In its own module so the driver CLI can use it without loading the server\n// (this file imports `@rebasepro/server`).\nexport { extractCauseMessage };\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 \"23001\": // restrict_violation\n // A row other rows still point at, under `ON DELETE RESTRICT` — the\n // default for a required link. `table` is the *referencing* table,\n // which is the one the caller has to clear or reassign first.\n return {\n message: `Cannot complete operation: rows in \"${table ?? \"another table\"}\" still reference this row in ` +\n `\"${context}\", and their foreign key${constraint ? ` (${constraint})` : \"\"} does not allow it to be ` +\n `deleted or its key changed. Delete or reassign those rows first.${detail ? ` ${detail}` : \"\"}${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"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"render-ddl-CtC3y9Ol.js","names":[],"sources":["../src/schema/plan/render-ddl.ts"],"sourcesContent":["/**\n * The SQL files, rendered from a {@link SchemaPlan}.\n *\n * Four files, because Atlas owns exactly one of them:\n *\n * - `schema.sql` — the desired state Atlas diffs the database against.\n * - `policies.sql` — the RLS policies. Atlas has no model for a policy.\n * - `search.sql` — the generated `tsvector` column, its GIN index and the\n * IMMUTABLE helpers its expression calls. Atlas's free tier refuses to\n * *parse* a file containing a function, and it wipes the dev database it\n * diffs against, so a helper seeded there is gone by the time the plan runs.\n * - `vector.sql` — the `vector` columns and their ANN indexes. Atlas cannot\n * *execute* these: it materialises `schema.sql` in a dev database that is\n * created empty and emptied again on every run, so `VECTOR(384)` resolves\n * against a database that structurally cannot have pgvector.\n * - `triggers.sql` — `autoValue: \"on_update\"`, which is a function plus a\n * binding, so it is out for the same reason search is.\n *\n * Everything not in `schema.sql` is excluded from Atlas's view by pattern\n * (`…ExcludePatterns` below) and applied by Rebase — at push time by the CLI,\n * at boot by the schema ensure. Without the excludes a desired state that omits\n * an object reads to Atlas as an instruction to drop it.\n */\nimport {\n fuzzyColumnDefinition,\n searchColumnDefinition,\n searchColumnStamps,\n searchExtensionStatements,\n searchHelperFunctions,\n searchIndexNames,\n searchIndexStatements,\n searchStampGuards\n} from \"../search-column\";\nimport {\n VECTOR_EXTENSION_OPT_IN,\n vectorColumnDefinition,\n vectorDimensionGuard,\n vectorExtensionDeclared,\n vectorExtensionStatement,\n vectorIndexNames,\n vectorIndexStatements\n} from \"../vector-index\";\nimport { collectionIndexStatements } from \"../collection-index\";\nimport { quoteSqlLiteral } from \"./plan-schema\";\nimport { triggerStatements } from \"./updated-at-trigger\";\nimport type { ColumnPlan, PgType, PolicyPlan, SchemaPlan, TablePlan } from \"./types\";\n\n/** What `schema.sql` may leave out, because another file owns it. */\nexport interface DdlRenderOptions {\n /** RLS policies. `false` puts them in `policies.sql` alone. */\n includePolicies?: boolean;\n /** The search column, its helpers and its index. See `search.sql`. */\n includeSearch?: boolean;\n /** The vector columns and their ANN indexes. See `vector.sql`. */\n includeVector?: boolean;\n}\n\n/**\n * A {@link PgType} as Postgres spells it.\n *\n * The one place. `getSqlColumnType` and `getDrizzleColumn` each carried their\n * own ladder and disagreed about `VARCHAR` widths, about `geopoint`, and about\n * whether an `increment` id honours `columnType`.\n */\nexport const renderPgType = (type: PgType): string => {\n switch (type.kind) {\n case \"text\": return \"TEXT\";\n case \"varchar\": return `VARCHAR(${type.length})`;\n case \"char\": return `CHAR(${type.length})`;\n case \"uuid\": return \"UUID\";\n case \"enum\": return `\"${type.schema}\".\"${type.name}\"`;\n case \"smallint\": return \"SMALLINT\";\n case \"integer\": return \"INTEGER\";\n case \"bigint\": return \"BIGINT\";\n case \"smallserial\": return \"SMALLSERIAL\";\n case \"serial\": return \"SERIAL\";\n case \"bigserial\": return \"BIGSERIAL\";\n case \"real\": return \"REAL\";\n case \"doublePrecision\": return \"DOUBLE PRECISION\";\n case \"numeric\":\n if (type.precision === undefined) return \"NUMERIC\";\n return type.scale === undefined\n ? `NUMERIC(${type.precision})`\n : `NUMERIC(${type.precision}, ${type.scale})`;\n case \"boolean\": return \"BOOLEAN\";\n case \"timestamptz\": return \"TIMESTAMP WITH TIME ZONE\";\n case \"date\": return \"DATE\";\n case \"time\": return \"TIME\";\n case \"json\": return \"JSON\";\n case \"jsonb\": return \"JSONB\";\n case \"vector\": return `VECTOR(${type.dimensions})`;\n case \"bytea\": return \"BYTEA\";\n case \"tsvector\": return \"tsvector\";\n case \"array\": return `${renderPgType(type.of)}[]`;\n }\n};\n\n/**\n * Everything after the column name, in one order for every SQL emitter.\n *\n * `CREATE TABLE` and `ADD COLUMN` used to assemble this separately, which is\n * how a required relation column came out `NOT NULL` after `db push` and\n * nullable after a boot-ensure. `identity` is part of the type rather than a\n * DEFAULT, which is why it is rendered here and not in the DEFAULT slot.\n */\nexport const renderColumnDefinition = (column: ColumnPlan): string => {\n // `auth-users-columns` is the single description of what an auth user\n // table's columns must be, and it hands over a rendered definition.\n if (column.sqlDefinition) return column.sqlDefinition;\n\n if (column.generated) {\n return `${renderPgType(column.type)} GENERATED ALWAYS AS (${column.generated.expression}) STORED`;\n }\n\n let definition = renderPgType(column.type);\n if (column.default?.kind === \"identity\") definition += \" GENERATED BY DEFAULT AS IDENTITY\";\n if (column.primaryKey) definition += \" PRIMARY KEY\";\n if (column.unique) definition += \" UNIQUE\";\n if (column.default && column.default.kind !== \"identity\") {\n definition += ` DEFAULT ${column.default.kind === \"sql\" ? column.default.expression : column.default.sql}`;\n }\n if (!column.nullable && !column.primaryKey) definition += \" NOT NULL\";\n return definition;\n};\n\n/**\n * `PRIMARY KEY (\"a\", \"b\")`, the clause a composite key takes inside a\n * `CREATE TABLE`. Shared by `schema.sql` and boot-ensure's `CREATE TABLE`, so\n * the two cannot list the columns differently.\n */\nexport const renderPrimaryKeyConstraint = (table: Pick<TablePlan, \"primaryKey\">): string =>\n `PRIMARY KEY (${table.primaryKey.map(c => `\"${c}\"`).join(\", \")})`;\n\n/** `\"col\" TYPE …`, as it appears inside a `CREATE TABLE`. */\nconst renderColumn = (column: ColumnPlan): string =>\n `\"${column.column}\" ${renderColumnDefinition(column)}`;\n\n// ── Policies ─────────────────────────────────────────────────────────────────\n\n/**\n * One policy as its `DROP` / `CREATE` pair — each a complete statement.\n *\n * This is the primitive the boot-time RLS applier runs one statement at a time\n * (the runtime's DB handle speaks the extended query protocol, which forbids\n * multiple commands in one execute), while `db push` writes the joined string.\n */\nexport const renderPolicyStatements = (\n table: Pick<TablePlan, \"schema\" | \"table\">,\n policy: PolicyPlan\n): string[] => {\n const target = `\"${table.schema}\".\"${table.table}\"`;\n let create = `CREATE POLICY \"${policy.name}\" ON ${target} AS ${policy.mode.toUpperCase()} ` +\n `FOR ${policy.operation.toUpperCase()} TO ${policy.roles.map(r => `\"${r}\"`).join(\", \")}`;\n if (policy.using) create += ` USING (${policy.using})`;\n if (policy.withCheck) create += ` WITH CHECK (${policy.withCheck})`;\n // The one policy an older naming stored this rule's operations under —\n // see `PolicyPlan.supersedes`. In the same per-table transaction, so a\n // request sees the old policy or the new ones, never neither.\n const superseded = (policy.supersedes ?? []).map(name => `DROP POLICY IF EXISTS \"${name}\" ON ${target};`);\n return [...superseded, `DROP POLICY IF EXISTS \"${policy.name}\" ON ${target};`, `${create};`];\n};\n\n/** `ALTER TABLE … ENABLE ROW LEVEL SECURITY;` — every generated table is locked. */\nexport const renderEnableRls = (table: Pick<TablePlan, \"schema\" | \"table\">): string =>\n `ALTER TABLE \"${table.schema}\".\"${table.table}\" ENABLE ROW LEVEL SECURITY;`;\n\nconst statementsToDdl = (statements: string[]): string => statements.map(s => `${s}\\n`).join(\"\");\n\n// ── schema.sql ───────────────────────────────────────────────────────────────\n\nexport function renderPostgresDdl(plan: SchemaPlan, options: DdlRenderOptions = {}): string {\n const includePolicies = options.includePolicies !== false;\n const includeSearch = options.includeSearch !== false;\n const includeVector = options.includeVector !== false;\n\n let ddl = \"-- This file is auto-generated by the Rebase DDL generator. Do not edit manually.\\n\\n\";\n\n // 1. Custom schemas.\n //\n // `rebase` is unconditional and load-bearing. The RLS helper functions live\n // in it, so the migration preamble creates it — which puts it in Atlas's\n // replayed state, and anything in that state but absent from this desired\n // schema gets a `DROP SCHEMA … CASCADE` planned against it. That would take\n // the auth tables.\n plan.schemas.forEach(schema => { ddl += `CREATE SCHEMA IF NOT EXISTS \"${schema}\";\\n`; });\n if (plan.schemas.length > 0) ddl += \"\\n\";\n\n // 1b. Search support, for collections that opted in.\n //\n // Extensions and helper functions come before every CREATE TABLE because a\n // generated column's expression is resolved at creation time: a table whose\n // search column calls `rebase_search_text` cannot be created before that\n // function exists. Both are `IF NOT EXISTS` / `OR REPLACE`, so a file\n // replayed against a live database is a no-op here.\n const searchTables = plan.tables.filter(t => t.search);\n if (searchTables.length > 0) {\n if (!includeSearch) {\n ddl += \"-- Full-text search support lives in `search.sql`, applied separately.\\n\\n\";\n } else {\n const extensions = Array.from(new Set(searchTables.flatMap(t => searchExtensionStatements(t.search!))));\n const helpers = Array.from(new Set(searchTables.flatMap(t => searchHelperFunctions(t.search!))));\n ddl += \"-- Full-text search support (collections declaring a `search` block)\\n\";\n extensions.forEach(s => { ddl += `${s}\\n`; });\n if (extensions.length > 0) ddl += \"\\n\";\n helpers.forEach(s => { ddl += `${s}\\n\\n`; });\n }\n }\n\n // 1c. Vector support. Only ever a note, never a statement — unlike 1b.\n // `CREATE EXTENSION vector` is the one thing this file must never carry\n // whichever way the flag goes: Atlas rejects an extension in a desired\n // state as a paid feature.\n if (!includeVector && plan.tables.some(t => t.vectorColumns.length > 0)) {\n ddl += \"-- Vector columns and their ANN indexes live in `vector.sql`, applied separately.\\n\\n\";\n }\n\n // 1d. `autoValue: \"on_update\"`. A trigger is a function plus a binding, so\n // it is out of Atlas's sight for the reason search is — and out\n // unconditionally, because `schema.sql` has no flag that would make Atlas\n // able to read one.\n if (plan.tables.some(t => t.triggers.length > 0)) {\n ddl += \"-- `autoValue: \\\"on_update\\\"` triggers live in `triggers.sql`, applied separately.\\n\\n\";\n }\n\n // 2. Enum types.\n plan.enums.forEach(enumPlan => {\n ddl += `CREATE TYPE \"${enumPlan.schema}\".\"${enumPlan.name}\" AS ENUM (${enumPlan.labels.map(quoteSqlLiteral).join(\", \")});\\n`;\n });\n if (ddl.endsWith(\";\\n\")) ddl += \"\\n\";\n\n // 3. Tables, in the plan's order — each collection, then any junction its\n // relations imply.\n const fkStatements: string[] = [];\n // Indexes follow the tables for the same reason the FK constraints do: the\n // table has to exist first.\n const indexStatements: string[] = [];\n // Policies are emitted after every CREATE TABLE, like the FK constraints: a\n // policy may reference other tables (a junction's derived policies always\n // reference both endpoints; `policy.existsIn` references a join table), and\n // CREATE POLICY validates those relations at creation time.\n const policyStatements: string[] = [];\n\n for (const table of plan.tables) {\n const emitted = table.columns.filter(column => {\n // A vector column is not Atlas's — `vector.sql` adds it. Filtered\n // here rather than upstream so the column keeps its place in the\n // generator's order when it *is* included, which is what the\n // derived-names contract renders.\n if (!includeVector && column.type.kind === \"vector\") return false;\n if (!includeSearch && column.source.kind === \"search\") return false;\n // A declared property already emits this column (an explicit\n // `postId` beside the `belongsTo` that uses it); the relation\n // contributes only the constraint.\n if (column.columnOwnedByProperty) return false;\n return true;\n });\n\n ddl += `CREATE TABLE \"${table.schema}\".\"${table.table}\" (\\n`;\n const lines = emitted.map(column => ` ${renderColumn(column)}`);\n // A key over several columns — a junction's two endpoints, or a\n // collection with several `isId` properties — is one table constraint;\n // a single-column key is already inline on its column.\n if (table.primaryKey.length > 1) {\n lines.push(` ${renderPrimaryKeyConstraint(table)}`);\n }\n ddl += lines.join(\",\\n\");\n ddl += \"\\n);\\n\\n\";\n\n for (const column of table.columns) {\n if (column.foreignKey) fkStatements.push(`${column.foreignKey.sql};`);\n }\n\n if (includeSearch && table.search) {\n indexStatements.push(...searchIndexStatements(table.search));\n }\n // ANN indexes are emitted with the other indexes rather than inline,\n // because `CREATE INDEX` is a statement and a column definition is not\n // — and because a column too wide for pgvector to index still needs its\n // column. Absent alongside their column when vector is carved out.\n if (includeVector && table.vector) {\n indexStatements.push(...vectorIndexStatements(table.vector));\n for (const skip of table.vector.skipped) {\n indexStatements.push(`-- No ANN index on \"${skip.schema}\".\"${skip.table}\".\"${skip.column}\": ${skip.reason}`);\n }\n }\n // The collection's own `indexes:` block. Last of the three index\n // producers, and the only one the developer wrote deliberately rather\n // than getting as a side effect of another feature.\n indexStatements.push(...collectionIndexStatements(table.indexes));\n\n if (includePolicies) {\n // No FORCE: authenticated requests run as the non-owner\n // `rebase_user` role, which plain ENABLE already binds. The owner\n // (server context) must bypass — it is the trusted plane.\n ddl += `${renderEnableRls(table)}\\n`;\n ddl += \"\\n\";\n for (const policy of table.policies) {\n policyStatements.push(statementsToDdl(renderPolicyStatements(table, policy)));\n }\n }\n }\n\n // Indexes before foreign keys. Today every FK targets a primary key created\n // inline, so the order is safe by accident; the moment a declared\n // `unique: true` index can back an FK target, a constraint emitted first\n // would reference an index that does not exist yet.\n if (indexStatements.length > 0) {\n ddl += \"-- Indexes\\n\";\n ddl += indexStatements.join(\"\\n\") + \"\\n\\n\";\n }\n\n if (fkStatements.length > 0) {\n ddl += \"-- Foreign Key Constraints\\n\";\n ddl += fkStatements.join(\"\\n\") + \"\\n\\n\";\n }\n\n if (policyStatements.length > 0) {\n ddl += \"-- Row Level Security Policies\\n\";\n ddl += policyStatements.join(\"\");\n ddl += \"\\n\";\n }\n\n return ddl;\n}\n\n// ── policies.sql ─────────────────────────────────────────────────────────────\n\nexport function renderPoliciesDdl(plan: SchemaPlan): string {\n let ddl = \"-- This file contains RLS policies generated by Rebase. Applied separately from migrations.\\n\\n\";\n\n // Collections first, then the junctions no collection declares — the order\n // the boot-time applier plans them in.\n for (const table of plan.tables.filter(t => t.kind === \"collection\")) {\n ddl += `${renderEnableRls(table)}\\n`;\n ddl += \"\\n\";\n if (table.policies.length === 0) continue;\n let announced: string | undefined;\n for (const policy of table.policies) {\n // Say which policies the author did not write. They are permissive,\n // so they OR with the declared rules and widen the final ACL beyond\n // what `securityRules` reads like — and re-appear after any manual\n // DROP, because a push asserts the declared state. Once per *rule*:\n // one rule with four operations is one thing the author did not\n // write, not four.\n if (policy.injected && policy.ruleKey !== announced) {\n ddl += \"-- Injected by Rebase (not from this collection's securityRules).\\n\";\n ddl += `-- Set \\`disableDefaultPolicies: true\\` on \"${table.slug}\" to drop these and own its RLS outright.\\n`;\n }\n announced = policy.ruleKey;\n ddl += statementsToDdl(renderPolicyStatements(table, policy));\n }\n ddl += \"\\n\";\n }\n\n for (const table of plan.tables.filter(t => t.kind === \"junction\")) {\n ddl += `${renderEnableRls(table)}\\n`;\n ddl += \"\\n\";\n if (table.policies.length === 0) continue;\n const declaringSlugs = (table.declaringSlugs ?? []).join('\", \"');\n ddl += `-- Derived by Rebase for the junction \"${table.table}\" (no collection declares it).\\n`;\n ddl += \"-- Reads require both endpoint rows to be visible; writes follow the update\\n\";\n ddl += `-- rules of \"${declaringSlugs}\". Set \\`disableDefaultPolicies: true\\` on the\\n`;\n ddl += \"-- declaring collection(s) to drop these and police the junction yourself.\\n\";\n for (const policy of table.policies) {\n ddl += statementsToDdl(renderPolicyStatements(table, policy));\n }\n ddl += \"\\n\";\n }\n\n return ddl;\n}\n\n// ── search.sql ───────────────────────────────────────────────────────────────\n\nexport function renderSearchDdl(plan: SchemaPlan): string {\n const tables = plan.tables.filter(t => t.search);\n if (tables.length === 0) return \"\";\n\n const extensions = Array.from(new Set(tables.flatMap(t => searchExtensionStatements(t.search!))));\n const helpers = Array.from(new Set(tables.flatMap(t => searchHelperFunctions(t.search!))));\n\n let ddl = \"-- This file is auto-generated by the Rebase DDL generator. Do not edit manually.\\n\";\n ddl += \"--\\n\";\n ddl += \"-- Full-text search for the collections declaring a `search` block.\\n\";\n ddl += \"-- Applied by Rebase, not by Atlas — see generatePostgresSearchDdl.\\n\\n\";\n\n extensions.forEach(s => { ddl += `${s}\\n`; });\n if (extensions.length > 0) ddl += \"\\n\";\n helpers.forEach(s => { ddl += `${s}\\n\\n`; });\n\n for (const table of tables) {\n const spec = table.search!;\n const target = `\"${table.schema}\".\"${table.table}\"`;\n // ADD COLUMN IF NOT EXISTS rather than the inline definition the\n // CREATE TABLE would use: by the time this runs the table exists,\n // whether Atlas just created it or it has been live for a year. The\n // guard first, because `ADD COLUMN IF NOT EXISTS` is a no-op against an\n // existing column — without it the file would report success, leave the\n // old expression in place, and then stamp it as current.\n searchStampGuards(spec).forEach(s => { ddl += `${s}\\n`; });\n ddl += `ALTER TABLE ${target} ADD COLUMN IF NOT EXISTS ${searchColumnDefinition(spec)};\\n`;\n const fuzzy = fuzzyColumnDefinition(spec);\n if (fuzzy) ddl += `ALTER TABLE ${target} ADD COLUMN IF NOT EXISTS ${fuzzy};\\n`;\n searchColumnStamps(spec).forEach(s => { ddl += `${s.sql}\\n`; });\n searchIndexStatements(spec).forEach(s => { ddl += `${s}\\n`; });\n ddl += \"\\n\";\n }\n\n return ddl;\n}\n\n/**\n * Glob patterns telling Atlas to leave an object alone.\n *\n * Fully qualified, `schema.table.object`, matching the include list. The\n * two-part form is what Atlas wants when the connection URL scopes it to one\n * schema and is *silently ignored* otherwise: it reads `posts.search_vector` as\n * a table named `search_vector` in a schema named `posts`, matches nothing, and\n * reports no error for the pattern that never fired.\n */\nexport const searchExcludePatternsOf = (plan: SchemaPlan): string[] => {\n const patterns: string[] = [];\n for (const table of plan.tables) {\n if (!table.search) continue;\n for (const column of table.columns) {\n if (column.source.kind !== \"search\") continue;\n patterns.push(`${table.schema}.${table.table}.${column.column}`);\n }\n for (const index of searchIndexNames(table.search)) {\n patterns.push(`${table.schema}.${table.table}.${index}`);\n }\n }\n return patterns;\n};\n\n// ── vector.sql ───────────────────────────────────────────────────────────────\n\nexport function renderVectorDdl(plan: SchemaPlan): string {\n const tables = plan.tables.filter(t => t.vectorColumns.length > 0);\n if (tables.length === 0) return \"\";\n\n let ddl = \"-- This file is auto-generated by the Rebase DDL generator. Do not edit manually.\\n\";\n ddl += \"--\\n\";\n ddl += \"-- Vector columns and their ANN indexes, for the collections declaring a\\n\";\n ddl += \"-- `vector` property. Applied by Rebase, not by Atlas — see\\n\";\n ddl += \"-- generatePostgresVectorDdl.\\n\\n\";\n // The note is not decoration. Someone reading this file after a failed push\n // is looking for the reason the type does not exist, and \"no CREATE\n // EXTENSION here\" is invisible unless it is written down.\n ddl += vectorExtensionDeclared(plan.options.databaseExtensions)\n ? `${vectorExtensionStatement()}\\n\\n`\n : \"-- pgvector is not installed from here: no database declared it. To let Rebase\\n\" +\n `-- install it, add ${VECTOR_EXTENSION_OPT_IN} in config/resources.ts.\\n` +\n \"-- Otherwise install it once by hand; the column below needs the type either way.\\n\\n\";\n\n for (const table of tables) {\n const target = `\"${table.schema}\".\"${table.table}\"`;\n for (const spec of table.vectorColumns) {\n // Guard before the ADD, so a changed `dimensions` is reported\n // rather than skipped over — see `vectorDimensionGuard`.\n ddl += `${vectorDimensionGuard(spec)}\\n`;\n ddl += `ALTER TABLE ${target} ADD COLUMN IF NOT EXISTS ${vectorColumnDefinition(spec)};\\n`;\n }\n if (table.vector) {\n vectorIndexStatements(table.vector).forEach(s => { ddl += `${s}\\n`; });\n for (const skip of table.vector.skipped) {\n ddl += `-- No ANN index on \"${skip.schema}\".\"${skip.table}\".\"${skip.column}\": ${skip.reason}\\n`;\n }\n }\n ddl += \"\\n\";\n }\n\n return ddl;\n}\n\n/** @see searchExcludePatternsOf */\nexport const vectorExcludePatternsOf = (plan: SchemaPlan): string[] => {\n const patterns: string[] = [];\n for (const table of plan.tables) {\n const names = [\n ...table.vectorColumns.map(spec => spec.column),\n ...(table.vector ? vectorIndexNames(table.vector) : [])\n ];\n for (const name of names) patterns.push(`${table.schema}.${table.table}.${name}`);\n }\n return patterns;\n};\n\n// ── triggers.sql ─────────────────────────────────────────────────────────────\n\n/**\n * `autoValue: \"on_update\"`, as the file Rebase applies itself.\n *\n * Empty when nothing declares one — the caller writes no file then, and removes\n * a stale one.\n */\nexport function renderTriggersDdl(plan: SchemaPlan): string {\n const tables = plan.tables.filter(t => t.triggers.length > 0);\n if (tables.length === 0) return \"\";\n\n let ddl = \"-- This file is auto-generated by the Rebase DDL generator. Do not edit manually.\\n\";\n ddl += \"--\\n\";\n ddl += \"-- `updated_at` triggers, for the properties declaring `autoValue: \\\"on_update\\\"`.\\n\";\n ddl += \"-- Applied by Rebase, not by Atlas — a trigger is a function plus a binding,\\n\";\n ddl += \"-- and Atlas's free tier refuses to parse a desired state containing one.\\n\\n\";\n\n for (const statement of plan.functions.filter(isTriggerFunction)) {\n ddl += `${statement}\\n\\n`;\n }\n for (const table of tables) {\n for (const trigger of table.triggers) {\n triggerStatements(trigger).forEach(s => { ddl += `${s}\\n`; });\n }\n ddl += \"\\n\";\n }\n return ddl;\n}\n\nconst isTriggerFunction = (statement: string): boolean => statement.includes(\"RETURNS trigger\");\n\n/** @see searchExcludePatternsOf */\nexport const triggerExcludePatternsOf = (plan: SchemaPlan): string[] =>\n plan.tables.flatMap(table => table.triggers.map(t => `${table.schema}.${table.table}.${t.name}`));\n"],"mappings":"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"}
|