@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
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Local filesystem storage controller
3
3
  */
4
- import { StorageController, LocalStorageConfig } from "./types.js";
4
+ import { StorageController, LocalStorageConfig, type StoredObjectInfo, type StoredObjectRange, type PutObjectOptions, type StoredObjectStream } from "./types.js";
5
5
  import { UploadFileProps, UploadFileResult, DownloadConfig, StorageListResult } from "@rebasepro/types";
6
6
  /**
7
7
  * Bucket used when a call names none.
@@ -10,6 +10,22 @@ import { UploadFileProps, UploadFileResult, DownloadConfig, StorageListResult }
10
10
  * bare key lives.
11
11
  */
12
12
  export declare const DEFAULT_BUCKET = "default";
13
+ /**
14
+ * Refuse a key that names one of local storage's own files rather than an
15
+ * object: the metadata beside an object, or a write in progress.
16
+ *
17
+ * Both live in the object namespace, beside the objects they serve. A key that
18
+ * names one used to be stored like any other, and so overwrote another
19
+ * object's metadata: uploading `a.png.metadata.json` changed the content type
20
+ * `a.png` is served with — the authorize hook asked about the one key while
21
+ * the other changed — and the uploader's own file vanished from listings and
22
+ * went with `a.png` when that was deleted. Read, it served the metadata.
23
+ *
24
+ * Local only: S3 and GCS keep metadata on the object, so every key there is
25
+ * an object. A 400, at every operation, because every one resolves its path
26
+ * through `getFullPath`.
27
+ */
28
+ export declare function refuseLocalReservedKey(key: string): void;
13
29
  /**
14
30
  * Local filesystem storage implementation
15
31
  * Stores files in a directory structure: {basePath}/{bucket}/{path}
@@ -17,8 +33,26 @@ export declare const DEFAULT_BUCKET = "default";
17
33
  export declare class LocalStorageController implements StorageController {
18
34
  private config;
19
35
  private basePath;
36
+ /**
37
+ * Folder entries read by a listing's first page, for its continuation
38
+ * pages. Keyed by the folder's absolute path (bucket included). Not
39
+ * caller-scoped, because the entries are not: every caller of this method
40
+ * gets the same names, and the routes authorize the prefix before calling
41
+ * it. Insertion-ordered, so the first key is the oldest.
42
+ */
43
+ private readonly listingSnapshots;
20
44
  constructor(config: LocalStorageConfig);
21
45
  getType(): "local";
46
+ /**
47
+ * Whether the file system under the storage root folds case — macOS and
48
+ * Windows by default — so `Avatar.png` and `avatar.png` are one file here
49
+ * and two on S3, GCS or a Linux disk.
50
+ *
51
+ * Read by writing a probe file and asking for it in the other case. The
52
+ * root is created if it is not there yet; the probe is removed. `false`
53
+ * when the root cannot be written: nothing is known.
54
+ */
55
+ isCaseInsensitive(): Promise<boolean>;
22
56
  /**
23
57
  * The bucket directories that exist under the storage root, plus `default`.
24
58
  *
@@ -63,19 +97,52 @@ export declare class LocalStorageController implements StorageController {
63
97
  * depend on it having run.
64
98
  */
65
99
  private getFullPath;
100
+ /** See {@link StorageController.maxFileSize}. */
101
+ maxFileSize(): number;
66
102
  /**
67
103
  * Validate file before upload
68
104
  */
69
105
  private validateFile;
70
- putObject({ file, key, metadata, bucket }: UploadFileProps): Promise<UploadFileResult>;
106
+ putObject({ file, key, metadata, bucket }: UploadFileProps, options?: PutObjectOptions): Promise<UploadFileResult>;
71
107
  getSignedUrl(key: string, bucket?: string): Promise<DownloadConfig>;
72
108
  getObject(key: string, bucket?: string): Promise<File | null>;
