@rebasepro/server 0.16.0 → 0.16.1-canary.g041c925

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 (210) hide show
  1. package/dist/{GCSStorageController-BEmDYFKc.js → GCSStorageController-Bl7nmhdv.js} +7 -6
  2. package/dist/{GCSStorageController-BEmDYFKc.js.map → GCSStorageController-Bl7nmhdv.js.map} +1 -1
  3. package/dist/{S3StorageController-B2EDXXMH.js → S3StorageController-CWvvrRpF.js} +7 -6
  4. package/dist/{S3StorageController-B2EDXXMH.js.map → S3StorageController-CWvvrRpF.js.map} +1 -1
  5. package/dist/{admin_block-DLILvzle.js → admin_block-BeypnEfb.js} +6 -6
  6. package/dist/admin_block-BeypnEfb.js.map +1 -0
  7. package/dist/api/ast-schema-editor.d.ts +23 -0
  8. package/dist/api/contract-routes.d.ts +1 -1
  9. package/dist/api/errors.d.ts +1 -1
  10. package/dist/api/index.d.ts +3 -3
  11. package/dist/api/live-schema-routes.d.ts +122 -0
  12. package/dist/api/logs-routes.d.ts +1 -1
  13. package/dist/api/mount.d.ts +41 -0
  14. package/dist/api/rest/api-generator.d.ts +2 -2
  15. package/dist/api/rest/index.d.ts +1 -1
  16. package/dist/api/rest/query-parser.d.ts +1 -1
  17. package/dist/api/schema-editor-routes.d.ts +1 -1
  18. package/dist/api/types.d.ts +12 -4
  19. package/dist/{schema-editor-routes-CV9k0w3G.js → ast-schema-editor-CDGgrIn7.js} +37 -45
  20. package/dist/ast-schema-editor-CDGgrIn7.js.map +1 -0
  21. package/dist/auth/adapter-middleware.d.ts +2 -2
  22. package/dist/auth/admin-roles-route.d.ts +2 -2
  23. package/dist/auth/admin-user-ops.d.ts +3 -3
  24. package/dist/auth/admin-users-route.d.ts +4 -6
  25. package/dist/auth/api-keys/api-key-middleware.d.ts +2 -2
  26. package/dist/auth/api-keys/api-key-permission-guard.d.ts +1 -1
  27. package/dist/auth/api-keys/api-key-routes.d.ts +2 -2
  28. package/dist/auth/api-keys/api-key-store.d.ts +1 -1
  29. package/dist/auth/api-keys/index.d.ts +9 -9
  30. package/dist/auth/apple-oauth.d.ts +2 -2
  31. package/dist/auth/auth-hooks.d.ts +4 -4
  32. package/dist/auth/bitbucket-oauth.d.ts +2 -2
  33. package/dist/auth/builtin-auth-adapter.d.ts +8 -4
  34. package/dist/auth/capabilities.d.ts +51 -0
  35. package/dist/auth/captcha.d.ts +86 -0
  36. package/dist/auth/cookie-utils.d.ts +2 -2
  37. package/dist/auth/discord-oauth.d.ts +2 -2
  38. package/dist/auth/facebook-oauth.d.ts +2 -2
  39. package/dist/auth/github-oauth.d.ts +2 -2
  40. package/dist/auth/gitlab-oauth.d.ts +2 -2
  41. package/dist/auth/google-oauth.d.ts +1 -1
  42. package/dist/auth/index.d.ts +58 -54
  43. package/dist/auth/jwks-routes.d.ts +1 -1
  44. package/dist/auth/jwt.d.ts +1 -1
  45. package/dist/auth/linkedin-oauth.d.ts +2 -2
  46. package/dist/auth/magic-link-routes.d.ts +10 -3
  47. package/dist/auth/mfa-gate.d.ts +1 -1
  48. package/dist/auth/mfa-routes.d.ts +3 -3
  49. package/dist/auth/microsoft-oauth.d.ts +2 -2
  50. package/dist/auth/middleware.d.ts +6 -7
  51. package/dist/auth/otp-routes.d.ts +89 -0
  52. package/dist/auth/rate-limiter.d.ts +2 -2
  53. package/dist/auth/require-auth.d.ts +1 -1
  54. package/dist/auth/reset-password-admin.d.ts +4 -4
  55. package/dist/auth/routes.d.ts +18 -10
  56. package/dist/auth/session-routes.d.ts +3 -3
  57. package/dist/auth/slack-oauth.d.ts +2 -2
  58. package/dist/auth/spotify-oauth.d.ts +2 -2
  59. package/dist/auth/token-revocation.d.ts +2 -2
  60. package/dist/auth/twitter-oauth.d.ts +2 -2
  61. package/dist/{auth-5Et5mnUA.js → auth-DzarZhLS.js} +603 -4376
  62. package/dist/auth-DzarZhLS.js.map +1 -0
  63. package/dist/backup/backup-common.d.ts +1 -1
  64. package/dist/backup/backup-routes.d.ts +3 -3
  65. package/dist/backup/index.d.ts +3 -3
  66. package/dist/{backup-C6ljYVTp.js → backup-CRdZkA6c.js} +7 -6
  67. package/dist/{backup-C6ljYVTp.js.map → backup-CRdZkA6c.js.map} +1 -1
  68. package/dist/boot/bindings.d.ts +81 -0
  69. package/dist/boot/boot.d.ts +5 -5
  70. package/dist/boot/bundle.d.ts +10 -1
  71. package/dist/boot/driver.d.ts +1 -1
  72. package/dist/boot/env.d.ts +28 -1
  73. package/dist/boot/fetch-bundle.d.ts +144 -1
  74. package/dist/boot/local-provisioner.d.ts +30 -0
  75. package/dist/boot/options.d.ts +30 -6
  76. package/dist/boot/resource-adapters.d.ts +31 -0
  77. package/dist/boot/resource-loading.d.ts +10 -0
  78. package/dist/boot/role.d.ts +3 -2
  79. package/dist/boot/sources.d.ts +1 -1
  80. package/dist/boot/version-skew.d.ts +1 -1
  81. package/dist/collections/BackendCollectionRegistry.d.ts +1 -1
  82. package/dist/collections/loader.d.ts +1 -1
  83. package/dist/{contract-routes-DZ-LBpSL.js → contract-routes-DDSRyGSl.js} +8 -7
  84. package/dist/contract-routes-DDSRyGSl.js.map +1 -0
  85. package/dist/cron/cron-routes.d.ts +2 -2
  86. package/dist/cron/cron-scheduler.d.ts +3 -3
  87. package/dist/cron/index.d.ts +8 -8
  88. package/dist/{cron-loader-YhhQeVBM.js → cron-loader-d9WMFENB.js} +8 -7
  89. package/dist/{cron-loader-YhhQeVBM.js.map → cron-loader-d9WMFENB.js.map} +1 -1
  90. package/dist/{cron-routes-maM_RlUu.js → cron-routes-B_GKclr5.js} +9 -8
  91. package/dist/cron-routes-B_GKclr5.js.map +1 -0
  92. package/dist/{cron-scheduler-DIpYBmZP.js → cron-scheduler-BB82dWuU.js} +9 -9
  93. package/dist/cron-scheduler-BB82dWuU.js.map +1 -0
  94. package/dist/{cron-store-DfH_4Cd9.js → cron-store-BTZqwkM1.js} +10 -9
  95. package/dist/{cron-store-DfH_4Cd9.js.map → cron-store-BTZqwkM1.js.map} +1 -1
  96. package/dist/{ddl-bootstrap-Cywoj8Ta.js → ddl-bootstrap-euwN8FOy.js} +21 -6
  97. package/dist/ddl-bootstrap-euwN8FOy.js.map +1 -0
  98. package/dist/deploy/pod-contract.d.ts +139 -0
  99. package/dist/dev-secrets.d.ts +52 -0
  100. package/dist/{dynamic-import-Dvh-K5fl.js → dynamic-import-X40pTZUQ.js} +5 -4
  101. package/dist/{dynamic-import-Dvh-K5fl.js.map → dynamic-import-X40pTZUQ.js.map} +1 -1
  102. package/dist/email/dev-sink.d.ts +88 -0
  103. package/dist/email/index.d.ts +9 -7
  104. package/dist/email/link-base.d.ts +1 -1
  105. package/dist/email/smtp-email-service.d.ts +2 -2
  106. package/dist/email/templates.d.ts +12 -0
  107. package/dist/email/types.d.ts +18 -6
  108. package/dist/env.d.ts +1 -1
  109. package/dist/{errors-EBYiaJ2E.js → errors-DBwpj9N8.js} +8 -7
  110. package/dist/errors-DBwpj9N8.js.map +1 -0
  111. package/dist/{function-loader-DDS1v7YX.js → function-loader-LLdmBFoL.js} +9 -8
  112. package/dist/{function-loader-DDS1v7YX.js.map → function-loader-LLdmBFoL.js.map} +1 -1
  113. package/dist/{function-routes-Btcez1T-.js → function-routes-ClT6UQpD.js} +8 -7
  114. package/dist/function-routes-ClT6UQpD.js.map +1 -0
  115. package/dist/functions/context.d.ts +141 -0
  116. package/dist/functions/define-function.d.ts +1 -1
  117. package/dist/functions/function-routes.d.ts +9 -3
  118. package/dist/functions/guards.d.ts +76 -0
  119. package/dist/functions/index.d.ts +96 -5
  120. package/dist/functions/index.js +919 -0
  121. package/dist/functions/index.js.map +1 -0
  122. package/dist/functions/internal.d.ts +26 -0
  123. package/dist/functions/proxy.d.ts +1 -1
  124. package/dist/functions/request-timeout.d.ts +9 -1
  125. package/dist/functions/runtime-env.d.ts +92 -0
  126. package/dist/functions/wait-until.d.ts +74 -0
  127. package/dist/history/history-routes.d.ts +2 -2
  128. package/dist/history/index.d.ts +1 -1
  129. package/dist/index.d.ts +65 -54
  130. package/dist/index.es.js +3498 -305
  131. package/dist/index.es.js.map +1 -1
  132. package/dist/init/docs.d.ts +1 -1
  133. package/dist/init/middlewares.d.ts +1 -1
  134. package/dist/init/shutdown.d.ts +4 -0
  135. package/dist/init/storage.d.ts +1 -1
  136. package/dist/init/surfaces.d.ts +10 -0
  137. package/dist/init.d.ts +171 -22
  138. package/dist/jobs/index.d.ts +5 -5
  139. package/dist/{jobs-CyOKXXlu.js → jobs-CeZzdeR8.js} +10 -9
  140. package/dist/{jobs-CyOKXXlu.js.map → jobs-CeZzdeR8.js.map} +1 -1
  141. package/dist/{jwt-DxH9fLPt.js → jwt-CpMqxVct.js} +9 -8
  142. package/dist/{jwt-DxH9fLPt.js.map → jwt-CpMqxVct.js.map} +1 -1
  143. package/dist/{logger-DfvF_8r-.js → logger-DS03e908.js} +104 -10
  144. package/dist/logger-DS03e908.js.map +1 -0
  145. package/dist/{logs-routes-CWBLQj2l.js → logs-routes-DB72iQSr.js} +36 -9
  146. package/dist/logs-routes-DB72iQSr.js.map +1 -0
  147. package/dist/metrics/index.d.ts +1 -1
  148. package/dist/{openapi-generator-BCKJRUS4.js → openapi-generator-wFJo22gg.js} +7 -11
  149. package/dist/{openapi-generator-BCKJRUS4.js.map → openapi-generator-wFJo22gg.js.map} +1 -1
  150. package/dist/{proxy-Bj5DVllb.js → proxy-CMymhnwG.js} +9 -6
  151. package/dist/{proxy-Bj5DVllb.js.map → proxy-CMymhnwG.js.map} +1 -1
  152. package/dist/query-parser-C3hHu17g.js +355 -0
  153. package/dist/query-parser-C3hHu17g.js.map +1 -0
  154. package/dist/{request-timeout-BuFoEKwT.js → request-timeout-C8gkc-j7.js} +21 -6
  155. package/dist/request-timeout-C8gkc-j7.js.map +1 -0
  156. package/dist/rls-audit/index.d.ts +111 -0
  157. package/dist/{rolldown-runtime-DSJWtz9O.js → rolldown-runtime-dW7B1o5h.js} +4 -3
  158. package/dist/schema-edit/apply-schema-change.d.ts +141 -0
  159. package/dist/schema-edit/github-repository.d.ts +67 -0
  160. package/dist/schema-edit/local-git-repository.d.ts +28 -0
  161. package/dist/schema-edit/project-root.d.ts +43 -0
  162. package/dist/schema-edit/remote-source.d.ts +25 -0
  163. package/dist/schema-edit/schema-edit-permissions.d.ts +129 -0
  164. package/dist/schema-editor-routes-CRKMZnXu.js +87 -0
  165. package/dist/schema-editor-routes-CRKMZnXu.js.map +1 -0
  166. package/dist/schemas-DBxgjM9A.js +4153 -0
  167. package/dist/schemas-DBxgjM9A.js.map +1 -0
  168. package/dist/{selection-_z6TM1DB.js → selection-CRpqKUbt.js} +6 -5
  169. package/dist/{selection-_z6TM1DB.js.map → selection-CRpqKUbt.js.map} +1 -1
  170. package/dist/services/webhook-service.d.ts +1 -1
  171. package/dist/singleton.d.ts +7 -0
  172. package/dist/{src-CrCxd8km.js → src-6VqGNeDB.js} +122 -5
  173. package/dist/src-6VqGNeDB.js.map +1 -0
  174. package/dist/{src-BPfYOeN4.js → src-C5vFEj-t.js} +8 -6
  175. package/dist/src-C5vFEj-t.js.map +1 -0
  176. package/dist/storage/GCSStorageController.d.ts +1 -1
  177. package/dist/storage/LocalStorageController.d.ts +1 -1
  178. package/dist/storage/S3StorageController.d.ts +1 -1
  179. package/dist/storage/cache-headers.d.ts +87 -0
  180. package/dist/storage/index.d.ts +11 -11
  181. package/dist/storage/path-pattern.d.ts +58 -0
  182. package/dist/storage/policies.d.ts +88 -0
  183. package/dist/storage/range.d.ts +63 -0
  184. package/dist/storage/rendition-cache.d.ts +45 -0
  185. package/dist/storage/routes.d.ts +17 -5
  186. package/dist/storage/storage-registry.d.ts +1 -1
  187. package/dist/storage/triggers.d.ts +66 -0
  188. package/dist/storage/tus-handler.d.ts +34 -3
  189. package/dist/topics/runtime.d.ts +68 -0
  190. package/dist/{types-DSnOC4mF.js → types-BfKcm9do.js} +5 -4
  191. package/dist/{types-DSnOC4mF.js.map → types-BfKcm9do.js.map} +1 -1
  192. package/dist/utils/host.d.ts +58 -0
  193. package/dist/utils/logger.d.ts +0 -15
  194. package/dist/utils/request-id.d.ts +1 -1
  195. package/functions/package.json +24 -0
  196. package/package.json +13 -7
  197. package/dist/admin_block-DLILvzle.js.map +0 -1
  198. package/dist/auth-5Et5mnUA.js.map +0 -1
  199. package/dist/contract-routes-DZ-LBpSL.js.map +0 -1
  200. package/dist/cron-routes-maM_RlUu.js.map +0 -1
  201. package/dist/cron-scheduler-DIpYBmZP.js.map +0 -1
  202. package/dist/ddl-bootstrap-Cywoj8Ta.js.map +0 -1
  203. package/dist/errors-EBYiaJ2E.js.map +0 -1
  204. package/dist/function-routes-Btcez1T-.js.map +0 -1
  205. package/dist/logger-DfvF_8r-.js.map +0 -1
  206. package/dist/logs-routes-CWBLQj2l.js.map +0 -1
  207. package/dist/request-timeout-BuFoEKwT.js.map +0 -1
  208. package/dist/schema-editor-routes-CV9k0w3G.js.map +0 -1
  209. package/dist/src-BPfYOeN4.js.map +0 -1
  210. package/dist/src-CrCxd8km.js.map +0 -1
