@rebasepro/server-postgres 0.17.3 → 0.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. package/LICENSE +0 -1
  2. package/README.md +31 -0
  3. package/dist/BranchService-ByLfM_Tt.js +360 -0
  4. package/dist/BranchService-ByLfM_Tt.js.map +1 -0
  5. package/dist/__vite-browser-external-BnuHet1e.js +46 -0
  6. package/dist/__vite-browser-external-BnuHet1e.js.map +1 -0
  7. package/dist/arg-Dni7MzLB.js +87 -0
  8. package/dist/arg-Dni7MzLB.js.map +1 -0
  9. package/dist/auth-users-columns-D2LBFrMH.js +116 -0
  10. package/dist/auth-users-columns-D2LBFrMH.js.map +1 -0
  11. package/dist/backup-argv.d.ts +19 -0
  12. package/dist/backup-cli-oM9WQVlV.js +408 -0
  13. package/dist/backup-cli-oM9WQVlV.js.map +1 -0
  14. package/dist/backup-service-Br3tqwsy.js +715 -0
  15. package/dist/backup-service-Br3tqwsy.js.map +1 -0
  16. package/dist/branch-argv.d.ts +28 -0
  17. package/dist/branch-prune.d.ts +77 -0
  18. package/dist/cli-collections-path.d.ts +31 -0
  19. package/dist/cli-errors-DxN4yzdH.js +657 -0
  20. package/dist/cli-errors-DxN4yzdH.js.map +1 -0
  21. package/dist/cli-errors.d.ts +123 -0
  22. package/dist/cli-flags.d.ts +101 -0
  23. package/dist/cli-helpers.d.ts +94 -10
  24. package/dist/cli-output-CNdMql-L.js +47 -0
  25. package/dist/cli-output-CNdMql-L.js.map +1 -0
  26. package/dist/cli.js +2020 -0
  27. package/dist/cli.js.map +1 -0
  28. package/dist/collection-index-BHgT_TKd.js +276 -0
  29. package/dist/collection-index-BHgT_TKd.js.map +1 -0
  30. package/dist/{collections-schema-version-BMeu3cgv.js → collections-schema-version-T6cGvrDi.js} +1 -2
  31. package/dist/{collections-schema-version-BMeu3cgv.js.map → collections-schema-version-T6cGvrDi.js.map} +1 -1
  32. package/dist/{connection-GOKU3Hu5.js → databasePoolManager-Bj5FbeAs.js} +112 -40
  33. package/dist/databasePoolManager-Bj5FbeAs.js.map +1 -0
  34. package/dist/databasePoolManager.d.ts +27 -0
  35. package/dist/doctor-D7HWmGzs.js +974 -0
  36. package/dist/doctor-D7HWmGzs.js.map +1 -0
  37. package/dist/{ensure-collection-policies-UCqgv_8c.js → ensure-collection-policies-2c2NV8zX.js} +7 -27
  38. package/dist/ensure-collection-policies-2c2NV8zX.js.map +1 -0
  39. package/dist/ensure-collection-tables-D4wJzM4y.js +820 -0
  40. package/dist/ensure-collection-tables-D4wJzM4y.js.map +1 -0
  41. package/dist/ensure-tables-CIojK-Rx.js +650 -0
  42. package/dist/ensure-tables-CIojK-Rx.js.map +1 -0
  43. package/dist/generate-drizzle-schema-CwEgVyjO.js +123 -0
  44. package/dist/generate-drizzle-schema-CwEgVyjO.js.map +1 -0
  45. package/dist/generate-drizzle-schema-logic-so2Z_lyC.js +528 -0
  46. package/dist/generate-drizzle-schema-logic-so2Z_lyC.js.map +1 -0
  47. package/dist/{ensure-collection-tables-DgVixhX3.js → generate-postgres-ddl-logic-CtnpIBVv.js} +126 -984
  48. package/dist/generate-postgres-ddl-logic-CtnpIBVv.js.map +1 -0
  49. package/dist/generated-schema-staleness-DQdfJl2A.js +157 -0
  50. package/dist/generated-schema-staleness-DQdfJl2A.js.map +1 -0
  51. package/dist/index.d.ts +12 -0
  52. package/dist/index.es.js +7423 -1133
  53. package/dist/index.es.js.map +1 -1
  54. package/dist/introspect-db-logic-C6LQdTxj.js +1378 -0
  55. package/dist/introspect-db-logic-C6LQdTxj.js.map +1 -0
  56. package/dist/policy-drift-kNJ6X2Uj.js +313 -0
  57. package/dist/policy-drift-kNJ6X2Uj.js.map +1 -0
  58. package/dist/{backup-service-DCk7KhhL.js → rls-bootstrap-sql-DLDnUjtx.js} +737 -2689
  59. package/dist/rls-bootstrap-sql-DLDnUjtx.js.map +1 -0
  60. package/dist/{rls-enforcement-DvAbL9YJ.js → rls-enforcement-DeYEjYLt.js} +65 -190
  61. package/dist/rls-enforcement-DeYEjYLt.js.map +1 -0
  62. package/dist/schema/bin/generate-drizzle-schema.d.ts +1 -0
  63. package/dist/schema/bin/generate-postgres-ddl.d.ts +1 -0
  64. package/dist/schema/column-type-drift.d.ts +79 -0
  65. package/dist/schema/doctor-cli.js +129 -0
  66. package/dist/schema/doctor-cli.js.map +1 -0
  67. package/dist/schema/doctor.d.ts +42 -0
  68. package/dist/schema/ensure-collection-tables.d.ts +90 -0
  69. package/dist/schema/generate-drizzle-schema.d.ts +1 -1
  70. package/dist/schema/generate-drizzle-schema.js +24 -0
  71. package/dist/schema/generate-drizzle-schema.js.map +1 -0
  72. package/dist/schema/generate-next-step.d.ts +30 -0
  73. package/dist/schema/generate-postgres-ddl-logic.d.ts +28 -0
  74. package/dist/schema/generate-postgres-ddl.d.ts +1 -1
  75. package/dist/schema/generate-postgres-ddl.js +138 -0
  76. package/dist/schema/generate-postgres-ddl.js.map +1 -0
  77. package/dist/schema/generated-schema-staleness.d.ts +37 -0
  78. package/dist/schema/introspect-db-queries.d.ts +34 -0
  79. package/dist/schema/introspect-db.js +866 -0
  80. package/dist/schema/introspect-db.js.map +1 -0
  81. package/dist/security/rls-enforcement.d.ts +63 -2
  82. package/dist/services/BranchService.d.ts +86 -1
  83. package/dist/services/collection-helpers.d.ts +1 -1
  84. package/dist/services/realtimeService.d.ts +30 -2
  85. package/dist/services/row-pipeline.d.ts +12 -1
  86. package/dist/source-Br7L7GOI.js +410 -0
  87. package/dist/source-Br7L7GOI.js.map +1 -0
  88. package/dist/utils/pg-error-utils.d.ts +3 -1
  89. package/dist/utils/pg-numeric-number-patch.d.ts +31 -0
  90. package/dist/vector-index-DkWxm2de.js +309 -0
  91. package/dist/vector-index-DkWxm2de.js.map +1 -0
  92. package/package.json +32 -27
  93. package/dist/backup-service-DCk7KhhL.js.map +0 -1
  94. package/dist/collection-index-BRUg10H5.js +0 -5690
  95. package/dist/collection-index-BRUg10H5.js.map +0 -1
  96. package/dist/connection-GOKU3Hu5.js.map +0 -1
  97. package/dist/ensure-collection-policies-UCqgv_8c.js.map +0 -1
  98. package/dist/ensure-collection-tables-DgVixhX3.js.map +0 -1
  99. package/dist/rls-bootstrap-sql-B5C9LoJ6.js +0 -244
  100. package/dist/rls-bootstrap-sql-B5C9LoJ6.js.map +0 -1
  101. package/dist/rls-enforcement-DvAbL9YJ.js.map +0 -1
  102. package/dist/src-DiB5RP2Z.js +0 -541
  103. package/dist/src-DiB5RP2Z.js.map +0 -1
  104. package/dist/websocket-BZ4H5wUz.js +0 -8198
  105. package/dist/websocket-BZ4H5wUz.js.map +0 -1
  106. package/src/PostgresAdapter.ts +0 -91
  107. package/src/PostgresBackendDriver.ts +0 -1857
  108. package/src/PostgresBootstrapper.ts +0 -1157
  109. package/src/auth/ensure-tables.ts +0 -896
  110. package/src/auth/schema-version.ts +0 -260
  111. package/src/auth/services.ts +0 -1610
  112. package/src/backup/backup-cli.ts +0 -520
  113. package/src/backup/backup-cron.ts +0 -212
  114. package/src/backup/backup-logic.ts +0 -93
  115. package/src/backup/backup-service.ts +0 -485
  116. package/src/backup/index.ts +0 -12
  117. package/src/backup/pg-tools.ts +0 -403
  118. package/src/backup/retention.ts +0 -86
  119. package/src/cli-errors.ts +0 -265
  120. package/src/cli-helpers.ts +0 -569
  121. package/src/cli-output.ts +0 -43
  122. package/src/cli.ts +0 -1339
  123. package/src/collections/PostgresCollectionRegistry.ts +0 -108
  124. package/src/collections/buildRegistry.ts +0 -68
  125. package/src/collections/validate-relations.ts +0 -402
  126. package/src/connection.ts +0 -333
  127. package/src/data-transformer.ts +0 -845
  128. package/src/databasePoolManager.ts +0 -95
  129. package/src/history/HistoryService.ts +0 -254
  130. package/src/history/ensure-history-table.ts +0 -53
  131. package/src/index.ts +0 -19
  132. package/src/interfaces.ts +0 -60
  133. package/src/module-dir.ts +0 -7
  134. package/src/schema/atlas-argv.ts +0 -94
  135. package/src/schema/auth-schema.ts +0 -298
  136. package/src/schema/auth-users-columns.ts +0 -131
  137. package/src/schema/carved-out-migration.ts +0 -404
  138. package/src/schema/classify-change.ts +0 -436
  139. package/src/schema/collection-index.ts +0 -427
  140. package/src/schema/collections-schema-version.ts +0 -103
  141. package/src/schema/destructive-sql.ts +0 -94
  142. package/src/schema/doctor-cli.ts +0 -66
  143. package/src/schema/doctor-policy-checks.ts +0 -105
  144. package/src/schema/doctor.ts +0 -837
  145. package/src/schema/drizzle-ddl.ts +0 -33
  146. package/src/schema/dynamic-tables.test.ts +0 -302
  147. package/src/schema/dynamic-tables.ts +0 -293
  148. package/src/schema/ensure-collection-policies.ts +0 -198
  149. package/src/schema/ensure-collection-tables.test.ts +0 -518
  150. package/src/schema/ensure-collection-tables.ts +0 -1267
  151. package/src/schema/generate-drizzle-schema-logic.ts +0 -957
  152. package/src/schema/generate-drizzle-schema.ts +0 -143
  153. package/src/schema/generate-postgres-ddl-logic.ts +0 -1339
  154. package/src/schema/generate-postgres-ddl.ts +0 -180
  155. package/src/schema/generate-schema-commit.ts +0 -267
  156. package/src/schema/generated-schema-staleness.ts +0 -284
  157. package/src/schema/introspect-db-constraints.ts +0 -385
  158. package/src/schema/introspect-db-inference.ts +0 -255
  159. package/src/schema/introspect-db-logic.ts +0 -1387
  160. package/src/schema/introspect-db-naming.ts +0 -15
  161. package/src/schema/introspect-db-project.ts +0 -78
  162. package/src/schema/introspect-db-queries.ts +0 -326
  163. package/src/schema/introspect-db-structure.ts +0 -670
  164. package/src/schema/introspect-db-types.ts +0 -56
  165. package/src/schema/introspect-db.ts +0 -257
  166. package/src/schema/introspect-runtime.test.ts +0 -260
  167. package/src/schema/introspect-runtime.ts +0 -327
  168. package/src/schema/non-sql-collections.test.ts +0 -131
  169. package/src/schema/rls-bootstrap-sql.ts +0 -288
  170. package/src/schema/search-column.ts +0 -643
  171. package/src/schema/test-schema.ts +0 -11
  172. package/src/schema/vector-index.ts +0 -491
  173. package/src/security/anonymous-grants.test.ts +0 -73
  174. package/src/security/policy-drift.test.ts +0 -435
  175. package/src/security/policy-drift.ts +0 -473
  176. package/src/security/rls-enforcement.ts +0 -505
  177. package/src/services/BranchService.ts +0 -326
  178. package/src/services/FetchService.ts +0 -2115
  179. package/src/services/PersistService.ts +0 -557
  180. package/src/services/RelationService.ts +0 -978
  181. package/src/services/RelationWriteService.ts +0 -653
  182. package/src/services/cdc/CdcListener.ts +0 -103
  183. package/src/services/cdc/junction-tables.ts +0 -91
  184. package/src/services/cdc/trigger-cdc.ts +0 -173
  185. package/src/services/channel-bus/ChannelBus.ts +0 -44
  186. package/src/services/channel-bus/PostgresChannelBus.ts +0 -299
  187. package/src/services/channel-bus/index.ts +0 -114
  188. package/src/services/channel-history.ts +0 -424
  189. package/src/services/channel-presence.ts +0 -185
  190. package/src/services/collection-helpers.ts +0 -420
  191. package/src/services/dataService.ts +0 -256
  192. package/src/services/index.ts +0 -14
  193. package/src/services/junction-writes.ts +0 -295
  194. package/src/services/nested-path.ts +0 -145
  195. package/src/services/pg-notify-listener.ts +0 -151
  196. package/src/services/realtimeService.ts +0 -2545
  197. package/src/services/row-pipeline.ts +0 -240
  198. package/src/services/write-denial.ts +0 -55
  199. package/src/types.ts +0 -4
  200. package/src/utils/connection-string.ts +0 -58
  201. package/src/utils/drizzle-conditions.ts +0 -2743
  202. package/src/utils/pg-array-null-patch.ts +0 -42
  203. package/src/utils/pg-error-utils.ts +0 -391
  204. package/src/websocket.ts +0 -855