109
+ /** The file a read of `key` addresses — `getObject`'s resolution, `local://` URLs included. */
110
+ private readPath;
111
+ /** See {@link StorageController.statObject}. A `stat` and the metadata sidecar; the file is not read. */
112
+ statObject(key: string, bucket?: string): Promise<StoredObjectInfo | null>;
113
+ /**
114
+ * See {@link StorageController.getObjectStream}. The file — or only the
115
+ * requested range of it — read from disk a chunk at a time, opened on the
116
+ * first read.
117
+ */
118
+ getObjectStream(key: string, options?: {
119
+ bucket?: string;
120
+ range?: StoredObjectRange;
121
+ }): Promise<StoredObjectStream | null>;
73
122
  deleteObject(key: string, bucket?: string): Promise<void>;
74
123
  listObjects(prefix: string, options?: {
75
124
  bucket?: string;
76
125
  maxResults?: number;
77
126
  pageToken?: string;
78
127
  }): Promise<StorageListResult>;
128
+ /**
129
+ * A folder's entries for a listing page.
130
+ *
131
+ * A page token is an index into these entries, and every continuation
132
+ * page used to read the whole folder again to find its place: a folder of
133
+ * n entries listed in pages of p cost n/p full reads. A first page reads
134
+ * the folder and, when there is a next page, keeps the read; a
135
+ * continuation page indexes into it while all of these hold:
136
+ *
137
+ * - the folder's mtime is the one read with it. Adding, removing or
138
+ * renaming an entry changes it, which is the invalidation;
139
+ * - it is under {@link LISTING_SNAPSHOT_TTL_MS} old;
140
+ * - the folder had not changed in the {@link LISTING_SNAPSHOT_RACY_MS}
141
+ * before the read. Otherwise it is not kept at all.
142
+ *
143
+ * A first page always reads the folder as it is.
144
+ */
145
+ private listingEntries;
79
146
  /**
80
147
  * Get the absolute filesystem path for serving files
81
148
  * Used by the storage routes to serve files directly
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * S3-compatible storage controller (works with AWS S3 and MinIO)
3
3
  */
4
- import { S3StorageConfig, StorageController } from "./types.js";
5
- import { DownloadConfig, StorageListResult, UploadFileProps, UploadFileResult } from "@rebasepro/types";
4
+ import { S3StorageConfig, StorageController, type StoredObjectInfo, type StoredObjectRange, type PutObjectOptions, type StoredObjectStream } from "./types.js";
5
+ import { DownloadConfig, DownloadMetadata, StorageListResult, UploadFileProps, UploadFileResult } from "@rebasepro/types";
6
6
  /**
7
7
  * S3-compatible storage implementation
8
8
  * Works with AWS S3 and MinIO (with forcePathStyle option)
@@ -24,6 +24,8 @@ export declare class S3StorageController implements StorageController {
24
24
  * can reach — and a mistyped one came back as "file not found".
25
25
  */
26
26
  knownBuckets(): string[];
27
+ /** See {@link StorageController.maxFileSize}. */
28
+ maxFileSize(): number;
27
29
  /**
28
30
  * Validate file before upload
29
31
  */