@@ -5,7 +5,7 @@
5
5
  * optional peer dependency — users who don't need GCS never pay for the
6
6
  * import cost or need it installed.
7
7
  */
8
- import { StorageController, GCSStorageConfig } from "./types";
8
+ import { StorageController, GCSStorageConfig } from "./types.js";
9
9
  import { UploadFileProps, UploadFileResult, DownloadConfig, StorageListResult } from "@rebasepro/types";
10
10
  /**
11
11
  * Google Cloud Storage implementation of `StorageController`.
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Local filesystem storage controller
3
3
  */
4
- import { StorageController, LocalStorageConfig } from "./types";
4
+ import { StorageController, LocalStorageConfig } from "./types.js";
5
5
  import { UploadFileProps, UploadFileResult, DownloadConfig, StorageListResult } from "@rebasepro/types";
6
6
  /**
7
7
  * Bucket used when a call names none.
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * S3-compatible storage controller (works with AWS S3 and MinIO)
3
3
  */
4
- import { S3StorageConfig, StorageController } from "./types";
4
+ import { S3StorageConfig, StorageController } from "./types.js";
5
5
  import { DownloadConfig, StorageListResult, UploadFileProps, UploadFileResult } from "@rebasepro/types";
6
6
  /**
7
7
  * S3-compatible storage implementation
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Cache and revalidation headers for served objects.
3
+ *
4
+ * ## Why every byte still goes through the process
5
+ *
6
+ * `routes.ts` deliberately proxies rather than redirecting to a signed URL —
7
+ * mixed content and unreachable VPC endpoints, both documented there. That
8
+ * decision stands. What it cost was caching: a proxied response only caches as
9
+ * well as its headers say, and the local path sent none at all, so every load
10
+ * of every image re-read the file and re-sent the body.
11
+ *
12
+ * This module is the headers. It does not change where bytes come from; it
13
+ * lets the client stop asking for them.
14
+ *
15
+ * ## Why `immutable` is wrong here, and was
16
+ *
17
+ * The remote path sent `public, max-age=3600, immutable`, and the transform
18
+ * paths sent it with `max-age=31536000`. `immutable` is a promise that the body
19
+ * at this URL will never change. A storage key can be overwritten — `putObject`
20
+ * on an existing key is an ordinary operation — so the promise is false for
21
+ * every object this server serves, and the symptom is an upload that replaces a
22
+ * file which then does not visibly change for an hour, or a year.
23
+ *
24
+ * Revalidation replaces it. With a validator the client asks "still this one?"
25
+ * and gets a 304 with no body, which costs a round trip instead of a transfer.
26
+ *
27
+ * ## Why `public` was a leak
28
+ *
29
+ * The same header said `public` for every object, including one that required
30
+ * authentication to fetch. `public` is explicit permission for a *shared* cache
31
+ * — a CDN, a corporate proxy — to store the response and serve it to somebody
32
+ * else. Private objects are now `private`, which is the directive that says a
33
+ * browser may keep it but a shared cache may not.
34
+ */
35
+ import type { Context } from "hono";
36
+ /**
37
+ * A weak validator built from size and modification time.
38
+ *
39
+ * Weak (`W/`) because it is not a digest of the body: two different bodies of
40
+ * the same length written in the same millisecond would collide. That is the
41
+ * same trade nginx makes for static files, and the alternative — hashing every
42
+ * object on every request — costs more than it saves for content this server is
43
+ * already streaming from somewhere else.
44
+ */
45
+ export declare function buildEntityTag(size: number, lastModifiedMs: number): string;
46
+ export interface ObjectValidators {
47
+ etag: string;
48
+ /** RFC 9110 date, second-resolution. */
49
+ lastModified: string;
50
+ lastModifiedMs: number;
51
+ }
52
+ export declare function objectValidators(size: number, lastModifiedMs: number): ObjectValidators;
53
+ export interface CacheControlOptions {
54
+ /**
55
+ * Whether a *shared* cache may store this. True only for objects that need
56
+ * no credentials to fetch.
57
+ */
58
+ isPublic: boolean;
59
+ /**
60
+ * How long a client may reuse the response without asking. Short by
61
+ * default, because the object behind it is mutable.
62
+ */
63
+ maxAgeSeconds: number;
64
+ /**
65
+ * Public responses only: how long a stale copy may be served while a
66
+ * revalidation happens in the background. Shared caches honour it; it turns
67
+ * the revalidation round trip into something the user does not wait for.
68
+ */
69
+ staleWhileRevalidateSeconds?: number;
70
+ }
71
+ export declare function cacheControl(options: CacheControlOptions): string;
72
+ /**
73
+ * Whether the request already holds this exact representation.
74
+ *
75
+ * `If-None-Match` wins outright when present: it is an exact validator, whereas
76
+ * `If-Modified-Since` has second resolution and is only consulted in its
77
+ * absence — again the order RFC 9110 requires.
78
+ */
79
+ export declare function isNotModified(c: Context, validators: ObjectValidators): boolean;
80
+ /**
81
+ * Put the validators and the cache policy on a response.
82
+ *
83
+ * Applied to the 304 as well as the 200: a 304 that omits them tells the client
84
+ * nothing about how long the answer holds, so the next load revalidates again
85
+ * immediately and the round trip repeats forever.
86
+ */
87
+ export declare function applyCacheHeaders(c: Context, validators: ObjectValidators, options: CacheControlOptions): void;
@@ -11,17 +11,17 @@
11
11
  * For other providers (Azure Blob, etc.), implement the
