@tickernelz/wardmote-db 2026.1109.0 → 2026.1112.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. package/dist/agent-chat-migration.test.js +2 -2
  2. package/dist/agent-chat-migration.test.js.map +1 -1
  3. package/dist/backup-lib.d.ts +38 -0
  4. package/dist/backup-lib.d.ts.map +1 -1
  5. package/dist/backup-lib.js +290 -35
  6. package/dist/backup-lib.js.map +1 -1
  7. package/dist/backup-lib.test.js +215 -4
  8. package/dist/backup-lib.test.js.map +1 -1
  9. package/dist/backup.js +10 -5
  10. package/dist/backup.js.map +1 -1
  11. package/dist/better-auth-account-issuer-migration.test.js +1 -1
  12. package/dist/better-auth-account-issuer-migration.test.js.map +1 -1
  13. package/dist/built-in-agent-unique-marker-migration.test.js +1 -1
  14. package/dist/built-in-agent-unique-marker-migration.test.js.map +1 -1
  15. package/dist/chat-github-migration.test.js +1 -1
  16. package/dist/chat-github-migration.test.js.map +1 -1
  17. package/dist/chat-identity-migration-reconciliation.test.js +5 -5
  18. package/dist/chat-identity-migration-reconciliation.test.js.map +1 -1
  19. package/dist/chat-openwa-migration.test.js +2 -2
  20. package/dist/chat-openwa-migration.test.js.map +1 -1
  21. package/dist/chat-telegram-draft-ids-migration.test.js +1 -1
  22. package/dist/chat-telegram-draft-ids-migration.test.js.map +1 -1
  23. package/dist/chat-tenant-foreign-keys.test.js +1 -1
  24. package/dist/chat-tenant-foreign-keys.test.js.map +1 -1
  25. package/dist/check-migration-safety.js +2 -2
  26. package/dist/check-migration-safety.js.map +1 -1
  27. package/dist/check-migration-safety.test.js +10 -1
  28. package/dist/check-migration-safety.test.js.map +1 -1
  29. package/dist/client-options.test.js +6 -6
  30. package/dist/client-options.test.js.map +1 -1
  31. package/dist/client.d.ts +1 -22
  32. package/dist/client.d.ts.map +1 -1
  33. package/dist/client.js +1 -16
  34. package/dist/client.js.map +1 -1
  35. package/dist/client.test.js +10 -10
  36. package/dist/client.test.js.map +1 -1
  37. package/dist/company-secret-proposals-migration.test.js +1 -1
  38. package/dist/company-secret-proposals-migration.test.js.map +1 -1
  39. package/dist/connection-grants-phase4-migration.test.js +1 -1
  40. package/dist/connection-grants-phase4-migration.test.js.map +1 -1
  41. package/dist/connections-v3-schema-core-migration.test.js +2 -2
  42. package/dist/connections-v3-schema-core-migration.test.js.map +1 -1
  43. package/dist/decision-queue-migrations.test.js +1 -1
  44. package/dist/decision-queue-migrations.test.js.map +1 -1
  45. package/dist/embedded-identity.d.ts +12 -0
  46. package/dist/embedded-identity.d.ts.map +1 -0
  47. package/dist/embedded-identity.js +96 -0
  48. package/dist/embedded-identity.js.map +1 -0
  49. package/dist/embedded-identity.test.d.ts +2 -0
  50. package/dist/embedded-identity.test.d.ts.map +1 -0
  51. package/dist/embedded-identity.test.js +91 -0
  52. package/dist/embedded-identity.test.js.map +1 -0
  53. package/dist/embedded-postgres-lifecycle.d.ts +0 -9
  54. package/dist/embedded-postgres-lifecycle.d.ts.map +1 -1
  55. package/dist/embedded-postgres-lifecycle.js +0 -9
  56. package/dist/embedded-postgres-lifecycle.js.map +1 -1
  57. package/dist/embedded-postgres-native.test.js +2 -2
  58. package/dist/embedded-postgres-native.test.js.map +1 -1
  59. package/dist/heartbeat-run-issue-privacy-migration.test.js +4 -4
  60. package/dist/heartbeat-run-issue-privacy-migration.test.js.map +1 -1
  61. package/dist/inbox-archive-agent-policies-migration.test.js +1 -1
  62. package/dist/inbox-archive-agent-policies-migration.test.js.map +1 -1
  63. package/dist/index.d.ts +2 -1
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +2 -1
  66. package/dist/index.js.map +1 -1
  67. package/dist/issue-comment-derived-attribution-migration.test.js +1 -1
  68. package/dist/issue-comment-derived-attribution-migration.test.js.map +1 -1
  69. package/dist/issue-comment-on-behalf-migration.test.js +1 -1
  70. package/dist/issue-comment-on-behalf-migration.test.js.map +1 -1
  71. package/dist/migration-runtime.d.ts.map +1 -1
  72. package/dist/migration-runtime.js +21 -11
  73. package/dist/migration-runtime.js.map +1 -1
  74. package/dist/migrations/0359_wardmote_bundled_skill_slugs.sql +100 -0
  75. package/dist/migrations/0360_wardmote_channel_values.sql +11 -0
  76. package/dist/migrations/0361_wardmote_stored_modes.sql +14 -0
  77. package/dist/migrations/0362_wardmote_runner_adapter_type.sql +34 -0
  78. package/dist/migrations/0363_wardmote_camel_keys.sql +57 -0
  79. package/dist/migrations/0364_wardmote_catalog_keys.sql +92 -0
  80. package/dist/migrations/0365_wardmote_plugin_ids.sql +118 -0
  81. package/dist/migrations/0366_wardmote_managed_environment_key.sql +13 -0
  82. package/dist/migrations/0367_wardmote_schema_ids.sql +31 -0
  83. package/dist/migrations/0368_wardmote_mcp_scopes.sql +27 -0
  84. package/dist/migrations/0369_wardmote_chat_state_key.sql +10 -0
  85. package/dist/migrations/0370_wardmote_wake_context_key.sql +4 -0
  86. package/dist/migrations/0371_wardmote_session_config_keys.sql +7 -0
  87. package/dist/migrations/0372_wardmote_tool_provider_types.sql +23 -0
  88. package/dist/migrations/meta/_journal.json +98 -0
  89. package/dist/native-runner-recovery-migration.test.js +1 -1
  90. package/dist/native-runner-recovery-migration.test.js.map +1 -1
  91. package/dist/nested-skill-folders-migration.test.js +2 -2
  92. package/dist/orphaned-claimed-wakeups-migration.test.js +1 -1
  93. package/dist/orphaned-claimed-wakeups-migration.test.js.map +1 -1
  94. package/dist/pipelines-schema.test.js +1 -1
  95. package/dist/pipelines-schema.test.js.map +1 -1
  96. package/dist/postgres-connection-recovery.test.js +1 -1
  97. package/dist/postgres-connection-recovery.test.js.map +1 -1
  98. package/dist/private-task-draft-assets-migration.test.js +1 -1
  99. package/dist/private-task-draft-assets-migration.test.js.map +1 -1
  100. package/dist/run-event-payload.js +1 -1
  101. package/dist/run-event-payload.js.map +1 -1
  102. package/dist/run-event-payload.test.js +1 -1
  103. package/dist/run-event-payload.test.js.map +1 -1
  104. package/dist/run-identity-migration.test.js +1 -1
  105. package/dist/run-identity-migration.test.js.map +1 -1
  106. package/dist/runtime-config.d.ts.map +1 -1
  107. package/dist/runtime-config.js +10 -8
  108. package/dist/runtime-config.js.map +1 -1
  109. package/dist/runtime-config.test.js +11 -11
  110. package/dist/runtime-config.test.js.map +1 -1
  111. package/dist/schema/chat_channels.d.ts +5 -5
  112. package/dist/schema/chat_channels.d.ts.map +1 -1
  113. package/dist/schema/chat_channels.js +4 -19
  114. package/dist/schema/chat_channels.js.map +1 -1
  115. package/dist/schema/company_secrets.js +1 -1
  116. package/dist/schema/company_secrets.js.map +1 -1
  117. package/dist/schema/environments.js +1 -1
  118. package/dist/schema/environments.js.map +1 -1
  119. package/dist/schema/heartbeat_runs.js +1 -1
  120. package/dist/schema/heartbeat_runs.js.map +1 -1
  121. package/dist/schema/issue_autonomy_windows.d.ts +2 -2
  122. package/dist/schema/issue_autonomy_windows.js +1 -1
  123. package/dist/schema/issue_autonomy_windows.js.map +1 -1
  124. package/dist/schema/plugin_entities.d.ts +0 -14
  125. package/dist/schema/plugin_entities.d.ts.map +1 -1
  126. package/dist/schema/plugin_entities.js +0 -14
  127. package/dist/schema/plugin_entities.js.map +1 -1
  128. package/dist/schema/plugin_state.d.ts +0 -23
  129. package/dist/schema/plugin_state.d.ts.map +1 -1
  130. package/dist/schema/plugin_state.js +0 -23
  131. package/dist/schema/plugin_state.js.map +1 -1
  132. package/dist/schema/plugins.d.ts +3 -3
  133. package/dist/schema/plugins.d.ts.map +1 -1
  134. package/dist/schema/plugins.js.map +1 -1
  135. package/dist/schema/tool_access.d.ts +2 -2
  136. package/dist/schema/tool_access.js +3 -3
  137. package/dist/schema/tool_access.js.map +1 -1
  138. package/dist/schema/user_secret_definitions.js +1 -1
  139. package/dist/schema/user_secret_definitions.js.map +1 -1
  140. package/dist/schema/web_push_subscriptions.d.ts +2 -2
  141. package/dist/session-goal-migration.test.js +3 -3
  142. package/dist/session-goal-migration.test.js.map +1 -1
  143. package/dist/speko-voice-migration.test.js +1 -1
  144. package/dist/speko-voice-migration.test.js.map +1 -1
  145. package/dist/status-card-migrations.test.js +1 -1
  146. package/dist/status-card-migrations.test.js.map +1 -1
  147. package/dist/test-embedded-postgres.d.ts.map +1 -1
  148. package/dist/test-embedded-postgres.js +9 -8
  149. package/dist/test-embedded-postgres.js.map +1 -1
  150. package/dist/test-embedded-postgres.test.js +3 -3
  151. package/dist/test-embedded-postgres.test.js.map +1 -1
  152. package/dist/vercel-connect-credential-source-migration.test.js +2 -2
  153. package/dist/vercel-connect-credential-source-migration.test.js.map +1 -1
  154. package/dist/wardmote-camel-key-migration.test.d.ts +2 -0
  155. package/dist/wardmote-camel-key-migration.test.d.ts.map +1 -0
  156. package/dist/wardmote-camel-key-migration.test.js +111 -0
  157. package/dist/wardmote-camel-key-migration.test.js.map +1 -0
  158. package/dist/wardmote-catalog-key-migration.test.d.ts +2 -0
  159. package/dist/wardmote-catalog-key-migration.test.d.ts.map +1 -0
  160. package/dist/wardmote-catalog-key-migration.test.js +140 -0
  161. package/dist/wardmote-catalog-key-migration.test.js.map +1 -0
  162. package/dist/wardmote-chat-state-key-migration.test.d.ts +2 -0
  163. package/dist/wardmote-chat-state-key-migration.test.d.ts.map +1 -0
  164. package/dist/wardmote-chat-state-key-migration.test.js +58 -0
  165. package/dist/wardmote-chat-state-key-migration.test.js.map +1 -0
  166. package/dist/wardmote-managed-environment-key-migration.test.d.ts +2 -0
  167. package/dist/wardmote-managed-environment-key-migration.test.d.ts.map +1 -0
  168. package/dist/wardmote-managed-environment-key-migration.test.js +67 -0
  169. package/dist/wardmote-managed-environment-key-migration.test.js.map +1 -0
  170. package/dist/wardmote-mcp-scope-migration.test.d.ts +2 -0
  171. package/dist/wardmote-mcp-scope-migration.test.d.ts.map +1 -0
  172. package/dist/wardmote-mcp-scope-migration.test.js +93 -0
  173. package/dist/wardmote-mcp-scope-migration.test.js.map +1 -0
  174. package/dist/wardmote-plugin-id-migration.test.d.ts +2 -0
  175. package/dist/wardmote-plugin-id-migration.test.d.ts.map +1 -0
  176. package/dist/wardmote-plugin-id-migration.test.js +127 -0
  177. package/dist/wardmote-plugin-id-migration.test.js.map +1 -0
  178. package/dist/wardmote-runner-adapter-type-migration.test.d.ts +2 -0
  179. package/dist/wardmote-runner-adapter-type-migration.test.d.ts.map +1 -0
  180. package/dist/wardmote-runner-adapter-type-migration.test.js +124 -0
  181. package/dist/wardmote-runner-adapter-type-migration.test.js.map +1 -0
  182. package/dist/wardmote-schema-id-migration.test.d.ts +2 -0
  183. package/dist/wardmote-schema-id-migration.test.d.ts.map +1 -0
  184. package/dist/wardmote-schema-id-migration.test.js +113 -0
  185. package/dist/wardmote-schema-id-migration.test.js.map +1 -0
  186. package/dist/wardmote-session-config-keys-migration.test.d.ts +2 -0
  187. package/dist/wardmote-session-config-keys-migration.test.d.ts.map +1 -0
  188. package/dist/wardmote-session-config-keys-migration.test.js +71 -0
  189. package/dist/wardmote-session-config-keys-migration.test.js.map +1 -0
  190. package/dist/wardmote-skill-slug-migration.test.d.ts +2 -0
  191. package/dist/wardmote-skill-slug-migration.test.d.ts.map +1 -0
  192. package/dist/wardmote-skill-slug-migration.test.js +128 -0
  193. package/dist/wardmote-skill-slug-migration.test.js.map +1 -0
  194. package/dist/wardmote-tool-provider-types-migration.test.d.ts +2 -0
  195. package/dist/wardmote-tool-provider-types-migration.test.d.ts.map +1 -0
  196. package/dist/wardmote-tool-provider-types-migration.test.js +92 -0
  197. package/dist/wardmote-tool-provider-types-migration.test.js.map +1 -0
  198. package/dist/wardmote-wake-context-key-migration.test.d.ts +2 -0
  199. package/dist/wardmote-wake-context-key-migration.test.d.ts.map +1 -0
  200. package/dist/wardmote-wake-context-key-migration.test.js +59 -0
  201. package/dist/wardmote-wake-context-key-migration.test.js.map +1 -0
  202. package/package.json +2 -2
