@rebasepro/server 0.23.0 → 0.24.1-canary.g19596ef

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 (287) hide show
  1. package/README.md +28 -3
  2. package/bin/rebase-server.js +4 -2
  3. package/dist/GCSStorageController-DEga1ULx.js +402 -0
  4. package/dist/GCSStorageController-DEga1ULx.js.map +1 -0
  5. package/dist/S3StorageController-Btx-6bJ5.js +480 -0
  6. package/dist/S3StorageController-Btx-6bJ5.js.map +1 -0
  7. package/dist/api/ast-schema-editor.d.ts +92 -1
  8. package/dist/api/errors.d.ts +41 -0
  9. package/dist/api/live-schema-routes.d.ts +64 -10
  10. package/dist/api/logs-routes.d.ts +39 -1
  11. package/dist/api/mount-path.d.ts +30 -0
  12. package/dist/api/mount.d.ts +6 -13
  13. package/dist/api/openapi-generator.d.ts +17 -0
  14. package/dist/api/rest/api-generator.d.ts +97 -11
  15. package/dist/api/rest/auth-collection-writes.d.ts +28 -21
  16. package/dist/api/rest/field-access-query.d.ts +28 -8
  17. package/dist/api/rest/field-ops.d.ts +19 -7
  18. package/dist/api/rest/query-body.d.ts +10 -0
  19. package/dist/api/rest/write-validation.d.ts +9 -7
  20. package/dist/api/schema-editor-routes.d.ts +10 -1
  21. package/dist/api/sql-export-routes.d.ts +8 -0
  22. package/dist/api/types.d.ts +52 -1
  23. package/dist/{ast-schema-editor-Mvr50v_S.js → ast-schema-editor-mCeSC03x.js} +310 -12
  24. package/dist/ast-schema-editor-mCeSC03x.js.map +1 -0
  25. package/dist/auth/access.d.ts +156 -0
  26. package/dist/auth/adapter-middleware.d.ts +2 -1
  27. package/dist/auth/address-ownership.d.ts +16 -1
  28. package/dist/auth/admin-roles-route.d.ts +4 -2
  29. package/dist/auth/admin-roles.d.ts +17 -20
  30. package/dist/auth/admin-user-ops.d.ts +24 -2
  31. package/dist/auth/admin-users-route.d.ts +2 -0
  32. package/dist/auth/api-keys/api-key-middleware.d.ts +64 -56
  33. package/dist/auth/api-keys/api-key-routes.d.ts +44 -11
  34. package/dist/auth/api-keys/api-key-store.d.ts +31 -8
  35. package/dist/auth/api-keys/api-key-types.d.ts +14 -16
  36. package/dist/auth/api-keys/http-operation.d.ts +19 -0
  37. package/dist/auth/api-keys/index.d.ts +11 -11
  38. package/dist/auth/api-keys/key-grant.d.ts +41 -0
  39. package/dist/auth/api-keys/legacy-permissions.d.ts +33 -0
  40. package/dist/auth/auth-hooks.d.ts +46 -7
  41. package/dist/auth/builtin-auth-adapter.d.ts +21 -0
  42. package/dist/auth/cookie-utils.d.ts +7 -0
  43. package/dist/auth/deliverable-address.d.ts +6 -0
  44. package/dist/auth/email-change-routes.d.ts +44 -0
  45. package/dist/auth/expired-token-sweep.d.ts +71 -0
  46. package/dist/auth/impersonation.d.ts +110 -0
  47. package/dist/auth/index.d.ts +4 -2
  48. package/dist/auth/interfaces.d.ts +120 -59
  49. package/dist/auth/jwks-routes.d.ts +5 -1
  50. package/dist/auth/jwt-crypto.d.ts +7 -0
  51. package/dist/auth/jwt-keys.d.ts +4 -0
  52. package/dist/auth/jwt.d.ts +319 -157
  53. package/dist/auth/magic-link-routes.d.ts +5 -6
  54. package/dist/auth/mfa-crypto.d.ts +9 -3
  55. package/dist/auth/mfa-gate.d.ts +4 -2
  56. package/dist/auth/mfa-routes.d.ts +8 -9
  57. package/dist/auth/middleware.d.ts +26 -7
  58. package/dist/auth/otp-routes.d.ts +5 -6
  59. package/dist/auth/passwordless-signup.d.ts +27 -0
  60. package/dist/auth/platform-token.d.ts +122 -0
  61. package/dist/auth/rate-limiter.d.ts +99 -31
  62. package/dist/auth/registration-policy.d.ts +17 -0
  63. package/dist/auth/require-auth.d.ts +1 -1
  64. package/dist/auth/routes.d.ts +61 -0
  65. package/dist/auth/scope-routes.d.ts +22 -0
  66. package/dist/auth/session-routes.d.ts +17 -6
  67. package/dist/auth/token-revocation.d.ts +56 -1
  68. package/dist/auth/user-picking.d.ts +37 -0
  69. package/dist/auth/verify-credential.d.ts +49 -0
  70. package/dist/{auth-B-GIMpDG.js → auth-CSBky1mt.js} +4564 -1929
  71. package/dist/auth-CSBky1mt.js.map +1 -0
  72. package/dist/auth_adapter-CIItCTc8.js +21 -0
  73. package/dist/auth_adapter-CIItCTc8.js.map +1 -0
  74. package/dist/backend/create-backend.d.ts +281 -0
  75. package/dist/backend/default-backend.d.ts +35 -0
  76. package/dist/backend/no-collections.d.ts +19 -0
  77. package/dist/backend/options.d.ts +912 -0
  78. package/dist/backend/scope.d.ts +23 -0
  79. package/dist/backend/services.d.ts +54 -0
  80. package/dist/backend-DTAOsLQc.js.map +1 -1
  81. package/dist/backup/backup-common.d.ts +24 -3
  82. package/dist/backup/backup-routes.d.ts +46 -5
  83. package/dist/backup/backup-schedule.d.ts +33 -0
  84. package/dist/backup/backup-storage.d.ts +16 -0
  85. package/dist/backup/index.d.ts +5 -1
  86. package/dist/backup-DBGRUVTS.js +585 -0
  87. package/dist/backup-DBGRUVTS.js.map +1 -0
  88. package/dist/boot/boot.d.ts +2 -2
  89. package/dist/boot/bundle.d.ts +19 -0
  90. package/dist/boot/driver.d.ts +3 -0
  91. package/dist/boot/env.d.ts +58 -6
  92. package/dist/boot/http-server.d.ts +22 -0
  93. package/dist/boot/options.d.ts +16 -1
  94. package/dist/boot/provision.d.ts +4 -2
  95. package/dist/boot/resource-adapters.d.ts +11 -14
  96. package/dist/boot/runtime-cors.d.ts +17 -0
  97. package/dist/boot/security-headers.d.ts +26 -0
  98. package/dist/boot/sources.d.ts +9 -0
  99. package/dist/boot/static-routing.d.ts +65 -0
  100. package/dist/collection_patch-BRu-BvDv.js +472 -0
  101. package/dist/collection_patch-BRu-BvDv.js.map +1 -0
  102. package/dist/{contract-routes-CbFjuBwa.js → contract-routes-DiFwiLg2.js} +5 -8
  103. package/dist/contract-routes-DiFwiLg2.js.map +1 -0
  104. package/dist/cron/cron-loader.d.ts +77 -18
  105. package/dist/cron/cron-routes.d.ts +1 -1
  106. package/dist/cron/cron-scheduler.d.ts +94 -23
  107. package/dist/cron/cron-store.d.ts +37 -5
  108. package/dist/cron/define-cron.d.ts +6 -2
  109. package/dist/cron/index.d.ts +3 -3
  110. package/dist/cron-loader-NHqRfKF0.js +202 -0
  111. package/dist/cron-loader-NHqRfKF0.js.map +1 -0
  112. package/dist/{cron-routes-eE8nif_b.js → cron-routes-DsH8Jwn5.js} +34 -8
  113. package/dist/cron-routes-DsH8Jwn5.js.map +1 -0
  114. package/dist/{cron-scheduler-B0pLfAix.js → cron-scheduler-D1DT0nw5.js} +349 -65
  115. package/dist/cron-scheduler-D1DT0nw5.js.map +1 -0
  116. package/dist/cron-store-DEpVstUU.js +577 -0
  117. package/dist/cron-store-DEpVstUU.js.map +1 -0
  118. package/dist/{ddl-bootstrap-C6mo0Kmz.js → ddl-bootstrap-BNqi2NZe.js} +2 -2
  119. package/dist/{ddl-bootstrap-C6mo0Kmz.js.map → ddl-bootstrap-BNqi2NZe.js.map} +1 -1
  120. package/dist/email/index.d.ts +2 -2
  121. package/dist/email/smtp-email-service.d.ts +23 -0
  122. package/dist/email/templates.d.ts +22 -0
  123. package/dist/email/types.d.ts +44 -0
  124. package/dist/env.d.ts +1 -39
  125. package/dist/{errors-DWsX4yTd.js → errors-Cqp86KnA.js} +263 -10
  126. package/dist/errors-Cqp86KnA.js.map +1 -0
  127. package/dist/filter-dialect-O1HK6Nvr.js +1628 -0
  128. package/dist/filter-dialect-O1HK6Nvr.js.map +1 -0
  129. package/dist/{function-loader-xnbDAPfa.js → function-loader-DbCSoNJj.js} +69 -39
  130. package/dist/function-loader-DbCSoNJj.js.map +1 -0
  131. package/dist/{function-routes-Chet4-lB.js → function-routes-BSTClQNa.js} +28 -15
  132. package/dist/function-routes-BSTClQNa.js.map +1 -0
  133. package/dist/functions/context.d.ts +26 -6
  134. package/dist/functions/define-function.d.ts +10 -6
  135. package/dist/functions/function-loader.d.ts +75 -18
  136. package/dist/functions/function-routes.d.ts +2 -1
  137. package/dist/functions/guards.d.ts +22 -5
  138. package/dist/functions/index.d.ts +6 -5
  139. package/dist/functions/index.js +148 -50
  140. package/dist/functions/index.js.map +1 -1
  141. package/dist/functions/internal.d.ts +2 -2
  142. package/dist/functions/proxy.d.ts +6 -2
  143. package/dist/functions/request-timeout.d.ts +7 -5
  144. package/dist/history/history-routes.d.ts +9 -0
  145. package/dist/history/retention-sweep.d.ts +81 -0
  146. package/dist/{history-recorder-B4MpJfJK.js → history-recorder-RZ1Ow4YY.js} +4 -4
  147. package/dist/{history-recorder-B4MpJfJK.js.map → history-recorder-RZ1Ow4YY.js.map} +1 -1
  148. package/dist/{history-store-BhxWOuz9.js → history-store-DPofGVDp.js} +2 -2
  149. package/dist/{history-store-BhxWOuz9.js.map → history-store-DPofGVDp.js.map} +1 -1
  150. package/dist/index.d.ts +32 -13
  151. package/dist/index.es.js +25329 -18393
  152. package/dist/index.es.js.map +1 -1
  153. package/dist/init/docs.d.ts +35 -10
  154. package/dist/init/health.d.ts +17 -2
  155. package/dist/init/process-safety.d.ts +7 -2
  156. package/dist/init/shutdown.d.ts +26 -1
  157. package/dist/init.d.ts +49 -811
  158. package/dist/internal-tables-L2t39RdT.js +136 -0
  159. package/dist/internal-tables-L2t39RdT.js.map +1 -0
  160. package/dist/jobs/job-queue.d.ts +8 -0
  161. package/dist/jobs/job-store.d.ts +24 -0
  162. package/dist/jobs/types.d.ts +25 -4
  163. package/dist/{jobs-CazMYhyy.js → jobs-8G-_agld.js} +235 -25
  164. package/dist/jobs-8G-_agld.js.map +1 -0
  165. package/dist/{jwt-DnQHNFCl.js → jwt-CBaFc34Y.js} +546 -447
  166. package/dist/jwt-CBaFc34Y.js.map +1 -0
  167. package/dist/{keys-CogCQpxG.js → keys-COzQ2aE8.js} +3 -17
  168. package/dist/{keys-CogCQpxG.js.map → keys-COzQ2aE8.js.map} +1 -1
  169. package/dist/load-failure-L5hvfDoG.js +80 -0
  170. package/dist/load-failure-L5hvfDoG.js.map +1 -0
  171. package/dist/{logger-DO2PZc4i.js → logger-Bt32sHFw.js} +41 -5
  172. package/dist/logger-Bt32sHFw.js.map +1 -0
  173. package/dist/{logs-routes-Bj4TYYUl.js → logs-routes-DQJhrf-b.js} +48 -8
  174. package/dist/logs-routes-DQJhrf-b.js.map +1 -0
  175. package/dist/mcp/consent-page.d.ts +1 -1
  176. package/dist/mcp/mcp-routes.d.ts +13 -0
  177. package/dist/mcp/mcp-tools.d.ts +23 -9
  178. package/dist/mcp/oauth-metadata.d.ts +21 -16
  179. package/dist/mcp/oauth-routes.d.ts +14 -1
  180. package/dist/metrics/index.d.ts +18 -0
  181. package/dist/metrics/pool-wait.d.ts +50 -0
  182. package/dist/offline-QNQgsPOq-B2yGeXkn.js +3305 -0
  183. package/dist/offline-QNQgsPOq-B2yGeXkn.js.map +1 -0
  184. package/dist/offline-store-Cm5sAX81-CALiO4Gn.js +1915 -0
  185. package/dist/offline-store-Cm5sAX81-CALiO4Gn.js.map +1 -0
  186. package/dist/{openapi-generator-O_O24MAT.js → openapi-generator-CeGW2dLC.js} +455 -80
  187. package/dist/openapi-generator-CeGW2dLC.js.map +1 -0
  188. package/dist/policy-PdzX-Opd.js +121 -0
  189. package/dist/policy-PdzX-Opd.js.map +1 -0
  190. package/dist/project_manifest-R6y_KHms.js +59 -0
  191. package/dist/project_manifest-R6y_KHms.js.map +1 -0
  192. package/dist/{proxy-Czngl3p9.js → proxy-BEKbsil6.js} +5 -4
  193. package/dist/proxy-BEKbsil6.js.map +1 -0
  194. package/dist/{query-parser-DGRVFNM3.js → query-parser-ECxP0nDw.js} +497 -52
  195. package/dist/query-parser-ECxP0nDw.js.map +1 -0
  196. package/dist/{request-timeout-C_4C2BeR.js → request-timeout-BiRelY-U.js} +17 -10
  197. package/dist/request-timeout-BiRelY-U.js.map +1 -0
  198. package/dist/resumable-upload-threshold-CKv-Ry0l.js +24 -0
  199. package/dist/resumable-upload-threshold-CKv-Ry0l.js.map +1 -0
  200. package/dist/schema-edit/apply-schema-change.d.ts +73 -3
  201. package/dist/schema-edit/local-source.d.ts +29 -0
  202. package/dist/schema-edit/project-root.d.ts +3 -2
  203. package/dist/schema-edit/remote-source.d.ts +20 -5
  204. package/dist/schema-edit/source-editor.d.ts +28 -0
  205. package/dist/schema-editor-routes-DRXBAbZy.js +136 -0
  206. package/dist/schema-editor-routes-DRXBAbZy.js.map +1 -0
  207. package/dist/serve-spa.d.ts +58 -0
  208. package/dist/services/outbound-post.d.ts +50 -0
  209. package/dist/services/outbound-url-guard.d.ts +34 -5
  210. package/dist/services/routed-realtime-service.d.ts +16 -0
  211. package/dist/services/webhook-service.d.ts +66 -14
  212. package/dist/singleton.d.ts +9 -5
  213. package/dist/soft-delete-params-CBryZXMq.js +668 -0
  214. package/dist/soft-delete-params-CBryZXMq.js.map +1 -0
  215. package/dist/sql-export-routes-D8ZaRvoR.js +240 -0
  216. package/dist/sql-export-routes-D8ZaRvoR.js.map +1 -0
  217. package/dist/{src-vkcwKXbT.js → src-D5Y3N-S4.js} +440 -20
  218. package/dist/src-D5Y3N-S4.js.map +1 -0
  219. package/dist/storage/GCSStorageController.d.ts +48 -2
  220. package/dist/storage/LocalStorageController.d.ts +69 -2
  221. package/dist/storage/S3StorageController.d.ts +39 -3
  222. package/dist/storage/image-transform.d.ts +15 -0
  223. package/dist/storage/index.d.ts +2 -2
  224. package/dist/storage/maintenance-routes.d.ts +23 -0
  225. package/dist/storage/maintenance.d.ts +126 -0
  226. package/dist/storage/property-limits.d.ts +41 -6
  227. package/dist/storage/rendition-cache.d.ts +9 -11
  228. package/dist/storage/request-keys.d.ts +15 -0
  229. package/dist/storage/request-principals.d.ts +30 -0
  230. package/dist/storage/requested-object.d.ts +74 -0
  231. package/dist/storage/routes.d.ts +52 -14
  232. package/dist/storage/source-variables.d.ts +10 -0
  233. package/dist/storage/streams.d.ts +42 -0
  234. package/dist/storage/tus-handler.d.ts +76 -7
  235. package/dist/storage/types.d.ts +101 -7
  236. package/dist/storage/upload-validation.d.ts +13 -0
  237. package/dist/storage-resumable-VO-cMpse-C_YX71pO.js +125 -0
  238. package/dist/storage-resumable-VO-cMpse-C_YX71pO.js.map +1 -0
  239. package/dist/strings.es-BeZWAodn.js +256 -0
  240. package/dist/strings.es-BeZWAodn.js.map +1 -0
  241. package/dist/upload-validation-CVobI5xO.js +172 -0
  242. package/dist/upload-validation-CVobI5xO.js.map +1 -0
  243. package/dist/utils/byte-size.d.ts +10 -0
  244. package/dist/utils/compression.d.ts +6 -0
  245. package/dist/utils/load-failure.d.ts +14 -0
  246. package/dist/utils/logger.d.ts +21 -0
  247. package/package.json +5 -5
  248. package/dist/GCSStorageController-CjrA4PMo.js +0 -242
  249. package/dist/GCSStorageController-CjrA4PMo.js.map +0 -1
  250. package/dist/S3StorageController-B6pKDNVj.js +0 -264
  251. package/dist/S3StorageController-B6pKDNVj.js.map +0 -1
  252. package/dist/admin-roles-vYdp_Pil.js +0 -36
  253. package/dist/admin-roles-vYdp_Pil.js.map +0 -1
  254. package/dist/admin_block-DxKLmdiv.js +0 -206
  255. package/dist/admin_block-DxKLmdiv.js.map +0 -1
  256. package/dist/ast-schema-editor-Mvr50v_S.js.map +0 -1
  257. package/dist/auth/api-keys/api-key-permission-guard.d.ts +0 -65
  258. package/dist/auth-B-GIMpDG.js.map +0 -1
  259. package/dist/backup-D7YR94N3.js +0 -253
  260. package/dist/backup-D7YR94N3.js.map +0 -1
  261. package/dist/contract-routes-CbFjuBwa.js.map +0 -1
  262. package/dist/cron-loader-DfTj2Hbi.js +0 -118
  263. package/dist/cron-loader-DfTj2Hbi.js.map +0 -1
  264. package/dist/cron-routes-eE8nif_b.js.map +0 -1
  265. package/dist/cron-scheduler-B0pLfAix.js.map +0 -1
  266. package/dist/cron-store-TcoGz-xS.js +0 -305
  267. package/dist/cron-store-TcoGz-xS.js.map +0 -1
  268. package/dist/dynamic-import-X40pTZUQ.js +0 -23
  269. package/dist/dynamic-import-X40pTZUQ.js.map +0 -1
  270. package/dist/errors-DWsX4yTd.js.map +0 -1
  271. package/dist/function-loader-xnbDAPfa.js.map +0 -1
  272. package/dist/function-routes-Chet4-lB.js.map +0 -1
  273. package/dist/jobs-CazMYhyy.js.map +0 -1
  274. package/dist/jwt-DnQHNFCl.js.map +0 -1
  275. package/dist/logger-DO2PZc4i.js.map +0 -1
  276. package/dist/logs-routes-Bj4TYYUl.js.map +0 -1
  277. package/dist/openapi-generator-O_O24MAT.js.map +0 -1
  278. package/dist/proxy-Czngl3p9.js.map +0 -1
  279. package/dist/query-parser-DGRVFNM3.js.map +0 -1
  280. package/dist/request-timeout-C_4C2BeR.js.map +0 -1
  281. package/dist/schema-editor-routes-C5-lh_jO.js +0 -87
  282. package/dist/schema-editor-routes-C5-lh_jO.js.map +0 -1
  283. package/dist/src-pmvW7BFx.js +0 -6688
  284. package/dist/src-pmvW7BFx.js.map +0 -1
  285. package/dist/src-vkcwKXbT.js.map +0 -1
  286. package/dist/types-BfKcm9do.js +0 -39
  287. package/dist/types-BfKcm9do.js.map +0 -1