12
12
  * `StorageController` interface and pass the instance directly to the `storage` config.
13
13
  */
14
- export * from "./types";
15
- export { LocalStorageController } from "./LocalStorageController";
16
- export { S3StorageController } from "./S3StorageController";
17
- export { GCSStorageController } from "./GCSStorageController";
18
- export { createStorageRoutes } from "./routes";
19
- export type { StorageRoutesConfig } from "./routes";
20
- export * from "./storage-registry";
21
- export { parseTransformOptions, transformImage, isTransformableImage, TransformCache } from "./image-transform";
22
- export type { ImageTransformOptions } from "./image-transform";
23
- export { TusHandler } from "./tus-handler";
24
- import { BackendStorageConfig, StorageController } from "./types";
14
+ export * from "./types.js";
15
+ export { LocalStorageController } from "./LocalStorageController.js";
16
+ export { S3StorageController } from "./S3StorageController.js";
17
+ export { GCSStorageController } from "./GCSStorageController.js";
18
+ export { createStorageRoutes } from "./routes.js";
19
+ export type { StorageRoutesConfig } from "./routes.js";
20
+ export * from "./storage-registry.js";
21
+ export { parseTransformOptions, transformImage, isTransformableImage, TransformCache } from "./image-transform.js";
22
+ export type { ImageTransformOptions } from "./image-transform.js";
23
+ export { TusHandler } from "./tus-handler.js";
24
+ import { BackendStorageConfig, StorageController } from "./types.js";
25
25
  /**
26
26
  * Create a storage controller from a config object.
27
27
  *
@@ -0,0 +1,58 @@
1
+ /**
2
+ * The storage key pattern language, in one place.
3
+ *
4
+ * `storagePolicies` invented it and `storageTriggers` needs exactly the same
5
+ * thing: match a key segment by segment, capture the named segments, and refuse
6
+ * a pattern that would silently do something other than it says. Two copies of
7
+ * a matcher used for access control is one copy too many — the day they drift
8
+ * is the day a policy and a trigger disagree about what `users/:uid/**` means,
9
+ * and only one of them is enforcing anything.
10
+ *
11
+ * The rules:
12
+ *
13
+ * - a literal segment matches itself
14
+ * - `*` matches exactly one segment
15
+ * - `:name` matches one segment and captures it
16
+ * - `**` matches the rest of the key, including nothing, and is only allowed as
17
+ * the final segment
18
+ *
19
+ * Compilation is strict and happens at boot: a `**` in the middle, a `:` with
20
+ * no name, or the same capture twice are all errors, because each of them is a
21
+ * pattern whose author believed something the matcher does not do.
22
+ */
23
+ /** A pattern that cannot be compiled. Thrown at boot, never per request. */
24
+ export declare class StoragePatternError extends Error {
25
+ constructor(message: string);
26
+ }
27
+ export interface CompiledStoragePattern {
28
+ segments: string[];
29
+ /** True when the last segment is `**`. */
30
+ trailing: boolean;
31
+ /** The pattern as written, for error messages and logs. */
32
+ source: string;
33
+ }
34
+ /**
35
+ * Split a key into segments, dropping empty ones.
36
+ *
37
+ * Empty segments come from a leading slash or a `//`, and treating them as real
38
+ * would let `users//x` match a pattern expecting a captured segment with the
39
+ * empty string. Keys reaching here are already sanitized against traversal; this
40
+ * is about matching, not safety.
41
+ */
42
+ export declare function segmentsOf(value: string): string[];
43
+ /**
44
+ * Compile a pattern, or throw {@link StoragePatternError}.
45
+ *
46
+ * `label` names the thing being compiled in any error — `storagePolicies[2]`,
47
+ * `storageTriggers[0]` — so a boot failure points at the line to edit.
48
+ */
49
+ export declare function compileStoragePattern(path: string, label: string): CompiledStoragePattern;
50
+ /**
51
+ * Match a key against a compiled pattern.
52
+ *
53
+ * Returns the captured segments on a match — an empty object for a pattern that
54
+ * captures nothing — and `undefined` on a miss. The distinction matters: a
55
+ * pattern with no `:name` still matches, and `{}` is a match while `undefined`
56
+ * is not.
57
+ */
58
+ export declare function matchStoragePattern(compiled: CompiledStoragePattern, key: string): Record<string, string> | undefined;
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Declarative access control for storage.
3
+ *
4
+ * Storage is not under row-level security, so `storageAuthorize` — a hook, a
5
+ * piece of imperative code — was the whole access-control model. That is the one
6
+ * place the claim "security lives in the database" does not hold, and it shows
7
+ * in the shape of the alternative: without a hook, **any authenticated caller
8
+ * may read, overwrite, delete or list any key they can name**, because storage
9
+ * keys share one flat namespace.
10
+ *
11
+ * A policy list says the same things declaratively, and can be read without
12
+ * running it:
13
+ *
14
+ * ```ts
15
+ * storagePolicies: [
16
+ * { path: "public/**", operations: ["read"], allow: "public" },
17
+ * { path: "users/:uid/**", allow: ({ params, user }) => user?.uid === params.uid }
18
+ * ]
19
+ * ```
20
+ *
21
+ * ## Deny by default, and why that is the whole design
22
+ *
23
+ * A key matched by no policy is refused. That makes every widening an explicit,
24
+ * reviewable line, and it makes a mistake in this module fail *closed* — a
25
+ * pattern that fails to match denies access rather than granting it. Authz code
26
+ * that fails open is worth more to an attacker than the feature is to anyone
27
+ * else, so the bias is deliberate and absolute.
28
+ *
29
+ * ## The hook remains, as an escape hatch and not as a fallback
30
+ *
31
+ * Ownership frequently lives in a row rather than in a key, which no pattern can
32
+ * express. So when policies match nothing, an explicit `storageAuthorize` is
33
+ * consulted before the request is refused. Present, it can widen; absent, the
34
+ * answer is no. Policies never *narrow* what the hook allowed and the hook never
35
+ * overrides a policy denial — each can only say yes, which keeps "who allowed
36
+ * this?" answerable by reading either one alone.
37
+ */
38
+ import type { StorageAuthorize, StorageAuthorizeContext, StorageOperation } from "@rebasepro/types";
39
+ /** Everything a predicate is told, plus whatever the path captured. */
40
+ export interface StoragePolicyContext extends StorageAuthorizeContext {
41
+ /** Segments captured by `:name` placeholders in the matched path. */
42
+ params: Record<string, string>;
43
+ }
44
+ export type StoragePolicyPredicate = (ctx: StoragePolicyContext) => boolean | Promise<boolean>;
45
+ export interface StoragePolicy {
46
+ /**
47
+ * A key pattern, matched segment by segment against the sanitized key.
48
+ *
49
+ * - a literal segment matches itself
50
+ * - `*` matches exactly one segment
51
+ * - `:name` matches one segment and captures it
52
+ * - `**` matches the rest of the key, including nothing, and is only
53
+ * allowed as the final segment
54
+ */
55
+ path: string;
56
+ /** Which operations this grants. Defaults to every one of them. */
57
+ operations?: StorageOperation[];
58
+ /**
59
+ * Who it grants to.
60
+ *
61
+ * - `"public"` — anyone, signed in or not
62
+ * - `"authenticated"` — any caller with a uid
63
+ * - a predicate — anything else, given the captured params and the user
64
+ */
65
+ allow: "public" | "authenticated" | StoragePolicyPredicate;
66
+ }
67
+ export declare class StoragePolicyError extends Error {
68
+ constructor(message: string);
69
+ }
70
+ /**
71
+ * Compile a policy list into a {@link StorageAuthorize}.
72
+ *
73
+ * Compiled once, at boot, so a malformed pattern fails the start rather than the
74
+ * first upload. `fallback` is an explicit `storageAuthorize`, consulted only
75
+ * when no policy matched.
76
+ */
77
+ export declare function compileStoragePolicies(policies: StoragePolicy[], fallback?: StorageAuthorize): StorageAuthorize;
78
+ /**
79
+ * The access-control function for a configuration, or `undefined` when it
80
+ * declares none.
81
+ *
82
+ * Kept separate from {@link compileStoragePolicies} so `init` has one call to
83
+ * make and one thing to reason about: policies, a hook, both, or neither.
84
+ */
85
+ export declare function resolveStorageAccessControl(config: {
86
+ storagePolicies?: StoragePolicy[];
87
+ storageAuthorize?: StorageAuthorize;
88
+ }): StorageAuthorize | undefined;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Byte-range requests for served objects.
3
+ *
4
+ * Without `Accept-Ranges`, a browser will not offer to seek in an audio or
5
+ * video element served from here: it has no way to ask for the middle of a file,
6
+ * so scrubbing means fetching the whole thing from the start, and on a long
7
+ * recording it means fetching it again for every seek. Safari goes further and
8
+ * refuses to play a `<video>` at all unless the first response is a `206`.
9
+ *
10
+ * So this is not a performance nicety. For media it is the difference between a
11
+ * player that works and one that does not.
12
+ *
13
+ * ## What is implemented, and what is refused
14
+ *
15
+ * A single range — `bytes=0-499`, `bytes=500-`, `bytes=-500`. That is what every
16
+ * browser sends for media playback.
17
+ *
18
+ * Multiple ranges in one request (`bytes=0-99,200-299`) are **not** served.
19
+ * Answering them means composing a `multipart/byteranges` body, and nothing that
20
+ * matters sends them; a server may always answer a range request with the whole
21
+ * `200` instead, which is what happens here. Refusing to guess is better than a
22
+ * half-implemented multipart encoder.
23
+ *
24
+ * An unsatisfiable range — one starting past the end of the object — is a `416`
25
+ * with a `Content-Range: bytes /<size>`, as RFC 9110 requires. Answering `200`
26
+ * there would hand a player the whole file when it asked for a byte that does
27
+ * not exist, and it would never notice.
28
+ */
29
+ /** A resolved, inclusive byte range within an object of known size. */
30
+ export interface ByteRange {
31
+ start: number;
32
+ /** Inclusive, as the header is. */
33
+ end: number;
34
+ /** `end - start + 1`. */
35
+ length: number;
36
+ }
37
+ export type RangeParse =
38
+ /** No `Range` header, or one this server declines to honour. Serve `200`. */
39
+ {
40
+ kind: "none";
41
+ }
42
+ /** Serve `206` with this range. */
43
+ | {
44
+ kind: "range";
45
+ range: ByteRange;
46
+ }
47
+ /** Serve `416`. The range cannot be satisfied for an object of this size. */
48
+ | {
49
+ kind: "unsatisfiable";
50
+ };
51
+ /**
52
+ * Parse a `Range` header against an object of `size` bytes.
53
+ *
54
+ * Deliberately conservative: anything malformed, multi-range, or in a unit other
55
+ * than bytes resolves to `none`, which serves the whole object. A range request
56
+ * answered with `200` is always legal; a range answered wrongly is a corrupted
57
+ * download.
58
+ */
59
+ export declare function parseRange(header: string | undefined, size: number): RangeParse;
60
+ /** The `Content-Range` for a served range. */
61
+ export declare function contentRange(range: ByteRange, size: number): string;
62
+ /** The `Content-Range` for a `416`, which names the size and no range. */
63
+ export declare function unsatisfiableContentRange(size: number): string;
@@ -0,0 +1,45 @@
1
+ import type { StorageController } from "./types.js";
2
+ /**
3
+ * Where renditions live inside the source bucket.
4
+ *
5
+ * Fixed rather than configurable: it is the target of a lifecycle rule and of
6
+ * the route-layer refusal below, and both of those are worth being able to
7
+ * write down once, in the documentation, for every deployment.
8
+ */
9
+ export declare const RENDITION_PREFIX = "_rebase/renditions/";
10
+ /** True for a key that names the reserved rendition space. */
11
+ export declare function isRenditionKey(key: string): boolean;
12
+ export interface RenditionCacheConfig {
13
+ /** Off unless set. See the module comment for why. */
14
+ enabled?: boolean;
15
+ }
16
+ export interface DurableRendition {
17
+ data: Buffer;
18
+ contentType: string;
19
+ }
20
+ export interface DurableRenditionCache {
21
+ /** The stored rendition for this cache key, or null. Never throws. */
22
+ get(controller: StorageController, cacheKey: string, bucket: string | undefined): Promise<DurableRendition | null>;
23
+ /** Store a rendition. Never throws: a failed write is a slower cache. */
24
+ put(controller: StorageController, cacheKey: string, bucket: string | undefined, rendition: DurableRendition): Promise<void>;
25
+ }
26
+ /**
27
+ * The object key for a rendition.
28
+ *
29
+ * Hashed rather than derived from the source path: the source key can be up to
30
+ * 1024 characters and the transform options are a JSON object, so a readable
31
+ * composition would exceed key limits on exactly the paths people use — deep
32
+ * per-user prefixes. The source key is kept in the object's metadata, where it
33
+ * is available to anyone auditing the prefix and to nothing on the hot path.
34
+ */
35
+ export declare function renditionKey(cacheKey: string, contentType: string): string;
36
+ /**
37
+ * A rendition's key when reading, where the content type is not yet known.
38
+ *
39
+ * Reads try the known extensions rather than storing an index: four `getObject`
40
+ * calls at worst is still one round trip in the common case — the first
41
+ * candidate is the format the request asked for — and an index is a second
42
+ * thing to keep consistent with the objects it describes.
43
+ */
44
+ export declare function renditionKeyCandidates(cacheKey: string, preferred?: string): string[];
45
+ export declare function createDurableRenditionCache(): DurableRenditionCache;
@@ -7,10 +7,12 @@
7
7
  * is used.