@@ -32,13 +34,47 @@ export declare class S3StorageController implements StorageController {
32
34
  * Get the bucket name - either from parameter or config
33
35
  */
34
36
  private getBucket;
35
- putObject({ file, key, metadata, bucket }: UploadFileProps): Promise<UploadFileResult>;
37
+ putObject({ file, key, metadata, bucket }: UploadFileProps, options?: PutObjectOptions): Promise<UploadFileResult>;
38
+ /**
39
+ * Upload a large file in parts, one part in memory at a time.
40
+ *
41
+ * Reading the whole file into one buffer held it twice over (the
42
+ * `arrayBuffer()`, then the SDK's view and the multipart parser's copy
43
+ * before it), and `PutObject` stops at 5 GB regardless. A slice of a
44
+ * `File` reads only that range — from disk, for the disk-backed files TUS
45
+ * and backups hand over — so memory is one part whatever the file's size.
46
+ * A failure aborts the upload: no partial object appears at the key, and
47
+ * no orphaned parts are kept (and billed).
48
+ */
49
+ private putMultipart;
36
50
  /**
37
51
  * Flatten nested metadata to string values (S3 requirement)
38
52
  */
39
53
  private flattenMetadata;
40
54
  getSignedUrl(key: string, bucket?: string): Promise<DownloadConfig>;
41
55
  getObject(key: string, bucket?: string): Promise<File | null>;
56
+ /**
57
+ * The bucket and key a call addresses: an `s3://` or `gs://` URL names its
58
+ * own bucket, anything else is a key in the given (or configured) bucket.
59
+ */
60
+ private resolveLocation;
61
+ /**
62
+ * See {@link StorageController.getMetadata}. One `HeadObject`. Without it,
63
+ * `GET /metadata/*` fell back to `getSignedUrl`, which signed a URL per
64
+ * call — ~134 µs of CPU — only for the route to throw it away.
65
+ */
66
+ getMetadata(key: string, bucket?: string): Promise<DownloadMetadata | null>;
67
+ /** See {@link StorageController.statObject}. One `HeadObject`; no body is read. */
68
+ statObject(key: string, bucket?: string): Promise<StoredObjectInfo | null>;
69
+ /**
70
+ * See {@link StorageController.getObjectStream}. One `GetObject`, with a
71
+ * `Range` when one is asked for, so only those bytes leave the bucket; the
72
+ * body is handed on as it arrives rather than collected first.
73
+ */
74
+ getObjectStream(key: string, options?: {
75
+ bucket?: string;
76
+ range?: StoredObjectRange;
77
+ }): Promise<StoredObjectStream | null>;
42
78
  deleteObject(key: string, bucket?: string): Promise<void>;
43
79
  listObjects(prefix: string, options?: {
44
80
  bucket?: string;
@@ -5,6 +5,16 @@
5
5
  * adjustment using the `sharp` library. Results are cached in an LRU
6
6
  * in-memory cache to avoid redundant processing.
7
7
  */
8
+ /**
9
+ * Raised by {@link transformImage} when `sharp` is not installed. The storage
10
+ * routes never reach it: they ask {@link imageTransformsAvailable} first and
11
+ * serve the original.
12
+ */
13
+ export declare class ImageTransformUnavailableError extends Error {
14
+ constructor();
15
+ }
16
+ /** Whether this process can transform images — whether `sharp` loads. */
17
+ export declare function imageTransformsAvailable(): Promise<boolean>;
8
18
  /** Options that can be specified via query parameters. */
9
19
  export interface ImageTransformOptions {
10
20
  width?: number;
@@ -70,6 +80,11 @@ export declare const DECODABLE_FORMATS: Set<string>;
70
80
  export declare class UntransformableImageError extends Error {
71
81
  constructor(message: string);
72
82
  }
83
+ /**
84
+ * The content type a transform with these options produces: its `format`, or
85
+ * WebP when it names none, as {@link transformImage} encodes it.
86
+ */
87
+ export declare function transformContentType(options: ImageTransformOptions): string;
73
88
  /** Check whether a content type is a transformable image. */
74
89
  export declare function isTransformableImage(contentType: string): boolean;
75
90
  /**
@@ -21,8 +21,8 @@ export * from "./storage-registry.js";
21
21
  export { parseTransformOptions, transformImage, isTransformableImage, TransformCache } from "./image-transform.js";
22
22
  export type { ImageTransformOptions } from "./image-transform.js";
23
23
  export { TusHandler } from "./tus-handler.js";
24
- export { createUploadConstraintResolver, assertUploadWithinPropertyLimits, isAcceptedFile, readUploadPropertyContext, UPLOAD_COLLECTION_FIELD, UPLOAD_PROPERTY_FIELD } from "./property-limits.js";
25
- export type { UploadConstraints, ResolveUploadConstraints } from "./property-limits.js";
24
+ export { createUploadConstraintResolver, createUploadPathResolver, assertUploadWithinPropertyLimits, assertUploadWithinPathLimits, isAcceptedFile, readUploadPropertyContext, UPLOAD_COLLECTION_FIELD, UPLOAD_PROPERTY_FIELD } from "./property-limits.js";
25
+ export type { UploadConstraints, ResolveUploadConstraints, ResolveUploadPathConstraints } from "./property-limits.js";
26
26
  import { BackendStorageConfig, StorageController } from "./types.js";
27
27
  /**
28
28
  * Create a storage controller from a config object.
@@ -0,0 +1,23 @@
1
+ /**
2
+ * `/admin/storage` — the routes behind `rebase storage orphans` and
3
+ * `rebase storage move`. Admin-only; the caller mounts them behind the admin
4
+ * gate (`database:read` to read, `database:write` to delete or move).
5
+ *
6
+ * GET /orphans?storageId=&olderThanHours= objects no row names
7
+ * DELETE /orphans?storageId=&olderThanHours= delete them, found again first
8
+ * POST /move { from, to, prefix?, cursor?, limit?, deleteSource? }
9
+ */
10
+ import { Hono } from "hono";
11
+ import type { CollectionConfig, DataDriver } from "@rebasepro/types";
12
+ import type { HonoEnv } from "../api/types.js";
13
+ import type { StorageController } from "./types.js";
14
+ import type { StorageRegistry } from "./storage-registry.js";
15
+ export interface StorageMaintenanceRoutesConfig {
16
+ registry?: StorageRegistry;
17
+ controller?: StorageController;
18
+ /** The collections whose rows are read for references, read per request. */
19
+ collections: () => readonly CollectionConfig[];
20
+ /** The unscoped driver serving a collection: every row, whoever wrote it. */
21
+ driverFor: (collection: CollectionConfig) => DataDriver | undefined;
22
+ }
23
+ export declare function createStorageMaintenanceRoutes(config: StorageMaintenanceRoutesConfig): Hono<HonoEnv>;
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Storage maintenance: the objects a source holds that no row names
3
+ * (`rebase storage orphans`), and copying a folder of objects from one source
4
+ * to another (`rebase storage move`).
5
+ *
6
+ * Nothing deletes a stored file when its field is cleared, its file replaced or
7
+ * its row deleted — two rows may hold one key, so the panel cannot know a file
8
+ * is unused. What can know is a scan of every row against every object, which
9
+ * is what this is. It errs one way only: a string anywhere in any row that
10
+ * could name an object keeps that object, whichever source it is in and
11
+ * whichever property holds it. A file is reported only when nothing points at
12
+ * it at all.
13
+ */
14
+ import type { CollectionConfig, DataDriver } from "@rebasepro/types";
15
+ import type { StorageController } from "./types.js";
16
+ /** One stored object, as a listing describes it. */
17
+ export interface StoredObjectEntry {
18
+ key: string;
19
+ size?: number;
20
+ /** ISO 8601, when the source's listing says. */
21
+ updated?: string;
22
+ }
23
+ /** A request this module cannot act on: a bad cursor, a source that is not served. */
24
+ export declare class InvalidMaintenanceRequestError extends Error {
25
+ }
26
+ /**
27
+ * The objects under a folder of a source, folder by folder, at most `limit` at
28
+ * a time. Hand the returned `cursor` back for the next ones; none means the
29
+ * walk is done. The internal `_rebase/` folder is not walked.
30
+ */
31
+ export declare function walkObjects(controller: StorageController, options?: {
32
+ prefix?: string;
33
+ cursor?: string;
34
+ limit?: number;
35
+ }): Promise<{
36
+ objects: StoredObjectEntry[];
37
+ cursor?: string;
38
+ }>;
39
+ /**
40
+ * The keys one stored string could name an object by.
41
+ *
42
+ * A file field stores a bare key, a `local://`, `s3://` or `gs://` URL with the
43
+ * bucket, or — with `storeUrl` — a download URL; text stores
44
+ * `rebase-storage:` references, and from before those, download URLs. Each
45
+ * spelling yields the key it names, and a key under a leading `default/` is
46
+ * also read without it, as the routes read it.
47
+ */
48
+ export declare function keysNamedBy(value: string): string[];
49
+ /** A collection whose rows could not be read, and why. */
50
+ export interface UnscannedCollection {
51
+ collection: string;
52
+ reason: string;
53
+ }
54
+ /**
55
+ * Remove from `keys` every key some row names.
56
+ *
57
+ * Every row of every collection is read — soft-deleted rows too, since a
58
+ * restore brings their files back — a page at a time, through the driver that
59
+ * serves the collection. What is left in `keys` afterwards is named by no row.
60
+ * A collection that could not be read is returned, and its rows count for
61
+ * nothing: a caller must not treat what is left as unused when any is.
62
+ */
63
+ export declare function removeReferencedKeys(keys: Set<string>, collections: readonly CollectionConfig[], driverFor: (collection: CollectionConfig) => DataDriver | undefined, options?: {
64
+ pageSize?: number;
65
+ }): Promise<{
66
+ scanned: string[];
67
+ unscanned: UnscannedCollection[];
68
+ }>;
69
+ /** What `findOrphans` found. */
70
+ export interface OrphanReport {
71
+ /** Objects in the source, internal ones aside. */
72
+ objects: number;
73
+ /** Objects no row names and older than the grace period. */
74
+ orphans: StoredObjectEntry[];
75
+ /**
76
+ * Objects no row names but too new to call unused — an upload whose row
77
+ * has not been saved yet looks exactly like this — or of unknown age.
78
+ */
79
+ recent: number;
80
+ scanned: string[];
81
+ unscanned: UnscannedCollection[];
82
+ }
83
+ /**
84
+ * The objects of one source that no row names.
85
+ *
86
+ * @param olderThanMs an object written less long ago than this is not called
87
+ * an orphan: the admin panel uploads a file before the row that names it
88
+ * is saved. One whose age the listing does not say is not either.
89
+ */
90
+ export declare function findOrphans(options: {
91
+ controller: StorageController;
92
+ collections: readonly CollectionConfig[];
93
+ driverFor: (collection: CollectionConfig) => DataDriver | undefined;
94
+ olderThanMs: number;
95
+ now?: number;
96
+ }): Promise<OrphanReport>;
97
+ /** What happened to one object in a move. */
98
+ export interface MovedObject {
99
+ key: string;
100
+ outcome: "copied" | "exists" | "failed";
101
+ /** Whether the source's copy was deleted afterwards. */
102
+ deleted?: boolean;
103
+ error?: string;
104
+ }
105
+ /**
106
+ * Copy the objects under a folder of one source into another, under the same
107
+ * keys, `limit` objects per call. Hand the returned `cursor` back for the next
108
+ * ones; none means the folder is done.
109
+ *
110
+ * A key the target already holds at the same size is left alone, so a move
111
+ * that stopped half-way is resumed by running it again. With `deleteSource`,
112
+ * the source's copy is deleted once the target holds one of the same size.
113
+ * The copy is the server's own write: the target's upload limits, which say
114
+ * what a user may upload, do not apply to a file already accepted once.
115
+ */
116
+ export declare function moveObjects(options: {
117
+ from: StorageController;
118
+ to: StorageController;
119
+ prefix?: string;
120
+ cursor?: string;
121
+ limit?: number;
122
+ deleteSource?: boolean;
123
+ }): Promise<{
124
+ moved: MovedObject[];
125
+ cursor?: string;
126
+ }>;
@@ -12,12 +12,14 @@ import type { CollectionConfig } from "@rebasepro/types";
12
12
  * executable in the avatar bucket, and the config that said otherwise was never
13
13
  * consulted.
14
14
  *
15
- * The limits are per *property*, so enforcing them needs to know which property
16
- * a request is for. Uploads carry that as context (a form field on `POST
17
- * /upload`, `Upload-Metadata` on the resumable path); when it is absent the
18
- * global cap is still the ceiling, exactly as before. That is the honest
19
- * fallback: an upload that names no property has no property limit to check,
20
- * and refusing every context-less upload would break every existing client.
15
+ * The limits are per *property*, and the server finds the property two ways.
16
+ * By where the file lands: a property's `storagePath` is where its files go, so
17
+ * every upload into it meets the property's limits (`createUploadPathResolver`)
18
+ * — the panel never named its property, and a direct caller need not. And by
19
+ * name, when the upload carries context (a form field on `POST /upload`,
20
+ * `Upload-Metadata` on the resumable path), which covers a property whose
21
+ * `storagePath` is a function. A key no property claims, uploaded with no
22
+ * context, meets the source's own limit alone.
21
23
  */
22
24
  /** What a property says a file may be. */
23
25
  export interface UploadConstraints {
@@ -39,6 +41,39 @@ export type ResolveUploadConstraints = (collectionSlug: string, propertyPath: st
39
41
  * to choose.
40
42
  */
41
43
  export declare function createUploadConstraintResolver(collections: readonly CollectionConfig[]): ResolveUploadConstraints;
44
+ /**
45
+ * The limits of every property whose storage path a key falls in.
46
+ *
47
+ * @param storageId the source the upload is written to, canonical.
48
+ * @param key the canonical key the upload is written to.
49
+ */
50
+ export type ResolveUploadPathConstraints = (storageId: string, key: string) => UploadConstraints[];
51
+ /**
52
+ * The storage-path rules of the collections this backend serves.
53
+ *
54
+ * A property's `maxSize` and `acceptedFiles` used to be checked only when an
55
+ * upload named the property — which the panel never did, and a direct caller
56
+ * simply does not — so the limits the docs called "enforced by the server"
57
+ * held for nobody. A property's `storagePath` is where its files go, so it is
58
+ * also where its limits hold: every upload into that path, through any door,
59
+ * meets them.
60
+ *
61
+ * A path computed by a function cannot be read without running it, and has no
62
+ * rule; neither has a property that declares no limits.
63
+ */
64
+ export declare function createUploadPathResolver(collections: readonly CollectionConfig[]): ResolveUploadPathConstraints;
65
+ /**
66
+ * Refuse an upload that no property claiming its path would accept.
67
+ *
68
+ * Several properties may share a path; the file is for one of them, so it is
69
+ * enough that one accepts it. When none does, the first one's refusal is the
70
+ * answer, naming its property.
71
+ */
72
+ export declare function assertUploadWithinPathLimits(constraints: readonly UploadConstraints[], file: {
73
+ size?: number;
74
+ type?: string;
75
+ name?: string;
76
+ }): void;
42
77
  /** Is this file one the property accepts? */
43
78
  export declare function isAcceptedFile(accepted: readonly string[] | undefined, contentType: string | undefined, fileName: string | undefined): boolean;
44
79
  /**
@@ -28,8 +28,15 @@ export interface DurableRendition {
28
28
  contentType: string;
29
29
  }
30
30
  export interface DurableRenditionCache {
31
- /** The stored rendition for this cache key, or null. Never throws. */
32
- get(controller: StorageController, cacheKey: string, bucket: string | undefined): Promise<DurableRendition | null>;
31
+ /**
32
+ * The stored rendition for this cache key, or null. Never throws.
33
+ *
34
+ * `contentType` is the type the transform produces
35
+ * (`transformContentType(options)`). The cache key carries the options,
36
+ * format included, so a key only ever holds a rendition of that type, and
37
+ * one read of that one object key answers the question.
38
+ */
39
+ get(controller: StorageController, cacheKey: string, bucket: string | undefined, contentType: string): Promise<DurableRendition | null>;
33
40
  /** Store a rendition. Never throws: a failed write is a slower cache. */
34
41
  put(controller: StorageController, cacheKey: string, bucket: string | undefined, rendition: DurableRendition): Promise<void>;
35
42
  }
@@ -43,13 +50,4 @@ export interface DurableRenditionCache {
43
50
  * is available to anyone auditing the prefix and to nothing on the hot path.
44
51
  */
45
52
  export declare function renditionKey(cacheKey: string, contentType: string): string;
46
- /**
47
- * A rendition's key when reading, where the content type is not yet known.
48
- *
49
- * Reads try the known extensions rather than storing an index: four `getObject`
50
- * calls at worst is still one round trip in the common case — the first
51
- * candidate is the format the request asked for — and an index is a second
52
- * thing to keep consistent with the objects it describes.
53
- */
54
- export declare function renditionKeyCandidates(cacheKey: string, preferred?: string): string[];
55
53
  export declare function createDurableRenditionCache(): DurableRenditionCache;
@@ -10,6 +10,7 @@
10
10
  * write path's idea of an acceptable key and bucket the same.
11
11
  */
12
12
  import { ApiError } from "../api/errors.js";
13
+ import { type RequestedStorageObject } from "./requested-object.js";
13
14
  import type { StorageController } from "./types.js";
14
15
  /**
15
16
  * Canonicalize a caller-supplied storage key, answering 400 when it names
@@ -22,6 +23,20 @@ import type { StorageController } from "./types.js";
22
23
  * See `keys.ts` for why an unacceptable key is refused rather than repaired.
23
24
  */
24
25
  export declare function canonicalKeyOrBadRequest(key: string): string;
26
+ /**
27
+ * The object a `/file/*`, `/metadata/*` or `DELETE /file/*` request's wildcard
28
+ * path names, answering 400 when it names none.
29
+ *
30
+ * Derived by {@link requestedStorageObject}, the function `publicObjectAuth`
31
+ * and `fileTokenAuth` derive it with, so the key the route acts on is the key
32
+ * they decided about.
33
+ */
34
+ export declare function requestedObjectOrBadRequest(wildcard: string): RequestedStorageObject;
35
+ /**
36
+ * {@link requestedObjectOrBadRequest} for a path that is already text rather
37
+ * than a URL path — the folder route's JSON body.
38
+ */
39
+ export declare function objectOfPathOrBadRequest(path: string): RequestedStorageObject;
25
40
  /**
26
41
  * Canonicalize a caller-supplied bucket name, answering 400 when it is not one.
27
42
  *
@@ -0,0 +1,30 @@
1
+ /**
2
+ * The two principals a storage read can be granted without a user: the reader
3
+ * of a declared-public object, and the bearer of a download token.
4
+ *
5
+ * `checkAuthorized` does not ask the `storageAuthorize` hook about either — the
6
+ * public prefix and the token are the authorization. That skip used to be
7
+ * decided on the principal's `uid` string (`"public"`, `"download-token"`), so
8
+ * any identity carrying that uid skipped the hook: a custom `AuthAdapter` whose
9
+ * `verifyRequest` returns `{ uid: "public" }` for a guest could then upload,
10
+ * delete and list anywhere, with the hook never asked.
11
+ *
12
+ * Each principal is now one object, set only by the middleware that granted
13
+ * it (`publicObjectAuth`, `fileTokenAuth`) and recognised by identity. An
14
+ * adapter builds a new object per request, so it can name the uid but never be
15
+ * the grant.
16
+ */
17
+ /** The principal `publicObjectAuth` grants an anonymous read of a public object. */
18
+ export declare const PUBLIC_OBJECT_PRINCIPAL: {
19
+ readonly uid: string;
20
+ readonly roles: string[];
21
+ };
22
+ /** The principal `fileTokenAuth` grants a read covered by a valid download token. */
23
+ export declare const DOWNLOAD_TOKEN_PRINCIPAL: {
24
+ readonly uid: string;
25
+ readonly roles: string[];
26
+ };
27
+ /** Whether `user` is the grant `publicObjectAuth` made — the object, not the uid. */
28
+ export declare function isPublicObjectGrant(user: unknown): boolean;
29
+ /** Whether `user` is the grant `fileTokenAuth` made — the object, not the uid. */
30
+ export declare function isDownloadTokenGrant(user: unknown): boolean;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Which object a storage request's URL path names.
3
+ *
4
+ * Three parties read the path of a `GET /file/*`, `GET /metadata/*` or
5
+ * `DELETE /file/*`: the route, which serves or deletes an object;
6
+ * `publicObjectAuth`, which lets an anonymous caller through when that object
7
+ * is public; and `fileTokenAuth`, which compares it with the path a download
8
+ * token grants. A decision is only about the object acted on if it is made on
9
+ * the same key, so all three derive it here — the wildcard, the decoding, the
10
+ * bucket segment and the canonical key — and none of them spells a step of its
11
+ * own.
12
+ *
13
+ * They used to. `publicObjectAuth` judged the raw path with a check that strips
14
+ * everything up to a `://`, while the route canonicalized it, folding `//` to
15
+ * `/`: `GET /file/notes://public/secret.txt` was public to the one and served
16
+ * the private key `notes:/public/secret.txt` from the other, with the authorize
17
+ * hook never asked.
18
+ *
19
+ * Nothing here parses a scheme. A request path is a key, optionally behind the
20
+ * one bucket segment the routes recognise; a `://` in it is a `:` followed by
21
+ * an empty segment, and canonicalization folds the empty segment away.
22
+ */
23
+ /** The object a request path names. */
24
+ export interface RequestedStorageObject {
25
+ /** The bucket. Always `"default"`: the only bucket a request path can name. */
26
+ bucket: string;
27
+ /** The canonical key — the one the hook is asked about and the controller acts on. */
28
+ key: string;
29
+ }
30
+ /**
31
+ * The wildcard part of a storage route's path, still percent-encoded.
32
+ *
33
+ * Hono's `c.req.param('*')` does not work reliably in sub-routers mounted
34
+ * via `app.route(prefix, subRouter)`. Instead it is derived from the
35
+ * fully-resolved `c.req.path` and `c.req.routePath`.
36
+ *
37
+ * For a route `/metadata/*` mounted at `/api/storage`, a request to
38
+ * `/api/storage/metadata/default/file.jpg` yields routePath
39
+ * `/api/storage/metadata/*`. The prefix (everything before `/*`) is stripped,
40
+ * plus one character for the trailing `/`, to obtain `default/file.jpg`.
41
+ */
42
+ export declare function storageRequestWildcard(c: {
43
+ req: {
44
+ path: string;
45
+ routePath: string;
46
+ };
47
+ }): string;
48
+ /**
49
+ * The object a decoded path names: a key, behind an optional `default/` bucket
50
+ * segment (matched case-insensitively). Throws {@link InvalidStorageKeyError}
51
+ * when the key cannot be canonicalized.
52
+ *
53
+ * For a path that is already text — the folder route's JSON body. A URL path
54
+ * goes through {@link requestedStorageObject}, which decodes it first.
55
+ */
56
+ export declare function storageObjectOfPath(decodedPath: string): RequestedStorageObject;
57
+ /**
58
+ * The object a request's wildcard path names, or {@link InvalidStorageKeyError}.
59
+ *
60
+ * The wildcard arrives as Hono leaves it — `decodeURI`d, with the reserved
61
+ * characters (`/`, `:`, `%25` among them) still escaped — so one
62
+ * `decodeURIComponent` yields the path the client encoded. A `%` that does not
63
+ * begin an escape names no key, and is refused like any other key that cannot be
64
+ * canonicalized rather than thrown as a `URIError`.
65
+ */
66
+ export declare function requestedStorageObject(wildcard: string): RequestedStorageObject;
67
+ /**
68
+ * {@link requestedStorageObject}, or `null` when the path names no object.
69
+ *
70
+ * For the middleware, which must fail closed without an exception: a path that
71
+ * names no object is neither public nor covered by any download token, and the
72
+ * route answers it with a 400 of its own.
73
+ */
74
+ export declare function tryRequestedStorageObject(wildcard: string): RequestedStorageObject | null;