@@ -1,212 +0,0 @@
1
- /**
2
- * Scheduled backups, wired into the server cron system.
3
- *
4
- * A backend enables nightly (or custom-schedule) backups by dropping a cron
5
- * file that default-exports {@link createBackupCron}. The heavy lifting —
6
- * dump, upload, prune — reuses the same primitives as the CLI so behaviour
7
- * is identical between a manual `rebase db backup` and an automated run.
8
- */
9
- import fs from "fs";
10
- import type { CronJobDefinition } from "@rebasepro/types";
11
- import type { StorageController } from "@rebasepro/server";
12
- import {
13
- BackupDestination,
14
- parseBackupDestination,
15
- parseDbNameFromUrl,
16
- resolveConnectionString
17
- } from "./pg-tools";
18
- // NOTE: `./backup-service` pulls in `execa` (ESM-only). It is imported
19
- // lazily inside the handler so the pure `backupCronConfigFromEnv` parser can
20
- // be unit-tested under CommonJS without loading it.
21
-
22
- export interface BackupCronConfig {
23
- /** Cron expression, e.g. `"0 3 * * *"` for 03:00 daily. */
24
- schedule: string;
25
- /** Postgres connection string. Defaults to `DATABASE_URL`. */
26
- connectionString: string;
27
- /** Where backups are written: a local path or an `s3://` / `gs://` URL. */
28
- destination: BackupDestination;
29
- /**
30
- * Storage controller used for `s3`/`gcs` destinations. Reuse the one the
31
- * backend already configured (S3/GCS/Local StorageController). Not needed
32
- * for local destinations.
33
- */
34
- storage?: StorageController;
35
- /** Delete backups older than this many days. `0` disables pruning. */
36
- retentionDays?: number;
37
- /** Always keep at least this many recent backups regardless of age. */
38
- keepMinimum?: number;
39
- /** Schemas to exclude from the dump (defaults to Atlas revision schema). */
40
- excludeSchemas?: string[];
41
- /** Cron job display name. */
42
- name?: string;
43
- /** Whether the job is enabled. */
44
- enabled?: boolean;
45
- }
46
-
47
- export interface EnvResolution {
48
- /** Resolved config, present when a schedule is configured. */
49
- config?: Omit<BackupCronConfig, "storage">;
50
- /** True when `BACKUP_SCHEDULE` is unset — the cron should be a no-op. */
51
- disabled?: boolean;
52
- /** Human-readable reason the config could not be built. */
53
- error?: string;
54
- }
55
-
56
- /**
57
- * Build backup-cron config purely from environment variables.
58
- * Recognised keys:
59
- * - `BACKUP_SCHEDULE` cron expression (required to enable)
60
- * - `BACKUP_DESTINATION` local path or `s3://` / `gs://` URL (required)
61
- * - `BACKUP_RETENTION_DAYS` integer days (optional)
62
- * - `BACKUP_KEEP_MINIMUM` integer count (optional)
63
- * - `DATABASE_URL` connection string
64
- *
65
- * Pure and side-effect free so it can be unit-tested without a database.
66
- */
67
- export function backupCronConfigFromEnv(env: Record<string, string | undefined>): EnvResolution {
68
- const schedule = env.BACKUP_SCHEDULE?.trim();
69
- if (!schedule) {
70
- return { disabled: true };
71
- }
72
-
73
- const connectionString = resolveConnectionString(env);
74
- if (!connectionString) {
75
- return { error: "BACKUP_SCHEDULE is set but DATABASE_URL is not configured." };
76
- }
77
-
78
- const destinationRaw = env.BACKUP_DESTINATION?.trim();
79
- if (!destinationRaw) {
80
- return { error: "BACKUP_SCHEDULE is set but BACKUP_DESTINATION is not configured." };
81
- }
82
- const destination = parseBackupDestination(destinationRaw);
83
-
84
- const retentionDays = parseOptionalInt(env.BACKUP_RETENTION_DAYS);
85
- if (retentionDays === "invalid") {
86
- return { error: `BACKUP_RETENTION_DAYS must be an integer, got "${env.BACKUP_RETENTION_DAYS}".` };
87
- }
88
- const keepMinimum = parseOptionalInt(env.BACKUP_KEEP_MINIMUM);
89
- if (keepMinimum === "invalid") {
90
- return { error: `BACKUP_KEEP_MINIMUM must be an integer, got "${env.BACKUP_KEEP_MINIMUM}".` };
91
- }
92
-
93
- return {
94
- config: {
95
- schedule,
96
- connectionString,
97
- destination,
98
- retentionDays: retentionDays ?? undefined,
99
- keepMinimum: keepMinimum ?? undefined
100
- }
101
- };
102
- }
103
-
104
- function parseOptionalInt(value: string | undefined): number | null | "invalid" {
105
- if (value === undefined || value.trim() === "") return null;
106
- const n = Number(value);
107
- if (!Number.isInteger(n) || n < 0) return "invalid";
108
- return n;
109
- }
110
-
111
- /**
112
- * Create a {@link CronJobDefinition} that dumps the database, uploads the
113
- * result to the configured destination, and prunes old backups. Object
114
- * destinations require {@link BackupCronConfig.storage}.
115
- */
116
- export function createBackupCron(config: BackupCronConfig): CronJobDefinition {
117
- const dbName = parseDbNameFromUrl(config.connectionString) ?? "database";
118
- const excludeSchemas = config.excludeSchemas ?? ["rebase"];
119
-
120
- return {
121
- name: config.name ?? "Scheduled database backup",
122
- schedule: config.schedule,
123
- description: "Dumps the Postgres database and uploads it to the configured backup destination.",
124
- enabled: config.enabled ?? true,
125
- // Backups of a large database can take a while; allow up to an hour.
126
- timeoutSeconds: 3600,
127
- async handler({ log }) {
128
- const { createDump, pruneBackups, uploadBackup, validateDump } = await import("./backup-service");
129
- const { destination } = config;
130
-
131
- if (destination.kind !== "local" && !config.storage) {
132
- throw new Error(
133
- `Backup destination is ${destination.kind} but no storage controller was provided. ` +
134
- "Pass the backend's configured StorageController to createBackupCron({ storage })."
135
- );
136
- }
137
-
138
- log(`Starting backup of "${dbName}"…`);
139
- const outDir = destination.kind === "local" ? destination.path : undefined;
140
- const dump = await createDump({
141
- connectionString: config.connectionString,
142
- dbName,
143
- outDir,
144
- excludeSchemas
145
- });
146
- log(`Dump created: ${dump.fileName} (${formatBytes(dump.sizeBytes)})`);
147
-
148
- // Validate BEFORE pruning: a corrupt-but-exit-0 dump must never be
149
- // the reason the last good backup gets deleted.
150
- const check = await validateDump(dump.localFile);
151
- if (!check.ok) {
152
- // Clean up the bad temp file for object destinations.
153
- if (destination.kind !== "local" && fs.existsSync(dump.localFile)) {
154
- fs.unlinkSync(dump.localFile);
155
- }
156
- if (dump.globalsFile && destination.kind !== "local" && fs.existsSync(dump.globalsFile)) {
157
- fs.unlinkSync(dump.globalsFile);
158
- }
159
- throw new Error(`New backup failed validation — skipping upload and pruning to protect existing backups. ${check.reason}`);
160
- }
161
-
162
- let storedKey = dump.localFile;
163
- try {
164
- if (destination.kind !== "local") {
165
- const uploaded = await uploadBackup(config.storage!, dump.localFile, destination);
166
- storedKey = uploaded.storageUrl;
167
- log(`Uploaded to ${uploaded.storageUrl}`);
168
- // Upload the roles sidecar so a restore can recreate the
169
- // roles the dump's GRANT/RLS statements depend on.
170
- if (dump.globalsFile && fs.existsSync(dump.globalsFile)) {
171
- const g = await uploadBackup(config.storage!, dump.globalsFile, destination);
172
- log(`Uploaded roles sidecar to ${g.storageUrl}`);
173
- }
174
- }
175
- } finally {
176
- // For object-storage destinations the local dump was a temp
177
- // file — remove it once uploaded (or on failure).
178
- if (destination.kind !== "local" && fs.existsSync(dump.localFile)) {
179
- fs.unlinkSync(dump.localFile);
180
- }
181
- if (dump.globalsFile && destination.kind !== "local" && fs.existsSync(dump.globalsFile)) {
182
- fs.unlinkSync(dump.globalsFile);
183
- }
184
- }
185
-
186
- let pruned: string[] = [];
187
- if (config.retentionDays && config.retentionDays > 0) {
188
- pruned = await pruneBackups(
189
- destination,
190
- { retentionDays: config.retentionDays, keepMinimum: config.keepMinimum },
191
- config.storage
192
- );
193
- if (pruned.length > 0) {
194
- log(`Pruned ${pruned.length} backup(s) older than ${config.retentionDays} day(s).`);
195
- }
196
- }
197
-
198
- return {
199
- backup: storedKey,
200
- sizeBytes: dump.sizeBytes,
201
- pruned: pruned.length
202
- };
203
- }
204
- };
205
- }
206
-
207
- function formatBytes(bytes: number): string {
208
- if (bytes < 1024) return `${bytes} B`;
209
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
210
- if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
211
- return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
212
- }
@@ -1,93 +0,0 @@
1
- /**
2
- * Pure-ish orchestration for the backup/restore paths, kept free of `execa`
3
- * and `pg` value imports so it can be unit-tested under jest (the impure
4
- * edges — spawning processes, opening connections — live in
5
- * `backup-service.ts`, which is vitest/runtime only).
6
- *
7
- * The functions here take their side-effecting dependency as an argument
8
- * (a statement runner, an object deleter) so tests can inject fakes.
9
- */
10
- import { globalsFileForDump, splitGlobalsStatements } from "./pg-tools";
11
-
12
- /**
13
- * Replay a `pg_dumpall --globals-only` script one statement at a time,
14
- * tolerating per-statement failures. On a same-cluster restore the roles
15
- * usually already exist (`CREATE ROLE` → "already exists") and on a managed
16
- * provider an `ALTER ROLE <superuser>` may be refused; neither should abort
17
- * recreation of the roles that *are* missing. Returns how many statements
18
- * applied vs were skipped.
19
- *
20
- * `runStatement` executes one SQL statement and rejects on error.
21
- */
22
- export async function applyGlobalsWith(
23
- runStatement: (sql: string) => Promise<void>,
24
- globalsSql: string,
25
- log: (message: string) => void = () => {}
26
- ): Promise<{ applied: number; skipped: number }> {
27
- let applied = 0;
28
- let skipped = 0;
29
- for (const statement of splitGlobalsStatements(globalsSql)) {
30
- try {
31
- await runStatement(statement);
32
- applied++;
33
- } catch (err) {
34
- skipped++;
35
- const firstLine = statement.split("\n")[0].slice(0, 80);
36
- log(` • Skipped global: ${firstLine} (${err instanceof Error ? err.message : String(err)})`);
37
- }
38
- }
39
- return { applied, skipped };
40
- }
41
-
42
- /**
43
- * Delete each pruned dump together with its `.globals.sql` roles sidecar, so
44
- * pruning never orphans the roles file. The sidecar is best-effort — older
45
- * backups predate it, so a missing-sidecar failure is swallowed while a
46
- * failure deleting the dump itself propagates.
47
- *
48
- * `deleteObject` removes one key and rejects if it cannot (e.g. not found).
49
- */
50
- export async function pruneWith(
51
- keys: string[],
52
- deleteObject: (key: string) => Promise<void>
53
- ): Promise<void> {
54
- for (const key of keys) {
55
- await deleteObject(key);
56
- try {
57
- await deleteObject(globalsFileForDump(key));
58
- } catch {
59
- // Sidecar may not exist for older backups — ignore.
60
- }
61
- }
62
- }
63
-
64
- /**
65
- * Remove a dump artifact abandoned by a failed tool run.
66
- *
67
- * `pg_dump` creates its `--file=` target before it finishes connecting, so any
68
- * failure — a URL libpq rejects, a dropped connection, a full disk — leaves a
69
- * 0-byte file behind. That corpse is not inert: `rebase db backups list` used
70
- * to show it as an ordinary entry, and `selectBackupsToPrune` ranks by
71
- * timestamp alone, so it occupies a protected `keepMinimum` slot and can push
72
- * a real backup out of retention.
73
- *
74
- * Best-effort on purpose: the caller is already throwing, and a failed unlink
75
- * must not replace the real diagnosis with an ENOENT/EPERM from the cleanup.
76
- *
77
- * Lives here rather than in `backup-service.ts` for the reason at the top of
78
- * this file — that module's top-level `execa` import makes it unloadable under
79
- * jest, so anything placed there cannot be unit-tested.
80
- *
81
- * `remove` deletes one file and throws if it cannot; `exists` reports presence.
82
- */
83
- export function discardPartialDumpWith(
84
- exists: (file: string) => boolean,
85
- remove: (file: string) => void,
86
- file: string
87
- ): void {
88
- try {
89
- if (exists(file)) remove(file);
90
- } catch {
91
- // Ignored: see above.
92
- }
93
- }