8
8
  */
9
9
  import { Hono } from "hono";
10
- import { StorageController, type StorageAuthorize, type StorageAuthorizeData } from "./types";
11
- import { type StorageRegistry } from "./storage-registry";
10
+ import { StorageController, type StorageAuthorize, type StorageAuthorizeData } from "./types.js";
11
+ import { type StorageRegistry } from "./storage-registry.js";
12
12
  import { type StorageSourceDefinition, type AuthAdapter } from "@rebasepro/types";
13
- import { HonoEnv } from "../api/types";
13
+ import { HonoEnv } from "../api/types.js";
14
+ import { type StorageTrigger } from "./triggers.js";
15
+ import { type RenditionCacheConfig } from "./rendition-cache.js";
14
16
  /**
15
17
  * Decide what to actually serve for a stored content type.
16
18
  *
@@ -24,8 +26,8 @@ export declare function resolveServedContentType(storedContentType: string): {
24
26
  };
25
27
  export interface StorageRoutesConfig {
26
28
  /**
27
- * Single storage controller (backward-compatible).
28
- * Used as fallback when no `registry` is provided.
29
+ * A single storage controller, for a backend with one storage source.
30
+ * Used when no `registry` is provided.
29
31
  */
30
32
  controller?: StorageController;
31
33
  /**
@@ -71,6 +73,16 @@ export interface StorageRoutesConfig {
71
73
  * resolved.
72
74
  */