@@ -55,7 +55,7 @@ async function assertConstraints(sql, row) {
55
55
  }
56
56
  describePostgres("persistent agent chat migration", () => {
57
57
  it("applies to a fresh database and enforces conversation identity and message retry uniqueness", async () => {
58
- const database = await startEmbeddedPostgresTestDatabase("paperclip-chat-migration-fresh-");
58
+ const database = await startEmbeddedPostgresTestDatabase("wardmote-chat-migration-fresh-");
59
59
  cleanups.push(database.cleanup);
60
60
  const sql = postgres(database.connectionString, { max: 1, onnotice: () => { } });
61
61
  try {
@@ -66,7 +66,7 @@ describePostgres("persistent agent chat migration", () => {
66
66
  }
67
67
  }, 30_000);
68
68
  it("replays over pre-release columns and constraints without losing history or weakening the state guard", async () => {
69
- const database = await startEmbeddedPostgresTestDatabase("paperclip-chat-migration-replay-");
69
+ const database = await startEmbeddedPostgresTestDatabase("wardmote-chat-migration-replay-");
70
70
  cleanups.push(database.cleanup);
71
71
  const sql = postgres(database.connectionString, { max: 1, onnotice: () => { } });
72
72
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"agent-chat-migration.test.js","sourceRoot":"","sources":["../src/agent-chat-migration.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent-chat-migration.test.js","sourceRoot":"","sources":["../src/agent-chat-migration.test.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,18 @@ export type BackupRetentionPolicy = {
2
2
  dailyDays: number;
3
3
  weeklyWeeks: number;
4
4
  monthlyMonths: number;
5
+ /** Keep only this many newest verified backups; null or absent keeps the daily/weekly/monthly tiers. */
6
+ maxFiles?: number | null;
7
+ };
8
+ export type BackupDiskSpace = {
9
+ freeBytes: number;
10
+ totalBytes: number;
11
+ };
12
+ export type BackupSpaceGuard = {
13
+ reservePercent: number;
14
+ reserveBytes: number;
15
+ headroomFactor?: number;
16
+ statfs?: (dir: string) => BackupDiskSpace;
5
17
  };
6
18
  export type RunDatabaseBackupOptions = {
7
19
  connectionString: string;
@@ -20,17 +32,43 @@ export type RunDatabaseBackupOptions = {
20
32
  backupEngine?: "auto" | "pg_dump" | "javascript";
21
33
  /** Optional durability gate. A rejected archive must not prune history. */
22
34
  verifyBeforePrune?: (backupFile: string) => Promise<void>;
35
+ /** Writes under `<backupDir>/pinned`, outside rotation, for release and rollback dumps. */
36
+ pinned?: boolean;
37
+ /** Deletes pinned backups older than this many days during rotated runs; null or 0 keeps them. */
38
+ pinnedMaxAgeDays?: number | null;
39
+ /** Skips the backup instead of filling the volume when free space is short. */
40
+ spaceGuard?: BackupSpaceGuard | null;
23
41
  };
24
42
  export type RunDatabaseBackupResult = {
25
43
  backupFile: string;
26
44
  sizeBytes: number;
27
45
  prunedCount: number;
46
+ /** Rotated backups that could not be read; kept, not counted toward maxFiles. */
47
+ unreadableBackups?: string[];
28
48
  };
29
49
  export type RunDatabaseRestoreOptions = {
30
50
  connectionString: string;
31
51
  backupFile: string;
32
52
  connectTimeoutSeconds?: number;
33
53
  };
54
+ export declare const DATABASE_BACKUP_FILENAME_PREFIX = "wardmote";
55
+ export declare const PINNED_BACKUP_DIRNAME = "pinned";
56
+ export declare const DATABASE_BACKUP_SKIPPED_MARKER = "backup-skipped.json";
57
+ export declare class DatabaseBackupSkippedError extends Error {
58
+ readonly backupDir: string;
59
+ readonly freeBytes: number;
60
+ readonly requiredBytes: number;
61
+ readonly code = "database_backup_skipped_low_disk";
62
+ constructor(backupDir: string, freeBytes: number, requiredBytes: number);
63
+ }
64
+ export declare function readBackupDiskSpace(dir: string): BackupDiskSpace;
65
+ export declare function pinnedBackupDir(backupDir: string): string;
66
+ /** Decompresses a gzip backup end to end; rejects empty, truncated or corrupt archives. */
67
+ export declare function verifyGzipBackup(file: string): Promise<void>;
68
+ /** Deletes partial backups left by a killed backup process; returns the number removed. */
69
+ export declare function removeAbandonedPartialBackups(backupDir: string, nowMs?: number): number;
70
+ /** Renames backups written under the retired filename prefix so retention keeps pruning them; idempotent. */
71
+ export declare function adoptRetiredBackupFilenames(backupDir: string): number;
34
72
  export declare function createBufferedTextFileWriter(filePath: string, maxBufferedBytes?: number): {
35
73
  emit(line: string): void;
36
74
  drain(): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"backup-lib.d.ts","sourceRoot":"","sources":["../src/backup-lib.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,qBAAqB,GAAG;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,qBAAqB,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC1C,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,YAAY,CAAC;IACjD,2EAA2E;IAC3E,iBAAiB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3D,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC,CAAC;AAmZF,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,MAAM,EAAE,gBAAgB,SAAoC;IA8B/G,IAAI,OAAO,MAAM;IAUX,KAAK;IAOL,QAAQ,QAAQ,MAAM,GAAG,MAAM;IAS/B,KAAK;IAQL,KAAK;EAgBd;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,wBAAwB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CA8iBxG;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoCvF;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,uBAAuB,GAAG,MAAM,CAIlF"}
1
+ {"version":3,"file":"backup-lib.d.ts","sourceRoot":"","sources":["../src/backup-lib.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,9 @@
1
- import { createReadStream, createWriteStream, existsSync, mkdirSync, readdirSync, statSync, unlinkSync } from "node:fs";
2
- import { basename, resolve } from "node:path";
1
+ import { createReadStream, createWriteStream, existsSync, mkdirSync, readdirSync, renameSync, rmSync, statfsSync, statSync, unlinkSync, writeFileSync } from "node:fs";
2
+ import { basename, join, resolve } from "node:path";
3
3
  import { createInterface } from "node:readline";
4
4
  import { spawn } from "node:child_process";
5
5
  import { open as openFile } from "node:fs/promises";
6
+ import { Writable } from "node:stream";
6
7
  import { pipeline } from "node:stream/promises";
7
8
  import { createGunzip, createGzip } from "node:zlib";
8
9
  import postgres from "postgres";
@@ -47,6 +48,225 @@ function monthlyRetentionCutoff(nowMs, monthlyMonths) {
47
48
  const now = new Date(nowMs);
48
49
  return Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - months, 1);
49
50
  }
51
+ export const DATABASE_BACKUP_FILENAME_PREFIX = "wardmote";
52
+ export const PINNED_BACKUP_DIRNAME = "pinned";
53
+ export const DATABASE_BACKUP_SKIPPED_MARKER = "backup-skipped.json";
54
+ const DEFAULT_BACKUP_HEADROOM_FACTOR = 1.5;
55
+ const DAY_MS = 24 * 60 * 60 * 1000;
56
+ export class DatabaseBackupSkippedError extends Error {
57
+ backupDir;
58
+ freeBytes;
59
+ requiredBytes;
60
+ code = "database_backup_skipped_low_disk";
61
+ constructor(backupDir, freeBytes, requiredBytes) {
62
+ super(`Database backup skipped: ${formatBackupSize(freeBytes)} free on the backup volume, ${formatBackupSize(requiredBytes)} required (last backup x${DEFAULT_BACKUP_HEADROOM_FACTOR} plus reserve).`);
63
+ this.backupDir = backupDir;
64
+ this.freeBytes = freeBytes;
65
+ this.requiredBytes = requiredBytes;
66
+ this.name = "DatabaseBackupSkippedError";
67
+ }
68
+ }
69
+ export function readBackupDiskSpace(dir) {
70
+ const stats = statfsSync(dir);
71
+ return { freeBytes: stats.bavail * stats.bsize, totalBytes: stats.blocks * stats.bsize };
72
+ }
73
+ export function pinnedBackupDir(backupDir) {
74
+ return join(backupDir, PINNED_BACKUP_DIRNAME);
75
+ }
76
+ const verifiedBackups = new Map();
77
+ function backupIdentity(file) {
78
+ const stats = statSync(file);
79
+ return `${stats.size}:${stats.mtimeMs}`;
80
+ }
81
+ /** Decompresses a gzip backup end to end; rejects empty, truncated or corrupt archives. */
82
+ export async function verifyGzipBackup(file) {
83
+ if (statSync(file).size === 0)
84
+ throw new Error(`Backup ${basename(file)} is empty`);
85
+ await pipeline(createReadStream(file, { highWaterMark: 1024 * 1024 }), createGunzip({ chunkSize: 1024 * 1024 }), new Writable({ write(_chunk, _encoding, callback) { callback(); } }));
86
+ }
87
+ function isArchiveCorruption(error) {
88
+ const code = error?.code;
89
+ return typeof code === "string" && code.startsWith("Z_");
90
+ }
91
+ async function checkBackup(file) {
92
+ const stats = statSync(file, { throwIfNoEntry: false });
93
+ if (!stats || stats.size === 0)
94
+ return "invalid";
95
+ if (!file.endsWith(".gz"))
96
+ return "valid";
97
+ if (verifiedBackups.get(file) === `${stats.size}:${stats.mtimeMs}`)
98
+ return "valid";
99
+ try {
100
+ await verifyGzipBackup(file);
101
+ }
102
+ catch (error) {
103
+ return isArchiveCorruption(error) ? "invalid" : "unreadable";
104
+ }
105
+ verifiedBackups.set(file, backupIdentity(file));
106
+ return "valid";
107
+ }
108
+ function listRotationBackups(backupDir, filenamePrefix) {
109
+ if (!existsSync(backupDir))
110
+ return [];
111
+ const entries = [];
112
+ for (const name of readdirSync(backupDir)) {
113
+ if (!name.startsWith(`${filenamePrefix}-`) || !isBackupFilename(name))
114
+ continue;
115
+ const fullPath = resolve(backupDir, name);
116
+ const stats = statSync(fullPath, { throwIfNoEntry: false });
117
+ if (!stats?.isFile())
118
+ continue;
119
+ entries.push({ fullPath, sizeBytes: stats.size, mtimeMs: stats.mtimeMs });
120
+ }
121
+ return entries.sort((a, b) => b.mtimeMs - a.mtimeMs);
122
+ }
123
+ function removeBackupFile(file) {
124
+ rmSync(file, { force: true });
125
+ verifiedBackups.delete(file);
126
+ }
127
+ async function pruneToMaxFiles(backupDir, filenamePrefix, maxFiles, newestFile) {
128
+ let kept = 1;
129
+ let removed = 0;
130
+ const unreadable = [];
131
+ for (const entry of listRotationBackups(backupDir, filenamePrefix)) {
132
+ if (entry.fullPath === newestFile)
133
+ continue;
134
+ const check = await checkBackup(entry.fullPath);
135
+ if (check === "unreadable") {
136
+ unreadable.push(entry.fullPath);
137
+ continue;
138
+ }
139
+ if (kept < maxFiles && check === "valid") {
140
+ kept += 1;
141
+ continue;
142
+ }
143
+ removeBackupFile(entry.fullPath);
144
+ removed += 1;
145
+ }
146
+ return { removed, unreadable };
147
+ }
148
+ function prunePinnedBackups(backupDir, maxAgeDays, nowMs = Date.now()) {
149
+ if (!maxAgeDays || maxAgeDays <= 0)
150
+ return 0;
151
+ const dir = pinnedBackupDir(backupDir);
152
+ if (!existsSync(dir))
153
+ return 0;
154
+ let removed = 0;
155
+ for (const name of readdirSync(dir)) {
156
+ if (!isBackupFilename(name))
157
+ continue;
158
+ const fullPath = resolve(dir, name);
159
+ const stats = statSync(fullPath, { throwIfNoEntry: false });
160
+ if (!stats?.isFile() || nowMs - stats.mtimeMs < maxAgeDays * DAY_MS)
161
+ continue;
162
+ removeBackupFile(fullPath);
163
+ removed += 1;
164
+ }
165
+ return removed;
166
+ }
167
+ function backupReserveBytes(guard, totalBytes) {
168
+ return Math.max(guard.reserveBytes, Math.ceil((totalBytes * guard.reservePercent) / 100));
169
+ }
170
+ /** Frees room for one more backup by dropping the oldest rotated files, never the newest valid one; otherwise records a skip and throws. */
171
+ async function ensureBackupSpace(input) {
172
+ const { guard } = input;
173
+ const statfs = guard.statfs ?? readBackupDiskSpace;
174
+ const existing = listRotationBackups(input.backupDir, input.filenamePrefix);
175
+ const lastSize = existing.find((entry) => entry.sizeBytes > 0)?.sizeBytes ?? 0;
176
+ const space = statfs(input.targetDir);
177
+ const requiredBytes = Math.ceil(lastSize * (guard.headroomFactor ?? DEFAULT_BACKUP_HEADROOM_FACTOR)) +
178
+ backupReserveBytes(guard, space.totalBytes);
179
+ if (space.freeBytes >= requiredBytes)
180
+ return 0;
181
+ if (input.rotate) {
182
+ const invalid = [];
183
+ const valid = [];
184
+ for (const entry of existing) {
185
+ const check = await checkBackup(entry.fullPath);
186
+ if (check === "valid")
187
+ valid.push(entry);
188
+ else if (check === "invalid")
189
+ invalid.push(entry);
190
+ }
191
+ const chosen = [];
192
+ let reclaimed = 0;
193
+ for (const entry of [...invalid, ...valid.slice(1).reverse()]) {
194
+ if (space.freeBytes + reclaimed >= requiredBytes)
195
+ break;
196
+ chosen.push(entry);
197
+ reclaimed += entry.sizeBytes;
198
+ }
199
+ if (space.freeBytes + reclaimed >= requiredBytes) {
200
+ for (const entry of chosen)
201
+ removeBackupFile(entry.fullPath);
202
+ return chosen.length;
203
+ }
204
+ writeFileSync(join(input.backupDir, DATABASE_BACKUP_SKIPPED_MARKER), JSON.stringify({
205
+ skippedAt: new Date().toISOString(),
206
+ freeBytes: space.freeBytes,
207
+ requiredBytes,
208
+ totalBytes: space.totalBytes,
209
+ }));
210
+ }
211
+ throw new DatabaseBackupSkippedError(input.backupDir, space.freeBytes, requiredBytes);
212
+ }
213
+ const RETIRED_BACKUP_FILENAME_PREFIX = "paperclip";
214
+ function isBackupFilename(name) {
215
+ return name.endsWith(".sql") || name.endsWith(".sql.gz");
216
+ }
217
+ const PARTIAL_BACKUP_PATTERN = /\.(\d+)\.partial$/;
218
+ const ABANDONED_PARTIAL_BACKUP_AGE_MS = 24 * 60 * 60 * 1000;
219
+ const activePartialBackupFiles = new Set();
220
+ function partialBackupPath(finalPath) {
221
+ return `${finalPath}.${process.pid}.partial`;
222
+ }
223
+ function isProcessAlive(pid) {
224
+ try {
225
+ process.kill(pid, 0);
226
+ return true;
227
+ }
228
+ catch (error) {
229
+ return error.code === "EPERM";
230
+ }
231
+ }
232
+ /** Deletes partial backups left by a killed backup process; returns the number removed. */
233
+ export function removeAbandonedPartialBackups(backupDir, nowMs = Date.now()) {
234
+ if (!existsSync(backupDir))
235
+ return 0;
236
+ let removed = 0;
237
+ for (const name of readdirSync(backupDir)) {
238
+ const match = PARTIAL_BACKUP_PATTERN.exec(name);
239
+ if (!match || !isBackupFilename(name.slice(0, match.index)))
240
+ continue;
241
+ const fullPath = resolve(backupDir, name);
242
+ const pid = Number(match[1]);
243
+ const ownerGone = pid === process.pid ? !activePartialBackupFiles.has(fullPath) : !isProcessAlive(pid);
244
+ if (!ownerGone) {
245
+ const mtimeMs = statSync(fullPath, { throwIfNoEntry: false })?.mtimeMs;
246
+ if (mtimeMs === undefined || nowMs - mtimeMs < ABANDONED_PARTIAL_BACKUP_AGE_MS)
247
+ continue;
248
+ }
249
+ rmSync(fullPath, { force: true });
250
+ removed += 1;
251
+ }
252
+ return removed;
253
+ }
254
+ /** Renames backups written under the retired filename prefix so retention keeps pruning them; idempotent. */
255
+ export function adoptRetiredBackupFilenames(backupDir) {
256
+ if (!existsSync(backupDir))
257
+ return 0;
258
+ let renamed = 0;
259
+ for (const name of readdirSync(backupDir)) {
260
+ if (!name.startsWith(`${RETIRED_BACKUP_FILENAME_PREFIX}-`) || !isBackupFilename(name))
261
+ continue;
262
+ const next = resolve(backupDir, `${DATABASE_BACKUP_FILENAME_PREFIX}${name.slice(RETIRED_BACKUP_FILENAME_PREFIX.length)}`);
263
+ if (existsSync(next))
264
+ continue;
265
+ renameSync(resolve(backupDir, name), next);
266
+ renamed += 1;
267
+ }
268
+ return renamed;
269
+ }
50
270
  /**
51
271
  * Tiered backup pruning:
52
272
  * - Daily tier: keep ALL backups from the last `dailyDays` days
@@ -65,7 +285,7 @@ function pruneOldBackups(backupDir, retention, filenamePrefix) {
65
285
  for (const name of readdirSync(backupDir)) {
66
286
  if (!name.startsWith(`${filenamePrefix}-`))
67
287
  continue;
68
- if (!name.endsWith(".sql") && !name.endsWith(".sql.gz"))
288
+ if (!isBackupFilename(name))
69
289
  continue;
70
290
  const fullPath = resolve(backupDir, name);
71
291
  const stat = statSync(fullPath);
@@ -419,7 +639,7 @@ export function createBufferedTextFileWriter(filePath, maxBufferedBytes = DEFAUL
419
639
  };
420
640
  }
421
641
  export async function runDatabaseBackup(opts) {
422
- const filenamePrefix = opts.filenamePrefix ?? "paperclip";
642
+ const filenamePrefix = opts.filenamePrefix ?? DATABASE_BACKUP_FILENAME_PREFIX;
423
643
  const retention = opts.retention;
424
644
  const connectTimeout = Math.max(1, Math.trunc(opts.connectTimeoutSeconds ?? 5));
425
645
  const backupEngine = opts.backupEngine ?? "auto";
@@ -435,13 +655,57 @@ export async function runDatabaseBackup(opts) {
435
655
  sqlClosed = true;
436
656
  await sql.end();
437
657
  };
438
- mkdirSync(opts.backupDir, { recursive: true });
658
+ const targetDir = opts.pinned ? pinnedBackupDir(opts.backupDir) : opts.backupDir;
659
+ mkdirSync(targetDir, { recursive: true });
660
+ if (filenamePrefix === DATABASE_BACKUP_FILENAME_PREFIX)
661
+ adoptRetiredBackupFilenames(opts.backupDir);
439
662
  // Initial and final sleep checks can run within the same second. A failed
440
663
  // second checkpoint must never overwrite the first verified recovery point.
441
664
  const checkpointSuffix = opts.verifyBeforePrune ? `-${randomUUID()}` : "";
442
- const sqlFile = resolve(opts.backupDir, `${filenamePrefix}-${timestamp()}${checkpointSuffix}.sql`);
443
- const backupFile = `${sqlFile}.gz`;
665
+ removeAbandonedPartialBackups(targetDir);
666
+ let freedForSpace = 0;
667
+ if (opts.spaceGuard) {
668
+ try {
669
+ freedForSpace = await ensureBackupSpace({
670
+ backupDir: opts.backupDir,
671
+ targetDir,
672
+ filenamePrefix,
673
+ guard: opts.spaceGuard,
674
+ rotate: !opts.pinned,
675
+ });
676
+ }
677
+ catch (error) {
678
+ await closeSql();
679
+ throw error;
680
+ }
681
+ }
682
+ const finalSqlFile = resolve(targetDir, `${filenamePrefix}-${timestamp()}${checkpointSuffix}.sql`);
683
+ const backupFile = `${finalSqlFile}.gz`;
684
+ const sqlFile = partialBackupPath(finalSqlFile);
685
+ const partialBackupFile = partialBackupPath(backupFile);
686
+ activePartialBackupFiles.add(sqlFile);
687
+ activePartialBackupFiles.add(partialBackupFile);
444
688
  const writer = createBufferedTextFileWriter(sqlFile);
689
+ const finalizeBackup = async () => {
690
+ await verifyGzipBackup(partialBackupFile);
691
+ renameSync(partialBackupFile, backupFile);
692
+ verifiedBackups.set(backupFile, backupIdentity(backupFile));
693
+ const sizeBytes = statSync(backupFile).size;
694
+ await opts.verifyBeforePrune?.(backupFile);
695
+ if (opts.pinned)
696
+ return { backupFile, sizeBytes, prunedCount: freedForSpace };
697
+ const rotated = retention.maxFiles && retention.maxFiles > 0
698
+ ? await pruneToMaxFiles(opts.backupDir, filenamePrefix, retention.maxFiles, backupFile)
699
+ : { removed: pruneOldBackups(opts.backupDir, retention, filenamePrefix), unreadable: [] };
700
+ const prunedCount = freedForSpace + rotated.removed + prunePinnedBackups(opts.backupDir, opts.pinnedMaxAgeDays);
701
+ rmSync(join(opts.backupDir, DATABASE_BACKUP_SKIPPED_MARKER), { force: true });
702
+ return {
703
+ backupFile,
704
+ sizeBytes,
705
+ prunedCount,
706
+ ...(rotated.unreadable.length > 0 ? { unreadableBackups: rotated.unreadable } : {}),
707
+ };
708
+ };
445
709
  try {
446
710
  if (backupEngine === "pg_dump" || (backupEngine === "auto" && canUsePgDump)) {
447
711
  await sql `SELECT 1`;
@@ -450,15 +714,15 @@ export async function runDatabaseBackup(opts) {
450
714
  await closeSql();
451
715
  await runPgDumpBackup({
452
716
  connectionString: opts.connectionString,
453
- backupFile,
717
+ backupFile: partialBackupFile,
454
718
  connectTimeout,
455
719
  });
456
720
  dumped = true;
457
721
  }
458
722
  catch (error) {
459
- if (existsSync(backupFile)) {
723
+ if (existsSync(partialBackupFile)) {
460
724
  try {
461
- unlinkSync(backupFile);
725
+ unlinkSync(partialBackupFile);
462
726
  }
463
727
  catch { /* ignore */ }
464
728
  }
@@ -474,10 +738,7 @@ export async function runDatabaseBackup(opts) {
474
738
  // pg_dump succeeds, archive verification or persistence failures
475
739
  // must retain their original error and must not reuse a closed writer.
476
740
  await writer.abort();
477
- const sizeBytes = statSync(backupFile).size;
478
- await opts.verifyBeforePrune?.(backupFile);
479
- const prunedCount = pruneOldBackups(opts.backupDir, retention, filenamePrefix);
480
- return { backupFile, sizeBytes, prunedCount };
741
+ return await finalizeBackup();
481
742
  }
482
743
  }
483
744
  await sql `SELECT 1`;
@@ -489,7 +750,7 @@ export async function runDatabaseBackup(opts) {
489
750
  const emitStatementBoundary = () => {
490
751
  emit(STATEMENT_BREAKPOINT);
491
752
  };
492
- emit("-- Paperclip database backup");
753
+ emit("-- Wardmote database backup");
493
754
  emit(`-- Created: ${new Date().toISOString()}`);
494
755
  emit("");
495
756
  emitStatement("BEGIN;");
@@ -884,35 +1145,26 @@ export async function runDatabaseBackup(opts) {
884
1145
  await writer.close();
885
1146
  // Compress the SQL file with gzip
886
1147
  const sqlReadStream = createReadStream(sqlFile);
887
- const gzWriteStream = createWriteStream(backupFile);
1148
+ const gzWriteStream = createWriteStream(partialBackupFile);
888
1149
  await pipeline(sqlReadStream, createGzip(), gzWriteStream);
889
1150
  unlinkSync(sqlFile);
890
- const sizeBytes = statSync(backupFile).size;
891
- await opts.verifyBeforePrune?.(backupFile);
892
- const prunedCount = pruneOldBackups(opts.backupDir, retention, filenamePrefix);
893
- return {
894
- backupFile,
895
- sizeBytes,
896
- prunedCount,
897
- };
1151
+ return await finalizeBackup();
898
1152
  }
899
1153
  catch (error) {
900
1154
  await writer.abort();
901
- if (existsSync(backupFile)) {
902
- try {
903
- unlinkSync(backupFile);
904
- }
905
- catch { /* ignore */ }
906
- }
907
- if (existsSync(sqlFile)) {
908
- try {
909
- unlinkSync(sqlFile);
1155
+ for (const file of [backupFile, partialBackupFile, sqlFile]) {
1156
+ if (existsSync(file)) {
1157
+ try {
1158
+ unlinkSync(file);
1159
+ }
1160
+ catch { /* ignore */ }
910
1161
  }
911
- catch { /* ignore */ }
912
1162
  }
913
1163
  throw error;
914
1164
  }
915
1165
  finally {
1166
+ activePartialBackupFiles.delete(sqlFile);
1167
+ activePartialBackupFiles.delete(partialBackupFile);
916
1168
  await closeSql();
917
1169
  }
918
1170
  }
@@ -953,6 +1205,9 @@ export async function runDatabaseRestore(opts) {
953
1205
  export function formatDatabaseBackupResult(result) {
954
1206
  const size = formatBackupSize(result.sizeBytes);
955
1207
  const pruned = result.prunedCount > 0 ? `; pruned ${result.prunedCount} old backup(s)` : "";
956
- return `${result.backupFile} (${size}${pruned})`;
1208
+ const unreadable = result.unreadableBackups?.length
1209
+ ? `; kept ${result.unreadableBackups.length} unreadable backup(s)`
1210
+ : "";
1211
+ return `${result.backupFile} (${size}${pruned}${unreadable})`;
957
1212
  }
958
1213
  //# sourceMappingURL=backup-lib.js.map