@rebasepro/server-postgres 0.24.0 → 0.24.1-canary.g56af4a4
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-BRt78gfa.js → BranchService-Bsn2wfaj.js} +3 -2
- package/dist/{BranchService-BRt78gfa.js.map → BranchService-Bsn2wfaj.js.map} +1 -1
- package/dist/PostgresBackendDriver.d.ts +83 -2
- package/dist/auth/ensure-tables.d.ts +12 -0
- package/dist/auth/services.d.ts +6 -0
- package/dist/backup/backup-cli.d.ts +14 -0
- package/dist/backup/backup-service.d.ts +2 -2
- package/dist/backup/pg-binary.d.ts +5 -0
- package/dist/{backup-cli-DW5p9_zv.js → backup-cli-aUfRv16h.js} +37 -29
- package/dist/backup-cli-aUfRv16h.js.map +1 -0
- package/dist/{backup-service-EwcDVG-8.js → backup-service-BTBB7RXn.js} +55 -21
- package/dist/backup-service-BTBB7RXn.js.map +1 -0
- package/dist/boot-ddl-DVpsFN6r.js +105 -0
- package/dist/boot-ddl-DVpsFN6r.js.map +1 -0
- package/dist/cli-errors-CaI4ntyD.js +1919 -0
- package/dist/cli-errors-CaI4ntyD.js.map +1 -0
- package/dist/cli-errors.d.ts +51 -5
- package/dist/{cli-flags-BglvpjHv.js → cli-flags-B-Dawocl.js} +5 -2
- package/dist/cli-flags-B-Dawocl.js.map +1 -0
- package/dist/cli-helpers.d.ts +40 -0
- package/dist/cli-output.d.ts +16 -28
- package/dist/cli-push.d.ts +87 -0
- package/dist/cli.js +563 -182
- package/dist/cli.js.map +1 -1
- package/dist/{column-plan-helpers-1-LQD0yI.js → column-plan-helpers-drZIsSNf.js} +10 -4
- package/dist/{column-plan-helpers-1-LQD0yI.js.map → column-plan-helpers-drZIsSNf.js.map} +1 -1
- package/dist/{databasePoolManager-Bj5FbeAs.js → connection-BPoNILnD.js} +19 -110
- package/dist/connection-BPoNILnD.js.map +1 -0
- package/dist/connection.d.ts +9 -1
- package/dist/databasePoolManager-BkEy2w18.js +119 -0
- package/dist/databasePoolManager-BkEy2w18.js.map +1 -0
- package/dist/{ensure-collection-tables-kkkHk8oo.js → deferred-index-build-DP1_SNFo.js} +484 -346
- package/dist/deferred-index-build-DP1_SNFo.js.map +1 -0
- package/dist/{doctor-C-sYWbmt.js → doctor-C4WiftHm.js} +63 -52
- package/dist/doctor-C4WiftHm.js.map +1 -0
- package/dist/ensure-collection-policies-CXpbeNag.js +541 -0
- package/dist/ensure-collection-policies-CXpbeNag.js.map +1 -0
- package/dist/ensure-collection-tables-BhsU6qUE.js +532 -0
- package/dist/ensure-collection-tables-BhsU6qUE.js.map +1 -0
- package/dist/{ensure-tables-BmI_tRxc.js → ensure-tables-CHsgyLqg.js} +172 -76
- package/dist/ensure-tables-CHsgyLqg.js.map +1 -0
- package/dist/enum-labels-kept-D8fI1MoX.js +56 -0
- package/dist/enum-labels-kept-D8fI1MoX.js.map +1 -0
- package/dist/generate-drizzle-schema-Bz52jH2h.js +57 -0
- package/dist/generate-drizzle-schema-Bz52jH2h.js.map +1 -0
- package/dist/{generate-drizzle-schema-logic-sSFDp6LR.js → generate-drizzle-schema-logic-B74WN37S.js} +5 -5
- package/dist/generate-drizzle-schema-logic-B74WN37S.js.map +1 -0
- package/dist/generate-files-CdEckX6c.js +199 -0
- package/dist/generate-files-CdEckX6c.js.map +1 -0
- package/dist/{generate-postgres-ddl-logic-BJsLaVNX.js → generate-postgres-ddl-logic-B64O2oZF.js} +3 -3
- package/dist/{generate-postgres-ddl-logic-BJsLaVNX.js.map → generate-postgres-ddl-logic-B64O2oZF.js.map} +1 -1
- package/dist/generated-sql.d.ts +8 -2
- package/dist/history/HistoryService.d.ts +38 -6
- package/dist/history/ensure-history-table.d.ts +42 -4
- package/dist/index.es.js +2922 -7475
- package/dist/index.es.js.map +1 -1
- package/dist/{introspect-db-logic-kCETE8TY.js → introspect-db-logic-C-Di8dA9.js} +44 -6
- package/dist/introspect-db-logic-C-Di8dA9.js.map +1 -0
- package/dist/{introspect-db-queries-C_Q5VgQw.js → introspect-db-queries-D6kAqReI.js} +23 -1
- package/dist/introspect-db-queries-D6kAqReI.js.map +1 -0
- package/dist/pg-error-utils-vJezlXdI.js +388 -0
- package/dist/pg-error-utils-vJezlXdI.js.map +1 -0
- package/dist/{plan-schema-DU9exq6C.js → plan-schema-B1YF_tYq.js} +151 -41
- package/dist/plan-schema-B1YF_tYq.js.map +1 -0
- package/dist/{policy-drift-B-J2hhm0.js → policy-drift-Dm8i-s1J.js} +23 -6
- package/dist/policy-drift-Dm8i-s1J.js.map +1 -0
- package/dist/push-index-prebuild-Db4AQF4i.js +45 -0
- package/dist/push-index-prebuild-Db4AQF4i.js.map +1 -0
- package/dist/push-legacy-renames-BK6ilQq2.js +34 -0
- package/dist/push-legacy-renames-BK6ilQq2.js.map +1 -0
- package/dist/{render-ddl-Ds2t_d9V.js → render-ddl-CtC3y9Ol.js} +8 -4
- package/dist/render-ddl-CtC3y9Ol.js.map +1 -0
- package/dist/{rls-bootstrap-sql-_KNnjanK.js → rls-bootstrap-sql-RQUljnJ1.js} +125 -1270
- package/dist/rls-bootstrap-sql-RQUljnJ1.js.map +1 -0
- package/dist/{rls-enforcement-CfXOJJaW.js → rls-enforcement-DvzqlaL1.js} +129 -17
- package/dist/rls-enforcement-DvzqlaL1.js.map +1 -0
- package/dist/schema/auth-schema.d.ts +2 -2
- package/dist/schema/boot-ddl.d.ts +47 -0
- package/dist/schema/collection-index.d.ts +45 -0
- package/dist/schema/column-plan-helpers.d.ts +5 -0
- package/dist/schema/deferred-index-build.d.ts +131 -0
- package/dist/schema/destructive-sql.d.ts +7 -0
- package/dist/schema/doctor-cli.js +110 -6
- package/dist/schema/doctor-cli.js.map +1 -1
- package/dist/schema/doctor-search-scale.d.ts +45 -0
- package/dist/schema/doctor.d.ts +20 -1
- package/dist/schema/ensure-collection-policies.d.ts +73 -35
- package/dist/schema/ensure-collection-tables.d.ts +56 -3
- package/dist/schema/enum-labels-kept.d.ts +26 -0
- package/dist/schema/generate-drizzle-schema.js +1 -1
- package/dist/schema/generate-files.d.ts +44 -0
- package/dist/schema/generate-next-step.d.ts +0 -13
- package/dist/schema/generate-postgres-ddl.js +10 -81
- package/dist/schema/generate-postgres-ddl.js.map +1 -1
- package/dist/schema/introspect-db-logic.d.ts +12 -0
- package/dist/schema/introspect-db-queries.d.ts +11 -1
- package/dist/schema/introspect-db-structure.d.ts +10 -0
- package/dist/schema/introspect-db.js +4 -5
- package/dist/schema/introspect-db.js.map +1 -1
- package/dist/schema/migration-post-process.d.ts +34 -0
- package/dist/schema/plan/diff-plan.d.ts +126 -0
- package/dist/schema/plan/types.d.ts +8 -0
- package/dist/schema/push-index-prebuild.d.ts +25 -0
- package/dist/schema/push-legacy-renames.d.ts +20 -0
- package/dist/schema/rls-bootstrap-sql.d.ts +15 -0
- package/dist/security/rls-enforcement.d.ts +32 -4
- package/dist/services/FetchService.d.ts +58 -8
- package/dist/services/PersistService.d.ts +15 -0
- package/dist/services/RelationService.d.ts +88 -3
- package/dist/services/cdc/CdcListener.d.ts +17 -6
- package/dist/services/cdc/trigger-cdc.d.ts +151 -27
- package/dist/services/channel-history.d.ts +15 -4
- package/dist/services/collection-helpers.d.ts +15 -0
- package/dist/services/field-viewer.d.ts +15 -1
- package/dist/services/pg-notify-listener.d.ts +7 -0
- package/dist/services/realtimeService.d.ts +314 -14
- package/dist/services/row-pipeline.d.ts +16 -9
- package/dist/services/socket-liveness.d.ts +42 -2
- package/dist/services/sql-script.d.ts +207 -3
- package/dist/services/vector-iterative-scan.d.ts +40 -0
- package/dist/services/write-depth.d.ts +6 -0
- package/dist/services/write-transaction-scope.d.ts +9 -0
- package/dist/socket-liveness-D3UD5_BW.js +7570 -0
- package/dist/socket-liveness-D3UD5_BW.js.map +1 -0
- package/dist/socket-opt-ins.d.ts +64 -0
- package/dist/source-DBDbBTL4.js +811 -0
- package/dist/source-DBDbBTL4.js.map +1 -0
- package/dist/tsx-loader.d.ts +14 -0
- package/dist/utils/drizzle-conditions.d.ts +21 -1
- package/dist/utils/error-cause.d.ts +7 -0
- package/dist/utils/pg-error-utils.d.ts +2 -4
- package/dist/utils/result-rows.d.ts +10 -0
- package/dist/websocket.d.ts +13 -0
- package/package.json +7 -7
- package/dist/backup-cli-DW5p9_zv.js.map +0 -1
- package/dist/backup-service-EwcDVG-8.js.map +0 -1
- package/dist/cli-errors-DsA-K9uP.js +0 -893
- package/dist/cli-errors-DsA-K9uP.js.map +0 -1
- package/dist/cli-flags-BglvpjHv.js.map +0 -1
- package/dist/cli-output-CNdMql-L.js +0 -47
- package/dist/cli-output-CNdMql-L.js.map +0 -1
- package/dist/collection-index-DdnsxiJ_.js +0 -286
- package/dist/collection-index-DdnsxiJ_.js.map +0 -1
- package/dist/databasePoolManager-Bj5FbeAs.js.map +0 -1
- package/dist/doctor-C-sYWbmt.js.map +0 -1
- package/dist/ensure-collection-policies-B1ureSIV.js +0 -160
- package/dist/ensure-collection-policies-B1ureSIV.js.map +0 -1
- package/dist/ensure-collection-tables-kkkHk8oo.js.map +0 -1
- package/dist/ensure-tables-BmI_tRxc.js.map +0 -1
- package/dist/generate-drizzle-schema-93M0lUxK.js +0 -123
- package/dist/generate-drizzle-schema-93M0lUxK.js.map +0 -1
- package/dist/generate-drizzle-schema-logic-sSFDp6LR.js.map +0 -1
- package/dist/introspect-db-logic-kCETE8TY.js.map +0 -1
- package/dist/introspect-db-queries-C_Q5VgQw.js.map +0 -1
- package/dist/plan-schema-DU9exq6C.js.map +0 -1
- package/dist/policy-drift-B-J2hhm0.js.map +0 -1
- package/dist/render-ddl-Ds2t_d9V.js.map +0 -1
- package/dist/rls-bootstrap-sql-_KNnjanK.js.map +0 -1
- package/dist/rls-enforcement-CfXOJJaW.js.map +0 -1
- package/dist/source-Br7L7GOI.js +0 -410
- package/dist/source-Br7L7GOI.js.map +0 -1
- package/dist/utils/sql-redaction.d.ts +0 -21
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-DBDbBTL4.js","names":["styles","process"],"sources":["../src/schema/collection-index.ts","../src/cli-output.ts","../../../node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/vendor/ansi-styles/index.js","../../../node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/vendor/supports-color/index.js","../../../node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/utilities.js","../../../node_modules/.pnpm/chalk@5.6.2/node_modules/chalk/source/index.js"],"sourcesContent":["/**\n * The one place a collection's `indexes:` block becomes `CREATE INDEX`.\n *\n * Like `search-column.ts` and `vector-index.ts`, this module exists so the DDL\n * generator and the boot-time ensure render the *same* specification rather\n * than describing the same index twice, differently.\n *\n * ## Why every form here is core Postgres\n *\n * `rebase db push` runs `atlas schema apply`, which materialises the desired\n * state in a bare scratch database to plan against. `--exclude` does not\n * suppress that replay, and `CREATE EXTENSION` cannot go in the file. So an\n * index wanting `gin_trgm_ops` or `vector_cosine_ops` would parse, plan, and\n * then fail against a database the author has never heard of. Those are\n * refused here instead, and redirected to the feature that owns them: trigram\n * search is `search:`, ANN is a `vector` property.\n *\n * Verified against atlas v1.2.3 and Postgres 18 before this was written: plain,\n * composite with `DESC NULLS LAST`, partial, unique, covering `INCLUDE`, `GIN`\n * and expression indexes all parse, apply, and re-plan clean. The Atlas\n * limitation that forced the search carve-out is that it will not parse a file\n * containing a function *definition* — a function *call* inside an index is\n * fine. That is why this module needs no carve-out and search did.\n *\n * ## Why the name carries a hash\n *\n * `CREATE INDEX IF NOT EXISTS` is a **name** check, not a definition check. A\n * readable name means a changed declaration keeps the old index and reports\n * success, forever. Hashing the index's *semantics* into its name makes a\n * redefinition a different object: the new one is built before the old one is\n * dropped, there is never a window with no index, and drift detection reduces\n * to a set difference over names.\n */\nimport type { CollectionConfig, CollectionIndex, IndexPredicate, Property, ResolvedRelation } from \"@rebasepro/types\";\nimport { isPostgresCollectionConfig } from \"@rebasepro/types\";\nimport { getTableName, resolveCollectionRelations } from \"@rebasepro/common\";\nimport { sha1Hex, truncateToBytes } from \"@rebasepro/utils\";\n\n/** Resolve a property key to its column name. Injected to avoid an import cycle. */\nexport type ResolveColumnName = (propName: string, prop?: Property | null) => string;\n\nexport type IndexMethod = \"btree\" | \"gin\" | \"brin\";\n\n/** A key column, with the ordering Postgres will actually apply. */\nexport interface ResolvedIndexKey {\n column: string;\n /** Always concrete. `btree` defaults ascending; unordered methods report `asc`. */\n direction: \"asc\" | \"desc\";\n /** Postgres's own default: `last` under `asc`, `first` under `desc`. */\n nulls: \"first\" | \"last\";\n}\n\n/** A predicate resolved onto column names, ready to render and to hash. */\nexport type ResolvedPredicate =\n | { column: string; op: \"=\" | \"!=\" | \"<\" | \"<=\" | \">\" | \">=\"; value: string | number | boolean }\n | { column: string; op: \"is null\" | \"is not null\" }\n | { column: string; op: \"in\"; value: readonly (string | number)[] }\n | { and: readonly ResolvedPredicate[] };\n\n/** One index, fully resolved. The only shape the renderers accept. */\nexport interface CollectionIndexSpec {\n schema: string;\n table: string;\n method: IndexMethod;\n unique: boolean;\n keys: ResolvedIndexKey[];\n include: string[];\n predicate: ResolvedPredicate | null;\n /** The author's one-line justification. Never enters the name. */\n reason: string;\n /** Derived by {@link deriveIndexName}. Frozen — see the module comment. */\n indexName: string;\n /**\n * Set when Rebase derived this index for a foreign key rather than anybody\n * declaring it — see {@link foreignKeyIndexSpec}. Never enters the name, so\n * the derived index and a declared one over the same column are one object.\n *\n * Read by exactly one consumer: boot-ensure, which builds these in the\n * background on a table that already exists instead of holding the boot\n * for the build. Every renderer treats them like any other index.\n */\n derived?: \"foreign-key\";\n}\n\n/**\n * A declaration that cannot become an index.\n *\n * Thrown at build time, naming the collection and the array position, because\n * the alternative is a `CREATE INDEX` that fails during a push with a Postgres\n * error mentioning a column the author never wrote.\n */\nexport class CollectionIndexConfigError extends Error {\n readonly collectionSlug: string;\n readonly position: number;\n\n constructor(collectionSlug: string, position: number, message: string) {\n super(`${collectionSlug}.indexes[${position}]: ${message}`);\n this.name = \"CollectionIndexConfigError\";\n this.collectionSlug = collectionSlug;\n this.position = position;\n }\n}\n\n/** Postgres allows 32 key columns. See the doc comment on `on`. */\nexport const MAX_INDEX_KEYS = 5;\n\n/**\n * `_ix`/`_ux` plus `_` plus 7 hex — the part of the name that must always\n * survive truncation, and therefore is never inside the truncated portion.\n */\nconst NAME_SUFFIX_BYTES = 11;\n\n/**\n * Every Rebase-managed index name, and nothing else.\n *\n * The terminal `_ix_`/`_ux_` plus exactly seven lowercase hex characters is\n * what separates this scheme from every other producer in the codebase —\n * `_fkey`, `_gin`, `_trgm`, `_pkey`, `_key`, the vector distances, and the\n * `idx_` prefix auth uses. `_idx` was rejected as a tail because it is already\n * taken for real: `users_email_verification_token_idx` is byte-for-byte what a\n * naive `<table>_<column>_idx` derives on an auth-enabled `users` collection.\n *\n * Load-bearing for safety, not just tidiness. An index that does NOT match this\n * belongs to somebody else — a hand-written one, or one an introspected\n * database arrived with — and is excluded from the Atlas diff so the push\n * cannot drop it.\n */\nexport const isRebaseIndexName = (name: string): boolean => /_(?:ix|ux)_[0-9a-f]{7}$/.test(name);\n\nconst isOrderedMethod = (method: IndexMethod): boolean => method === \"btree\";\n\n/**\n * The parts of an index that decide what it *is*.\n *\n * A semantic projection, not the rendered statement — the same arrangement as\n * `getPolicyNameHash`, and for the same reason. A change to how this file\n * formats SQL (eliding a default `USING btree`, quoting differently, emitting\n * `NULLS LAST` explicitly) must not silently rename every index in every\n * deployed database. Hashing generator output would make every cosmetic edit a\n * fleet-wide DROP + CREATE.\n *\n * `reason` is deliberately absent: rewording a comment must not rebuild an\n * index. `nulls` is the *effective* placement, so writing Postgres's own\n * default down is a no-op rather than a redefinition.\n *\n * `v` is the only escape hatch, and it is expensive on purpose: bumping it\n * renames every index in the field.\n */\nexport const indexFingerprint = (spec: Omit<CollectionIndexSpec, \"indexName\">): string => sha1Hex(JSON.stringify({\n v: 1,\n s: spec.schema,\n t: spec.table,\n m: spec.method,\n u: spec.unique,\n k: spec.keys.map(k => [k.column, k.direction, k.nulls]),\n i: spec.include,\n w: spec.predicate\n})).substring(0, 7);\n\n/**\n * `<table>_<columns>_ix_<hash>`, or `_ux_` when unique.\n *\n * Truncation eats the readable head and never the hash. `toPostgresIdentifier`\n * truncates the whole string at 63 bytes, which on a hashed name would cut off\n * the one part that makes it unique — the failure already frozen into\n * `contracts/derived-names.txt`, where a foreign key is recorded with its\n * `_fkey` suffix truncated away, so a second foreign key on that table would\n * derive a byte-identical name.\n */\nexport const deriveIndexName = (spec: Omit<CollectionIndexSpec, \"indexName\">): string => {\n // Built suffix-first, so the two parts that carry meaning — the `_ix`/`_ux`\n // tag that {@link isRebaseIndexName} matches on, and the fingerprint — are\n // never in the string being truncated. Composing the whole name and then\n // trimming it to 63 loses both, silently: an 80-byte table name yields\n // `xxxx…xxx_610bb9e` with the tag gone, so the index stops being\n // recognisable as Rebase's and `db push` treats it as foreign forever.\n const tag = spec.unique ? \"ux\" : \"ix\";\n const suffix = `_${tag}_${indexFingerprint(spec)}`;\n const readable = `${spec.table}_${spec.keys.map(k => k.column).join(\"_\")}`;\n return `${truncateToBytes(readable, 63 - NAME_SUFFIX_BYTES)}${suffix}`;\n};\n\nconst quoteLiteral = (value: string | number | boolean): string => {\n if (typeof value === \"number\") return String(value);\n if (typeof value === \"boolean\") return value ? \"TRUE\" : \"FALSE\";\n return `'${value.replace(/'/g, \"''\")}'`;\n};\n\n/** Render a resolved predicate as the body of a `WHERE` clause. */\nexport const renderPredicate = (predicate: ResolvedPredicate): string => {\n if (\"and\" in predicate) {\n // Always parenthesised. Postgres would apply the same precedence\n // without it, but the rendered SQL is read by people diffing a plan.\n return predicate.and.map(renderPredicate).join(\" AND \");\n }\n switch (predicate.op) {\n case \"is null\":\n case \"is not null\":\n return `\"${predicate.column}\" ${predicate.op.toUpperCase()}`;\n case \"in\":\n return `\"${predicate.column}\" IN (${predicate.value.map(quoteLiteral).join(\", \")})`;\n default:\n return `\"${predicate.column}\" ${predicate.op} ${quoteLiteral(predicate.value)}`;\n }\n};\n\n/**\n * The `CREATE INDEX` for one spec.\n *\n * `concurrently` is a parameter rather than a string replacement on the way\n * out. `search-column.ts` and `vector-index.ts` both reach for\n * `.replace(\"CREATE INDEX IF NOT EXISTS\", …)` instead, which silently does\n * nothing for a UNIQUE index — the rendered text is `CREATE UNIQUE INDEX …`\n * and the pattern never matches.\n */\nexport const collectionIndexStatement = (\n spec: CollectionIndexSpec,\n options: { concurrently?: boolean; ifNotExists?: boolean } = {}\n): string => {\n const unique = spec.unique ? \"UNIQUE \" : \"\";\n const concurrently = options.concurrently ? \"CONCURRENTLY \" : \"\";\n const ifNotExists = options.ifNotExists ? \"IF NOT EXISTS \" : \"\";\n const using = spec.method === \"btree\" ? \"\" : ` USING ${spec.method}`;\n\n const keys = spec.keys.map(k => {\n if (!isOrderedMethod(spec.method)) return `\"${k.column}\"`;\n const direction = k.direction === \"desc\" ? \" DESC\" : \"\";\n // Emitted only when it is not what the direction already implies, so\n // the rendered SQL matches what `pg_get_indexdef` reads back and a\n // re-plan finds no difference.\n const impliedNulls = k.direction === \"desc\" ? \"first\" : \"last\";\n const nulls = k.nulls === impliedNulls ? \"\" : ` NULLS ${k.nulls.toUpperCase()}`;\n return `\"${k.column}\"${direction}${nulls}`;\n }).join(\", \");\n\n const include = spec.include.length > 0\n ? ` INCLUDE (${spec.include.map(c => `\"${c}\"`).join(\", \")})`\n : \"\";\n const where = spec.predicate ? ` WHERE ${renderPredicate(spec.predicate)}` : \"\";\n\n return `CREATE ${unique}INDEX ${concurrently}${ifNotExists}\"${spec.indexName}\" ` +\n `ON \"${spec.schema}\".\"${spec.table}\"${using} (${keys})${include}${where};`;\n};\n\nexport const collectionIndexStatements = (\n specs: readonly CollectionIndexSpec[],\n options: { concurrently?: boolean; ifNotExists?: boolean } = {}\n): string[] => specs.map(spec => collectionIndexStatement(spec, options));\n\nconst relationOf = (\n collection: CollectionConfig,\n propKey: string\n): ResolvedRelation | undefined => resolveCollectionRelations(collection)[propKey];\n\n/**\n * The column a property key indexes.\n *\n * A `belongsTo` resolves to its `localKey` — `primaryCategory` becomes\n * `primary_category_id` — which is the case an index is most often wanted for\n * and the case where the property key and the column differ. Everything else\n * goes through `resolveColumnName`.\n *\n * The other relation kinds have no local column at all: the foreign key lives\n * on the target's table, or in a junction. Indexing them here is refused\n * rather than resolved to a column that does not exist.\n */\nexport const resolveIndexableColumn = (\n collection: CollectionConfig,\n propKey: string,\n resolveColumnName: ResolveColumnName,\n fail: (message: string) => never\n): string => {\n const relation = relationOf(collection, propKey);\n if (relation) {\n if (relation.kind === \"belongsTo\") return relation.localKey;\n fail(\n `\"${propKey}\" is a ${relation.kind} relation, which has no column on this table — ` +\n `the foreign key lives on \"${relation.targetSlug}\". Declare the index there.`\n );\n }\n\n const property = collection.properties?.[propKey] as Property | undefined;\n if (!property) {\n fail(`\"${propKey}\" is not a property of this collection.`);\n }\n return resolveColumnName(propKey, property);\n};\n\nconst resolvePredicate = (\n collection: CollectionConfig,\n predicate: IndexPredicate,\n resolveColumnName: ResolveColumnName,\n fail: (message: string) => never\n): ResolvedPredicate => {\n if (\"and\" in predicate) {\n return { and: predicate.and.map(p => resolvePredicate(collection, p, resolveColumnName, fail)) };\n }\n const column = resolveIndexableColumn(collection, predicate.prop, resolveColumnName, fail);\n switch (predicate.op) {\n case \"is null\":\n case \"is not null\":\n return { column, op: predicate.op };\n case \"in\": {\n const seen = new Set(predicate.value);\n if (seen.size !== predicate.value.length) {\n fail(`the \\`in\\` list for \"${predicate.prop}\" repeats a value, which changes nothing.`);\n }\n return { column, op: \"in\", value: [...predicate.value] };\n }\n default:\n return { column, op: predicate.op, value: predicate.value };\n }\n};\n\n/** The primary key columns of a collection, for the \"you already have this\" refusal. */\nconst primaryKeyColumns = (collection: CollectionConfig, resolveColumnName: ResolveColumnName): string[] =>\n Object.entries(collection.properties ?? {})\n .filter(([, prop]) => prop && typeof prop === \"object\" && \"isId\" in prop && Boolean((prop as { isId?: unknown }).isId))\n .map(([key, prop]) => resolveColumnName(key, prop as Property));\n\n/**\n * Every index one collection declares, resolved and named.\n *\n * Throws {@link CollectionIndexConfigError} rather than dropping a bad entry:\n * an index that silently does not exist is the failure mode this whole feature\n * is here to remove.\n */\nexport const buildCollectionIndexSpecs = (\n collection: CollectionConfig,\n resolveColumnName: ResolveColumnName\n): CollectionIndexSpec[] => {\n if (!isPostgresCollectionConfig(collection)) return [];\n const declared = collection.indexes;\n if (!declared || declared.length === 0) return [];\n\n const slug = collection.slug ?? getTableName(collection);\n const schema = collection.schema ?? \"public\";\n const table = getTableName(collection);\n const pk = primaryKeyColumns(collection, resolveColumnName).sort().join(\",\");\n\n const specs: CollectionIndexSpec[] = [];\n const byName = new Map<string, number>();\n\n declared.forEach((index: CollectionIndex, position: number) => {\n const fail = (message: string): never => {\n throw new CollectionIndexConfigError(slug, position, message);\n };\n\n if (typeof index.reason !== \"string\" || index.reason.trim() === \"\") {\n fail(\"`reason` is required — see the doc comment. An index nobody can justify is one nobody can delete.\");\n }\n if (!Array.isArray(index.on) || index.on.length === 0) {\n fail(\"`on` must name at least one property.\");\n }\n if (index.on.length > MAX_INDEX_KEYS) {\n fail(`\\`on\\` has ${index.on.length} keys; the limit is ${MAX_INDEX_KEYS}. Payload columns belong in \\`include\\`.`);\n }\n\n const method: IndexMethod = index.using ?? \"btree\";\n const unique = method === \"btree\" && Boolean((index as { unique?: boolean }).unique);\n if (!isOrderedMethod(method)) {\n for (const key of index.on) {\n if (typeof key !== \"string\" && (\"direction\" in key || \"nulls\" in key)) {\n fail(`access method \"${method}\" does not support ASC/DESC or NULLS options.`);\n }\n }\n }\n\n const keys: ResolvedIndexKey[] = index.on.map(key => {\n const propKey = typeof key === \"string\" ? key : key.prop;\n const column = resolveIndexableColumn(collection, propKey, resolveColumnName, fail);\n const direction = (typeof key === \"string\" ? undefined : (key as { direction?: \"asc\" | \"desc\" }).direction) ?? \"asc\";\n const nulls = (typeof key === \"string\" ? undefined : (key as { nulls?: \"first\" | \"last\" }).nulls)\n ?? (direction === \"desc\" ? \"first\" : \"last\");\n return { column, direction, nulls };\n });\n\n const duplicateKey = keys.map(k => k.column).find((c, i, all) => all.indexOf(c) !== i);\n if (duplicateKey) fail(`\"${duplicateKey}\" appears twice in \\`on\\`.`);\n\n if (keys.map(k => k.column).sort().join(\",\") === pk && pk !== \"\") {\n fail(`this is the primary key — \"${table}_pkey\" already indexes exactly these columns.`);\n }\n\n const include = ((index as { include?: readonly string[] }).include ?? [])\n .map(propKey => resolveIndexableColumn(collection, propKey, resolveColumnName, fail));\n const overlap = include.find(c => keys.some(k => k.column === c));\n if (overlap) fail(`\"${overlap}\" is in both \\`on\\` and \\`include\\`; Postgres rejects the overlap.`);\n\n if (unique && keys.length === 1) {\n const propKey = typeof index.on[0] === \"string\" ? index.on[0] as string : (index.on[0] as { prop: string }).prop;\n const property = collection.properties?.[propKey] as { validation?: { unique?: boolean } } | undefined;\n if (property?.validation?.unique) {\n fail(\n `\"${propKey}\" already declares \\`validation.unique\\`, which compiles to an inline UNIQUE. ` +\n `Two declarations of one guarantee — remove one.`\n );\n }\n }\n\n const predicate = index.where\n ? resolvePredicate(collection, index.where, resolveColumnName, fail)\n : null;\n\n const withoutName = { schema, table, method, unique, keys, include, predicate, reason: index.reason };\n const indexName = deriveIndexName(withoutName);\n\n const clash = byName.get(indexName);\n if (clash !== undefined) {\n fail(`derives the same name as indexes[${clash}] — they are the same index declared twice.`);\n }\n byName.set(indexName, position);\n\n specs.push({ ...withoutName, indexName });\n });\n\n return specs;\n};\n\n/**\n * Every declared index across a set of collections, in a stable order.\n *\n * Sorted because the result reaches `schema.sql`, which `doctor` string-\n * compares against a regenerated copy — `generatePostgresDdl` does not sort its\n * collections, so leaving this in declaration order would make the artifact\n * depend on the order files happened to load in.\n */\nexport const buildCollectionIndexPlan = (\n collections: readonly CollectionConfig[],\n resolveColumnName: ResolveColumnName\n): CollectionIndexSpec[] =>\n sortIndexSpecs(collections.flatMap(collection => buildCollectionIndexSpecs(collection, resolveColumnName)));\n\n/**\n * The stable order every consumer of a set of index specs uses.\n *\n * Exported because the boot-time diff reads the specs off the schema plan\n * rather than rebuilding them, and it has to reach the same order the CLI's\n * exclude list and `schema.sql` do — a second sort written out beside this one\n * is a second order the moment either is edited.\n */\nexport const sortIndexSpecs = (specs: readonly CollectionIndexSpec[]): CollectionIndexSpec[] =>\n [...specs].sort((a, b) =>\n a.schema.localeCompare(b.schema) ||\n a.table.localeCompare(b.table) ||\n a.indexName.localeCompare(b.indexName));\n\n// ── Foreign-key indexes ──────────────────────────────────────────────────────\n\n/**\n * The `reason` a derived foreign-key index carries. Never part of the name —\n * see {@link indexFingerprint} — so rewording it renames nothing.\n */\nexport const FOREIGN_KEY_INDEX_REASON =\n \"Derived for a foreign key: Postgres indexes the key a constraint points at, never the column holding it.\";\n\n/**\n * Whether an index already serves an equality lookup on `column` alone.\n *\n * A btree whose *leading* key is the column does, unique or not — that is the\n * prefix rule, and it is why a composite `(author_id, created_at)` makes a\n * second index on `author_id` pure write amplification. A partial index does\n * not: it serves only the rows its predicate admits, and a foreign-key lookup\n * has to reach every row. Nor does GIN or BRIN, which answer different\n * questions than \"the rows whose key is one of these\".\n */\nexport const leadsWithColumn = (spec: Pick<CollectionIndexSpec, \"method\" | \"predicate\" | \"keys\">, column: string): boolean =>\n spec.method === \"btree\" && spec.predicate === null && spec.keys[0]?.column === column;\n\n/**\n * The btree a foreign-key column gets when nothing else already leads with it.\n *\n * Postgres creates the index a foreign key *references* (the target's primary\n * key) and never the one on the column *holding* it. Every read from the other\n * side of the link filters on that column — a `hasMany`/`hasOne` include, a\n * nested listing (`/authors/5/posts`, page and count), the reverse side of a\n * many-to-many — and so does the `ON DELETE` check when a target row goes. With\n * no index each of those is a sequential scan of the child table: measured at\n * 13 ms for 25 parents over 100k children, and about 250 ms per nested listing\n * at a million.\n *\n * Named by {@link deriveIndexName} like every other index, so it is a\n * Rebase-managed name (`_ix_<fingerprint>`), it is in the Atlas diff, and it\n * is byte-for-byte the name an author's own plain index on the column would\n * derive. That identity is what makes the derived index and a declared one the\n * same object rather than two.\n */\nexport const foreignKeyIndexSpec = (schema: string, table: string, column: string): CollectionIndexSpec => {\n const withoutName = {\n schema,\n table,\n method: \"btree\" as const,\n unique: false,\n keys: [{ column, direction: \"asc\" as const, nulls: \"last\" as const }],\n include: [],\n predicate: null,\n reason: FOREIGN_KEY_INDEX_REASON\n };\n return { ...withoutName, indexName: deriveIndexName(withoutName), derived: \"foreign-key\" };\n};\n","/**\n * Terminal output for the `rebase db|schema|doctor` commands.\n *\n * These commands used to write every line through `logger`, and that is a\n * category error with three separate consequences:\n *\n * - `logger` prefixes each line with its own level, so a box-drawn report\n * arrived as `ℹ️ [INFO] ┌─ ✗ Missing Column ───` and the frame no longer\n * lined up with anything;\n * - `logger` is gated by `LOG_LEVEL`, which ships in the scaffold's own\n * `.env.example` — a developer who quietened their dev server with\n * `LOG_LEVEL=warn` got a `rebase db push` that printed almost nothing and\n * still exited non-zero, indistinguishable from a crash;\n * - under `NODE_ENV=production` `logger` emits JSON, so the whole report\n * became log records with the chalk escape codes embedded in them.\n *\n * A CLI's report *is* its return value. It goes to the terminal unconditionally\n * and unadorned. `packages/cli` has always written its output this way; this is\n * the same three functions for the plugin CLI that `rebase` delegates to.\n *\n * `logger` still belongs in this package's *runtime* — a request handler has no\n * terminal and its lines want levels, timestamps and redaction. The rule is the\n * caller, not the severity: anything a developer reads because they typed a\n * command goes here, anything a server emits while running goes to `logger`.\n *\n * Errors and warnings go to stderr so `rebase db push > plan.txt` keeps the\n * diagnosis on the terminal where it is readable.\n */\nimport { parseEnvBoolean } from \"@rebasepro/types\";\n\n/** One line of human-facing output on stdout. */\nexport const out = (line = \"\"): void => {\n console.log(line);\n};\n\n/** One line of human-facing warning output on stderr. */\nexport const outWarn = (line = \"\"): void => {\n console.warn(line);\n};\n\n/** One line of human-facing error output on stderr. */\nexport const outError = (line = \"\"): void => {\n console.error(line);\n};\n\n/**\n * Whether a failure should be printed with its stack — `rebase --debug`, which\n * the launcher passes down as `REBASE_DEBUG`, or `REBASE_DEBUG` set by hand.\n */\nexport const wantsStack = (): boolean => parseEnvBoolean(process.env.REBASE_DEBUG) === true;\n\n/**\n * An error as a person reads it: its message, and its stack only under\n * `--debug`.\n *\n * The rule `rebase` itself follows (`bin/rebase.js`). These commands run in a\n * child process the launcher's handler never sees, so a syntax error in a\n * collection file reached the terminal as `Doctor failed CollectionConfigError:`\n * followed by twenty frames of loader internals, under a sentence that already\n * said which file and which line.\n */\nexport const describeFailure = (error: unknown): string => {\n if (!(error instanceof Error)) return String(error);\n if (wantsStack() && error.stack) return error.stack;\n return `${error.message}\\n Re-run with --debug for the stack trace.`;\n};\n","const ANSI_BACKGROUND_OFFSET = 10;\n\nconst wrapAnsi16 = (offset = 0) => code => `\\u001B[${code + offset}m`;\n\nconst wrapAnsi256 = (offset = 0) => code => `\\u001B[${38 + offset};5;${code}m`;\n\nconst wrapAnsi16m = (offset = 0) => (red, green, blue) => `\\u001B[${38 + offset};2;${red};${green};${blue}m`;\n\nconst styles = {\n\tmodifier: {\n\t\treset: [0, 0],\n\t\t// 21 isn't widely supported and 22 does the same thing\n\t\tbold: [1, 22],\n\t\tdim: [2, 22],\n\t\titalic: [3, 23],\n\t\tunderline: [4, 24],\n\t\toverline: [53, 55],\n\t\tinverse: [7, 27],\n\t\thidden: [8, 28],\n\t\tstrikethrough: [9, 29],\n\t},\n\tcolor: {\n\t\tblack: [30, 39],\n\t\tred: [31, 39],\n\t\tgreen: [32, 39],\n\t\tyellow: [33, 39],\n\t\tblue: [34, 39],\n\t\tmagenta: [35, 39],\n\t\tcyan: [36, 39],\n\t\twhite: [37, 39],\n\n\t\t// Bright color\n\t\tblackBright: [90, 39],\n\t\tgray: [90, 39], // Alias of `blackBright`\n\t\tgrey: [90, 39], // Alias of `blackBright`\n\t\tredBright: [91, 39],\n\t\tgreenBright: [92, 39],\n\t\tyellowBright: [93, 39],\n\t\tblueBright: [94, 39],\n\t\tmagentaBright: [95, 39],\n\t\tcyanBright: [96, 39],\n\t\twhiteBright: [97, 39],\n\t},\n\tbgColor: {\n\t\tbgBlack: [40, 49],\n\t\tbgRed: [41, 49],\n\t\tbgGreen: [42, 49],\n\t\tbgYellow: [43, 49],\n\t\tbgBlue: [44, 49],\n\t\tbgMagenta: [45, 49],\n\t\tbgCyan: [46, 49],\n\t\tbgWhite: [47, 49],\n\n\t\t// Bright color\n\t\tbgBlackBright: [100, 49],\n\t\tbgGray: [100, 49], // Alias of `bgBlackBright`\n\t\tbgGrey: [100, 49], // Alias of `bgBlackBright`\n\t\tbgRedBright: [101, 49],\n\t\tbgGreenBright: [102, 49],\n\t\tbgYellowBright: [103, 49],\n\t\tbgBlueBright: [104, 49],\n\t\tbgMagentaBright: [105, 49],\n\t\tbgCyanBright: [106, 49],\n\t\tbgWhiteBright: [107, 49],\n\t},\n};\n\nexport const modifierNames = Object.keys(styles.modifier);\nexport const foregroundColorNames = Object.keys(styles.color);\nexport const backgroundColorNames = Object.keys(styles.bgColor);\nexport const colorNames = [...foregroundColorNames, ...backgroundColorNames];\n\nfunction assembleStyles() {\n\tconst codes = new Map();\n\n\tfor (const [groupName, group] of Object.entries(styles)) {\n\t\tfor (const [styleName, style] of Object.entries(group)) {\n\t\t\tstyles[styleName] = {\n\t\t\t\topen: `\\u001B[${style[0]}m`,\n\t\t\t\tclose: `\\u001B[${style[1]}m`,\n\t\t\t};\n\n\t\t\tgroup[styleName] = styles[styleName];\n\n\t\t\tcodes.set(style[0], style[1]);\n\t\t}\n\n\t\tObject.defineProperty(styles, groupName, {\n\t\t\tvalue: group,\n\t\t\tenumerable: false,\n\t\t});\n\t}\n\n\tObject.defineProperty(styles, 'codes', {\n\t\tvalue: codes,\n\t\tenumerable: false,\n\t});\n\n\tstyles.color.close = '\\u001B[39m';\n\tstyles.bgColor.close = '\\u001B[49m';\n\n\tstyles.color.ansi = wrapAnsi16();\n\tstyles.color.ansi256 = wrapAnsi256();\n\tstyles.color.ansi16m = wrapAnsi16m();\n\tstyles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET);\n\tstyles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET);\n\tstyles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET);\n\n\t// From https://github.com/Qix-/color-convert/blob/3f0e0d4e92e235796ccb17f6e85c72094a651f49/conversions.js\n\tObject.defineProperties(styles, {\n\t\trgbToAnsi256: {\n\t\t\tvalue(red, green, blue) {\n\t\t\t\t// We use the extended greyscale palette here, with the exception of\n\t\t\t\t// black and white. normal palette only has 4 greyscale shades.\n\t\t\t\tif (red === green && green === blue) {\n\t\t\t\t\tif (red < 8) {\n\t\t\t\t\t\treturn 16;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (red > 248) {\n\t\t\t\t\t\treturn 231;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn Math.round(((red - 8) / 247) * 24) + 232;\n\t\t\t\t}\n\n\t\t\t\treturn 16\n\t\t\t\t\t+ (36 * Math.round(red / 255 * 5))\n\t\t\t\t\t+ (6 * Math.round(green / 255 * 5))\n\t\t\t\t\t+ Math.round(blue / 255 * 5);\n\t\t\t},\n\t\t\tenumerable: false,\n\t\t},\n\t\thexToRgb: {\n\t\t\tvalue(hex) {\n\t\t\t\tconst matches = /[a-f\\d]{6}|[a-f\\d]{3}/i.exec(hex.toString(16));\n\t\t\t\tif (!matches) {\n\t\t\t\t\treturn [0, 0, 0];\n\t\t\t\t}\n\n\t\t\t\tlet [colorString] = matches;\n\n\t\t\t\tif (colorString.length === 3) {\n\t\t\t\t\tcolorString = [...colorString].map(character => character + character).join('');\n\t\t\t\t}\n\n\t\t\t\tconst integer = Number.parseInt(colorString, 16);\n\n\t\t\t\treturn [\n\t\t\t\t\t/* eslint-disable no-bitwise */\n\t\t\t\t\t(integer >> 16) & 0xFF,\n\t\t\t\t\t(integer >> 8) & 0xFF,\n\t\t\t\t\tinteger & 0xFF,\n\t\t\t\t\t/* eslint-enable no-bitwise */\n\t\t\t\t];\n\t\t\t},\n\t\t\tenumerable: false,\n\t\t},\n\t\thexToAnsi256: {\n\t\t\tvalue: hex => styles.rgbToAnsi256(...styles.hexToRgb(hex)),\n\t\t\tenumerable: false,\n\t\t},\n\t\tansi256ToAnsi: {\n\t\t\tvalue(code) {\n\t\t\t\tif (code < 8) {\n\t\t\t\t\treturn 30 + code;\n\t\t\t\t}\n\n\t\t\t\tif (code < 16) {\n\t\t\t\t\treturn 90 + (code - 8);\n\t\t\t\t}\n\n\t\t\t\tlet red;\n\t\t\t\tlet green;\n\t\t\t\tlet blue;\n\n\t\t\t\tif (code >= 232) {\n\t\t\t\t\tred = (((code - 232) * 10) + 8) / 255;\n\t\t\t\t\tgreen = red;\n\t\t\t\t\tblue = red;\n\t\t\t\t} else {\n\t\t\t\t\tcode -= 16;\n\n\t\t\t\t\tconst remainder = code % 36;\n\n\t\t\t\t\tred = Math.floor(code / 36) / 5;\n\t\t\t\t\tgreen = Math.floor(remainder / 6) / 5;\n\t\t\t\t\tblue = (remainder % 6) / 5;\n\t\t\t\t}\n\n\t\t\t\tconst value = Math.max(red, green, blue) * 2;\n\n\t\t\t\tif (value === 0) {\n\t\t\t\t\treturn 30;\n\t\t\t\t}\n\n\t\t\t\t// eslint-disable-next-line no-bitwise\n\t\t\t\tlet result = 30 + ((Math.round(blue) << 2) | (Math.round(green) << 1) | Math.round(red));\n\n\t\t\t\tif (value === 2) {\n\t\t\t\t\tresult += 60;\n\t\t\t\t}\n\n\t\t\t\treturn result;\n\t\t\t},\n\t\t\tenumerable: false,\n\t\t},\n\t\trgbToAnsi: {\n\t\t\tvalue: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)),\n\t\t\tenumerable: false,\n\t\t},\n\t\thexToAnsi: {\n\t\t\tvalue: hex => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)),\n\t\t\tenumerable: false,\n\t\t},\n\t});\n\n\treturn styles;\n}\n\nconst ansiStyles = assembleStyles();\n\nexport default ansiStyles;\n","import process from 'node:process';\nimport os from 'node:os';\nimport tty from 'node:tty';\n\n// From: https://github.com/sindresorhus/has-flag/blob/main/index.js\n/// function hasFlag(flag, argv = globalThis.Deno?.args ?? process.argv) {\nfunction hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process.argv) {\n\tconst prefix = flag.startsWith('-') ? '' : (flag.length === 1 ? '-' : '--');\n\tconst position = argv.indexOf(prefix + flag);\n\tconst terminatorPosition = argv.indexOf('--');\n\treturn position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);\n}\n\nconst {env} = process;\n\nlet flagForceColor;\nif (\n\thasFlag('no-color')\n\t|| hasFlag('no-colors')\n\t|| hasFlag('color=false')\n\t|| hasFlag('color=never')\n) {\n\tflagForceColor = 0;\n} else if (\n\thasFlag('color')\n\t|| hasFlag('colors')\n\t|| hasFlag('color=true')\n\t|| hasFlag('color=always')\n) {\n\tflagForceColor = 1;\n}\n\nfunction envForceColor() {\n\tif ('FORCE_COLOR' in env) {\n\t\tif (env.FORCE_COLOR === 'true') {\n\t\t\treturn 1;\n\t\t}\n\n\t\tif (env.FORCE_COLOR === 'false') {\n\t\t\treturn 0;\n\t\t}\n\n\t\treturn env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3);\n\t}\n}\n\nfunction translateLevel(level) {\n\tif (level === 0) {\n\t\treturn false;\n\t}\n\n\treturn {\n\t\tlevel,\n\t\thasBasic: true,\n\t\thas256: level >= 2,\n\t\thas16m: level >= 3,\n\t};\n}\n\nfunction _supportsColor(haveStream, {streamIsTTY, sniffFlags = true} = {}) {\n\tconst noFlagForceColor = envForceColor();\n\tif (noFlagForceColor !== undefined) {\n\t\tflagForceColor = noFlagForceColor;\n\t}\n\n\tconst forceColor = sniffFlags ? flagForceColor : noFlagForceColor;\n\n\tif (forceColor === 0) {\n\t\treturn 0;\n\t}\n\n\tif (sniffFlags) {\n\t\tif (hasFlag('color=16m')\n\t\t\t|| hasFlag('color=full')\n\t\t\t|| hasFlag('color=truecolor')) {\n\t\t\treturn 3;\n\t\t}\n\n\t\tif (hasFlag('color=256')) {\n\t\t\treturn 2;\n\t\t}\n\t}\n\n\t// Check for Azure DevOps pipelines.\n\t// Has to be above the `!streamIsTTY` check.\n\tif ('TF_BUILD' in env && 'AGENT_NAME' in env) {\n\t\treturn 1;\n\t}\n\n\tif (haveStream && !streamIsTTY && forceColor === undefined) {\n\t\treturn 0;\n\t}\n\n\tconst min = forceColor || 0;\n\n\tif (env.TERM === 'dumb') {\n\t\treturn min;\n\t}\n\n\tif (process.platform === 'win32') {\n\t\t// Windows 10 build 10586 is the first Windows release that supports 256 colors.\n\t\t// Windows 10 build 14931 is the first release that supports 16m/TrueColor.\n\t\tconst osRelease = os.release().split('.');\n\t\tif (\n\t\t\tNumber(osRelease[0]) >= 10\n\t\t\t&& Number(osRelease[2]) >= 10_586\n\t\t) {\n\t\t\treturn Number(osRelease[2]) >= 14_931 ? 3 : 2;\n\t\t}\n\n\t\treturn 1;\n\t}\n\n\tif ('CI' in env) {\n\t\tif (['GITHUB_ACTIONS', 'GITEA_ACTIONS', 'CIRCLECI'].some(key => key in env)) {\n\t\t\treturn 3;\n\t\t}\n\n\t\tif (['TRAVIS', 'APPVEYOR', 'GITLAB_CI', 'BUILDKITE', 'DRONE'].some(sign => sign in env) || env.CI_NAME === 'codeship') {\n\t\t\treturn 1;\n\t\t}\n\n\t\treturn min;\n\t}\n\n\tif ('TEAMCITY_VERSION' in env) {\n\t\treturn /^(9\\.(0*[1-9]\\d*)\\.|\\d{2,}\\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;\n\t}\n\n\tif (env.COLORTERM === 'truecolor') {\n\t\treturn 3;\n\t}\n\n\tif (env.TERM === 'xterm-kitty') {\n\t\treturn 3;\n\t}\n\n\tif (env.TERM === 'xterm-ghostty') {\n\t\treturn 3;\n\t}\n\n\tif (env.TERM === 'wezterm') {\n\t\treturn 3;\n\t}\n\n\tif ('TERM_PROGRAM' in env) {\n\t\tconst version = Number.parseInt((env.TERM_PROGRAM_VERSION || '').split('.')[0], 10);\n\n\t\tswitch (env.TERM_PROGRAM) {\n\t\t\tcase 'iTerm.app': {\n\t\t\t\treturn version >= 3 ? 3 : 2;\n\t\t\t}\n\n\t\t\tcase 'Apple_Terminal': {\n\t\t\t\treturn 2;\n\t\t\t}\n\t\t\t// No default\n\t\t}\n\t}\n\n\tif (/-256(color)?$/i.test(env.TERM)) {\n\t\treturn 2;\n\t}\n\n\tif (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {\n\t\treturn 1;\n\t}\n\n\tif ('COLORTERM' in env) {\n\t\treturn 1;\n\t}\n\n\treturn min;\n}\n\nexport function createSupportsColor(stream, options = {}) {\n\tconst level = _supportsColor(stream, {\n\t\tstreamIsTTY: stream && stream.isTTY,\n\t\t...options,\n\t});\n\n\treturn translateLevel(level);\n}\n\nconst supportsColor = {\n\tstdout: createSupportsColor({isTTY: tty.isatty(1)}),\n\tstderr: createSupportsColor({isTTY: tty.isatty(2)}),\n};\n\nexport default supportsColor;\n","// TODO: When targeting Node.js 16, use `String.prototype.replaceAll`.\nexport function stringReplaceAll(string, substring, replacer) {\n\tlet index = string.indexOf(substring);\n\tif (index === -1) {\n\t\treturn string;\n\t}\n\n\tconst substringLength = substring.length;\n\tlet endIndex = 0;\n\tlet returnValue = '';\n\tdo {\n\t\treturnValue += string.slice(endIndex, index) + substring + replacer;\n\t\tendIndex = index + substringLength;\n\t\tindex = string.indexOf(substring, endIndex);\n\t} while (index !== -1);\n\n\treturnValue += string.slice(endIndex);\n\treturn returnValue;\n}\n\nexport function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) {\n\tlet endIndex = 0;\n\tlet returnValue = '';\n\tdo {\n\t\tconst gotCR = string[index - 1] === '\\r';\n\t\treturnValue += string.slice(endIndex, (gotCR ? index - 1 : index)) + prefix + (gotCR ? '\\r\\n' : '\\n') + postfix;\n\t\tendIndex = index + 1;\n\t\tindex = string.indexOf('\\n', endIndex);\n\t} while (index !== -1);\n\n\treturnValue += string.slice(endIndex);\n\treturn returnValue;\n}\n","import ansiStyles from '#ansi-styles';\nimport supportsColor from '#supports-color';\nimport { // eslint-disable-line import/order\n\tstringReplaceAll,\n\tstringEncaseCRLFWithFirstIndex,\n} from './utilities.js';\n\nconst {stdout: stdoutColor, stderr: stderrColor} = supportsColor;\n\nconst GENERATOR = Symbol('GENERATOR');\nconst STYLER = Symbol('STYLER');\nconst IS_EMPTY = Symbol('IS_EMPTY');\n\n// `supportsColor.level` → `ansiStyles.color[name]` mapping\nconst levelMapping = [\n\t'ansi',\n\t'ansi',\n\t'ansi256',\n\t'ansi16m',\n];\n\nconst styles = Object.create(null);\n\nconst applyOptions = (object, options = {}) => {\n\tif (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) {\n\t\tthrow new Error('The `level` option should be an integer from 0 to 3');\n\t}\n\n\t// Detect level if not set manually\n\tconst colorLevel = stdoutColor ? stdoutColor.level : 0;\n\tobject.level = options.level === undefined ? colorLevel : options.level;\n};\n\nexport class Chalk {\n\tconstructor(options) {\n\t\t// eslint-disable-next-line no-constructor-return\n\t\treturn chalkFactory(options);\n\t}\n}\n\nconst chalkFactory = options => {\n\tconst chalk = (...strings) => strings.join(' ');\n\tapplyOptions(chalk, options);\n\n\tObject.setPrototypeOf(chalk, createChalk.prototype);\n\n\treturn chalk;\n};\n\nfunction createChalk(options) {\n\treturn chalkFactory(options);\n}\n\nObject.setPrototypeOf(createChalk.prototype, Function.prototype);\n\nfor (const [styleName, style] of Object.entries(ansiStyles)) {\n\tstyles[styleName] = {\n\t\tget() {\n\t\t\tconst builder = createBuilder(this, createStyler(style.open, style.close, this[STYLER]), this[IS_EMPTY]);\n\t\t\tObject.defineProperty(this, styleName, {value: builder});\n\t\t\treturn builder;\n\t\t},\n\t};\n}\n\nstyles.visible = {\n\tget() {\n\t\tconst builder = createBuilder(this, this[STYLER], true);\n\t\tObject.defineProperty(this, 'visible', {value: builder});\n\t\treturn builder;\n\t},\n};\n\nconst getModelAnsi = (model, level, type, ...arguments_) => {\n\tif (model === 'rgb') {\n\t\tif (level === 'ansi16m') {\n\t\t\treturn ansiStyles[type].ansi16m(...arguments_);\n\t\t}\n\n\t\tif (level === 'ansi256') {\n\t\t\treturn ansiStyles[type].ansi256(ansiStyles.rgbToAnsi256(...arguments_));\n\t\t}\n\n\t\treturn ansiStyles[type].ansi(ansiStyles.rgbToAnsi(...arguments_));\n\t}\n\n\tif (model === 'hex') {\n\t\treturn getModelAnsi('rgb', level, type, ...ansiStyles.hexToRgb(...arguments_));\n\t}\n\n\treturn ansiStyles[type][model](...arguments_);\n};\n\nconst usedModels = ['rgb', 'hex', 'ansi256'];\n\nfor (const model of usedModels) {\n\tstyles[model] = {\n\t\tget() {\n\t\t\tconst {level} = this;\n\t\t\treturn function (...arguments_) {\n\t\t\t\tconst styler = createStyler(getModelAnsi(model, levelMapping[level], 'color', ...arguments_), ansiStyles.color.close, this[STYLER]);\n\t\t\t\treturn createBuilder(this, styler, this[IS_EMPTY]);\n\t\t\t};\n\t\t},\n\t};\n\n\tconst bgModel = 'bg' + model[0].toUpperCase() + model.slice(1);\n\tstyles[bgModel] = {\n\t\tget() {\n\t\t\tconst {level} = this;\n\t\t\treturn function (...arguments_) {\n\t\t\t\tconst styler = createStyler(getModelAnsi(model, levelMapping[level], 'bgColor', ...arguments_), ansiStyles.bgColor.close, this[STYLER]);\n\t\t\t\treturn createBuilder(this, styler, this[IS_EMPTY]);\n\t\t\t};\n\t\t},\n\t};\n}\n\nconst proto = Object.defineProperties(() => {}, {\n\t...styles,\n\tlevel: {\n\t\tenumerable: true,\n\t\tget() {\n\t\t\treturn this[GENERATOR].level;\n\t\t},\n\t\tset(level) {\n\t\t\tthis[GENERATOR].level = level;\n\t\t},\n\t},\n});\n\nconst createStyler = (open, close, parent) => {\n\tlet openAll;\n\tlet closeAll;\n\tif (parent === undefined) {\n\t\topenAll = open;\n\t\tcloseAll = close;\n\t} else {\n\t\topenAll = parent.openAll + open;\n\t\tcloseAll = close + parent.closeAll;\n\t}\n\n\treturn {\n\t\topen,\n\t\tclose,\n\t\topenAll,\n\t\tcloseAll,\n\t\tparent,\n\t};\n};\n\nconst createBuilder = (self, _styler, _isEmpty) => {\n\t// Single argument is hot path, implicit coercion is faster than anything\n\t// eslint-disable-next-line no-implicit-coercion\n\tconst builder = (...arguments_) => applyStyle(builder, (arguments_.length === 1) ? ('' + arguments_[0]) : arguments_.join(' '));\n\n\t// We alter the prototype because we must return a function, but there is\n\t// no way to create a function with a different prototype\n\tObject.setPrototypeOf(builder, proto);\n\n\tbuilder[GENERATOR] = self;\n\tbuilder[STYLER] = _styler;\n\tbuilder[IS_EMPTY] = _isEmpty;\n\n\treturn builder;\n};\n\nconst applyStyle = (self, string) => {\n\tif (self.level <= 0 || !string) {\n\t\treturn self[IS_EMPTY] ? '' : string;\n\t}\n\n\tlet styler = self[STYLER];\n\n\tif (styler === undefined) {\n\t\treturn string;\n\t}\n\n\tconst {openAll, closeAll} = styler;\n\tif (string.includes('\\u001B')) {\n\t\twhile (styler !== undefined) {\n\t\t\t// Replace any instances already present with a re-opening code\n\t\t\t// otherwise only the part of the string until said closing code\n\t\t\t// will be colored, and the rest will simply be 'plain'.\n\t\t\tstring = stringReplaceAll(string, styler.close, styler.open);\n\n\t\t\tstyler = styler.parent;\n\t\t}\n\t}\n\n\t// We can move both next actions out of loop, because remaining actions in loop won't have\n\t// any/visible effect on parts we add here. Close the styling before a linebreak and reopen\n\t// after next line to fix a bleed issue on macOS: https://github.com/chalk/chalk/pull/92\n\tconst lfIndex = string.indexOf('\\n');\n\tif (lfIndex !== -1) {\n\t\tstring = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex);\n\t}\n\n\treturn openAll + string + closeAll;\n};\n\nObject.defineProperties(createChalk.prototype, styles);\n\nconst chalk = createChalk();\nexport const chalkStderr = createChalk({level: stderrColor ? stderrColor.level : 0});\n\nexport {\n\tmodifierNames,\n\tforegroundColorNames,\n\tbackgroundColorNames,\n\tcolorNames,\n\n\t// TODO: Remove these aliases in the next major version\n\tmodifierNames as modifiers,\n\tforegroundColorNames as foregroundColors,\n\tbackgroundColorNames as backgroundColors,\n\tcolorNames as colors,\n} from './vendor/ansi-styles/index.js';\n\nexport {\n\tstdoutColor as supportsColor,\n\tstderrColor as supportsColorStderr,\n};\n\nexport default chalk;\n"],"x_google_ignoreList":[2,3,4,5],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tsx's loader, as the URL `node --import` takes, or null when none resolves.
|
|
3
|
+
*
|
|
4
|
+
* For running one of this driver's scripts in one Node process. The `tsx`
|
|
5
|
+
* binary is a relay — pnpm's `.bin/tsx` shell shim, then tsx's CLI in one Node,
|
|
6
|
+
* which spawns a second Node with this same loader to run the script — so
|
|
7
|
+
* spawning through it costs a shell and a Node start (~50–100 ms) per child.
|
|
8
|
+
*
|
|
9
|
+
* Resolved the way Node resolves the package: from the project the driver
|
|
10
|
+
* runs in first (its cwd is the backend, where a scaffold declares tsx), then
|
|
11
|
+
* from this package. A URL rather than a path: a Windows path is not a valid
|
|
12
|
+
* `--import` specifier.
|
|
13
|
+
*/
|
|
14
|
+
export declare function resolveTsxLoader(): string | null;
|
|
@@ -16,6 +16,16 @@ import { PostgresCollectionRegistry } from "../collections/PostgresCollectionReg
|
|
|
16
16
|
* have and has satisfied itself that widening is safe there.
|
|
17
17
|
*/
|
|
18
18
|
export type UnknownFilterFieldsMode = "error" | "warn";
|
|
19
|
+
/**
|
|
20
|
+
* The fields an unknown-field answer lists, sorted — never an `excludeFromApi`
|
|
21
|
+
* one, for any caller: the list is a hint for a typo, and naming a hidden
|
|
22
|
+
* column there answers the question the unknown-field answer leaves open.
|
|
23
|
+
*
|
|
24
|
+
* Read off the collection when there is one, with the function the REST check
|
|
25
|
+
* uses to answer a query on a hidden field — so its answer and this one are
|
|
26
|
+
* the same bytes. Off the table only when no collection is in hand.
|
|
27
|
+
*/
|
|
28
|
+
export declare function listedColumnKeys(table: PgTable<any>, collection: CollectionConfig | undefined): string[];
|
|
19
29
|
/**
|
|
20
30
|
* A user's search term, made safe to drop inside a `%…%` LIKE pattern.
|
|
21
31
|
*
|
|
@@ -237,6 +247,12 @@ export declare class DrizzleConditionBuilder {
|
|
|
237
247
|
* rather than a half-built condition.
|
|
238
248
|
*/
|
|
239
249
|
private static resolveFilterTarget;
|
|
250
|
+
/**
|
|
251
|
+
* The answer to a filter field that names nothing. Built by the function
|
|
252
|
+
* the REST check answers an `excludeFromApi` field with, so the two cannot
|
|
253
|
+
* be told apart by a byte.
|
|
254
|
+
*/
|
|
255
|
+
private static unknownFilterField;
|
|
240
256
|
/**
|
|
241
257
|
* `applications.status` — the relation, and the column of the target it
|
|
242
258
|
* addresses.
|
|
@@ -731,7 +747,7 @@ export declare class DrizzleConditionBuilder {
|
|
|
731
747
|
vector: number[];
|
|
732
748
|
distance?: "cosine" | "l2" | "inner_product";
|
|
733
749
|
threshold?: number;
|
|
734
|
-
}): {
|
|
750
|
+
}, collection?: CollectionConfig): {
|
|
735
751
|
orderBy: SQL;
|
|
736
752
|
filter?: SQL;
|
|
737
753
|
distanceSelect: SQL;
|
|
@@ -744,6 +760,10 @@ export declare class DrizzleConditionBuilder {
|
|
|
744
760
|
* like candidates. The type check is on the *physical* column
|
|
745
761
|
* (`vector(1536)`) rather than on the declared property, so it holds for an
|
|
746
762
|
* introspected collection too, where the property carries no Rebase type.
|
|
763
|
+
*
|
|
764
|
+
* The answer for a name that is not a column lists no `excludeFromApi`
|
|
765
|
+
* property; the REST check answers a hidden one with the same function, so
|
|
766
|
+
* the two cannot be told apart.
|
|
747
767
|
*/
|
|
748
768
|
private static resolveVectorColumn;
|
|
749
769
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Walk the error cause chain and return the deepest meaningful message.
|
|
3
|
+
*
|
|
4
|
+
* A leaf module, imported by the driver CLI: `pg-error-utils.ts`, where it used
|
|
5
|
+
* to live, imports `@rebasepro/server`, and the CLI must start without it.
|
|
6
|
+
*/
|
|
7
|
+
export declare function extractCauseMessage(error: unknown): string | null;
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* a message that is safe and helpful to show to end-users.
|
|
8
8
|
*/
|
|
9
9
|
import type { CollectionConfig } from "@rebasepro/types";
|
|
10
|
+
import { extractCauseMessage } from "./error-cause.js";
|
|
10
11
|
/** Shape of PostgreSQL errors with diagnostic metadata. */
|
|
11
12
|
export interface PostgresError extends Error {
|
|
12
13
|
code?: string;
|
|
@@ -41,10 +42,7 @@ export declare function extractPgError(error: unknown): PostgresError | null;
|
|
|
41
42
|
* the fallback paths exist for.
|
|
42
43
|
*/
|
|
43
44
|
export declare function reachedDatabase(error: unknown): boolean;
|
|
44
|
-
|
|
45
|
-
* Walk the error cause chain and return the deepest meaningful message.
|
|
46
|
-
*/
|
|
47
|
-
export declare function extractCauseMessage(error: unknown): string | null;
|
|
45
|
+
export { extractCauseMessage };
|
|
48
46
|
export interface ConnectFailure {
|
|
49
47
|
/** True when retrying cannot help: the connection string itself is wrong. */
|
|
50
48
|
fatal: boolean;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The rows a query answered with, whatever shape the driver handed them in.
|
|
3
|
+
*
|
|
4
|
+
* node-postgres answers one result for one statement, and an array of results
|
|
5
|
+
* — one per statement — for a string holding several (`SELECT 1; SELECT 2`).
|
|
6
|
+
* Reading `.rows` off that array gave `undefined`, and the caller got no rows
|
|
7
|
+
* at all for a script that ended in a SELECT. The last statement's rows are
|
|
8
|
+
* the script's answer, as in psql.
|
|
9
|
+
*/
|
|
10
|
+
export declare function rowsOfResult(result: unknown): Record<string, unknown>[];
|
package/dist/websocket.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { SubscriptionAuthContext } from "./services/realtimeService.js";
|
|
2
2
|
import { PostgresBackendDriver } from "./PostgresBackendDriver.js";
|
|
3
3
|
import type { AuthAdapter, RealtimeProvider, RealtimeSocketOptions, WebSocketMessage } from "@rebasepro/types";
|
|
4
|
+
import { type SocketOptIn } from "./socket-opt-ins.js";
|
|
4
5
|
import { type AdminScope } from "@rebasepro/types";
|
|
5
6
|
import { WebSocket } from "ws";
|
|
6
7
|
import type { Server } from "http";
|
|
@@ -19,6 +20,12 @@ export interface SocketRealtimeService {
|
|
|
19
20
|
addClient(clientId: string, ws: WebSocket): void;
|
|
20
21
|
rescopeClient(clientId: string, authContext: SubscriptionAuthContext): Promise<void>;
|
|
21
22
|
handleClientMessage(clientId: string, message: WebSocketMessage, authContext?: SubscriptionAuthContext): Promise<void> | void;
|
|
23
|
+
/**
|
|
24
|
+
* What the client opted into when it opened the socket (see
|
|
25
|
+
* `socket-opt-ins.ts`). Optional: a service that sends nothing that
|
|
26
|
+
* depends on one need not hear about them.
|
|
27
|
+
*/
|
|
28
|
+
setClientOptIns?(clientId: string, optIns: ReadonlySet<SocketOptIn>): void;
|
|
22
29
|
}
|
|
23
30
|
/**
|
|
24
31
|
* Narrow a bootstrapper's realtime provider to what the socket calls.
|
|
@@ -49,6 +56,12 @@ interface WsAuthConfig {
|
|
|
49
56
|
* whatever session it holds now — a refreshed token, or none.
|
|
50
57
|
*/
|
|
51
58
|
export declare const SESSION_ENDED_CLOSE_CODE = 4001;
|
|
59
|
+
/**
|
|
60
|
+
* The close code for a socket refused because its user already holds
|
|
61
|
+
* `REALTIME_MAX_SOCKETS_PER_USER` sockets on this server. The SDK reconnects
|
|
62
|
+
* with backoff, so a tab refused this way gets in once another one closes.
|
|
63
|
+
*/
|
|
64
|
+
export declare const TOO_MANY_SOCKETS_CLOSE_CODE = 4002;
|
|
52
65
|
/**
|
|
53
66
|
* WebSocket message types on the admin plane, and the scope each needs.
|
|
54
67
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/server-postgres",
|
|
3
|
-
"version": "0.24.
|
|
3
|
+
"version": "0.24.1-canary.g56af4a4",
|
|
4
4
|
"description": "PostgreSQL data source backend implementation for Rebase with Drizzle ORM",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cms",
|
|
@@ -50,11 +50,11 @@
|
|
|
50
50
|
"execa": "^9.6.1",
|
|
51
51
|
"pg": "^8.22.0",
|
|
52
52
|
"ws": "^8.21.1",
|
|
53
|
-
"@rebasepro/common": "0.24.
|
|
54
|
-
"@rebasepro/
|
|
55
|
-
"@rebasepro/
|
|
56
|
-
"@rebasepro/
|
|
57
|
-
"@rebasepro/utils": "0.24.
|
|
53
|
+
"@rebasepro/common": "0.24.1-canary.g56af4a4",
|
|
54
|
+
"@rebasepro/codegen": "0.24.1-canary.g56af4a4",
|
|
55
|
+
"@rebasepro/server": "0.24.1-canary.g56af4a4",
|
|
56
|
+
"@rebasepro/types": "0.24.1-canary.g56af4a4",
|
|
57
|
+
"@rebasepro/utils": "0.24.1-canary.g56af4a4"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@electric-sql/pglite": "0.5.6",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"typescript": "^6.0.3",
|
|
71
71
|
"vite": "^8.1.5",
|
|
72
72
|
"vitest": "^4.1.11",
|
|
73
|
-
"@rebasepro/client": "0.24.
|
|
73
|
+
"@rebasepro/client": "0.24.1-canary.g56af4a4"
|
|
74
74
|
},
|
|
75
75
|
"gitHead": "d935eefa5aa8d1009a2398cfac2c1e4ee9aeb6b6",
|
|
76
76
|
"publishConfig": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"backup-cli-DW5p9_zv.js","names":[],"sources":["../src/backup/backup-cli.ts"],"sourcesContent":["/**\n * CLI handlers for `rebase db backup`, `rebase db restore`, and\n * `rebase db backups list`. Kept out of the main `cli.ts` dispatcher so the\n * backup surface stays self-contained.\n */\nimport arg from \"arg\";\nimport path from \"path\";\nimport fs from \"fs\";\nimport os from \"os\";\nimport readline from \"readline\";\nimport chalk from \"chalk\";\n// Aliased: `out` is already a local in two of the commands below (the `--out`\n// backup destination).\nimport { out as print, outWarn, outError } from \"../cli-output\";\nimport { parseDriverLine } from \"../cli-flags\";\nimport type { StorageController } from \"@rebasepro/server\";\nimport { parseEnvBoolean } from \"@rebasepro/types\";\nimport {\n BackupDestination,\n globalsFileForDump,\n parseBackupDestination,\n parseDbNameFromUrl,\n resolveConnectionString,\n withDatabaseName\n} from \"./pg-tools\";\nimport {\n applyGlobals,\n BackupToolError,\n createDump,\n discardPartialDump,\n ensureDatabaseExists,\n listBackups,\n preflight,\n restoreDump,\n uploadBackup,\n validateDump\n} from \"./backup-service\";\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;\n}\n\n/**\n * Build a StorageController for an object-storage destination from the same\n * `S3_*` env vars the backend uses. Returns `null` for local destinations.\n */\nasync function resolveStorageForDestination(\n dest: BackupDestination,\n env: Record<string, string | undefined>\n): Promise<StorageController | null> {\n if (dest.kind === \"local\") return null;\n if (dest.kind === \"gcs\") {\n const { GCSStorageController } = await import(\"@rebasepro/server\");\n return new GCSStorageController({ type: \"gcs\", bucket: dest.bucket });\n }\n // s3 (also covers R2/MinIO/Hetzner/GCS-interop via S3_ENDPOINT)\n if (!env.S3_ACCESS_KEY_ID || !env.S3_SECRET_ACCESS_KEY) {\n throw new BackupToolError(\n \"S3 destination requires S3_ACCESS_KEY_ID and S3_SECRET_ACCESS_KEY in the environment.\",\n \"Set the same S3_* variables your backend uses for storage.\"\n );\n }\n const { S3StorageController } = await import(\"@rebasepro/server\");\n return new S3StorageController({\n type: \"s3\",\n bucket: dest.bucket,\n region: env.S3_REGION || \"auto\",\n accessKeyId: env.S3_ACCESS_KEY_ID,\n secretAccessKey: env.S3_SECRET_ACCESS_KEY,\n endpoint: env.S3_ENDPOINT,\n // Unset stays unset, so the controller decides from the endpoint the\n // way it does for the runtime's own storage. `=== \"true\"` made it an\n // explicit `false`, and a MinIO backup addressed the bucket as a host.\n forcePathStyle: parseEnvBoolean(env.S3_FORCE_PATH_STYLE)\n });\n}\n\nfunction requireConnection(): string {\n const conn = resolveConnectionString(process.env);\n if (!conn) {\n outError(chalk.red(\"✗ DATABASE_URL is not set. Make sure your .env file is configured.\"));\n process.exit(1);\n }\n return conn;\n}\n\nasync function promptConfirm(question: string): Promise<boolean> {\n // Non-interactive shells (CI, pipes) can't answer — treat as \"no\".\n if (!process.stdin.isTTY) return false;\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n try {\n const answer: string = await new Promise((resolve) => rl.question(question, resolve));\n return /^y(es)?$/i.test(answer.trim());\n } finally {\n rl.close();\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// rebase db backup\n// ─────────────────────────────────────────────────────────────────────────\nexport async function backupCommand(rawArgs: string[]): Promise<void> {\n const args = arg(\n {\n \"--out\": String,\n // The same alias `build` and `cloud env pull` carry. `--output` is\n // the canonical spelling in this CLI and this command took only\n // `--out`, so `rebase db backup --output ./backups` wrote to\n // ./backups *and* to the default directory — the flag was ignored\n // and its value became a positional.\n \"--output\": \"--out\",\n \"--exclude-schema\": [String],\n \"--no-owner\": Boolean,\n \"--enable-row-security\": Boolean,\n \"--row-security-role\": String,\n \"-o\": \"--out\"\n },\n { argv: rawArgs.slice(2), permissive: true }\n );\n\n if (args._.includes(\"--help\") || rawArgs.includes(\"--help\")) {\n printBackupHelp();\n return;\n }\n\n const connectionString = requireConnection();\n const dbName = parseDbNameFromUrl(connectionString) ?? \"database\";\n const out = args[\"--out\"] || process.env.BACKUP_DESTINATION || path.join(process.cwd(), \"backups\");\n const dest = parseBackupDestination(out);\n\n print(\"\");\n print(chalk.bold(\" 💾 Rebase DB Backup\"));\n print(chalk.gray(` Database: ${dbName}`));\n print(chalk.gray(` Destination: ${out}`));\n print(\"\");\n\n // Version pre-flight (doctor-style).\n const pf = await preflight(\"pg_dump\", connectionString);\n if (!pf.compatible) {\n outError(chalk.red(` ✗ ${pf.reason}`));\n process.exit(1);\n }\n print(chalk.gray(` Using pg_dump ${pf.toolMajor} against server ${pf.serverMajor}.`));\n\n // Opt-in, and loud. With row security on, pg_dump stops refusing to read\n // rows it cannot see and starts leaving them out — an exit-0 backup that\n // is quietly short. Anyone choosing that should know they chose it.\n const rowSecurity = args[\"--enable-row-security\"]\n ? { uid: \"rebase-db-backup\", roles: [args[\"--row-security-role\"] || \"admin\"] }\n : undefined;\n\n if (rowSecurity) {\n outWarn(\"\");\n outWarn(chalk.yellow(\" ⚠ Dumping with row-level security ON.\"));\n outWarn(chalk.gray(` Reading as roles [${rowSecurity.roles.join(\", \")}], which satisfies the generated`));\n outWarn(chalk.gray(\" `admin_full_access` policy. This backup contains exactly the rows those\"));\n outWarn(chalk.gray(\" policies admit — a table whose policies have no admin rule comes out short,\"));\n outWarn(chalk.gray(\" and pg_dump will not say so. Prefer granting the dumping role BYPASSRLS.\"));\n outWarn(\"\");\n }\n\n try {\n if (dest.kind === \"local\") {\n // Honour an explicit `…/name.dump` path; otherwise treat it as a\n // directory and auto-name the file.\n const explicitFile = dest.path.endsWith(\".dump\");\n const dump = await createDump({\n connectionString,\n dbName,\n outDir: explicitFile ? path.dirname(dest.path) : dest.path,\n fileName: explicitFile ? path.basename(dest.path) : undefined,\n excludeSchemas: args[\"--exclude-schema\"],\n noOwner: args[\"--no-owner\"],\n inheritStdio: true,\n rowSecurity\n });\n await assertDumpValid(dump.localFile);\n print(\"\");\n print(chalk.green(` ✓ Backup written to ${dump.localFile} (${formatBytes(dump.sizeBytes)})`));\n if (dump.globalsFile) {\n print(chalk.gray(` ✓ Roles captured to ${dump.globalsFile} (needed so RLS survives a restore).`));\n }\n } else {\n const storage = await resolveStorageForDestination(dest, process.env);\n const dump = await createDump({\n connectionString,\n dbName,\n excludeSchemas: args[\"--exclude-schema\"],\n noOwner: args[\"--no-owner\"],\n inheritStdio: true,\n rowSecurity\n });\n try {\n await assertDumpValid(dump.localFile);\n const uploaded = await uploadBackup(storage!, dump.localFile, dest);\n print(\"\");\n print(chalk.green(` ✓ Backup uploaded to ${uploaded.storageUrl} (${formatBytes(dump.sizeBytes)})`));\n // Upload the roles sidecar next to the dump so a restore can\n // recreate roles the dump's GRANT/RLS statements depend on.\n if (dump.globalsFile && fs.existsSync(dump.globalsFile)) {\n const globalsUpload = await uploadBackup(storage!, dump.globalsFile, dest);\n print(chalk.gray(` ✓ Roles uploaded to ${globalsUpload.storageUrl} (needed so RLS survives a restore).`));\n }\n print(chalk.gray(\" Ensure this bucket is private — backups may contain secrets and PII.\"));\n } finally {\n if (fs.existsSync(dump.localFile)) fs.unlinkSync(dump.localFile);\n if (dump.globalsFile && fs.existsSync(dump.globalsFile)) fs.unlinkSync(dump.globalsFile);\n }\n }\n print(\"\");\n } catch (err) {\n reportError(err);\n process.exit(1);\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// rebase db restore <backup>\n// ─────────────────────────────────────────────────────────────────────────\nconst RESTORE_FLAGS = {\n \"--target-db\": String,\n \"--create-db\": Boolean,\n \"--clean\": Boolean,\n \"--no-owner\": Boolean,\n \"--continue-on-error\": Boolean,\n \"--yes\": Boolean,\n \"-y\": \"--yes\"\n} satisfies arg.Spec;\n\n/** Parse a `rebase db restore …` line; the first positional is the backup. */\nexport function parseRestoreLine(rawArgs: string[]) {\n return parseDriverLine(RESTORE_FLAGS, rawArgs);\n}\n\nexport async function restoreCommand(rawArgs: string[]): Promise<void> {\n const args = parseRestoreLine(rawArgs);\n\n const backupArg = args._[0];\n if (!backupArg || rawArgs.includes(\"--help\")) {\n printRestoreHelp();\n if (!backupArg && !rawArgs.includes(\"--help\")) process.exit(1);\n return;\n }\n\n const baseConnection = requireConnection();\n\n // Choose the target connection: an explicit --target-db (or --create-db's\n // implied fresh db) swaps the database name so the live one isn't clobbered.\n const targetDb = args[\"--target-db\"] ?? parseDbNameFromUrl(baseConnection) ?? undefined;\n const targetConnection = args[\"--target-db\"]\n ? withDatabaseName(baseConnection, args[\"--target-db\"])\n : baseConnection;\n\n print(\"\");\n print(chalk.bold(\" ♻️ Rebase DB Restore\"));\n print(chalk.gray(` Source: ${backupArg}`));\n print(chalk.gray(` Target: ${targetDb ?? \"(from DATABASE_URL)\"}`));\n print(\"\");\n\n // Resolve the local file to restore from (download object-storage keys).\n // Also resolve the `.globals.sql` roles sidecar so cluster roles can be\n // recreated before the restore — without them the dump's GRANT/RLS\n // statements fail and RLS is silently lost.\n let localFile: string;\n let globalsSql: string | null = null;\n let cleanupTemp = false;\n try {\n if (/^(s3|gs):\\/\\//.test(backupArg)) {\n const dest = parseBackupDestination(backupArg.replace(/\\/[^/]+$/, \"\"));\n const storage = await resolveStorageForDestination(dest, process.env);\n if (!storage) throw new BackupToolError(\"Could not resolve storage for the given URL.\");\n const key = backupArg.replace(/^(s3|gs):\\/\\/[^/]+\\//, \"\");\n const bucket = backupArg.replace(/^(s3|gs):\\/\\/([^/]+)\\/.*$/, \"$2\");\n const file = await storage.getObject(key, bucket);\n if (!file) throw new BackupToolError(`Backup not found in storage: ${backupArg}`);\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), \"rebase-restore-\"));\n localFile = path.join(tmpDir, path.basename(key));\n fs.writeFileSync(localFile, Buffer.from(await file.arrayBuffer()));\n cleanupTemp = true;\n const globalsObj = await storage.getObject(globalsFileForDump(key), bucket);\n if (globalsObj) globalsSql = Buffer.from(await globalsObj.arrayBuffer()).toString(\"utf-8\");\n } else {\n localFile = path.resolve(backupArg);\n if (!fs.existsSync(localFile)) {\n outError(chalk.red(` ✗ Backup file not found: ${localFile}`));\n process.exit(1);\n }\n const globalsPath = globalsFileForDump(localFile);\n if (fs.existsSync(globalsPath)) globalsSql = fs.readFileSync(globalsPath, \"utf-8\");\n }\n\n // Version pre-flight. Check against the base connection — the server\n // version is identical for every database, and the target may not\n // exist yet when --create-db is used.\n const pf = await preflight(\"pg_restore\", baseConnection);\n if (!pf.compatible) {\n outError(chalk.red(` ✗ ${pf.reason}`));\n process.exit(1);\n }\n\n // `--create-db` needs a name before anything else can be decided.\n if (args[\"--create-db\"] && !targetDb) {\n outError(chalk.red(\" ✗ --create-db requires a resolvable target database name (use --target-db).\"));\n process.exit(1);\n }\n\n // Destructive-action gate. Restores overwrite data; never run without\n // an explicit yes (interactive confirmation or --yes).\n //\n // Ahead of `--create-db`, not after it. Creating the database first\n // meant an aborted run had already changed the cluster, while printing\n // \"No changes were made\" — and it left an empty database behind that a\n // second, confirmed run then reported as \"already exists\". The gate is\n // now the first thing that can stop the command, so its own message is\n // true whichever way the answer goes.\n if (!args[\"--yes\"]) {\n outWarn(chalk.yellow(\n ` ⚠️ This will restore into \"${targetDb ?? \"the target database\"}\" and may overwrite existing data.`\n ));\n const confirmed = await promptConfirm(chalk.yellow(\" Type 'yes' to continue: \"));\n if (!confirmed) {\n print(chalk.gray(\" Aborted. No changes were made.\"));\n process.exit(1);\n }\n }\n\n // Create the target database when requested.\n if (args[\"--create-db\"]) {\n const created = await ensureDatabaseExists(baseConnection, targetDb!);\n print(chalk.gray(created ? ` ✓ Created database \"${targetDb}\".` : ` • Database \"${targetDb}\" already exists.`));\n }\n\n // Recreate cluster roles before restoring so GRANT/RLS statements in\n // the dump apply. Best-effort and idempotent (see applyGlobals).\n if (globalsSql) {\n print(chalk.gray(\" Recreating cluster roles from the backup's roles sidecar…\"));\n const { applied, skipped } = await applyGlobals(\n targetConnection,\n globalsSql,\n (m) => print(chalk.gray(m))\n );\n print(chalk.gray(` ✓ Roles: ${applied} applied, ${skipped} skipped (already present or not permitted).`));\n } else {\n outWarn(chalk.yellow(\" ⚠️ No roles sidecar (.globals.sql) accompanies this backup.\"));\n outWarn(chalk.yellow(\" If the dump grants to roles that don't exist (e.g. rebase_user), the restore\"));\n outWarn(chalk.yellow(\" will fail — recreate those roles first, or use a backup that includes its globals.\"));\n }\n\n await restoreDump({\n connectionString: targetConnection,\n inputFile: localFile,\n clean: args[\"--clean\"],\n noOwner: args[\"--no-owner\"],\n // Fail loudly by default so a skipped GRANT never leaves RLS off.\n exitOnError: !args[\"--continue-on-error\"],\n inheritStdio: true\n });\n\n print(\"\");\n print(chalk.green(` ✓ Restore completed into \"${targetDb ?? \"the target database\"}\".`));\n print(\"\");\n } catch (err) {\n reportError(err);\n process.exit(1);\n } finally {\n if (cleanupTemp && typeof localFile! === \"string\" && fs.existsSync(localFile!)) {\n fs.rmSync(path.dirname(localFile!), { recursive: true, force: true });\n }\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// rebase db backups list\n// ─────────────────────────────────────────────────────────────────────────\nexport async function backupsCommand(rawArgs: string[]): Promise<void> {\n const action = rawArgs[2];\n if (!action || action === \"--help\") {\n printBackupsHelp();\n return;\n }\n if (action !== \"list\") {\n outError(chalk.red(`Unknown backups action: \"${action}\". Valid: list`));\n process.exit(1);\n }\n\n const args = arg({ \"--out\": String, \"--output\": \"--out\", \"-o\": \"--out\" }, { argv: rawArgs.slice(3), permissive: true });\n const out = args[\"--out\"] || process.env.BACKUP_DESTINATION || path.join(process.cwd(), \"backups\");\n const dest = parseBackupDestination(out);\n\n try {\n const storage = await resolveStorageForDestination(dest, process.env);\n const backups = await listBackups(dest, storage ?? undefined);\n print(\"\");\n if (backups.length === 0) {\n print(chalk.gray(` No backups found at ${out}.`));\n } else {\n print(chalk.bold(` 💾 ${backups.length} backup(s) at ${out}:`));\n print(\"\");\n for (const b of backups) {\n const when = b.createdAt ? b.createdAt.toISOString() : \"unknown date\";\n const name = dest.kind === \"local\" ? path.basename(b.key) : b.key;\n // An empty file is called out rather than listed as a peer of\n // the real ones. Older failures could leave a 0-byte dump here\n // (fixed at the source now), and retention protects the newest\n // by date whatever they contain — so a corpse left in place can\n // hold a `keepMinimum` slot against a backup that matters.\n const empty = b.sizeBytes === 0;\n const size = b.sizeBytes === undefined ? \"\" : ` — ${formatBytes(b.sizeBytes)}`;\n if (empty) {\n print(` ${chalk.red(\"○\")} ${chalk.bold(name)} ${chalk.gray(`— ${when}`)}${chalk.red(\" — EMPTY, not restorable\")}`);\n } else {\n print(` ${chalk.green(\"●\")} ${chalk.bold(name)} ${chalk.gray(`— ${when}${size}`)}`);\n }\n }\n if (backups.some(b => b.sizeBytes === 0)) {\n print(\"\");\n print(chalk.yellow(\" ⚠ Empty files above are leftovers from a failed backup. Delete them:\"));\n print(chalk.gray(\" they count as recent backups for retention but restore nothing.\"));\n }\n }\n print(\"\");\n } catch (err) {\n reportError(err);\n process.exit(1);\n }\n}\n\n/**\n * Verify a freshly written dump; abort the command if it looks corrupt.\n *\n * Discards the artifact on the way out. Refusing to *report* success was never\n * enough on its own: the file stayed on disk, `rebase db backups list` showed\n * it as an ordinary entry, and retention — which ranks by timestamp and never\n * looks at size — would protect it as one of the `keepMinimum` newest while\n * pruning a real backup underneath it. The roles sidecar goes too; the two are\n * uploaded and pruned as a pair, so half a pair is not a backup either.\n */\nasync function assertDumpValid(localFile: string): Promise<void> {\n const check = await validateDump(localFile);\n if (!check.ok) {\n discardPartialDump(globalsFileForDump(localFile));\n discardPartialDump(localFile);\n throw new BackupToolError(\n `The backup failed validation and was discarded: ${check.reason}`,\n \"The dump was corrupt or truncated, so nothing was kept. Investigate before relying on this destination.\"\n );\n }\n}\n\nfunction reportError(err: unknown): void {\n if (err instanceof BackupToolError) {\n outError(chalk.red(` ✗ ${err.message}`));\n if (err.hint) outError(chalk.gray(` ${err.hint}`));\n } else {\n outError(chalk.red(` ✗ ${err instanceof Error ? err.message : String(err)}`));\n }\n}\n\nfunction printBackupHelp(): void {\n print(`\n${chalk.bold(\"rebase db backup\")} — Create a database backup (pg_dump, custom format)\n\n${chalk.green.bold(\"Usage\")}\n rebase db backup [--out <path|s3://bucket/prefix>] [options]\n\n${chalk.green.bold(\"Options\")}\n ${chalk.blue(\"--out, -o\")} <dest> Local path or s3://…/gs://… URL (default: ./backups)\n ${chalk.blue(\"--exclude-schema\")} <s> Exclude a schema (repeatable)\n ${chalk.blue(\"--no-owner\")} Omit ownership commands from the dump\n ${chalk.blue(\"--enable-row-security\")} Dump as an admin subject instead of failing on RLS\n ${chalk.red(\"(may produce a partial dump — see below)\")}\n ${chalk.blue(\"--row-security-role\")} <r> Role to read as with the flag above (default: admin)\n\n${chalk.green.bold(\"Row-level security\")}\n On a managed Postgres the dumping role usually owns nothing and has no\n BYPASSRLS, so pg_dump refuses:\n\n ERROR: query would be affected by row-level security policy for table \"...\"\n\n That refusal is the safe behaviour. --enable-row-security replaces it by\n reading as an admin subject: Rebase sets app.uid/app.user_roles so the\n generated admin_full_access policy admits the dump. The dump then contains\n exactly what those policies admit ${chalk.red(\"and no error is raised for what they do not\")} —\n a table whose policies lack an admin rule comes out short, silently.\n\n Granting the dumping role BYPASSRLS is the option that keeps a backup\n meaning \"every row\".\n\n${chalk.green.bold(\"Notes\")}\n Backups may contain secrets and PII. Use private storage destinations and\n enable encryption-at-rest. See docs/backups.md.\n`);\n}\n\nfunction printRestoreHelp(): void {\n print(`\n${chalk.bold(\"rebase db restore\")} — Restore a database from a backup (pg_restore)\n\n${chalk.green.bold(\"Usage\")}\n rebase db restore <backup> [options]\n\n${chalk.green.bold(\"Arguments\")}\n <backup> Local .dump file, or s3://…/gs://… object key\n\n${chalk.green.bold(\"Options\")}\n ${chalk.blue(\"--target-db\")} <name> Restore into this database instead of DATABASE_URL's\n ${chalk.blue(\"--create-db\")} Create the target database first if it doesn't exist\n ${chalk.blue(\"--clean\")} Drop existing objects before recreating them\n ${chalk.blue(\"--no-owner\")} Ignore ownership from the dump\n ${chalk.blue(\"--continue-on-error\")} Log and continue past errors ${chalk.red(\"(may leave RLS un-enforced!)\")}\n ${chalk.blue(\"--yes, -y\")} Skip the interactive confirmation ${chalk.red(\"(destructive!)\")}\n\n${chalk.red.bold(\"Warning\")}\n Restore is destructive and never runs automatically. Without --yes it\n requires an interactive 'yes'. Prefer --create-db/--target-db to restore\n into a fresh database rather than overwriting a live one.\n\n By default the restore aborts on the first error (--exit-on-error) so a\n skipped GRANT never silently leaves RLS un-enforced. Roles are recreated\n from the backup's .globals.sql sidecar first; keep that file next to the\n dump. Use --continue-on-error only when you understand the consequences.\n`);\n}\n\nfunction printBackupsHelp(): void {\n print(`\n${chalk.bold(\"rebase db backups\")} — Manage stored backups\n\n${chalk.green.bold(\"Usage\")}\n rebase db backups list [--out <path|s3://bucket/prefix>]\n rebase db backup list [--out <path|s3://bucket/prefix>] ${chalk.gray(\"(the same thing)\")}\n`);\n}\n"],"mappings":";;;;;;;;;;;;;;;AAsCA,SAAS,YAAY,OAAuB;CACxC,IAAI,QAAQ,MAAM,OAAO,GAAG,MAAM;CAClC,IAAI,QAAQ,OAAO,MAAM,OAAO,IAAI,QAAQ,KAAA,CAAM,QAAQ,CAAC,EAAE;CAC7D,IAAI,QAAQ,OAAO,OAAO,MAAM,OAAO,IAAI,SAAS,OAAO,MAAA,CAAO,QAAQ,CAAC,EAAE;CAC7E,OAAO,IAAI,SAAS,OAAO,OAAO,MAAA,CAAO,QAAQ,CAAC,EAAE;AACxD;;;;;AAMA,eAAe,6BACX,MACA,KACiC;CACjC,IAAI,KAAK,SAAS,SAAS,OAAO;CAClC,IAAI,KAAK,SAAS,OAAO;EACrB,MAAM,EAAE,yBAAyB,MAAM,OAAO;EAC9C,OAAO,IAAI,qBAAqB;GAAE,MAAM;GAAO,QAAQ,KAAK;EAAO,CAAC;CACxE;CAEA,IAAI,CAAC,IAAI,oBAAoB,CAAC,IAAI,sBAC9B,MAAM,IAAI,gBACN,yFACA,4DACJ;CAEJ,MAAM,EAAE,wBAAwB,MAAM,OAAO;CAC7C,OAAO,IAAI,oBAAoB;EAC3B,MAAM;EACN,QAAQ,KAAK;EACb,QAAQ,IAAI,aAAa;EACzB,aAAa,IAAI;EACjB,iBAAiB,IAAI;EACrB,UAAU,IAAI;EAId,gBAAgB,gBAAgB,IAAI,mBAAmB;CAC3D,CAAC;AACL;AAEA,SAAS,oBAA4B;CACjC,MAAM,OAAO,wBAAwB,QAAQ,GAAG;CAChD,IAAI,CAAC,MAAM;EACP,SAAS,MAAM,IAAI,oEAAoE,CAAC;EACxF,QAAQ,KAAK,CAAC;CAClB;CACA,OAAO;AACX;AAEA,eAAe,cAAc,UAAoC;CAE7D,IAAI,CAAC,QAAQ,MAAM,OAAO,OAAO;CACjC,MAAM,KAAK,+BAAA,QAAS,gBAAgB;EAAE,OAAO,QAAQ;EAAO,QAAQ,QAAQ;CAAO,CAAC;CACpF,IAAI;EACA,MAAM,SAAiB,MAAM,IAAI,SAAS,YAAY,GAAG,SAAS,UAAU,OAAO,CAAC;EACpF,OAAO,YAAY,KAAK,OAAO,KAAK,CAAC;CACzC,UAAU;EACN,GAAG,MAAM;CACb;AACJ;AAKA,eAAsB,cAAc,SAAkC;CAClE,MAAM,QAAA,GAAA,WAAA,QAAA,CACF;EACI,SAAS;EAMT,YAAY;EACZ,oBAAoB,CAAC,MAAM;EAC3B,cAAc;EACd,yBAAyB;EACzB,uBAAuB;EACvB,MAAM;CACV,GACA;EAAE,MAAM,QAAQ,MAAM,CAAC;EAAG,YAAY;CAAK,CAC/C;CAEA,IAAI,KAAK,EAAE,SAAS,QAAQ,KAAK,QAAQ,SAAS,QAAQ,GAAG;EACzD,gBAAgB;EAChB;CACJ;CAEA,MAAM,mBAAmB,kBAAkB;CAC3C,MAAM,SAAS,mBAAmB,gBAAgB,KAAK;CACvD,MAAM,QAAM,KAAK,YAAY,QAAQ,IAAI,sBAAsB,KAAK,KAAK,QAAQ,IAAI,GAAG,SAAS;CACjG,MAAM,OAAO,uBAAuB,KAAG;CAEvC,IAAM,EAAE;CACR,IAAM,MAAM,KAAK,uBAAuB,CAAC;CACzC,IAAM,MAAM,KAAK,kBAAkB,QAAQ,CAAC;CAC5C,IAAM,MAAM,KAAK,kBAAkB,OAAK,CAAC;CACzC,IAAM,EAAE;CAGR,MAAM,KAAK,MAAM,UAAU,WAAW,gBAAgB;CACtD,IAAI,CAAC,GAAG,YAAY;EAChB,SAAS,MAAM,IAAI,OAAO,GAAG,QAAQ,CAAC;EACtC,QAAQ,KAAK,CAAC;CAClB;CACA,IAAM,MAAM,KAAK,mBAAmB,GAAG,UAAU,kBAAkB,GAAG,YAAY,EAAE,CAAC;CAKrF,MAAM,cAAc,KAAK,2BACnB;EAAE,KAAK;EAAoB,OAAO,CAAC,KAAK,0BAA0B,OAAO;CAAE,IAC3E,KAAA;CAEN,IAAI,aAAa;EACb,QAAQ,EAAE;EACV,QAAQ,MAAM,OAAO,0CAA0C,CAAC;EAChE,QAAQ,MAAM,KAAK,0BAA0B,YAAY,MAAM,KAAK,IAAI,EAAE,iCAAiC,CAAC;EAC5G,QAAQ,MAAM,KAAK,8EAA8E,CAAC;EAClG,QAAQ,MAAM,KAAK,kFAAkF,CAAC;EACtG,QAAQ,MAAM,KAAK,+EAA+E,CAAC;EACnG,QAAQ,EAAE;CACd;CAEA,IAAI;EACA,IAAI,KAAK,SAAS,SAAS;GAGvB,MAAM,eAAe,KAAK,KAAK,SAAS,OAAO;GAC/C,MAAM,OAAO,MAAM,WAAW;IAC1B;IACA;IACA,QAAQ,eAAe,KAAK,QAAQ,KAAK,IAAI,IAAI,KAAK;IACtD,UAAU,eAAe,KAAK,SAAS,KAAK,IAAI,IAAI,KAAA;IACpD,gBAAgB,KAAK;IACrB,SAAS,KAAK;IACd,cAAc;IACd;GACJ,CAAC;GACD,MAAM,gBAAgB,KAAK,SAAS;GACpC,IAAM,EAAE;GACR,IAAM,MAAM,MAAM,yBAAyB,KAAK,UAAU,IAAI,YAAY,KAAK,SAAS,EAAE,EAAE,CAAC;GAC7F,IAAI,KAAK,aACL,IAAM,MAAM,KAAK,2BAA2B,KAAK,YAAY,qCAAqC,CAAC;EAE3G,OAAO;GACH,MAAM,UAAU,MAAM,6BAA6B,MAAM,QAAQ,GAAG;GACpE,MAAM,OAAO,MAAM,WAAW;IAC1B;IACA;IACA,gBAAgB,KAAK;IACrB,SAAS,KAAK;IACd,cAAc;IACd;GACJ,CAAC;GACD,IAAI;IACA,MAAM,gBAAgB,KAAK,SAAS;IACpC,MAAM,WAAW,MAAM,aAAa,SAAU,KAAK,WAAW,IAAI;IAClE,IAAM,EAAE;IACR,IAAM,MAAM,MAAM,0BAA0B,SAAS,WAAW,IAAI,YAAY,KAAK,SAAS,EAAE,EAAE,CAAC;IAGnG,IAAI,KAAK,eAAe,GAAG,WAAW,KAAK,WAAW,GAAG;KACrD,MAAM,gBAAgB,MAAM,aAAa,SAAU,KAAK,aAAa,IAAI;KACzE,IAAM,MAAM,KAAK,2BAA2B,cAAc,WAAW,qCAAqC,CAAC;IAC/G;IACA,IAAM,MAAM,KAAK,0EAA0E,CAAC;GAChG,UAAU;IACN,IAAI,GAAG,WAAW,KAAK,SAAS,GAAG,GAAG,WAAW,KAAK,SAAS;IAC/D,IAAI,KAAK,eAAe,GAAG,WAAW,KAAK,WAAW,GAAG,GAAG,WAAW,KAAK,WAAW;GAC3F;EACJ;EACA,IAAM,EAAE;CACZ,SAAS,KAAK;EACV,YAAY,GAAG;EACf,QAAQ,KAAK,CAAC;CAClB;AACJ;AAKA,IAAM,gBAAgB;CAClB,eAAe;CACf,eAAe;CACf,WAAW;CACX,cAAc;CACd,uBAAuB;CACvB,SAAS;CACT,MAAM;AACV;;AAGA,SAAgB,iBAAiB,SAAmB;CAChD,OAAO,gBAAgB,eAAe,OAAO;AACjD;AAEA,eAAsB,eAAe,SAAkC;CACnE,MAAM,OAAO,iBAAiB,OAAO;CAErC,MAAM,YAAY,KAAK,EAAE;CACzB,IAAI,CAAC,aAAa,QAAQ,SAAS,QAAQ,GAAG;EAC1C,iBAAiB;EACjB,IAAI,CAAC,aAAa,CAAC,QAAQ,SAAS,QAAQ,GAAG,QAAQ,KAAK,CAAC;EAC7D;CACJ;CAEA,MAAM,iBAAiB,kBAAkB;CAIzC,MAAM,WAAW,KAAK,kBAAkB,mBAAmB,cAAc,KAAK,KAAA;CAC9E,MAAM,mBAAmB,KAAK,iBACxB,iBAAiB,gBAAgB,KAAK,cAAc,IACpD;CAEN,IAAM,EAAE;CACR,IAAM,MAAM,KAAK,yBAAyB,CAAC;CAC3C,IAAM,MAAM,KAAK,cAAc,WAAW,CAAC;CAC3C,IAAM,MAAM,KAAK,cAAc,YAAY,uBAAuB,CAAC;CACnE,IAAM,EAAE;CAMR,IAAI;CACJ,IAAI,aAA4B;CAChC,IAAI,cAAc;CAClB,IAAI;EACA,IAAI,gBAAgB,KAAK,SAAS,GAAG;GAEjC,MAAM,UAAU,MAAM,6BADT,uBAAuB,UAAU,QAAQ,YAAY,EAAE,CACjB,GAAM,QAAQ,GAAG;GACpE,IAAI,CAAC,SAAS,MAAM,IAAI,gBAAgB,8CAA8C;GACtF,MAAM,MAAM,UAAU,QAAQ,wBAAwB,EAAE;GACxD,MAAM,SAAS,UAAU,QAAQ,6BAA6B,IAAI;GAClE,MAAM,OAAO,MAAM,QAAQ,UAAU,KAAK,MAAM;GAChD,IAAI,CAAC,MAAM,MAAM,IAAI,gBAAgB,gCAAgC,WAAW;GAChF,MAAM,SAAS,GAAG,YAAY,KAAK,KAAK,GAAG,OAAO,GAAG,iBAAiB,CAAC;GACvE,YAAY,KAAK,KAAK,QAAQ,KAAK,SAAS,GAAG,CAAC;GAChD,GAAG,cAAc,WAAW,OAAO,KAAK,MAAM,KAAK,YAAY,CAAC,CAAC;GACjE,cAAc;GACd,MAAM,aAAa,MAAM,QAAQ,UAAU,mBAAmB,GAAG,GAAG,MAAM;GAC1E,IAAI,YAAY,aAAa,OAAO,KAAK,MAAM,WAAW,YAAY,CAAC,CAAC,CAAC,SAAS,OAAO;EAC7F,OAAO;GACH,YAAY,KAAK,QAAQ,SAAS;GAClC,IAAI,CAAC,GAAG,WAAW,SAAS,GAAG;IAC3B,SAAS,MAAM,IAAI,8BAA8B,WAAW,CAAC;IAC7D,QAAQ,KAAK,CAAC;GAClB;GACA,MAAM,cAAc,mBAAmB,SAAS;GAChD,IAAI,GAAG,WAAW,WAAW,GAAG,aAAa,GAAG,aAAa,aAAa,OAAO;EACrF;EAKA,MAAM,KAAK,MAAM,UAAU,cAAc,cAAc;EACvD,IAAI,CAAC,GAAG,YAAY;GAChB,SAAS,MAAM,IAAI,OAAO,GAAG,QAAQ,CAAC;GACtC,QAAQ,KAAK,CAAC;EAClB;EAGA,IAAI,KAAK,kBAAkB,CAAC,UAAU;GAClC,SAAS,MAAM,IAAI,+EAA+E,CAAC;GACnG,QAAQ,KAAK,CAAC;EAClB;EAWA,IAAI,CAAC,KAAK,UAAU;GAChB,QAAQ,MAAM,OACV,iCAAiC,YAAY,sBAAsB,mCACvE,CAAC;GAED,IAAI,CAAC,MADmB,cAAc,MAAM,OAAO,+BAA+B,CAAC,GACnE;IACZ,IAAM,MAAM,KAAK,kCAAkC,CAAC;IACpD,QAAQ,KAAK,CAAC;GAClB;EACJ;EAGA,IAAI,KAAK,gBAAgB;GACrB,MAAM,UAAU,MAAM,qBAAqB,gBAAgB,QAAS;GACpE,IAAM,MAAM,KAAK,UAAU,yBAAyB,SAAS,MAAM,iBAAiB,SAAS,kBAAkB,CAAC;EACpH;EAIA,IAAI,YAAY;GACZ,IAAM,MAAM,KAAK,6DAA6D,CAAC;GAC/E,MAAM,EAAE,SAAS,YAAY,MAAM,aAC/B,kBACA,aACC,MAAM,IAAM,MAAM,KAAK,CAAC,CAAC,CAC9B;GACA,IAAM,MAAM,KAAK,cAAc,QAAQ,YAAY,QAAQ,6CAA6C,CAAC;EAC7G,OAAO;GACH,QAAQ,MAAM,OAAO,gEAAgE,CAAC;GACtF,QAAQ,MAAM,OAAO,mFAAmF,CAAC;GACzG,QAAQ,MAAM,OAAO,yFAAyF,CAAC;EACnH;EAEA,MAAM,YAAY;GACd,kBAAkB;GAClB,WAAW;GACX,OAAO,KAAK;GACZ,SAAS,KAAK;GAEd,aAAa,CAAC,KAAK;GACnB,cAAc;EAClB,CAAC;EAED,IAAM,EAAE;EACR,IAAM,MAAM,MAAM,+BAA+B,YAAY,sBAAsB,GAAG,CAAC;EACvF,IAAM,EAAE;CACZ,SAAS,KAAK;EACV,YAAY,GAAG;EACf,QAAQ,KAAK,CAAC;CAClB,UAAU;EACN,IAAI,eAAe,OAAO,cAAe,YAAY,GAAG,WAAW,SAAU,GACzE,GAAG,OAAO,KAAK,QAAQ,SAAU,GAAG;GAAE,WAAW;GAAM,OAAO;EAAK,CAAC;CAE5E;AACJ;AAKA,eAAsB,eAAe,SAAkC;CACnE,MAAM,SAAS,QAAQ;CACvB,IAAI,CAAC,UAAU,WAAW,UAAU;EAChC,iBAAiB;EACjB;CACJ;CACA,IAAI,WAAW,QAAQ;EACnB,SAAS,MAAM,IAAI,4BAA4B,OAAO,eAAe,CAAC;EACtE,QAAQ,KAAK,CAAC;CAClB;CAGA,MAAM,SAAA,GAAA,WAAA,QAAA,CADW;EAAE,SAAS;EAAQ,YAAY;EAAS,MAAM;CAAQ,GAAG;EAAE,MAAM,QAAQ,MAAM,CAAC;EAAG,YAAY;CAAK,CACzG,CAAA,CAAK,YAAY,QAAQ,IAAI,sBAAsB,KAAK,KAAK,QAAQ,IAAI,GAAG,SAAS;CACjG,MAAM,OAAO,uBAAuB,KAAG;CAEvC,IAAI;EAEA,MAAM,UAAU,MAAM,YAAY,MAAM,MADlB,6BAA6B,MAAM,QAAQ,GAAG,KACjB,KAAA,CAAS;EAC5D,IAAM,EAAE;EACR,IAAI,QAAQ,WAAW,GACnB,IAAM,MAAM,KAAK,yBAAyB,MAAI,EAAE,CAAC;OAC9C;GACH,IAAM,MAAM,KAAK,QAAQ,QAAQ,OAAO,gBAAgB,MAAI,EAAE,CAAC;GAC/D,IAAM,EAAE;GACR,KAAK,MAAM,KAAK,SAAS;IACrB,MAAM,OAAO,EAAE,YAAY,EAAE,UAAU,YAAY,IAAI;IACvD,MAAM,OAAO,KAAK,SAAS,UAAU,KAAK,SAAS,EAAE,GAAG,IAAI,EAAE;IAM9D,MAAM,QAAQ,EAAE,cAAc;IAC9B,MAAM,OAAO,EAAE,cAAc,KAAA,IAAY,KAAK,MAAM,YAAY,EAAE,SAAS;IAC3E,IAAI,OACA,IAAM,KAAK,MAAM,IAAI,GAAG,EAAE,GAAG,MAAM,KAAK,IAAI,EAAE,GAAG,MAAM,KAAK,KAAK,MAAM,IAAI,MAAM,IAAI,0BAA0B,GAAG;SAElH,IAAM,KAAK,MAAM,MAAM,GAAG,EAAE,GAAG,MAAM,KAAK,IAAI,EAAE,GAAG,MAAM,KAAK,KAAK,OAAO,MAAM,GAAG;GAE3F;GACA,IAAI,QAAQ,MAAK,MAAK,EAAE,cAAc,CAAC,GAAG;IACtC,IAAM,EAAE;IACR,IAAM,MAAM,OAAO,yEAAyE,CAAC;IAC7F,IAAM,MAAM,KAAK,sEAAsE,CAAC;GAC5F;EACJ;EACA,IAAM,EAAE;CACZ,SAAS,KAAK;EACV,YAAY,GAAG;EACf,QAAQ,KAAK,CAAC;CAClB;AACJ;;;;;;;;;;;AAYA,eAAe,gBAAgB,WAAkC;CAC7D,MAAM,QAAQ,MAAM,aAAa,SAAS;CAC1C,IAAI,CAAC,MAAM,IAAI;EACX,mBAAmB,mBAAmB,SAAS,CAAC;EAChD,mBAAmB,SAAS;EAC5B,MAAM,IAAI,gBACN,mDAAmD,MAAM,UACzD,yGACJ;CACJ;AACJ;AAEA,SAAS,YAAY,KAAoB;CACrC,IAAI,eAAe,iBAAiB;EAChC,SAAS,MAAM,IAAI,OAAO,IAAI,SAAS,CAAC;EACxC,IAAI,IAAI,MAAM,SAAS,MAAM,KAAK,OAAO,IAAI,MAAM,CAAC;CACxD,OACI,SAAS,MAAM,IAAI,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,GAAG,CAAC;AAErF;AAEA,SAAS,kBAAwB;CAC7B,IAAM;EACR,MAAM,KAAK,kBAAkB,EAAE;;EAE/B,MAAM,MAAM,KAAK,OAAO,EAAE;;;EAG1B,MAAM,MAAM,KAAK,SAAS,EAAE;IAC1B,MAAM,KAAK,WAAW,EAAE;IACxB,MAAM,KAAK,kBAAkB,EAAE;IAC/B,MAAM,KAAK,YAAY,EAAE;IACzB,MAAM,KAAK,uBAAuB,EAAE;+BACT,MAAM,IAAI,0CAA0C,EAAE;IACjF,MAAM,KAAK,qBAAqB,EAAE;;EAEpC,MAAM,MAAM,KAAK,oBAAoB,EAAE;;;;;;;;;sCASH,MAAM,IAAI,6CAA6C,EAAE;;;;;;EAM7F,MAAM,MAAM,KAAK,OAAO,EAAE;;;CAG3B;AACD;AAEA,SAAS,mBAAyB;CAC9B,IAAM;EACR,MAAM,KAAK,mBAAmB,EAAE;;EAEhC,MAAM,MAAM,KAAK,OAAO,EAAE;;;EAG1B,MAAM,MAAM,KAAK,WAAW,EAAE;;;EAG9B,MAAM,MAAM,KAAK,SAAS,EAAE;IAC1B,MAAM,KAAK,aAAa,EAAE;IAC1B,MAAM,KAAK,aAAa,EAAE;IAC1B,MAAM,KAAK,SAAS,EAAE;IACtB,MAAM,KAAK,YAAY,EAAE;IACzB,MAAM,KAAK,qBAAqB,EAAE,oCAAoC,MAAM,IAAI,8BAA8B,EAAE;IAChH,MAAM,KAAK,WAAW,EAAE,mDAAmD,MAAM,IAAI,gBAAgB,EAAE;;EAEzG,MAAM,IAAI,KAAK,SAAS,EAAE;;;;;;;;;CAS3B;AACD;AAEA,SAAS,mBAAyB;CAC9B,IAAM;EACR,MAAM,KAAK,mBAAmB,EAAE;;EAEhC,MAAM,MAAM,KAAK,OAAO,EAAE;;+DAEmC,MAAM,KAAK,kBAAkB,EAAE;CAC7F;AACD"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"backup-service-EwcDVG-8.js","names":[],"sources":["../src/backup/pg-tools.ts","../src/backup/retention.ts","../src/backup/backup-logic.ts","../src/backup/backup-service.ts"],"sourcesContent":["/**\n * Pure helpers for the backup/restore commands.\n *\n * Everything in this file is side-effect free so it can be unit-tested\n * without a live Postgres server, matching the constraint that CI must\n * not require a database.\n */\n\nimport { forLibpq } from \"../utils/connection-string\";\n\n/**\n * A parsed backup destination. `--out` (and the scheduled-backup config)\n * accepts either a local filesystem path or an object-storage URL.\n */\nexport type BackupDestination =\n | { kind: \"local\"; path: string }\n | { kind: \"s3\"; bucket: string; prefix: string }\n | { kind: \"gcs\"; bucket: string; prefix: string };\n\n/**\n * Extract the database name from a Postgres connection string.\n * Returns `null` when the URL has no database path (e.g. bare host).\n */\nexport function parseDbNameFromUrl(connectionString: string): string | null {\n try {\n const parsed = new URL(connectionString);\n const name = parsed.pathname.replace(/^\\//, \"\").trim();\n return name.length > 0 ? name : null;\n } catch {\n // Fall back to a permissive regex for non-URL DSNs.\n const match = connectionString.match(/\\/([^/?]+)(\\?|$)/);\n return match && match[1] ? match[1] : null;\n }\n}\n\n/**\n * Swap the database name in a connection string, preserving credentials,\n * host, port and query params. Used by `--target-db` / `--create-db` so a\n * restore can target a fresh database instead of clobbering the live one.\n */\nexport function withDatabaseName(connectionString: string, dbName: string): string {\n try {\n const parsed = new URL(connectionString);\n parsed.pathname = `/${dbName}`;\n return parsed.toString();\n } catch {\n return connectionString.replace(/\\/([^/?]+)(\\?|$)/, `/${dbName}$2`);\n }\n}\n\n/**\n * Parse the major version out of `pg_dump --version` / `pg_restore --version`\n * output, e.g. `\"pg_dump (PostgreSQL) 16.2\"` → `16`. Handles the pre-10\n * `9.6.x` scheme (returns `9`) and beta strings like `\"17beta1\"`.\n */\nexport function parsePgToolMajor(versionOutput: string): number | null {\n const match = versionOutput.match(/(\\d+)(?:\\.(\\d+))?/);\n if (!match) return null;\n const major = Number(match[1]);\n if (!Number.isFinite(major)) return null;\n // Postgres 9.x used the first two numbers as the major (9.6, 9.4…).\n if (major === 9 && match[2] !== undefined) {\n return 9;\n }\n return major;\n}\n\n/**\n * Convert `SELECT current_setting('server_version_num')` (e.g. `160002`)\n * into a major version (`16`). Also accepts pre-10 encodings like `90603`\n * → `9`.\n */\nexport function serverVersionNumToMajor(versionNum: number | string): number | null {\n const num = typeof versionNum === \"string\" ? Number(versionNum) : versionNum;\n if (!Number.isFinite(num) || num <= 0) return null;\n if (num < 100000) {\n // 9.x scheme: 90603 = 9.6.3\n return Math.floor(num / 10000);\n }\n return Math.floor(num / 10000);\n}\n\nexport interface VersionCompatibility {\n compatible: boolean;\n reason?: string;\n}\n\n/**\n * pg_dump / pg_restore must be **at least** as new as the server they talk\n * to. A newer client against an older server is supported; an older client\n * against a newer server is not and produces corrupt or rejected output.\n */\nexport function checkToolServerCompatibility(\n toolMajor: number | null,\n serverMajor: number | null\n): VersionCompatibility {\n if (toolMajor === null) {\n return { compatible: false, reason: \"Could not determine the client tool version.\" };\n }\n if (serverMajor === null) {\n return { compatible: false, reason: \"Could not determine the Postgres server version.\" };\n }\n if (toolMajor < serverMajor) {\n return {\n compatible: false,\n reason:\n `Client tool is Postgres ${toolMajor} but the server is Postgres ${serverMajor}. ` +\n `pg_dump/pg_restore must be the same major version as the server or newer. ` +\n `Install Postgres ${serverMajor} client tools.`\n };\n }\n return { compatible: true };\n}\n\n/**\n * Build a deterministic, sortable backup file name:\n * `rebase-<db>-<YYYYMMDD>T<HHMMSS>Z.dump`\n *\n * The UTC timestamp is embedded so retention pruning can recover the\n * creation time from the object key alone, without extra metadata.\n */\nexport function buildBackupFilename(dbName: string, date: Date = new Date()): string {\n const iso = date.toISOString(); // 2026-07-14T09:12:03.123Z\n const stamp = iso.replace(/\\.\\d+Z$/, \"Z\").replace(/[-:]/g, \"\");\n const safeDb = dbName.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n return `rebase-${safeDb}-${stamp}.dump`;\n}\n\n/**\n * Recover the creation timestamp encoded in a backup file name by\n * {@link buildBackupFilename}. Returns `null` for names that don't match,\n * so foreign objects in a shared prefix are never pruned.\n */\nexport function parseBackupTimestamp(fileName: string): Date | null {\n const base = fileName.split(\"/\").pop() ?? fileName;\n const match = base.match(/-(\\d{8})T(\\d{6})Z\\.dump$/);\n if (!match) return null;\n const [, ymd, hms] = match;\n const iso =\n `${ymd.slice(0, 4)}-${ymd.slice(4, 6)}-${ymd.slice(6, 8)}` +\n `T${hms.slice(0, 2)}:${hms.slice(2, 4)}:${hms.slice(4, 6)}Z`;\n const date = new Date(iso);\n return Number.isNaN(date.getTime()) ? null : date;\n}\n\n/**\n * Parse a destination string into a structured {@link BackupDestination}.\n * `s3://bucket/prefix` and `gs://bucket/prefix` map to object storage;\n * anything else is treated as a local path.\n */\nexport function parseBackupDestination(out: string): BackupDestination {\n const s3 = out.match(/^s3:\\/\\/([^/]+)\\/?(.*)$/);\n if (s3) {\n return { kind: \"s3\", bucket: s3[1], prefix: stripTrailingSlash(s3[2]) };\n }\n const gcs = out.match(/^gs:\\/\\/([^/]+)\\/?(.*)$/);\n if (gcs) {\n return { kind: \"gcs\", bucket: gcs[1], prefix: stripTrailingSlash(gcs[2]) };\n }\n return { kind: \"local\", path: out };\n}\n\nfunction stripTrailingSlash(s: string): string {\n return s.replace(/\\/+$/, \"\");\n}\n\n/**\n * Join a storage prefix and a file name without producing a leading or\n * doubled slash.\n */\nexport function joinStorageKey(prefix: string, fileName: string): string {\n const clean = prefix.replace(/^\\/+|\\/+$/g, \"\");\n return clean.length > 0 ? `${clean}/${fileName}` : fileName;\n}\n\n/**\n * The identity `pg_dump` reads rows as, when row security is left on.\n *\n * Not optional, and that is the whole design. `pg_dump --enable-row-security`\n * on its own is the dangerous command in this file: it turns the \"query would\n * be affected by row-level security policy\" *error* into a dump that exits 0\n * and is silently missing every row the dumping role's policies exclude. A\n * backup that looks fine and restores most of your data is worse than one that\n * refused to run.\n *\n * So the flag is unreachable without a subject to evaluate the policies\n * against. Rebase's generated policies read `app.uid` and `app.user_roles`;\n * supplying an admin role satisfies the `admin_full_access` rule and the dump\n * sees everything that rule sees.\n */\nexport interface RowSecurityIdentity {\n /** Written to `app.uid`. Any non-empty value — it is only an audit trail. */\n uid: string;\n /** Written to `app.user_roles`. Must include a role the policies admit. */\n roles: string[];\n}\n\n/**\n * `PGOPTIONS` carrying an identity, for a libpq tool that has no other way to\n * set a GUC.\n *\n * A backslash escape rather than quoting, which is what libpq's `-c` parsing\n * takes: a space inside a value ends the option otherwise, so a role list is\n * comma-joined and never spaced.\n */\nexport function buildRowSecurityPgOptions(identity: RowSecurityIdentity): string {\n const escape = (value: string) => value.replace(/([\\\\ ])/g, \"\\\\$1\");\n return [\n `-c app.uid=${escape(identity.uid)}`,\n `-c app.user_id=${escape(identity.uid)}`,\n `-c app.user_roles=${escape(identity.roles.join(\",\"))}`\n ].join(\" \");\n}\n\n/**\n * Assemble the `pg_dump` argument vector. Uses the custom format (`-Fc`),\n * which is compressed and restorable selectively via `pg_restore`.\n */\nexport function buildPgDumpArgs(opts: {\n connectionString: string;\n outFile: string;\n /** Extra schemas/tables to exclude, e.g. Atlas revision tables. */\n excludeSchemas?: string[];\n /** Number of parallel jobs (directory format only; ignored for -Fc). */\n noOwner?: boolean;\n /**\n * Dump with row security on, as this identity. Omit — which is the default\n * — and `pg_dump` errors rather than skipping rows it cannot see.\n */\n rowSecurity?: RowSecurityIdentity;\n}): string[] {\n const args = [\"--format=custom\", \"--no-password\", `--file=${opts.outFile}`];\n if (opts.noOwner) {\n args.push(\"--no-owner\");\n }\n if (opts.rowSecurity) {\n args.push(\"--enable-row-security\");\n }\n for (const schema of opts.excludeSchemas ?? []) {\n args.push(`--exclude-schema=${schema}`);\n }\n args.push(forLibpq(opts.connectionString));\n return args;\n}\n\n/**\n * Whether a `pg_dump` failure is the row-security one, and what to do about it.\n *\n * The error text names the table and nothing else, so the first read of it is\n * \"why would a backup be affected by RLS at all?\" — the answer being that the\n * dumping role is not the tables' owner and has no `BYPASSRLS`, which is the\n * normal state of the `postgres` user on Cloud SQL, RDS and every other managed\n * Postgres. Nothing about that is visible from the message.\n *\n * Returns `null` for any other failure, so the caller reports it unchanged.\n */\nexport function diagnoseRowSecurityDumpFailure(error: unknown): string | null {\n const text = [\n (error as { stderr?: unknown })?.stderr,\n (error as { message?: unknown })?.message\n ].map(part => (typeof part === \"string\" ? part : \"\")).join(\"\\n\");\n\n if (!/row-level security policy/i.test(text)) return null;\n\n const table = text.match(/for table \"([^\"]+)\"/)?.[1];\n\n return [\n `pg_dump cannot read ${table ? `\"${table}\"` : \"one of the tables\"} because row-level security applies to it.`,\n \"\",\n \" The dumping role is neither the table's owner nor `BYPASSRLS`, which is the normal\",\n \" state of the `postgres` user on Cloud SQL, RDS and other managed Postgres — there is\",\n \" no superuser to hand out.\",\n \"\",\n \" Two ways out:\",\n \"\",\n \" • Grant the dumping role BYPASSRLS, or make it the owner, and run this again. The\",\n \" dump then contains every row, which is what a backup should mean.\",\n \"\",\n \" • Re-run with --enable-row-security to dump as an admin subject instead. Rebase\",\n \" sets `app.uid`/`app.user_roles` so the generated `admin_full_access` policy\",\n \" admits the dump. Read the warning it prints: the result contains exactly the\",\n \" rows those policies admit, and any table whose policies do not include an\",\n \" admin rule comes out short — with no error.\",\n \"\",\n \" Do not reach for a bare `pg_dump --enable-row-security` by hand. Without the\",\n \" settings above it succeeds and silently omits rows.\"\n ].join(\"\\n\");\n}\n\n/**\n * Assemble the `pg_restore` argument vector for a custom-format dump.\n */\nexport function buildPgRestoreArgs(opts: {\n connectionString: string;\n inputFile: string;\n /** Drop objects before recreating them (destructive but idempotent). */\n clean?: boolean;\n /**\n * Abort on the first error instead of logging and continuing. Defaults\n * ON: a restore that silently skips failed GRANT/RLS statements (because\n * a role is missing) \"succeeds\" with RLS un-enforced — a security hole.\n * Fail loudly instead so the operator knows the restore is incomplete.\n */\n exitOnError?: boolean;\n noOwner?: boolean;\n}): string[] {\n const args = [\"--format=custom\", \"--no-password\", `--dbname=${forLibpq(opts.connectionString)}`];\n if (opts.clean) {\n args.push(\"--clean\", \"--if-exists\");\n }\n if (opts.noOwner) {\n args.push(\"--no-owner\");\n }\n // Default to --exit-on-error unless explicitly disabled.\n if (opts.exitOnError !== false) {\n args.push(\"--exit-on-error\");\n }\n args.push(opts.inputFile);\n return args;\n}\n\n/**\n * Assemble the `pg_restore --list` argument vector. Reading a dump's table\n * of contents parses the whole archive without touching a database, so it is\n * a cheap integrity check that the file isn't truncated or corrupt.\n */\nexport function buildPgRestoreListArgs(inputFile: string): string[] {\n return [\"--list\", inputFile];\n}\n\n/**\n * Assemble the `pg_dumpall --globals-only` argument vector. Roles (and other\n * cluster-wide objects) live outside any single database, so a per-database\n * `pg_dump` omits them. Without the `rebase_user` role the RLS GRANT\n * statements in the main dump fail on restore and RLS is silently lost — so\n * every backup captures the globals into a sidecar `.globals.sql`.\n *\n * `--no-role-passwords` keeps role secrets out of the artifact (backups may\n * be shipped off-box); roles are recreated password-less and re-secured by\n * the operator.\n */\nexport function buildPgDumpallGlobalsArgs(opts: {\n connectionString: string;\n outFile: string;\n}): string[] {\n return [\n \"--globals-only\",\n \"--no-role-passwords\",\n \"--no-password\",\n `--file=${opts.outFile}`,\n `--dbname=${forLibpq(opts.connectionString)}`\n ];\n}\n\n/**\n * Derive the globals sidecar path/key for a given `.dump` file. Keeps the\n * two artifacts adjacent so listing, uploading and pruning can find one from\n * the other. A name that doesn't end in `.dump` is returned unchanged with a\n * `.globals.sql` suffix appended.\n */\nexport function globalsFileForDump(dumpPath: string): string {\n return dumpPath.endsWith(\".dump\")\n ? dumpPath.slice(0, -\".dump\".length) + \".globals.sql\"\n : dumpPath + \".globals.sql\";\n}\n\n/**\n * Split a `pg_dumpall --globals-only` script into individual statements.\n * Used when replaying globals on restore so each `CREATE ROLE` / `GRANT`\n * can run independently and a benign \"role already exists\" on one doesn't\n * abort the rest. Drops `--` comment lines and blank statements.\n */\nexport function splitGlobalsStatements(sql: string): string[] {\n const withoutComments = sql\n .split(\"\\n\")\n // `--` comments, and psql meta-commands. pg_dumpall 15+ wraps its output\n // in `\\restrict <token>` / `\\unrestrict <token>`, which are instructions\n // to psql, not SQL. This replay sends statements over a connection\n // instead, so they arrived at the server as `\\restrict …` and came back\n // as `syntax error at or near \"\\\"` — reported to the user as two skipped\n // globals per restore, which reads like roles failed to apply when\n // nothing did. They carry no state worth replaying: dropping them is\n // exactly what a SQL-level consumer should do.\n .filter((line) => {\n const trimmed = line.trim();\n return !trimmed.startsWith(\"--\") && !trimmed.startsWith(\"\\\\\");\n })\n .join(\"\\n\");\n return withoutComments\n .split(\";\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n}\n\n/**\n * Resolve the Postgres connection string the backup commands should use,\n * mirroring the precedence the branch command already relies on.\n */\nexport function resolveConnectionString(\n env: Record<string, string | undefined>\n): string | null {\n return env.DATABASE_URL || env.ADMIN_CONNECTION_STRING || null;\n}\n","/**\n * Retention pruning for scheduled backups. Pure logic — no I/O — so it can\n * be exhaustively unit-tested without touching a storage backend.\n */\nimport { parseBackupTimestamp } from \"./pg-tools\";\n\nexport interface BackupObject {\n /** Storage key / file name. */\n key: string;\n /**\n * Creation time. Optional — when absent it is recovered from the\n * timestamp encoded in the key by `buildBackupFilename`.\n */\n createdAt?: Date;\n /**\n * Size on disk, when the lister knows it (local destinations always do).\n *\n * Reported so an empty artifact cannot masquerade as a backup in\n * `rebase db backups list`. Deliberately NOT consulted by\n * {@link selectBackupsToPrune}: retention decides by age, and a rule that\n * silently skipped small files would make a genuinely tiny dump immortal.\n * Visibility is the fix here; the artifacts themselves are now removed at\n * the point of failure by `discardPartialDump`.\n */\n sizeBytes?: number;\n}\n\nexport interface RetentionOptions {\n /** Delete backups older than this many days. `0`/negative disables age pruning. */\n retentionDays?: number;\n /**\n * Always keep at least this many of the most recent backups, even if\n * they are older than `retentionDays`. Guards against wiping every\n * backup after a long outage. Default: 0 (no minimum).\n */\n keepMinimum?: number;\n /** Reference \"now\" — injectable for deterministic tests. */\n now?: Date;\n}\n\n/**\n * Decide which backups to delete under the given retention policy.\n *\n * Rules, applied in order:\n * 1. Objects whose key isn't a recognised rebase backup (no parseable\n * timestamp and no `createdAt`) are never selected — foreign files in a\n * shared bucket are left untouched.\n * 2. The `keepMinimum` newest backups are always retained.\n * 3. Of the remainder, any older than `retentionDays` are selected for\n * deletion.\n *\n * Returns the keys to delete, oldest first.\n */\nexport function selectBackupsToPrune(\n backups: BackupObject[],\n options: RetentionOptions\n): string[] {\n const retentionDays = options.retentionDays ?? 0;\n const keepMinimum = Math.max(0, options.keepMinimum ?? 0);\n const now = options.now ?? new Date();\n\n if (retentionDays <= 0) {\n return [];\n }\n\n // Resolve a timestamp for every object; drop the ones we can't date.\n const dated = backups\n .map((b) => {\n const createdAt = b.createdAt ?? parseBackupTimestamp(b.key);\n return createdAt ? { key: b.key, createdAt } : null;\n })\n .filter((b): b is { key: string; createdAt: Date } => b !== null);\n\n // Newest first so the first `keepMinimum` are the protected ones.\n dated.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());\n\n const cutoff = now.getTime() - retentionDays * 24 * 60 * 60 * 1000;\n\n const toDelete = dated\n .slice(keepMinimum)\n .filter((b) => b.createdAt.getTime() < cutoff);\n\n // Return oldest first for predictable, readable logs.\n toDelete.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());\n return toDelete.map((b) => b.key);\n}\n","/**\n * Pure-ish orchestration for the backup/restore paths, kept free of `execa`\n * and `pg` value imports so it can be unit-tested under jest (the impure\n * edges — spawning processes, opening connections — live in\n * `backup-service.ts`, which is vitest/runtime only).\n *\n * The functions here take their side-effecting dependency as an argument\n * (a statement runner, an object deleter) so tests can inject fakes.\n */\nimport { globalsFileForDump, splitGlobalsStatements } from \"./pg-tools\";\n\n/**\n * Replay a `pg_dumpall --globals-only` script one statement at a time,\n * tolerating per-statement failures. On a same-cluster restore the roles\n * usually already exist (`CREATE ROLE` → \"already exists\") and on a managed\n * provider an `ALTER ROLE <superuser>` may be refused; neither should abort\n * recreation of the roles that *are* missing. Returns how many statements\n * applied vs were skipped.\n *\n * `runStatement` executes one SQL statement and rejects on error.\n */\nexport async function applyGlobalsWith(\n runStatement: (sql: string) => Promise<void>,\n globalsSql: string,\n log: (message: string) => void = () => {}\n): Promise<{ applied: number; skipped: number }> {\n let applied = 0;\n let skipped = 0;\n for (const statement of splitGlobalsStatements(globalsSql)) {\n try {\n await runStatement(statement);\n applied++;\n } catch (err) {\n skipped++;\n const firstLine = statement.split(\"\\n\")[0].slice(0, 80);\n log(` • Skipped global: ${firstLine} (${err instanceof Error ? err.message : String(err)})`);\n }\n }\n return { applied, skipped };\n}\n\n/**\n * Delete each pruned dump together with its `.globals.sql` roles sidecar, so\n * pruning never orphans the roles file. The sidecar is best-effort — older\n * backups predate it, so a missing-sidecar failure is swallowed while a\n * failure deleting the dump itself propagates.\n *\n * `deleteObject` removes one key and rejects if it cannot (e.g. not found).\n */\nexport async function pruneWith(\n keys: string[],\n deleteObject: (key: string) => Promise<void>\n): Promise<void> {\n for (const key of keys) {\n await deleteObject(key);\n try {\n await deleteObject(globalsFileForDump(key));\n } catch {\n // Sidecar may not exist for older backups — ignore.\n }\n }\n}\n\n/**\n * Remove a dump artifact abandoned by a failed tool run.\n *\n * `pg_dump` creates its `--file=` target before it finishes connecting, so any\n * failure — a URL libpq rejects, a dropped connection, a full disk — leaves a\n * 0-byte file behind. That corpse is not inert: `rebase db backups list` used\n * to show it as an ordinary entry, and `selectBackupsToPrune` ranks by\n * timestamp alone, so it occupies a protected `keepMinimum` slot and can push\n * a real backup out of retention.\n *\n * Best-effort on purpose: the caller is already throwing, and a failed unlink\n * must not replace the real diagnosis with an ENOENT/EPERM from the cleanup.\n *\n * Lives here rather than in `backup-service.ts` for the reason at the top of\n * this file — that module's top-level `execa` import makes it unloadable under\n * jest, so anything placed there cannot be unit-tested.\n *\n * `remove` deletes one file and throws if it cannot; `exists` reports presence.\n */\nexport function discardPartialDumpWith(\n exists: (file: string) => boolean,\n remove: (file: string) => void,\n file: string\n): void {\n try {\n if (exists(file)) remove(file);\n } catch {\n // Ignored: see above.\n }\n}\n","/**\n * Backup / restore orchestration: thin, well-typed wrappers around\n * `pg_dump` and `pg_restore`, plus binary detection, version pre-flight,\n * and upload/prune against a storage backend.\n *\n * The pure argument- and policy-logic lives in `pg-tools.ts` and\n * `retention.ts`; this module is the impure edge (spawns processes, talks\n * to Postgres and storage) and is intentionally kept small.\n */\nimport fs from \"fs\";\nimport os from \"os\";\nimport path from \"path\";\nimport { execa } from \"execa\";\nimport type { StorageController } from \"@rebasepro/server\";\nimport { resolveLocalBin } from \"../cli-helpers\";\nimport {\n BackupDestination,\n buildBackupFilename,\n buildPgDumpArgs,\n buildPgDumpallGlobalsArgs,\n buildRowSecurityPgOptions,\n diagnoseRowSecurityDumpFailure,\n type RowSecurityIdentity,\n buildPgRestoreArgs,\n buildPgRestoreListArgs,\n checkToolServerCompatibility,\n globalsFileForDump,\n joinStorageKey,\n parsePgToolMajor,\n parseBackupTimestamp,\n serverVersionNumToMajor,\n VersionCompatibility\n} from \"./pg-tools\";\nimport { BackupObject, RetentionOptions, selectBackupsToPrune } from \"./retention\";\nimport { applyGlobalsWith, discardPartialDumpWith, pruneWith } from \"./backup-logic\";\n\n/**\n * Remove a dump artifact abandoned by a failed tool run. See\n * {@link discardPartialDumpWith} for why this exists and why the logic lives\n * in the execa-free module.\n */\nexport function discardPartialDump(file: string): void {\n discardPartialDumpWith(\n (f) => fs.existsSync(f),\n (f) => fs.unlinkSync(f),\n file\n );\n}\n\nexport class BackupToolError extends Error {\n constructor(message: string, readonly hint?: string) {\n super(message);\n this.name = \"BackupToolError\";\n }\n}\n\n/** Locate `pg_dump` / `pg_restore` / `pg_dumpall`, honouring an env override. */\nexport function resolvePgBinary(\n tool: \"pg_dump\" | \"pg_restore\" | \"pg_dumpall\",\n env: Record<string, string | undefined> = process.env\n): string | null {\n const overrideVar =\n tool === \"pg_dump\" ? env.PG_DUMP_PATH\n : tool === \"pg_restore\" ? env.PG_RESTORE_PATH\n : env.PG_DUMPALL_PATH;\n if (overrideVar && fs.existsSync(overrideVar)) return overrideVar;\n return resolveLocalBin(tool);\n}\n\n/** Run `<bin> --version` and extract the major version. */\nexport async function detectToolMajor(bin: string): Promise<number | null> {\n try {\n const { stdout } = await execa(bin, [\"--version\"]);\n return parsePgToolMajor(stdout);\n } catch {\n return null;\n }\n}\n\n/** Query the server for its major version via `server_version_num`. */\nexport async function getServerVersionMajor(connectionString: string): Promise<number | null> {\n const { Client } = await import(\"pg\");\n const client = new Client({ connectionString });\n await client.connect();\n try {\n const res = await client.query(\"SHOW server_version_num\");\n const raw = res.rows?.[0]?.server_version_num;\n return serverVersionNumToMajor(raw);\n } finally {\n await client.end();\n }\n}\n\nexport interface PreflightResult extends VersionCompatibility {\n bin: string;\n toolMajor: number | null;\n serverMajor: number | null;\n}\n\n/**\n * Verify the requested client tool exists and its major version is\n * compatible with the live server. Throws {@link BackupToolError} — with a\n * doctor-style hint — when the binary is missing.\n */\nexport async function preflight(\n tool: \"pg_dump\" | \"pg_restore\",\n connectionString: string,\n env: Record<string, string | undefined> = process.env\n): Promise<PreflightResult> {\n const bin = resolvePgBinary(tool, env);\n if (!bin) {\n throw new BackupToolError(\n `Could not find the '${tool}' binary.`,\n `Install the PostgreSQL client tools (e.g. 'brew install libpq' or 'apt-get install postgresql-client'), ` +\n `or set ${tool === \"pg_dump\" ? \"PG_DUMP_PATH\" : \"PG_RESTORE_PATH\"} to its full path.`\n );\n }\n const [toolMajor, serverMajor] = await Promise.all([\n detectToolMajor(bin),\n getServerVersionMajor(connectionString)\n ]);\n const compat = checkToolServerCompatibility(toolMajor, serverMajor);\n return { bin, toolMajor, serverMajor, ...compat };\n}\n\nexport interface BackupResult {\n /** Absolute path of the produced dump file on local disk. */\n localFile: string;\n fileName: string;\n sizeBytes: number;\n /**\n * Absolute path of the `.globals.sql` sidecar holding cluster-wide roles\n * (present unless globals capture was disabled or unavailable).\n */\n globalsFile?: string;\n globalsSizeBytes?: number;\n}\n\n/**\n * Produce a custom-format dump on local disk. When `outDir` is omitted the\n * file is written to the OS temp directory (used by the upload path, which\n * cleans it up afterwards).\n *\n * Alongside the `-Fc` dump it writes a `<name>.globals.sql` sidecar via\n * `pg_dumpall --globals-only` so the roles the dump's GRANT/RLS statements\n * depend on can be recreated on restore. Set `includeGlobals: false` to skip\n * it (e.g. when the caller has no privilege to read cluster globals).\n */\nexport async function createDump(opts: {\n connectionString: string;\n dbName: string;\n outDir?: string;\n fileName?: string;\n excludeSchemas?: string[];\n noOwner?: boolean;\n inheritStdio?: boolean;\n includeGlobals?: boolean;\n env?: Record<string, string | undefined>;\n /**\n * Dump with row security left on, reading as this identity.\n *\n * The escape hatch for a managed Postgres, where the dumping role owns\n * nothing and has no `BYPASSRLS`. Off by default, and deliberately so: with\n * row security on, `pg_dump` stops erroring on rows it cannot see and\n * simply omits them. See {@link RowSecurityIdentity}.\n */\n rowSecurity?: RowSecurityIdentity;\n}): Promise<BackupResult> {\n const env = opts.env ?? process.env;\n const bin = resolvePgBinary(\"pg_dump\", env);\n if (!bin) {\n throw new BackupToolError(\n \"Could not find the 'pg_dump' binary.\",\n \"Install the PostgreSQL client tools or set PG_DUMP_PATH.\"\n );\n }\n\n const fileName = opts.fileName ?? buildBackupFilename(opts.dbName);\n const outDir = opts.outDir ?? fs.mkdtempSync(path.join(os.tmpdir(), \"rebase-backup-\"));\n fs.mkdirSync(outDir, { recursive: true });\n const localFile = path.join(outDir, fileName);\n\n const args = buildPgDumpArgs({\n connectionString: opts.connectionString,\n outFile: localFile,\n excludeSchemas: opts.excludeSchemas,\n noOwner: opts.noOwner,\n rowSecurity: opts.rowSecurity\n });\n\n // `PGOPTIONS` is the only way to set a GUC on a tool that takes no SQL.\n // Built from the same object that added `--enable-row-security`, so the\n // flag cannot travel without the identity that makes it safe.\n const dumpEnv: Record<string, string> = { ...(env as Record<string, string>) };\n if (opts.rowSecurity) {\n dumpEnv.PGOPTIONS = [env.PGOPTIONS, buildRowSecurityPgOptions(opts.rowSecurity)]\n .filter(Boolean).join(\" \");\n }\n\n try {\n await execa(bin, args, {\n stdio: opts.inheritStdio ? \"inherit\" : \"pipe\",\n env: dumpEnv\n });\n } catch (error) {\n // pg_dump creates its `--file=` target before it finishes connecting,\n // so any failure here — a URL libpq rejects, a dropped connection, a\n // full disk — leaves a 0-byte file behind that nothing else removed.\n // That corpse is not inert: `rebase db backups list` shows it as an\n // ordinary entry, and `selectBackupsToPrune` ranks by timestamp alone,\n // so it occupies a protected `keepMinimum` slot and can push a real\n // backup out of retention. A missing backup is honest; an empty file\n // that reads as a backup is not.\n discardPartialDump(localFile);\n // The RLS failure names a table and no cause. Replace it with the\n // cause and the two ways out; anything else is re-thrown untouched.\n const diagnosis = diagnoseRowSecurityDumpFailure(error);\n if (!diagnosis) throw error;\n throw new BackupToolError(\n diagnosis,\n \"Run `rebase db backup --help` for the flag, and read what it says about partial dumps.\"\n );\n }\n\n const sizeBytes = fs.existsSync(localFile) ? fs.statSync(localFile).size : 0;\n\n const result: BackupResult = { localFile, fileName, sizeBytes };\n\n if (opts.includeGlobals !== false) {\n const dumpallBin = resolvePgBinary(\"pg_dumpall\", env);\n if (!dumpallBin) {\n throw new BackupToolError(\n \"Could not find the 'pg_dumpall' binary needed to capture cluster roles.\",\n \"Install the PostgreSQL client tools or set PG_DUMPALL_PATH. \" +\n \"To take a role-incomplete backup anyway, pass includeGlobals: false.\"\n );\n }\n const globalsFile = globalsFileForDump(localFile);\n try {\n await execa(\n dumpallBin,\n buildPgDumpallGlobalsArgs({ connectionString: opts.connectionString, outFile: globalsFile }),\n { stdio: opts.inheritStdio ? \"inherit\" : \"pipe\", env: { ...(env as Record<string, string>) } }\n );\n } catch (error) {\n // Same reasoning as the pg_dump catch above, and the dump goes with\n // it: the pair is uploaded and pruned together, so a dump whose\n // roles sidecar is missing restores without the roles its GRANT and\n // RLS statements need — the exact failure the sidecar exists to\n // prevent. Leaving half a pair on disk would look like a backup.\n discardPartialDump(globalsFile);\n discardPartialDump(localFile);\n throw error;\n }\n result.globalsFile = globalsFile;\n result.globalsSizeBytes = fs.existsSync(globalsFile) ? fs.statSync(globalsFile).size : 0;\n }\n\n return result;\n}\n\n/**\n * Cheap integrity check on a freshly written dump: it must be non-empty and\n * `pg_restore --list` must parse its table of contents without error. Used\n * before pruning older backups so a corrupt-but-exit-0 dump never becomes\n * the reason the last good backup is deleted.\n */\nexport async function validateDump(\n localFile: string,\n env: Record<string, string | undefined> = process.env\n): Promise<{ ok: boolean; reason?: string }> {\n if (!fs.existsSync(localFile)) {\n return { ok: false, reason: `Dump file does not exist: ${localFile}` };\n }\n if (fs.statSync(localFile).size === 0) {\n return { ok: false, reason: \"Dump file is empty (0 bytes).\" };\n }\n const bin = resolvePgBinary(\"pg_restore\", env);\n if (!bin) {\n // Can't verify without pg_restore; treat as inconclusive-but-fail so\n // pruning doesn't proceed on an unverified dump.\n return { ok: false, reason: \"Could not find 'pg_restore' to verify the dump.\" };\n }\n try {\n await execa(bin, buildPgRestoreListArgs(localFile), {\n stdio: \"pipe\",\n env: { ...(env as Record<string, string>) }\n });\n return { ok: true };\n } catch (err) {\n return { ok: false, reason: `pg_restore --list failed: ${err instanceof Error ? err.message : String(err)}` };\n }\n}\n\n/**\n * Replay a `pg_dumpall --globals-only` script to recreate cluster roles\n * before a restore, so the dump's GRANT/RLS statements (which reference\n * `rebase_user` and any owner roles) actually apply. Runs statement by\n * statement and tolerates per-statement failures — on a same-cluster restore\n * the roles usually already exist (`CREATE ROLE` → \"already exists\"), and on\n * a managed provider an `ALTER ROLE <superuser>` may be refused; neither\n * should abort role recreation. Returns how many statements applied vs were\n * skipped.\n */\nexport async function applyGlobals(\n connectionString: string,\n globalsSql: string,\n log: (message: string) => void = () => {}\n): Promise<{ applied: number; skipped: number }> {\n const { Client } = await import(\"pg\");\n const client = new Client({ connectionString });\n await client.connect();\n try {\n return await applyGlobalsWith(\n async (sql) => { await client.query(sql); },\n globalsSql,\n log\n );\n } finally {\n await client.end();\n }\n}\n\n/**\n * Restore a custom-format dump into the database named by\n * `connectionString`. Destructive when `clean` is set (drops objects\n * first). Never called automatically — the CLI gates it behind explicit\n * confirmation.\n *\n * Runs with `--exit-on-error` by default: a restore that logs-and-continues\n * past a failed GRANT (because a role was missing) reports success with RLS\n * un-enforced. Callers should recreate roles first (see {@link applyGlobals})\n * and only set `exitOnError: false` deliberately.\n */\nexport async function restoreDump(opts: {\n connectionString: string;\n inputFile: string;\n clean?: boolean;\n noOwner?: boolean;\n exitOnError?: boolean;\n inheritStdio?: boolean;\n env?: Record<string, string | undefined>;\n}): Promise<void> {\n const env = opts.env ?? process.env;\n const bin = resolvePgBinary(\"pg_restore\", env);\n if (!bin) {\n throw new BackupToolError(\n \"Could not find the 'pg_restore' binary.\",\n \"Install the PostgreSQL client tools or set PG_RESTORE_PATH.\"\n );\n }\n const args = buildPgRestoreArgs({\n connectionString: opts.connectionString,\n inputFile: opts.inputFile,\n clean: opts.clean,\n noOwner: opts.noOwner,\n exitOnError: opts.exitOnError\n });\n await execa(bin, args, {\n stdio: opts.inheritStdio ? \"inherit\" : \"pipe\",\n env: { ...(env as Record<string, string>) }\n });\n}\n\n/**\n * Create a database (if absent) by connecting to the maintenance\n * `postgres` database. Used by `restore --create-db`.\n */\nexport async function ensureDatabaseExists(\n adminConnectionString: string,\n dbName: string\n): Promise<boolean> {\n const { Client } = await import(\"pg\");\n const parsed = new URL(adminConnectionString);\n parsed.pathname = \"/postgres\";\n const client = new Client({ connectionString: parsed.toString() });\n await client.connect();\n try {\n const existing = await client.query(\"SELECT 1 FROM pg_database WHERE datname = $1\", [dbName]);\n if (existing.rowCount && existing.rowCount > 0) {\n return false;\n }\n // Identifier can't be parameterised; guard against injection.\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(dbName)) {\n throw new BackupToolError(`Invalid database name: \"${dbName}\"`);\n }\n await client.query(`CREATE DATABASE \"${dbName}\"`);\n return true;\n } finally {\n await client.end();\n }\n}\n\n/**\n * Upload a local dump to object storage under the destination's prefix.\n * Backups may contain secrets/PII, so the object is written with a\n * private/octet-stream content type; bucket-level ACLs must stay private.\n */\nexport async function uploadBackup(\n storage: StorageController,\n localFile: string,\n dest: Extract<BackupDestination, { kind: \"s3\" | \"gcs\" }>\n): Promise<{ key: string; storageUrl: string }> {\n const fileName = path.basename(localFile);\n const key = joinStorageKey(dest.prefix, fileName);\n const buffer = fs.readFileSync(localFile);\n const file = new File([buffer], fileName, { type: \"application/octet-stream\" });\n const result = await storage.putObject({\n file,\n key,\n bucket: dest.bucket,\n metadata: { \"rebase-backup\": \"1\" }\n });\n return { key, storageUrl: result.storageUrl };\n}\n\n/**\n * List existing backups at a destination. For local destinations reads the\n * directory; for object storage lists the prefix via the controller.\n */\nexport async function listBackups(\n dest: BackupDestination,\n storage?: StorageController\n): Promise<BackupObject[]> {\n if (dest.kind === \"local\") {\n if (!fs.existsSync(dest.path)) return [];\n const stat = fs.statSync(dest.path);\n const dir = stat.isDirectory() ? dest.path : path.dirname(dest.path);\n return fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(\".dump\"))\n .map((f) => {\n const full = path.join(dir, f);\n const stats = fs.statSync(full);\n const createdAt = parseBackupTimestamp(f) ?? stats.mtime;\n return { key: full, createdAt, sizeBytes: stats.size };\n })\n .sort((a, b) => (b.createdAt?.getTime() ?? 0) - (a.createdAt?.getTime() ?? 0));\n }\n\n if (!storage) {\n throw new BackupToolError(\n `Listing ${dest.kind} backups requires a configured storage backend.`\n );\n }\n // The server's lister, not a second copy of it: a listing is paged, in\n // ascending key order, and this one read a single page of 1,000 — the\n // OLDEST backups — so `rebase db backups list` stopped showing new ones,\n // and retention ranked a page that held none of the newest. The two copies\n // had already drifted apart once (over the roles sidecar).\n const { listBackupObjects } = await import(\"@rebasepro/server\");\n const listed = await listBackupObjects(dest, storage);\n return listed.map((info) => ({\n key: info.key,\n createdAt: info.createdAt ? new Date(info.createdAt) : undefined\n }));\n}\n\n/**\n * Apply a retention policy to a destination, deleting the backups selected\n * by {@link selectBackupsToPrune}. Returns the keys that were removed.\n */\nexport async function pruneBackups(\n dest: BackupDestination,\n options: RetentionOptions,\n storage?: StorageController\n): Promise<string[]> {\n const backups = await listBackups(dest, storage);\n const toDelete = selectBackupsToPrune(backups, options);\n\n // Delete each dump together with its `.globals.sql` sidecar (pruneWith\n // handles the pairing + best-effort sidecar). The deleter rejects on a\n // missing key so pruneWith can swallow an absent sidecar.\n const deleteObject =\n dest.kind === \"local\"\n ? async (key: string) => {\n if (!fs.existsSync(key)) throw new Error(`not found: ${key}`);\n fs.unlinkSync(key);\n }\n : async (key: string) => {\n if (!storage) throw new BackupToolError(\"Storage backend required to prune object backups.\");\n await storage.deleteObject(key, dest.bucket);\n };\n\n await pruneWith(toDelete, deleteObject);\n return toDelete;\n}\n"],"mappings":"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"}
|