73
75
  authorizeData?: () => StorageAuthorizeData | undefined;
76
+ /**
77
+ * Keep derived image renditions in the storage source instead of only in
78
+ * this process's memory. Off unless enabled — see `rendition-cache.ts` for
79
+ * why a read that writes to somebody's bucket is not a default.
80
+ */
81
+ renditionCache?: RenditionCacheConfig;
82
+ /**
83
+ * Run something when an object lands, or when one goes. See `triggers.ts`.
84
+ */
85
+ triggers?: StorageTrigger[];
74
86
  }
75
87
  /**
76
88
  * Extract the wildcard portion of a route path from the full request path.
@@ -10,7 +10,7 @@
10
10
  * - String properties use `storageId` in their config to specify which storage to use
11
11
  * - Properties without `storageId` fallback to "(default)"
12
12
  */
13
- import { StorageController } from "./types";
13
+ import { StorageController } from "./types.js";
14
14
  /**
15
15
  * The default storage identifier used when:
16
16
  * - A single storage controller is provided (not a map)
@@ -0,0 +1,66 @@
1
+ /** What happened to the object. */
2
+ export type StorageTriggerEvent = "finalize" | "delete";
3
+ export interface StorageEventContext {
4
+ /** Which event this is. */
5
+ event: StorageTriggerEvent;
6
+ /** The canonical storage key, as written. */
7
+ key: string;
8
+ /** The bucket, when the request named one. */
9
+ bucket?: string;
10
+ /** The storage source the object lives in. */
11
+ storageId: string;
12
+ /** Size in bytes. Present on `finalize`, absent on `delete`. */
13
+ size?: number;
14
+ /** The uploader's declared content type, when there was one. */
15
+ contentType?: string;
16
+ /** Segments captured by `:name` placeholders in the matched path. */
17
+ params: Record<string, string>;
18
+ /** Who made the request, when it was authenticated. */
19
+ user?: {
20
+ uid: string;
21
+ email?: string;
22
+ roles?: string[];
23
+ } | null;
24
+ /** When the event was raised, ISO 8601. */
25
+ at: string;
26
+ }
27
+ export type StorageTriggerHandler = (ctx: StorageEventContext) => void | Promise<void>;
28
+ export interface StorageTrigger {
29
+ /**
30
+ * A key pattern, matched segment by segment against the canonical key.
31
+ *
32
+ * - a literal segment matches itself
33
+ * - `*` matches exactly one segment
34
+ * - `:name` matches one segment and captures it
35
+ * - `**` matches the rest of the key, including nothing, and is only
36
+ * allowed as the final segment
37
+ */
38
+ path: string;
39
+ /** Which events fire it. Defaults to `["finalize"]`. */
40
+ events?: StorageTriggerEvent[];
41
+ /** A name for the logs. Defaults to the pattern. */
42
+ name?: string;
43
+ handler: StorageTriggerHandler;
44
+ }
45
+ /**
46
+ * A request's principal, in the shape a trigger is documented to receive.
47
+ *
48
+ * Hono's `user` variable is typed `AuthResult`, which admits `true` — some
49
+ * middleware sets a bare boolean to mean "authenticated, with no identity
50
+ * attached". A trigger asking "who did this?" is better told `null` than
51
+ * `true`, and better told `null` than a `{}` with no `uid`.
52
+ */
53
+ export declare function triggerUser(raw: unknown): StorageEventContext["user"];
54
+ export declare class StorageTriggerError extends Error {
55
+ constructor(message: string);
56
+ }
57
+ /** Fires the triggers a key and event match. Never throws. */
58
+ export type StorageTriggerDispatcher = (ctx: Omit<StorageEventContext, "params">) => Promise<void>;
59
+ /**
60
+ * Compile a trigger list into a dispatcher, or throw
61
+ * {@link StorageTriggerError}.
62
+ *
63
+ * Compiled at boot so a malformed pattern fails the start rather than the first
64
+ * upload — and, worse, rather than failing *silently* by matching nothing.
65
+ */
66
+ export declare function compileStorageTriggers(triggers: StorageTrigger[]): StorageTriggerDispatcher;
@@ -8,8 +8,8 @@
8
8
  * @see https://tus.io/protocols/resumable-upload