@@ -0,0 +1,585 @@
1
+ import { createRequire as __rebaseCreateRequire } from "module";
2
+ import __rebaseProcess from "process";
3
+ globalThis.process ??= __rebaseProcess;
4
+ __rebaseCreateRequire(import.meta.url);
5
+ import { n as __exportAll } from "./rolldown-runtime-dW7B1o5h.js";
6
+ import "./src-D5Y3N-S4.js";
7
+ import { a as parseEnvBoolean, r as isAlreadyExecutingSkip } from "./cron-scheduler-D1DT0nw5.js";
8
+ import { r as logger } from "./logger-Bt32sHFw.js";
9
+ import { i as errorHandler, t as ApiError } from "./errors-Cqp86KnA.js";
10
+ import { m as jwtOf } from "./jwt-CBaFc34Y.js";
11
+ import { t as S3StorageController } from "./S3StorageController-Btx-6bJ5.js";
12
+ import { t as GCSStorageController } from "./GCSStorageController-DEga1ULx.js";
13
+ import { Hono } from "hono";
14
+ import fs from "fs";
15
+ import path from "path";
16
+ //#region src/backup/backup-common.ts
17
+ /**
18
+ * Storage-generic backup helpers used by the admin backup routes.
19
+ *
20
+ * These live in server (which owns {@link StorageController}) so the
21
+ * admin API can list backups without depending on the Postgres driver —
22
+ * `server` must not import from `server-postgres`. The `pg_dump`-side
23
+ * copies of the tiny pure parsers live in `@rebasepro/server-postgres`;
24
+ * keep the two in sync if the naming scheme ever changes.
25
+ */
26
+ /** Parse a destination string (`s3://…`, `gs://…`, or a local path). */
27
+ function parseBackupDestination(out) {
28
+ const s3 = out.match(/^s3:\/\/([^/]+)\/?(.*)$/);
29
+ if (s3) return {
30
+ kind: "s3",
31
+ bucket: s3[1],
32
+ prefix: s3[2].replace(/\/+$/, "")
33
+ };
34
+ const gcs = out.match(/^gs:\/\/([^/]+)\/?(.*)$/);
35
+ if (gcs) return {
36
+ kind: "gcs",
37
+ bucket: gcs[1],
38
+ prefix: gcs[2].replace(/\/+$/, "")
39
+ };
40
+ return {
41
+ kind: "local",
42
+ path: out
43
+ };
44
+ }
45
+ /**
46
+ * Recover the UTC creation time encoded in a `rebase-<db>-<ts>.dump` name.
47
+ * Returns `null` for names that don't match, so foreign objects are ignored.
48
+ */
49
+ function parseBackupTimestamp(fileName) {
50
+ const match = (fileName.split("/").pop() ?? fileName).match(/-(\d{8})T(\d{6})Z\.dump$/);
51
+ if (!match) return null;
52
+ const [, ymd, hms] = match;
53
+ const iso = `${ymd.slice(0, 4)}-${ymd.slice(4, 6)}-${ymd.slice(6, 8)}T${hms.slice(0, 2)}:${hms.slice(2, 4)}:${hms.slice(4, 6)}Z`;
54
+ const date = new Date(iso);
55
+ return Number.isNaN(date.getTime()) ? null : date;
56
+ }
57
+ /**
58
+ * A backup is two files: the `.dump`, and beside it a `.globals.sql` holding
59
+ * the cluster roles the dump's GRANTs and RLS policies name. Without that
60
+ * sidecar a restore into a fresh cluster stops at the first GRANT to a role
61
+ * that does not exist there. The CLI uploads, restores and prunes the two as
62
+ * a pair. This listing used to know only the `.dump`, so a backup downloaded
63
+ * from Studio arrived without its roles.
64
+ *
65
+ * The suffix mirrors `globalsFileForDump` in `@rebasepro/server-postgres`.
66
+ */
67
+ var DUMP_SUFFIX = ".dump";
68
+ var GLOBALS_SUFFIX = ".globals.sql";
69
+ function globalsKeyFor(dumpKey) {
70
+ return dumpKey.slice(0, -5) + GLOBALS_SUFFIX;
71
+ }
72
+ /** The only files the download route will serve. */
73
+ function isBackupFile(key) {
74
+ return key.endsWith(DUMP_SUFFIX) || key.endsWith(GLOBALS_SUFFIX);
75
+ }
76
+ /**
77
+ * How many pages of an object-storage listing to read before giving up — at
78
+ * the providers' 1,000 keys a page, 100,000 objects, or 50,000 backups.
79
+ */
80
+ var MAX_LIST_PAGES = 100;
81
+ /**
82
+ * Every key under a prefix, across pages.
83
+ *
84
+ * S3 and GCS answer a listing a page at a time, in ascending key order, and a
85
+ * backup's key is `rebase-<db>-<UTC timestamp>`. Reading one page therefore
86
+ * returned the OLDEST thousand objects: with retention unset and an hourly
87
+ * schedule, the newest backup the panel and `rebase db backups list` showed
88
+ * stopped advancing after about three weeks, which reads as "backups stopped".
89
+ */
90
+ async function listAllObjectKeys(storage, prefix, bucket) {
91
+ const keys = [];
92
+ let pageToken;
93
+ for (let page = 0; page < MAX_LIST_PAGES; page++) {
94
+ const result = await storage.listObjects(prefix, {
95
+ bucket,
96
+ maxResults: 1e3,
97
+ pageToken
98
+ });
99
+ for (const item of result.items) keys.push(item.fullPath);
100
+ pageToken = result.nextPageToken;
101
+ if (!pageToken) return keys;
102
+ }
103
+ logger.warn(`[backups] Stopped listing ${bucket}/${prefix} after ${MAX_LIST_PAGES} pages (${keys.length} objects). Set BACKUP_RETENTION_DAYS so old backups are pruned, or give the backups a prefix of their own.`);
104
+ return keys;
105
+ }
106
+ /**
107
+ * Whether an object key is one the backup routes may serve: a backup file, at
108
+ * the destination's prefix and nowhere else.
109
+ *
110
+ * The local branch has always been held to the backup directory; this branch
111
+ * checked only the suffix, so any `.dump` in the bucket — `uploads/x.dump`, a
112
+ * neighbouring `nightlyish/` prefix — was readable by naming it. A key is taken
113
+ * as written, so a scheme (`s3://other-bucket/…`, which S3StorageController
114
+ * would follow) or a `.`/`..`/empty segment (which a filesystem-backed or
115
+ * S3-compatible store may resolve) is refused rather than interpreted.
116
+ */
117
+ function isObjectBackupKey(dest, key) {
118
+ if (!isBackupFile(key) || /^[a-z][a-z0-9+.-]*:\/\//i.test(key)) return false;
119
+ if (key.split("/").some((segment) => segment === "" || segment === "." || segment === "..")) return false;
120
+ return dest.prefix === "" || key.startsWith(`${dest.prefix}/`);
121
+ }
122
+ /**
123
+ * List the backups at a destination as {@link BackupInfo}, newest first.
124
+ * One entry per `.dump`, with its `.globals.sql` sidecar attached as
125
+ * `globalsKey` when there is one.
126
+ */
127
+ async function listBackupObjects(dest, storage) {
128
+ if (dest.kind === "local") {
129
+ const stat = await fs.promises.stat(dest.path).catch(() => null);
130
+ if (!stat) return [];
131
+ const dir = stat.isDirectory() ? dest.path : path.dirname(dest.path);
132
+ const files = await fs.promises.readdir(dir);
133
+ const present = new Set(files);
134
+ const dumps = files.filter((f) => f.endsWith(DUMP_SUFFIX));
135
+ const stats = await mapBounded(dumps, LOCAL_STAT_CONCURRENCY, (f) => fs.promises.stat(path.join(dir, f)));
136
+ return dumps.map((f, i) => {
137
+ const full = path.join(dir, f);
138
+ const st = stats[i];
139
+ const createdAt = parseBackupTimestamp(f) ?? st.mtime;
140
+ const globals = globalsKeyFor(f);
141
+ return {
142
+ key: full,
143
+ name: f,
144
+ sizeBytes: st.size,
145
+ createdAt: createdAt.toISOString(),
146
+ destinationKind: "local",
147
+ ...present.has(globals) ? { globalsKey: path.join(dir, globals) } : {}
148
+ };
149
+ }).sort(byNewest);
150
+ }
151
+ if (!storage) throw new Error(noControllerFor(dest));
152
+ const keys = await listAllObjectKeys(storage, dest.prefix ? `${dest.prefix}/` : "", dest.bucket);
153
+ const present = new Set(keys);
154
+ return keys.filter((key) => key.endsWith(DUMP_SUFFIX)).map((key) => {
155
+ const createdAt = parseBackupTimestamp(key);
156
+ const globals = globalsKeyFor(key);
157
+ return {
158
+ key,
159
+ name: key.split("/").pop() || key,
160
+ createdAt: createdAt ? createdAt.toISOString() : void 0,
161
+ destinationKind: dest.kind,
162
+ ...present.has(globals) ? { globalsKey: globals } : {}
163
+ };
164
+ }).sort(byNewest);
165
+ }
166
+ /** Stats in flight at once while listing a local backup folder. */
167
+ var LOCAL_STAT_CONCURRENCY = 32;
168
+ /** `fn` over `items`, at most `limit` at a time; results in input order. */
169
+ async function mapBounded(items, limit, fn) {
170
+ const results = new Array(items.length);
171
+ let next = 0;
172
+ const worker = async () => {
173
+ while (next < items.length) {
174
+ const i = next++;
175
+ results[i] = await fn(items[i]);
176
+ }
177
+ };
178
+ await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
179
+ return results;
180
+ }
181
+ function noControllerFor(dest) {
182
+ return `No storage controller for ${dest.kind === "s3" ? "s3" : "gs"}://${dest.bucket}${dest.prefix ? `/${dest.prefix}` : ""}, so its backups cannot be read.`;
183
+ }
184
+ function byNewest(a, b) {
185
+ const ta = a.createdAt ? Date.parse(a.createdAt) : 0;
186
+ return (b.createdAt ? Date.parse(b.createdAt) : 0) - ta;
187
+ }
188
+ /**
189
+ * The absolute path of a local backup file, or `null` for anything the download
190
+ * route must not serve: a path outside the backup directory, a file that is not
191
+ * a backup, or one that does not exist.
192
+ */
193
+ function resolveLocalBackupFile(dest, key) {
194
+ const dir = (fs.existsSync(dest.path) ? fs.statSync(dest.path) : null)?.isDirectory() ? dest.path : path.dirname(dest.path);
195
+ const resolvedDir = path.resolve(dir);
196
+ const resolved = path.resolve(key);
197
+ if (!resolved.startsWith(resolvedDir + path.sep) || !isBackupFile(resolved)) return null;
198
+ return fs.existsSync(resolved) ? resolved : null;
199
+ }
200
+ /** Read a single backup's bytes. Guards against path traversal for local. */
201
+ async function readBackupBytes(dest, key, storage) {
202
+ if (dest.kind === "local") {
203
+ const resolved = resolveLocalBackupFile(dest, key);
204
+ if (!resolved) return null;
205
+ return {
206
+ bytes: new Uint8Array(fs.readFileSync(resolved)),
207
+ name: path.basename(resolved)
208
+ };
209
+ }
210
+ if (!storage) throw new Error(noControllerFor(dest));
211
+ if (!isObjectBackupKey(dest, key)) return null;
212
+ const file = await storage.getObject(key, dest.bucket);
213
+ if (!file) return null;
214
+ return {
215
+ bytes: new Uint8Array(await file.arrayBuffer()),
216
+ name: key.split("/").pop() || key
217
+ };
218
+ }
219
+ /**
220
+ * Whether `key` names a backup the download route would serve, without
221
+ * opening it: the same guards as {@link openBackupStream}, and the file or
222
+ * object is there. An object store without `statObject` is taken at its key —
223
+ * reading the object to find out would be the download itself.
224
+ */
225
+ async function findBackup(dest, key, storage) {
226
+ if (dest.kind === "local") {
227
+ const resolved = resolveLocalBackupFile(dest, key);
228
+ return resolved ? { name: path.basename(resolved) } : null;
229
+ }
230
+ if (!storage) throw new Error(noControllerFor(dest));
231
+ if (!isObjectBackupKey(dest, key)) return null;
232
+ if (storage.statObject && !await storage.statObject(key, dest.bucket)) return null;
233
+ return { name: key.split("/").pop() || key };
234
+ }
235
+ /**
236
+ * A single backup, opened for streaming, with its size. Same guards as
237
+ * {@link readBackupBytes}.
238
+ *
239
+ * What the download route serves. A dump is the size of the database, and
240
+ * reading it whole put all of it on the API process's heap — twice for a local
241
+ * destination (`readFileSync`, then a copy) and on top of the controller's own
242
+ * buffer for object storage — and a file over 2 GiB never downloaded at all:
243
+ * `readFileSync` throws `ERR_FS_FILE_TOO_LARGE` there.
244
+ *
245
+ * A local file is read from disk as the response is written, and so is an
246
+ * object through a controller with `getObjectStream` (the built-in S3 and GCS
247
+ * ones). A custom controller without it still arrives through
248
+ * {@link StorageController.getObject}, a buffered `File` by contract;
249
+ * streaming that avoids the second copy.
250
+ */
251
+ async function openBackupStream(dest, key, storage) {
252
+ if (dest.kind === "local") {
253
+ const resolved = resolveLocalBackupFile(dest, key);
254
+ if (!resolved) return null;
255
+ const { size } = await fs.promises.stat(resolved);
256
+ return {
257
+ stream: fileStream(resolved),
258
+ size,
259
+ name: path.basename(resolved)
260
+ };
261
+ }
262
+ if (!storage) throw new Error(noControllerFor(dest));
263
+ if (!isObjectBackupKey(dest, key)) return null;
264
+ const name = key.split("/").pop() || key;
265
+ if (storage.getObjectStream) {
266
+ const object = await storage.getObjectStream(key, { bucket: dest.bucket });
267
+ return object ? {
268
+ stream: object.stream,
269
+ size: object.size,
270
+ name
271
+ } : null;
272
+ }
273
+ const file = await storage.getObject(key, dest.bucket);
274
+ if (!file) return null;
275
+ return {
276
+ stream: file.stream(),
277
+ size: file.size,
278
+ name
279
+ };
280
+ }
281
+ /** A file on disk as a web stream, read a chunk at a time as the response is written. */
282
+ function fileStream(filePath) {
283
+ const file = fs.createReadStream(filePath);
284
+ const chunks = file[Symbol.asyncIterator]();
285
+ return new ReadableStream({
286
+ async pull(controller) {
287
+ const next = await chunks.next();
288
+ if (next.done) controller.close();
289
+ else controller.enqueue(next.value);
290
+ },
291
+ cancel() {
292
+ file.destroy();
293
+ }
294
+ });
295
+ }
296
+ //#endregion
297
+ //#region src/backup/backup-storage.ts
298
+ /**
299
+ * The storage controller that reads a backup bucket.
300
+ *
301
+ * A backup destination is its own bucket, named by `BACKUP_DESTINATION`, and
302
+ * the documented backup cron writes to it through a controller it builds from
303
+ * the `S3_*` variables (GCS: application default credentials) — as
304
+ * `rebase db backups list` does. The admin routes have to read it the same way.
305
+ * The app's file storage is not that controller: it is configured for the
306
+ * uploads bucket, it is a local directory in development, and in production
307
+ * without an uploads bucket there is none at all.
308
+ */
309
+ /** The destination `BACKUP_DESTINATION` names, or `null` when it is unset. */
310
+ function backupDestinationFromEnv(env) {
311
+ const out = env.BACKUP_DESTINATION?.trim();
312
+ return out ? parseBackupDestination(out) : null;
313
+ }
314
+ /** The destination as `BACKUP_DESTINATION` spells it. */
315
+ function describeBackupDestination(dest) {
316
+ if (dest.kind === "local") return dest.path;
317
+ return `${dest.kind === "s3" ? "s3" : "gs"}://${dest.bucket}${dest.prefix ? `/${dest.prefix}` : ""}`;
318
+ }
319
+ /**
320
+ * A controller for an object-storage destination's bucket, built from the
321
+ * environment the way the backup cron and `rebase db backups list` build
322
+ * theirs. Throws when an S3 destination has no credentials to read it with.
323
+ */
324
+ function backupStorageFromEnv(dest, env) {
325
+ if (dest.kind === "gcs") return new GCSStorageController({
326
+ type: "gcs",
327
+ bucket: dest.bucket
328
+ });
329
+ if (!env.S3_ACCESS_KEY_ID || !env.S3_SECRET_ACCESS_KEY) throw new Error("An S3 backup destination needs S3_ACCESS_KEY_ID and S3_SECRET_ACCESS_KEY in the environment — the same S3_* variables the backup cron uses.");
330
+ return new S3StorageController({
331
+ type: "s3",
332
+ bucket: dest.bucket,
333
+ region: env.S3_REGION || "auto",
334
+ accessKeyId: env.S3_ACCESS_KEY_ID,
335
+ secretAccessKey: env.S3_SECRET_ACCESS_KEY,
336
+ endpoint: env.S3_ENDPOINT,
337
+ forcePathStyle: parseEnvBoolean(env.S3_FORCE_PATH_STYLE)
338
+ });
339
+ }
340
+ //#endregion
341
+ //#region src/backup/backup-routes.ts
342
+ /** Why a destination could not be read, as the answer says it. */
343
+ function unreadable(dest, err) {
344
+ const reason = err instanceof Error ? err.message : String(err);
345
+ return ApiError.serviceUnavailable(`Cannot read the backups at ${describeBackupDestination(dest)}: ${reason}`);
346
+ }
347
+ /**
348
+ * The `purpose` of a download link's token. `verifyAccessToken` refuses
349
+ * any token with a purpose, and the storage, MFA and MCP verifiers each demand
350
+ * their own, so a link is never a session or any other credential, and none of
351
+ * those is a link.
352
+ */
353
+ var BACKUP_DOWNLOAD_PURPOSE = "backup-download";
354
+ /** The token's audience: the one route that redeems it. */
355
+ var BACKUP_DOWNLOAD_AUDIENCE = "rebase:admin/backups/download";
356
+ /** What a link grants: one backup, at one destination. */
357
+ function linkScope(dest, key) {
358
+ return `backup:${describeBackupDestination(dest)}:${key}`;
359
+ }
360
+ /**
361
+ * Whether `token` is a live link for `key` at `dest`, signed by `jwt` — the
362
+ * backend answering. Never throws: anything else is a no.
363
+ */
364
+ async function linkGrants(jwt, token, dest, key) {
365
+ if (!jwt.isConfigured()) return false;
366
+ const claims = await jwt.verifyPurposeToken(BACKUP_DOWNLOAD_PURPOSE, token).catch(() => null);
367
+ return claims?.aud === BACKUP_DOWNLOAD_AUDIENCE && claims.scope === linkScope(dest, key);
368
+ }
369
+ /** The configured destination, or the 400 every backup route answers without one. */
370
+ function destinationOf(config) {
371
+ const dest = config.getDestination();
372
+ if (!dest) throw ApiError.badRequest("Backups are not configured (set BACKUP_DESTINATION).");
373
+ return dest;
374
+ }
375
+ /** {@link BackupRoutesConfig.storageFor}, with its default, for a destination that has a controller. */
376
+ function storageResolver(config) {
377
+ const storageFor = config.storageFor ?? ((dest) => backupStorageFromEnv(dest, process.env));
378
+ /** The controller for `dest`, or none for a local one. A destination it cannot read is an error, never `[]`. */
379
+ return (dest) => {
380
+ if (dest.kind === "local") return void 0;
381
+ try {
382
+ return storageFor(dest);
383
+ } catch (err) {
384
+ throw unreadable(dest, err);
385
+ }
386
+ };
387
+ }
388
+ /** One backup's bytes as the response, streamed: a dump is the size of the database. See `openBackupStream`. */
389
+ async function serveBackup(c, dest, key, storage) {
390
+ const result = await openBackupStream(dest, key, storage);
391
+ if (!result) throw ApiError.notFound(`Backup not found: ${key}`);
392
+ c.header("Content-Type", "application/octet-stream");
393
+ c.header("Content-Length", String(result.size));
394
+ c.header("Content-Disposition", `attachment; filename="${result.name}"`);
395
+ c.header("Cache-Control", "no-store");
396
+ return c.body(result.stream);
397
+ }
398
+ /**
399
+ * `GET /download?key=…&token=…`: a download link spent in place of the bearer
400
+ * header, for the one backup it names.
401
+ *
402
+ * Mount it at the backups prefix AHEAD of every gate on the admin prefix: the
403
+ * auth adapter's own admin routers authenticate everything under `/admin`
404
+ * before the Backups gate is reached. A request with no `token` passes
405
+ * through untouched; one with a token is answered here — the backup, or a
406
+ * 401 — and never falls through to the header: a bad link is a bad link.
407
+ *
408
+ * A browser cannot put a header on a navigation, so before links the SDK
409
+ * fetched the dump and held it whole in the tab as a Blob. With a link the
410
+ * browser streams it to disk.
411
+ */
412
+ function createBackupLinkRoutes(config) {
413
+ const router = new Hono();
414
+ router.onError(errorHandler);
415
+ const storageOf = storageResolver(config);
416
+ router.get("/download", async (c, next) => {
417
+ const token = c.req.query("token");
418
+ if (token === void 0) return next();
419
+ const dest = destinationOf(config);
420
+ const key = c.req.query("key") ?? "";
421
+ if (!await linkGrants(jwtOf(c), token, dest, key)) throw ApiError.unauthenticated(`This download link is not valid for this backup, or it has expired. Links last 60 seconds: ask for a new one.`);
422
+ return serveBackup(c, dest, key, storageOf(dest));
423
+ });
424
+ return router;
425
+ }
426
+ /**
427
+ * Admin REST routes for the Backups panel.
428
+ *
429
+ * Routes (mounted under `/admin/backups`, admin-guarded by the caller):
430
+ * GET / → list available backups, and the scheduled job's last run
431
+ * GET /download → download a backup's bytes (?key=…)
432
+ * POST /download-url → a 60-second link to one backup, `{ key }` → {@link BackupDownloadLink}
433
+ *
434
+ * The link is spent at `GET /download?key=…&token=…`, which
435
+ * {@link createBackupLinkRoutes} answers ahead of the gate.
436
+ */
437
+ function createBackupRoutes(config) {
438
+ const router = new Hono();
439
+ router.onError(errorHandler);
440
+ const storageOf = storageResolver(config);
441
+ router.get("/", async (c) => {
442
+ const schedule = config.getSchedule ? await config.getSchedule() : null;
443
+ const dest = config.getDestination();
444
+ if (!dest) return c.json({
445
+ backups: [],
446
+ destinationKind: "local",
447
+ configured: false,
448
+ schedule
449
+ });
450
+ const backups = await listBackupObjects(dest, storageOf(dest)).catch((err) => {
451
+ throw unreadable(dest, err);
452
+ });
453
+ const localDiskOfThisProcess = dest.kind === "local" && config.scheduledElsewhere === true && schedule !== null;
454
+ return c.json({
455
+ backups,
456
+ destinationKind: dest.kind,
457
+ configured: true,
458
+ schedule,
459
+ ...localDiskOfThisProcess ? { localDiskOfThisProcess } : {}
460
+ });
461
+ });
462
+ router.get("/download", async (c) => {
463
+ const dest = destinationOf(config);
464
+ const key = c.req.query("key");
465
+ if (!key) throw ApiError.badRequest("Missing 'key' query parameter.");
466
+ return serveBackup(c, dest, key, storageOf(dest));
467
+ });
468
+ router.post("/download-url", async (c) => {
469
+ const dest = destinationOf(config);
470
+ const body = await c.req.json().catch(() => null);
471
+ const key = typeof body === "object" && body !== null && "key" in body ? body.key : void 0;
472
+ if (typeof key !== "string" || !key) throw ApiError.badRequest("Send the backup's key: { \"key\": \"…\" }.");
473
+ if (!await findBackup(dest, key, storageOf(dest))) throw ApiError.notFound(`Backup not found: ${key}`);
474
+ const jwt = jwtOf(c);
475
+ if (!jwt.isConfigured()) throw ApiError.serviceUnavailable("Download links are signed with the JWT secret, and this server has none (auth.jwtSecret). Download through GET /download with the Authorization header instead.");
476
+ const token = await jwt.signPurposeToken(BACKUP_DOWNLOAD_PURPOSE, {
477
+ aud: BACKUP_DOWNLOAD_AUDIENCE,
478
+ scope: linkScope(dest, key)
479
+ }, 60);
480
+ const download = new URL("download", c.req.url).pathname;
481
+ return c.json({
482
+ url: `${download}?key=${encodeURIComponent(key)}&token=${encodeURIComponent(token)}`,
483
+ token,
484
+ expiresIn: 60
485
+ });
486
+ });
487
+ return router;
488
+ }
489
+ //#endregion
490
+ //#region src/backup/backup-schedule.ts
491
+ /**
492
+ * The mark `createBackupCron` (in `@rebasepro/server-postgres`) puts on the
493
+ * definition it returns.
494
+ *
495
+ * A registry symbol, so it is the same value across two installed copies of
496
+ * either package, and neither package has to import the other: `server` must
497
+ * not depend on `server-postgres`. The job cannot be recognised any other way —
498
+ * its id is the cron file's name and its display name is configurable.
499
+ */
500
+ var BACKUP_CRON_MARK = Symbol.for("rebase.backupCron");
501
+ /** Whether a cron definition is the scheduled backup. */
502
+ function isBackupCronDefinition(definition) {
503
+ return Reflect.get(definition, BACKUP_CRON_MARK) === true;
504
+ }
505
+ /**
506
+ * How many recent log entries to look through for the last scheduled run. A
507
+ * skip — a slot that found the previous run still going — is logged as a
508
+ * success with nothing done, and must not stand in for the run it skipped; nor
509
+ * may a run someone started by hand, which is not the schedule's.
510
+ */
511
+ var RECENT_RUNS = 50;
512
+ /** A log entry as the panel reports a run. */
513
+ function outcomeOf(entry) {
514
+ return {
515
+ startedAt: entry.startedAt,
516
+ finishedAt: entry.finishedAt,
517
+ success: entry.success,
518
+ ...entry.success || !entry.error ? {} : { error: entry.error },
519
+ manual: entry.manual === true
520
+ };
521
+ }
522
+ /**
523
+ * The scheduled backup job and its last run, or `null` when the deployment
524
+ * has none — registered or refused.
525
+ */
526
+ async function readBackupSchedule(source) {
527
+ const [jobId] = source.jobIdsWhere(isBackupCronDefinition);
528
+ if (jobId === void 0) {
529
+ const [refused] = source.rejectedJobsWhere(isBackupCronDefinition);
530
+ return refused ? {
531
+ jobId: refused.id,
532
+ name: refused.name,
533
+ schedule: refused.schedule,
534
+ enabled: false,
535
+ refused: refused.reason
536
+ } : null;
537
+ }
538
+ const job = await source.fetchJob(jobId);
539
+ if (!job) return null;
540
+ const disabledInCode = !job.enabled && source.jobIdsWhere((definition) => isBackupCronDefinition(definition) && definition.enabled === false).includes(jobId);
541
+ let recent;
542
+ let historyError;
543
+ try {
544
+ recent = await source.getJobLogsFromDb(jobId, RECENT_RUNS);
545
+ } catch (err) {
546
+ logger.error(`[backups] Could not read the run history of the backup job "${jobId}"`, { error: err });
547
+ recent = [];
548
+ historyError = "The backup job's run history could not be read. The server log has the reason.";
549
+ }
550
+ const runs = recent.filter((entry) => !isAlreadyExecutingSkip(entry));
551
+ const lastScheduled = runs.find((entry) => entry.manual !== true);
552
+ const lastManual = runs.find((entry) => entry.manual === true);
553
+ const manualSince = lastManual && (!lastScheduled || lastManual.startedAt > lastScheduled.startedAt) ? lastManual : void 0;
554
+ return {
555
+ jobId,
556
+ name: job.name,
557
+ schedule: job.schedule,
558
+ enabled: job.enabled,
559
+ ...disabledInCode ? { disabledInCode } : {},
560
+ ...job.nextRunAt ? { nextRunAt: job.nextRunAt } : {},
561
+ ...historyError ? { historyError } : {},
562
+ ...lastScheduled ? { lastRun: outcomeOf(lastScheduled) } : {},
563
+ ...manualSince ? { lastManualRun: outcomeOf(manualSince) } : {}
564
+ };
565
+ }
566
+ //#endregion
567
+ //#region src/backup/index.ts
568
+ var backup_exports = /* @__PURE__ */ __exportAll({
569
+ BACKUP_DOWNLOAD_LINK_TTL_SECONDS: () => 60,
570
+ backupDestinationFromEnv: () => backupDestinationFromEnv,
571
+ createBackupLinkRoutes: () => createBackupLinkRoutes,
572
+ createBackupRoutes: () => createBackupRoutes,
573
+ findBackup: () => findBackup,
574
+ listAllObjectKeys: () => listAllObjectKeys,
575
+ listBackupObjects: () => listBackupObjects,
576
+ openBackupStream: () => openBackupStream,
577
+ parseBackupDestination: () => parseBackupDestination,
578
+ parseBackupTimestamp: () => parseBackupTimestamp,
579
+ readBackupBytes: () => readBackupBytes,
580
+ readBackupSchedule: () => readBackupSchedule
581
+ });
582
+ //#endregion
583
+ export { listBackupObjects as a, readBackupBytes as c, backupStorageFromEnv as i, createBackupLinkRoutes as n, parseBackupDestination as o, createBackupRoutes as r, parseBackupTimestamp as s, backup_exports as t };
584
+
585
+ //# sourceMappingURL=backup-DBGRUVTS.js.map