9
9
  */
10
10
  import type { Context } from "hono";
11
- import type { StorageController } from "./types";
12
- import { type StorageRegistry } from "./storage-registry";
11
+ import type { StorageController } from "./types.js";
12
+ import { type StorageRegistry } from "./storage-registry.js";
13
13
  /**
14
14
  * TUS resumable upload handler.
15
15
  *
@@ -33,6 +33,16 @@ export declare class TusHandler {
33
33
  * can only answer for the destination it is told about.
34
34
  */
35
35
  private authorizeUpload?;
36
+ /**
37
+ * Called once the bytes are stored, so storage triggers fire for the
38
+ * resumable path too. A trigger wired only to `POST /upload` would miss
39
+ * exactly the uploads big enough to be worth reacting to.
40
+ *
41
+ * Never throws: the dispatcher swallows handler errors, because the
42
+ * object is already written and failing here would tell the client to
43
+ * repeat a write that succeeded.
44
+ */
45
+ private onFinalized?;
36
46
  private uploads;
37
47
  private tusDir;
38
48
  private cleanupTimer?;
@@ -49,7 +59,28 @@ export declare class TusHandler {
49
59
  * Every routing value the write will use is passed in, because the hook
50
60
  * can only answer for the destination it is told about.
51
61
  */
52
- authorizeUpload?: ((c: Context, key: string, bucket: string, storageId?: string) => Promise<void>) | undefined);
62
+ authorizeUpload?: ((c: Context, key: string, bucket: string, storageId?: string) => Promise<void>) | undefined,
63
+ /**
64
+ * Called once the bytes are stored, so storage triggers fire for the
65
+ * resumable path too. A trigger wired only to `POST /upload` would miss
66
+ * exactly the uploads big enough to be worth reacting to.
67
+ *
68
+ * Never throws: the dispatcher swallows handler errors, because the
69
+ * object is already written and failing here would tell the client to
70
+ * repeat a write that succeeded.
71
+ */
72
+ onFinalized?: ((event: {
73
+ key: string;
74
+ bucket?: string;
75
+ storageId?: string;
76
+ size: number;
77
+ contentType?: string;
78
+ user?: {
79
+ uid: string;
80
+ email?: string;
81
+ roles?: string[];
82
+ } | null;
83
+ }) => Promise<void>) | undefined);
53
84
  /** Ensure the temp directory exists. */
54
85
  private ensureDir;
55
86
  /** Start periodic cleanup of stale uploads. */
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Topics, on the durable job queue.
3
+ *
4
+ * ## Why fan out at publish time
5
+ *
6
+ * Publishing writes **one job row per subscription**, not one row for the
7
+ * event. That is the shape SNS→SQS has and the reason is the same: each
8
+ * subscriber then retries on its own schedule, and a subscription that is
9
+ * broken or slow neither blocks the others nor causes them to run again. A
10
+ * single row per event forces one retry policy across every consumer, so the
11
+ * fourth attempt of a failing audit log re-delivers a welcome email that
12
+ * already went out.
13
+ *
14
+ * It also makes "did this subscriber get it" a row somebody can look at, which
15
+ * is the property the job queue was built for: work that gives up is *kept*,
16
+ * because a queue that silently drops what it could not deliver is
17
+ * indistinguishable from one with nothing to do.
18
+ *
19
+ * ## What at-least-once means here
20
+ *
21
+ * A handler can see the same event twice — a worker that dies holding a job
22
+ * releases it after the visibility timeout, and the next worker starts the
23
+ * handler again from the top. Handlers must tolerate that. It is the honest
24
+ * name for what a retrying queue does, which is why `at-most-once` is refused
25
+ * at declaration rather than quietly given the other guarantee.
26
+ *
27
+ * ## Transactional publish
28
+ *
29
+ * `enqueue` is a row insert, so a publish inside a transaction that rolls back
30
+ * was never published — the difference between "email sent for an order that
31
+ * does not exist" and nothing having happened at all.
32
+ */
33
+ import { type ResourceDeclaration, type TopicRuntime } from "@rebasepro/types";
34
+ import type { JobHandler, JobQueueClient } from "./../jobs/types.js";
35
+ /** The job task name a subscription's deliveries are recorded under. */
36
+ export declare function topicTaskName(topic: string, subscription: string): string;
37
+ /** Attempts a subscription gets before its delivery is left failed. */
38
+ export declare const DEFAULT_TOPIC_ATTEMPTS = 5;
39
+ /**
40
+ * Build the job handlers every declared subscription needs.
41
+ *
42
+ * Returned rather than registered, because the queue takes its whole task map
43
+ * at construction — and a task map assembled in one place is one somebody can
44
+ * print.
45
+ */
46
+ export declare function topicJobHandlers(): Record<string, JobHandler<never>>;
47
+ export interface TopicRuntimeOptions {
48
+ queue: JobQueueClient;
49
+ /** Declared topics, so publishing to an undeclared one can be refused. */
50
+ topics: ResourceDeclaration[];
51
+ }
52
+ /**
53
+ * Build the runtime topics publish through.
54
+ *
55
+ * Publishing to a topic nothing declared throws. The handle API makes it hard
56
+ * to reach, but a string name from an ejected project or a dynamic caller can —
57
+ * and enqueueing under a task no worker handles would write rows nobody ever
58
+ * runs.
59
+ */
60
+ export declare function createTopicRuntime(options: TopicRuntimeOptions): TopicRuntime;
61
+ /**
62
+ * Install the runtime, and hand back a teardown.
63
+ *
64
+ * Publishing without one throws rather than resolving — an event that
65
+ * disappears into a resolved promise is the failure a queue exists to
66
+ * prevent — so it is installed for exactly as long as a backend is up.
67
+ */
68
+ export declare function installTopicRuntime(options: TopicRuntimeOptions): () => void;
@@ -1,6 +1,7 @@
1
- import { createRequire as __createRequire } from "module";
2
- import "process";
3
- __createRequire(import.meta.url);
1
+ import { createRequire as __rebaseCreateRequire } from "module";
2
+ import __rebaseProcess from "process";
3
+ globalThis.process ??= __rebaseProcess;
4
+ __rebaseCreateRequire(import.meta.url);
4
5
  //#region src/storage/types.ts
5
6
  /**
6
7
  * Default maximum file size (50MB)
@@ -35,4 +36,4 @@ var DOCUMENT_MIME_TYPES = [
35
36
  //#endregion
36
37
  export { DOCUMENT_MIME_TYPES as n, IMAGE_MIME_TYPES as r, DEFAULT_MAX_FILE_SIZE as t };
37
38
 
38
- //# sourceMappingURL=types-DSnOC4mF.js.map
39
+ //# sourceMappingURL=types-BfKcm9do.js.map