@rebasepro/server 0.16.0 → 0.16.1-canary.g2d1aec8

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 (160) hide show
  1. package/dist/api/contract-routes.d.ts +1 -1
  2. package/dist/api/errors.d.ts +1 -1
  3. package/dist/api/index.d.ts +3 -3
  4. package/dist/api/live-schema-routes.d.ts +111 -0
  5. package/dist/api/logs-routes.d.ts +1 -1
  6. package/dist/api/mount.d.ts +41 -0
  7. package/dist/api/rest/api-generator.d.ts +2 -2
  8. package/dist/api/rest/index.d.ts +1 -1
  9. package/dist/api/rest/query-parser.d.ts +1 -1
  10. package/dist/api/schema-editor-routes.d.ts +1 -1
  11. package/dist/api/types.d.ts +3 -4
  12. package/dist/{schema-editor-routes-CV9k0w3G.js → ast-schema-editor-BpFYpT38.js} +2 -39
  13. package/dist/ast-schema-editor-BpFYpT38.js.map +1 -0
  14. package/dist/auth/adapter-middleware.d.ts +2 -2
  15. package/dist/auth/admin-roles-route.d.ts +2 -2
  16. package/dist/auth/admin-user-ops.d.ts +3 -3
  17. package/dist/auth/admin-users-route.d.ts +4 -4
  18. package/dist/auth/api-keys/api-key-middleware.d.ts +2 -2
  19. package/dist/auth/api-keys/api-key-permission-guard.d.ts +1 -1
  20. package/dist/auth/api-keys/api-key-routes.d.ts +2 -2
  21. package/dist/auth/api-keys/api-key-store.d.ts +1 -1
  22. package/dist/auth/api-keys/index.d.ts +9 -9
  23. package/dist/auth/apple-oauth.d.ts +2 -2
  24. package/dist/auth/auth-hooks.d.ts +3 -3
  25. package/dist/auth/bitbucket-oauth.d.ts +2 -2
  26. package/dist/auth/builtin-auth-adapter.d.ts +7 -4
  27. package/dist/auth/captcha.d.ts +86 -0
  28. package/dist/auth/cookie-utils.d.ts +2 -2
  29. package/dist/auth/discord-oauth.d.ts +2 -2
  30. package/dist/auth/facebook-oauth.d.ts +2 -2
  31. package/dist/auth/github-oauth.d.ts +2 -2
  32. package/dist/auth/gitlab-oauth.d.ts +2 -2
  33. package/dist/auth/google-oauth.d.ts +1 -1
  34. package/dist/auth/index.d.ts +56 -54
  35. package/dist/auth/jwks-routes.d.ts +1 -1
  36. package/dist/auth/jwt.d.ts +1 -1
  37. package/dist/auth/linkedin-oauth.d.ts +2 -2
  38. package/dist/auth/magic-link-routes.d.ts +10 -3
  39. package/dist/auth/mfa-gate.d.ts +1 -1
  40. package/dist/auth/mfa-routes.d.ts +3 -3
  41. package/dist/auth/microsoft-oauth.d.ts +2 -2
  42. package/dist/auth/middleware.d.ts +5 -5
  43. package/dist/auth/rate-limiter.d.ts +2 -2
  44. package/dist/auth/require-auth.d.ts +1 -1
  45. package/dist/auth/reset-password-admin.d.ts +4 -4
  46. package/dist/auth/routes.d.ts +13 -4
  47. package/dist/auth/session-routes.d.ts +3 -3
  48. package/dist/auth/slack-oauth.d.ts +2 -2
  49. package/dist/auth/spotify-oauth.d.ts +2 -2
  50. package/dist/auth/token-revocation.d.ts +2 -2
  51. package/dist/auth/twitter-oauth.d.ts +2 -2
  52. package/dist/{auth-5Et5mnUA.js → auth-C74dqR2B.js} +250 -15
  53. package/dist/auth-C74dqR2B.js.map +1 -0
  54. package/dist/backup/backup-common.d.ts +1 -1
  55. package/dist/backup/backup-routes.d.ts +3 -3
  56. package/dist/backup/index.d.ts +3 -3
  57. package/dist/{backup-C6ljYVTp.js → backup-BJ86ah4T.js} +2 -2
  58. package/dist/{backup-C6ljYVTp.js.map → backup-BJ86ah4T.js.map} +1 -1
  59. package/dist/boot/boot.d.ts +5 -5
  60. package/dist/boot/bundle.d.ts +1 -1
  61. package/dist/boot/driver.d.ts +1 -1
  62. package/dist/boot/env.d.ts +13 -1
  63. package/dist/boot/options.d.ts +30 -6
  64. package/dist/boot/role.d.ts +3 -2
  65. package/dist/boot/sources.d.ts +1 -1
  66. package/dist/collections/BackendCollectionRegistry.d.ts +1 -1
  67. package/dist/collections/loader.d.ts +1 -1
  68. package/dist/{contract-routes-DZ-LBpSL.js → contract-routes-BEq7euZg.js} +2 -2
  69. package/dist/contract-routes-BEq7euZg.js.map +1 -0
  70. package/dist/cron/cron-routes.d.ts +2 -2
  71. package/dist/cron/cron-scheduler.d.ts +2 -2
  72. package/dist/cron/index.d.ts +8 -8
  73. package/dist/{cron-loader-YhhQeVBM.js → cron-loader-BMvtW6-J.js} +2 -2
  74. package/dist/{cron-loader-YhhQeVBM.js.map → cron-loader-BMvtW6-J.js.map} +1 -1
  75. package/dist/{cron-routes-maM_RlUu.js → cron-routes-BvYk-Kmi.js} +2 -2
  76. package/dist/{cron-routes-maM_RlUu.js.map → cron-routes-BvYk-Kmi.js.map} +1 -1
  77. package/dist/{cron-scheduler-DIpYBmZP.js → cron-scheduler-Buf-uVam.js} +2 -2
  78. package/dist/{cron-scheduler-DIpYBmZP.js.map → cron-scheduler-Buf-uVam.js.map} +1 -1
  79. package/dist/{cron-store-DfH_4Cd9.js → cron-store-CyF-oInV.js} +3 -3
  80. package/dist/{cron-store-DfH_4Cd9.js.map → cron-store-CyF-oInV.js.map} +1 -1
  81. package/dist/{ddl-bootstrap-Cywoj8Ta.js → ddl-bootstrap-DgSnERYN.js} +17 -3
  82. package/dist/ddl-bootstrap-DgSnERYN.js.map +1 -0
  83. package/dist/dev-secrets.d.ts +52 -0
  84. package/dist/email/dev-sink.d.ts +67 -0
  85. package/dist/email/index.d.ts +9 -7
  86. package/dist/email/link-base.d.ts +1 -1
  87. package/dist/email/smtp-email-service.d.ts +1 -1
  88. package/dist/env.d.ts +1 -1
  89. package/dist/{errors-EBYiaJ2E.js → errors-D_LwNKRM.js} +5 -5
  90. package/dist/errors-D_LwNKRM.js.map +1 -0
  91. package/dist/{function-loader-DDS1v7YX.js → function-loader-BrLmC_-y.js} +3 -3
  92. package/dist/{function-loader-DDS1v7YX.js.map → function-loader-BrLmC_-y.js.map} +1 -1
  93. package/dist/functions/context.d.ts +141 -0
  94. package/dist/functions/define-function.d.ts +1 -1
  95. package/dist/functions/function-routes.d.ts +2 -2
  96. package/dist/functions/guards.d.ts +76 -0
  97. package/dist/functions/index.d.ts +96 -5
  98. package/dist/functions/index.js +919 -0
  99. package/dist/functions/index.js.map +1 -0
  100. package/dist/functions/internal.d.ts +26 -0
  101. package/dist/functions/proxy.d.ts +1 -1
  102. package/dist/functions/request-timeout.d.ts +9 -1
  103. package/dist/functions/runtime-env.d.ts +92 -0
  104. package/dist/functions/wait-until.d.ts +74 -0
  105. package/dist/history/history-routes.d.ts +2 -2
  106. package/dist/history/index.d.ts +1 -1
  107. package/dist/index.d.ts +63 -54
  108. package/dist/index.es.js +2183 -164
  109. package/dist/index.es.js.map +1 -1
  110. package/dist/init/docs.d.ts +1 -1
  111. package/dist/init/middlewares.d.ts +1 -1
  112. package/dist/init/shutdown.d.ts +4 -0
  113. package/dist/init/storage.d.ts +1 -1
  114. package/dist/init/surfaces.d.ts +10 -0
  115. package/dist/init.d.ts +104 -18
  116. package/dist/jobs/index.d.ts +5 -5
  117. package/dist/{jobs-CyOKXXlu.js → jobs-Bn6nhY8J.js} +3 -3
  118. package/dist/{jobs-CyOKXXlu.js.map → jobs-Bn6nhY8J.js.map} +1 -1
  119. package/dist/{jwt-DxH9fLPt.js → jwt-BbJi0TR0.js} +2 -2
  120. package/dist/{jwt-DxH9fLPt.js.map → jwt-BbJi0TR0.js.map} +1 -1
  121. package/dist/{logger-DfvF_8r-.js → logger-TdvXIGqR.js} +101 -8
  122. package/dist/logger-TdvXIGqR.js.map +1 -0
  123. package/dist/metrics/index.d.ts +1 -1
  124. package/dist/{proxy-Bj5DVllb.js → proxy-QJKSS-CV.js} +5 -3
  125. package/dist/{proxy-Bj5DVllb.js.map → proxy-QJKSS-CV.js.map} +1 -1
  126. package/dist/{request-timeout-BuFoEKwT.js → request-timeout-OofPCHQT.js} +17 -3
  127. package/dist/request-timeout-OofPCHQT.js.map +1 -0
  128. package/dist/rls-audit/index.d.ts +111 -0
  129. package/dist/schema-edit/apply-schema-change.d.ts +141 -0
  130. package/dist/schema-edit/github-repository.d.ts +67 -0
  131. package/dist/schema-edit/local-git-repository.d.ts +28 -0
  132. package/dist/schema-edit/project-root.d.ts +29 -0
  133. package/dist/schema-edit/remote-source.d.ts +25 -0
  134. package/dist/schema-edit/schema-edit-permissions.d.ts +129 -0
  135. package/dist/schema-editor-routes-yh2cQcdJ.js +44 -0
  136. package/dist/schema-editor-routes-yh2cQcdJ.js.map +1 -0
  137. package/dist/services/webhook-service.d.ts +1 -1
  138. package/dist/singleton.d.ts +7 -0
  139. package/dist/storage/GCSStorageController.d.ts +1 -1
  140. package/dist/storage/LocalStorageController.d.ts +1 -1
  141. package/dist/storage/S3StorageController.d.ts +1 -1
  142. package/dist/storage/cache-headers.d.ts +87 -0
  143. package/dist/storage/index.d.ts +11 -11
  144. package/dist/storage/policies.d.ts +88 -0
  145. package/dist/storage/range.d.ts +63 -0
  146. package/dist/storage/routes.d.ts +3 -3
  147. package/dist/storage/storage-registry.d.ts +1 -1
  148. package/dist/storage/tus-handler.d.ts +2 -2
  149. package/dist/utils/host.d.ts +58 -0
  150. package/dist/utils/logger.d.ts +0 -15
  151. package/dist/utils/request-id.d.ts +1 -1
  152. package/functions/package.json +24 -0
  153. package/package.json +13 -7
  154. package/dist/auth-5Et5mnUA.js.map +0 -1
  155. package/dist/contract-routes-DZ-LBpSL.js.map +0 -1
  156. package/dist/ddl-bootstrap-Cywoj8Ta.js.map +0 -1
  157. package/dist/errors-EBYiaJ2E.js.map +0 -1
  158. package/dist/logger-DfvF_8r-.js.map +0 -1
  159. package/dist/request-timeout-BuFoEKwT.js.map +0 -1
  160. package/dist/schema-editor-routes-CV9k0w3G.js.map +0 -1
@@ -4,15 +4,16 @@ __createRequire(import.meta.url);
4
4
  import { i as __toESM, n as __exportAll } from "./rolldown-runtime-DSJWtz9O.js";
5
5
  import { C as ListLimitError, D as isAnonymousUid, E as ANONYMOUS_USER_ID, T as resolveClientListLimit, a as deserializeLogicalCondition, i as deserializeFilter, r as UnknownFilterOperatorError } from "./src-BPfYOeN4.js";
6
6
  import "./src-CrCxd8km.js";
7
- import { n as createDdlBootstrapper, o as revokeInternalTableSql, s as isSQLAdmin } from "./ddl-bootstrap-Cywoj8Ta.js";
8
- import { S as canonicalStorageId, T as require_jsonwebtoken, _ as verifyMfaPendingToken, a as generateMfaPendingToken, c as getJwks, d as hasAsymmetricSigningKey, f as hashRefreshToken, g as verifyDownloadToken, h as verifyAccessToken, i as generateDownloadToken, l as getRefreshTokenExpiry, n as configureJwt, o as generateRefreshToken, p as isJwtConfigured, r as generateAccessToken, s as getAccessTokenExpiry, t as MAX_COOKIE_AGE_MS, u as getRefreshTokenTtlMs, w as tryCanonicalStorageKey } from "./jwt-DxH9fLPt.js";
9
- import { t as logger } from "./logger-DfvF_8r-.js";
10
- import { n as errorHandler, t as ApiError } from "./errors-EBYiaJ2E.js";
7
+ import { n as createDdlBootstrapper, o as revokeInternalTableSql, s as isSQLAdmin } from "./ddl-bootstrap-DgSnERYN.js";
8
+ import { S as canonicalStorageId, T as require_jsonwebtoken, _ as verifyMfaPendingToken, a as generateMfaPendingToken, c as getJwks, d as hasAsymmetricSigningKey, f as hashRefreshToken, g as verifyDownloadToken, h as verifyAccessToken, i as generateDownloadToken, l as getRefreshTokenExpiry, n as configureJwt, o as generateRefreshToken, p as isJwtConfigured, r as generateAccessToken, s as getAccessTokenExpiry, t as MAX_COOKIE_AGE_MS, u as getRefreshTokenTtlMs, w as tryCanonicalStorageKey } from "./jwt-BbJi0TR0.js";
9
+ import { t as logger } from "./logger-TdvXIGqR.js";
10
+ import { n as errorHandler, t as ApiError } from "./errors-D_LwNKRM.js";
11
11
  import { createHash, randomBytes, randomInt } from "node:crypto";
12
12
  import { Hono } from "hono";
13
13
  import { promisify } from "util";
14
14
  import { createCipheriv, createDecipheriv, createHash as createHash$1, createHmac, createPublicKey, randomBytes as randomBytes$1, randomUUID as randomUUID$1, scrypt, timingSafeEqual } from "crypto";
15
15
  import { getConnInfo } from "@hono/node-server/conninfo";
16
+ import { createMiddleware } from "hono/factory";
16
17
  //#region ../types/src/controllers/storage.ts
17
18
  /**
18
19
  * Path prefix that marks an object as **public**. Files stored under this
@@ -2458,6 +2459,64 @@ function createEmailService(config) {
2458
2459
  return new SMTPEmailService(config);
2459
2460
  }
2460
2461
  //#endregion
2462
+ //#region src/email/dev-sink.ts
2463
+ /** How many messages the ring buffer keeps before dropping the oldest. */
2464
+ var DEFAULT_CAPACITY = 50;
2465
+ /**
2466
+ * Absolute http(s) URLs in a message body.
2467
+ *
2468
+ * Deliberately simple: it reads `href="…"` first, because that is where a real
2469
+ * link lives, and falls back to bare URLs in the text part. Trailing markup and
2470
+ * punctuation are trimmed so the result can be pasted straight into a browser.
2471
+ */
2472
+ function extractLinks(html, text) {
2473
+ const found = [];
2474
+ const push = (url) => {
2475
+ const cleaned = url.replace(/[)\]}>.,;'"]+$/, "");
2476
+ if (cleaned && !found.includes(cleaned)) found.push(cleaned);
2477
+ };
2478
+ if (html) for (const match of html.matchAll(/href\s*=\s*["'](https?:\/\/[^"']+)["']/gi)) push(match[1]);
2479
+ if (text) for (const match of text.matchAll(/https?:\/\/[^\s<>"']+/gi)) push(match[0]);
2480
+ return found;
2481
+ }
2482
+ /** Whatever `to` was given as, rendered for a log line. */
2483
+ var formatRecipient = (to) => Array.isArray(to) ? to.join(", ") : to;
2484
+ /**
2485
+ * Create a sink. Each call is independent, which is what lets a test hold one
2486
+ * without touching whatever the process is using.
2487
+ */
2488
+ function createDevEmailSink(options = {}) {
2489
+ const capacity = options.capacity ?? DEFAULT_CAPACITY;
2490
+ const messages = [];
2491
+ let nextId = 1;
2492
+ let announced = false;
2493
+ const sendEmail = async (mail) => {
2494
+ const captured = {
2495
+ id: nextId++,
2496
+ at: (/* @__PURE__ */ new Date()).toISOString(),
2497
+ to: formatRecipient(mail.to),
2498
+ subject: mail.subject,
2499
+ html: mail.html,
2500
+ text: mail.text,
2501
+ links: extractLinks(mail.html, mail.text)
2502
+ };
2503
+ messages.unshift(captured);
2504
+ if (messages.length > capacity) messages.length = capacity;
2505
+ if (!announced) {
2506
+ announced = true;
2507
+ logger.warn("No SMTP is configured, so auth email is being captured here instead of sent. Follow the link below to complete the flow. Set SMTP_HOST to deliver mail for real; this sink is unavailable when NODE_ENV=production.");
2508
+ }
2509
+ logger.info(`[email] ${captured.subject} → ${captured.to}` + (captured.links.length ? `\n ${captured.links.join("\n ")}` : "\n (no links in this message)"));
2510
+ };
2511
+ return {
2512
+ sendEmail,
2513
+ list: () => [...messages],
2514
+ clear: () => {
2515
+ messages.length = 0;
2516
+ }
2517
+ };
2518
+ }
2519
+ //#endregion
2461
2520
  //#region src/auth/rate-limit-store.ts
2462
2521
  /**
2463
2522
  * The default store: a sliding window in this process's memory.
@@ -2763,6 +2822,174 @@ function createDataRateLimiter(config = {}) {
2763
2822
  });
2764
2823
  }
2765
2824
  //#endregion
2825
+ //#region src/auth/captcha.ts
2826
+ /**
2827
+ * Bot protection on the auth endpoints that cost something to hit.
2828
+ *
2829
+ * Rate limiting was the whole defence, and it bounds a single caller rather
2830
+ * than a botnet: a thousand addresses each sending one request per minute never
2831
+ * touch a per-IP window, and `/auth/register` and `/auth/forgot-password` both
2832
+ * send mail. So the bill for an unprotected signup form is paid in reputation on
2833
+ * a sending domain, not in CPU.
2834
+ *
2835
+ * This adds a challenge the caller has to have solved, verified server-side
2836
+ * against the provider before the route does any work.
2837
+ *
2838
+ * ## Fail closed, and why that is the safe direction here
2839
+ *
2840
+ * If the provider cannot be reached, verification fails and the request is
2841
+ * refused. That is the opposite of the choice made for the RLS audit, which
2842
+ * logs and continues, and the difference is what failure means: an audit that
2843
+ * does not run leaves you where you were, while a challenge that is not checked
2844
+ * leaves the door open for exactly as long as the outage lasts. An attacker who
2845
+ * can cause the outage would otherwise be able to turn the protection off.
2846
+ *
2847
+ * The cost is that a provider outage blocks sign-ups. That is visible, loud, and
2848
+ * recoverable by unsetting one config key — which is a better failure than a
2849
+ * silent one nobody notices until the mail domain is blocklisted.
2850
+ *
2851
+ * ## Why the verifier is injected
2852
+ *
2853
+ * Same reason as the RLS scanner: so the whole thing is testable without a
2854
+ * network, and so no provider SDK enters this package's dependency graph. The
2855
+ * built-in verifier is `fetch` and a form post, which is the entire protocol for
2856
+ * both providers this supports.
2857
+ */
2858
+ var VERIFY_ENDPOINT = {
2859
+ turnstile: "https://challenges.cloudflare.com/turnstile/v0/siteverify",
2860
+ hcaptcha: "https://api.hcaptcha.com/siteverify"
2861
+ };
2862
+ var DEFAULT_CAPTCHA_ROUTES = [
2863
+ "register",
2864
+ "forgotPassword",
2865
+ "magicLink"
2866
+ ];
2867
+ var DEFAULT_TIMEOUT_MS = 5e3;
2868
+ var DEFAULT_TOKEN_FIELD = "captchaToken";
2869
+ /** Header names the two widgets post by convention. */
2870
+ var TOKEN_HEADERS = ["cf-turnstile-response", "h-captcha-response"];
2871
+ /**
2872
+ * The built-in verifier: a form post, which is the whole protocol.
2873
+ *
2874
+ * A non-200, a timeout or a malformed body are all "not verified" rather than
2875
+ * exceptions — the caller cannot distinguish them and should not be told which
2876
+ * one happened.
2877
+ */
2878
+ function createHttpCaptchaVerifier(options) {
2879
+ const endpoint = VERIFY_ENDPOINT[options.provider];
2880
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
2881
+ const doFetch = options.fetchImpl ?? fetch;
2882
+ return async ({ token, remoteIp }) => {
2883
+ const body = new URLSearchParams({
2884
+ secret: options.secret,
2885
+ response: token
2886
+ });
2887
+ if (remoteIp) body.set("remoteip", remoteIp);
2888
+ const controller = new AbortController();
2889
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
2890
+ try {
2891
+ const response = await doFetch(endpoint, {
2892
+ method: "POST",
2893
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
2894
+ body,
2895
+ signal: controller.signal
2896
+ });
2897
+ if (!response.ok) return {
2898
+ success: false,
2899
+ errorCodes: [`http-${response.status}`]
2900
+ };
2901
+ const parsed = await response.json();
2902
+ return {
2903
+ success: parsed?.success === true,
2904
+ errorCodes: parsed?.["error-codes"]
2905
+ };
2906
+ } catch (err) {
2907
+ return {
2908
+ success: false,
2909
+ errorCodes: [err instanceof Error && err.name === "AbortError" ? "timeout" : "unreachable"]
2910
+ };
2911
+ } finally {
2912
+ clearTimeout(timer);
2913
+ }
2914
+ };
2915
+ }
2916
+ /**
2917
+ * Resolve a config into a verifier, or explain why it cannot be used.
2918
+ *
2919
+ * Returns `undefined` when the feature is off. Throws when it is on but
2920
+ * unusable — a misconfigured challenge must not boot as an absent one, because
2921
+ * the whole point is that it is there.
2922
+ */
2923
+ function resolveCaptchaVerifier(config) {
2924
+ if (!config?.enabled) return void 0;
2925
+ if (config.verify) return config.verify;
2926
+ if (!config.provider) throw new Error("Captcha is enabled but no `provider` is set. Use \"turnstile\" or \"hcaptcha\", or supply your own `verify` function.");
2927
+ if (!(config.provider in VERIFY_ENDPOINT)) throw new Error(`Captcha \`provider\` is "${config.provider}", which is not supported. Use "turnstile" or "hcaptcha", or supply your own \`verify\` function.`);
2928
+ if (!config.secret) throw new Error(`Captcha is enabled for ${config.provider} but no \`secret\` is set. The token has to be verified server-side; a widget on its own protects nothing.`);
2929
+ return createHttpCaptchaVerifier({
2930
+ provider: config.provider,
2931
+ secret: config.secret,
2932
+ timeoutMs: config.timeoutMs
2933
+ });
2934
+ }
2935
+ /** Read the challenge token out of a request, body field first, then headers. */
2936
+ async function extractCaptchaToken(c, field) {
2937
+ for (const header of TOKEN_HEADERS) {
2938
+ const value = c.req.header(header);
2939
+ if (value) return value;
2940
+ }
2941
+ try {
2942
+ const value = (await c.req.json())?.[field];
2943
+ return typeof value === "string" && value.length > 0 ? value : void 0;
2944
+ } catch {
2945
+ return;
2946
+ }
2947
+ }
2948
+ /**
2949
+ * Middleware refusing a request whose challenge is missing or unverified.
2950
+ *
2951
+ * The response says only that the challenge failed. Which of "absent",
2952
+ * "malformed", "already used" or "provider unreachable" it was is a log line,
2953
+ * because telling a script which one it hit is telling it how to get closer.
2954
+ */
2955
+ function createCaptchaMiddleware(options) {
2956
+ const field = options.tokenField ?? DEFAULT_TOKEN_FIELD;
2957
+ return createMiddleware(async (c, next) => {
2958
+ const token = await extractCaptchaToken(c, field);
2959
+ if (!token) {
2960
+ logger.debug(`[captcha] ${options.route}: no token on the request`);
2961
+ throw ApiError.badRequest("A captcha challenge is required for this request.", "CAPTCHA_REQUIRED");
2962
+ }
2963
+ const result = await options.verify({
2964
+ token,
2965
+ remoteIp: c.req.header("cf-connecting-ip") ?? void 0
2966
+ });
2967
+ if (!result.success) {
2968
+ logger.warn(`[captcha] ${options.route}: verification failed` + (result.errorCodes?.length ? ` (${result.errorCodes.join(", ")})` : ""));
2969
+ throw ApiError.badRequest("The captcha challenge could not be verified.", "CAPTCHA_FAILED");
2970
+ }
2971
+ await next();
2972
+ });
2973
+ }
2974
+ /**
2975
+ * A middleware per protected route, or an empty map when the feature is off.
2976
+ *
2977
+ * Routes not named get nothing at all rather than a pass-through, so an unnamed
2978
+ * route costs exactly what it did before.
2979
+ */
2980
+ function buildCaptchaMiddlewares(config) {
2981
+ const verify = resolveCaptchaVerifier(config);
2982
+ if (!verify) return {};
2983
+ const routes = config?.routes ?? DEFAULT_CAPTCHA_ROUTES;
2984
+ const middlewares = {};
2985
+ for (const route of routes) middlewares[route] = createCaptchaMiddleware({
2986
+ verify,
2987
+ tokenField: config?.tokenField,
2988
+ route
2989
+ });
2990
+ return middlewares;
2991
+ }
2992
+ //#endregion
2766
2993
  //#region ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/core/core.js
2767
2994
  var _a$1;
2768
2995
  function $constructor(name, initializer, params) {
@@ -7475,7 +7702,7 @@ function mountSessionRoutes(opts) {
7475
7702
  clearRefreshCookie(c, config.cookieAuth);
7476
7703
  const accessToken = extractBearerToken(c.req.header("authorization"));
7477
7704
  if (ops.afterLogout && accessToken !== void 0) {
7478
- const { verifyAccessToken } = await import("./jwt-DxH9fLPt.js").then((n) => n.m);
7705
+ const { verifyAccessToken } = await import("./jwt-BbJi0TR0.js").then((n) => n.m);
7479
7706
  const payload = verifyAccessToken(accessToken);
7480
7707
  if (payload) ops.afterLogout(payload.uid).catch((err) => {
7481
7708
  logger.error("[AuthHooks] afterLogout error", { error: err instanceof Error ? err.message : err });
@@ -7714,7 +7941,7 @@ function getMagicLinkExpiry() {
7714
7941
  * Follows the same delegation pattern as `mountMfaRoutes()` and `mountSessionRoutes()`.
7715
7942
  */
7716
7943
  function mountMagicLinkRoutes(deps) {
7717
- const { router, config, ops, parseBody, buildAuthResponse, createSessionAndTokens, applyTransformHook } = deps;
7944
+ const { router, config, ops, parseBody, buildAuthResponse, createSessionAndTokens, applyTransformHook, captchaMiddleware } = deps;
7718
7945
  const { authRepo, emailService, emailConfig } = config;
7719
7946
  const magicLinkSchema = object({ email: string().email("Invalid email address").max(255) });
7720
7947
  const verifyMagicLinkSchema = object({ token: string().min(1, "Token is required") });
@@ -7725,7 +7952,7 @@ function mountMagicLinkRoutes(deps) {
7725
7952
  * POST /auth/magic-link
7726
7953
  * Request a magic link email
7727
7954
  */
7728
- router.post("/magic-link", strictAuthLimiter, async (c) => {
7955
+ router.post("/magic-link", strictAuthLimiter, ...captchaMiddleware ? [captchaMiddleware] : [], async (c) => {
7729
7956
  const { email } = parseBody(magicLinkSchema, await c.req.json());
7730
7957
  if (!isEmailConfigured()) throw ApiError.serviceUnavailable("Email service not configured. Magic link login is not available.", "EMAIL_NOT_CONFIGURED");
7731
7958
  const user = await authRepo.getUserByEmail(email);
@@ -7877,6 +8104,7 @@ function getPasswordResetExpiry() {
7877
8104
  }
7878
8105
  function createAuthRoutes(config) {
7879
8106
  if (config.defaultRole && isAdministrativeRole(config.defaultRole)) throw new Error(`CRITICAL SECURITY ERROR: defaultRole cannot be '${config.defaultRole}'. Administrative privilege escalation via registration is strictly forbidden (administrative roles: ${ADMINISTRATIVE_ROLES.join(", ")}). Use the POST /admin/bootstrap endpoint to promote the initial administrator.`);
8107
+ const captcha = buildCaptchaMiddlewares(config.captcha);
7880
8108
  const router = new Hono();
7881
8109
  router.onError(errorHandler);
7882
8110
  const authRepo = config.authRepo;
@@ -8017,7 +8245,7 @@ function createAuthRoutes(config) {
8017
8245
  * POST /auth/register
8018
8246
  * Create a new account with email/password
8019
8247
  */
8020
- router.post("/register", defaultAuthLimiter, async (c) => {
8248
+ router.post("/register", defaultAuthLimiter, ...captcha.register ? [captcha.register] : [], async (c) => {
8021
8249
  const { email, password, displayName } = parseBody(registerSchema, await c.req.json());
8022
8250
  if (config.disableSelfRegistration) throw ApiError.forbidden("Registration is disabled", "REGISTRATION_DISABLED");
8023
8251
  let bootstrapRegistration = false;
@@ -8065,7 +8293,7 @@ function createAuthRoutes(config) {
8065
8293
  * POST /auth/login
8066
8294
  * Login with email/password
8067
8295
  */
8068
- router.post("/login", defaultAuthLimiter, async (c) => {
8296
+ router.post("/login", defaultAuthLimiter, ...captcha.login ? [captcha.login] : [], async (c) => {
8069
8297
  const { email, password } = parseBody(loginSchema, await c.req.json());
8070
8298
  if (ops.beforeLogin) await ops.beforeLogin(email, "login");
8071
8299
  let user;
@@ -8222,7 +8450,7 @@ function createAuthRoutes(config) {
8222
8450
  * POST /auth/forgot-password
8223
8451
  * Request password reset email
8224
8452
  */
8225
- router.post("/forgot-password", strictAuthLimiter, async (c) => {
8453
+ router.post("/forgot-password", strictAuthLimiter, ...captcha.forgotPassword ? [captcha.forgotPassword] : [], async (c) => {
8226
8454
  const { email } = parseBody(forgotPasswordSchema, await c.req.json());
8227
8455
  if (!isEmailConfigured()) throw ApiError.serviceUnavailable("Email service not configured. Password reset is not available.", "EMAIL_NOT_CONFIGURED");
8228
8456
  const user = await authRepo.getUserByEmail(email);
@@ -8470,7 +8698,8 @@ function createAuthRoutes(config) {
8470
8698
  parseBody,
8471
8699
  buildAuthResponse,
8472
8700
  createSessionAndTokens,
8473
- applyTransformHook
8701
+ applyTransformHook,
8702
+ captchaMiddleware: captcha.magicLink
8474
8703
  });
8475
8704
  return router;
8476
8705
  }
@@ -8850,7 +9079,7 @@ function createAdminUsersRoute(config) {
8850
9079
  * when the user passes a plain `RebaseAuthConfig` object.
8851
9080
  */
8852
9081
  function createBuiltinAuthAdapter(config) {
8853
- const { authRepository, emailService, emailConfig, allowRegistration = false, disableSelfRegistration = false, allowAnonymous = false, allowUserLookup = false, defaultRole, oauthProviders = [], allowedRedirectUris, serviceKey, authHooks, collectionAuthConfig, enableMagicLink = false, cookieAuth } = config;
9082
+ const { authRepository, emailService, emailConfig, allowRegistration = false, disableSelfRegistration = false, allowAnonymous = false, allowUserLookup = false, captcha, defaultRole, oauthProviders = [], allowedRedirectUris, serviceKey, authHooks, collectionAuthConfig, enableMagicLink = false, cookieAuth } = config;
8854
9083
  const resolvedOps = resolveAuthHooks(authHooks);
8855
9084
  return {
8856
9085
  id: "rebase-builtin",
@@ -8937,7 +9166,8 @@ function createBuiltinAuthAdapter(config) {
8937
9166
  allowedRedirectUris,
8938
9167
  authHooks,
8939
9168
  enableMagicLink,
8940
- cookieAuth
9169
+ cookieAuth,
9170
+ captcha
8941
9171
  });
8942
9172
  },
8943
9173
  createAdminRoutes() {
@@ -10734,9 +10964,11 @@ function createCustomAuthAdapter(options) {
10734
10964
  //#endregion
10735
10965
  //#region src/auth/index.ts
10736
10966
  var auth_exports = /* @__PURE__ */ __exportAll({
10967
+ DEFAULT_CAPTCHA_ROUTES: () => DEFAULT_CAPTCHA_ROUTES,
10737
10968
  MemoryRateLimitStore: () => MemoryRateLimitStore,
10738
10969
  RateLimitStoreConfigurationError: () => RateLimitStoreConfigurationError,
10739
10970
  apiKeyKeyGenerator: () => apiKeyKeyGenerator,
10971
+ buildCaptchaMiddlewares: () => buildCaptchaMiddlewares,
10740
10972
  configureJwt: () => configureJwt,
10741
10973
  createAdapterAuthMiddleware: () => createAdapterAuthMiddleware,
10742
10974
  createApiKeyRoutes: () => createApiKeyRoutes,
@@ -10746,6 +10978,7 @@ var auth_exports = /* @__PURE__ */ __exportAll({
10746
10978
  createAuthRoutes: () => createAuthRoutes,
10747
10979
  createBitbucketProvider: () => createBitbucketProvider,
10748
10980
  createBuiltinAuthAdapter: () => createBuiltinAuthAdapter,
10981
+ createCaptchaMiddleware: () => createCaptchaMiddleware,
10749
10982
  createCustomAuthAdapter: () => createCustomAuthAdapter,
10750
10983
  createDataRateLimiter: () => createDataRateLimiter,
10751
10984
  createDiscordProvider: () => createDiscordProvider,
@@ -10753,6 +10986,7 @@ var auth_exports = /* @__PURE__ */ __exportAll({
10753
10986
  createGitHubProvider: () => createGitHubProvider,
10754
10987
  createGitLabProvider: () => createGitLabProvider,
10755
10988
  createGoogleProvider: () => createGoogleProvider,
10989
+ createHttpCaptchaVerifier: () => createHttpCaptchaVerifier,
10756
10990
  createJwksRoutes: () => createJwksRoutes,
10757
10991
  createLinkedinProvider: () => createLinkedinProvider,
10758
10992
  createMicrosoftProvider: () => createMicrosoftProvider,
@@ -10794,6 +11028,7 @@ var auth_exports = /* @__PURE__ */ __exportAll({
10794
11028
  requireAdmin: () => requireAdmin,
10795
11029
  requireAuth: () => requireAuth,
10796
11030
  resolveAuthHooks: () => resolveAuthHooks,
11031
+ resolveCaptchaVerifier: () => resolveCaptchaVerifier,
10797
11032
  resolveRateLimitStoreKind: () => resolveRateLimitStoreKind,
10798
11033
  safeCompare: () => safeCompare,
10799
11034
  strictAuthLimiter: () => strictAuthLimiter,
@@ -10806,6 +11041,6 @@ var auth_exports = /* @__PURE__ */ __exportAll({
10806
11041
  verifyPassword: () => verifyPassword
10807
11042
  });
10808
11043
  //#endregion
10809
- export { createAdapterAuthMiddleware as $, _coercedNumber as A, validatePasswordStrength as B, providerVerifiedEmail as C, parseQueryOptions as Ct, _enum as D, ZodNumber as E, isPublicStoragePath as Et, createEmailService as F, getPasswordResetTemplate as G, generateSecurePassword as H, assertEmailLinkBases as I, resolveEmailBranding as J, getUserInvitationTemplate as K, resolveEmailLinkBase as L, createDataRateLimiter as M, MemoryRateLimitStore as N, object as O, SMTPEmailService as P, raw as Q, resolveAuthHooks as R, pkceTokenParams as S, parseGroupBy as St, createBuiltinAuthAdapter as T, PUBLIC_STORAGE_PREFIX as Tt, getEmailVerificationTemplate as U, verifyPassword as V, getMagicLinkTemplate as W, escapeHtml as X, RawHtml as Y, html as Z, verifyOidcIdToken as _, scopeDataDriver as _t, resolveRateLimitStoreKind as a, publicObjectAuth as at, createGoogleProvider as b, orderByEntriesToTuples as bt, createSlackProvider as c, requireAuth as ct, createDiscordProvider as d, createStorageApiKeyGuard as dt, createAuthMiddleware as et, createTwitterProvider as f, isApiKeyToken as ft, tryVerifyOidcIdToken as g, SERVICE_IDENTITY as gt, createMicrosoftProvider as h, safeCompare as ht, createApiKeyStore as i, optionalAuth as it, DEFAULT_FUNCTIONS_ANONYMOUS_LIMIT as j, string as k, createBitbucketProvider as l, createApiKeyPreAuth as lt, createAppleProvider as m, extractBearerToken as mt, createCustomAuthAdapter as n, extractUserFromToken as nt, createSqlRateLimitStore as o, queryTokenAuth as ot, createFacebookProvider as p, validateApiKey as pt, getWelcomeEmailTemplate as q, createApiKeyRoutes as r, fileTokenAuth as rt, createSpotifyProvider as s, requireAdmin as st, auth_exports as t, createRequireAuth as tt, createGitLabProvider as u, createFunctionApiKeyGuard as ut, createGitHubProvider as v, httpMethodToOperation as vt, createJwksRoutes as w, resolveListLimitParam as wt, oauthCodeFlowSchema as x, parseAggregateSelect as xt, createLinkedinProvider as y, isOperationAllowed as yt, hashPassword as z };
11044
+ export { html as $, _coercedNumber as A, resolveAuthHooks as B, providerVerifiedEmail as C, orderByEntriesToTuples as Ct, _enum as D, resolveListLimitParam as Dt, ZodNumber as E, parseQueryOptions as Et, extractLinks as F, getEmailVerificationTemplate as G, validatePasswordStrength as H, SMTPEmailService as I, getUserInvitationTemplate as J, getMagicLinkTemplate as K, createEmailService as L, createDataRateLimiter as M, MemoryRateLimitStore as N, object as O, PUBLIC_STORAGE_PREFIX as Ot, createDevEmailSink as P, escapeHtml as Q, assertEmailLinkBases as R, pkceTokenParams as S, isOperationAllowed as St, createBuiltinAuthAdapter as T, parseGroupBy as Tt, verifyPassword as U, hashPassword as V, generateSecurePassword as W, resolveEmailBranding as X, getWelcomeEmailTemplate as Y, RawHtml as Z, verifyOidcIdToken as _, safeCompare as _t, resolveRateLimitStoreKind as a, fileTokenAuth as at, createGoogleProvider as b, hasAdministrativeRole as bt, createSlackProvider as c, queryTokenAuth as ct, createDiscordProvider as d, createApiKeyPreAuth as dt, raw as et, createTwitterProvider as f, createFunctionApiKeyGuard as ft, tryVerifyOidcIdToken as g, extractBearerToken as gt, createMicrosoftProvider as h, validateApiKey as ht, createApiKeyStore as i, extractUserFromToken as it, DEFAULT_FUNCTIONS_ANONYMOUS_LIMIT as j, string as k, isPublicStoragePath as kt, createBitbucketProvider as l, requireAdmin as lt, createAppleProvider as m, isApiKeyToken as mt, createCustomAuthAdapter as n, createAuthMiddleware as nt, createSqlRateLimitStore as o, optionalAuth as ot, createFacebookProvider as p, createStorageApiKeyGuard as pt, getPasswordResetTemplate as q, createApiKeyRoutes as r, createRequireAuth as rt, createSpotifyProvider as s, publicObjectAuth as st, auth_exports as t, createAdapterAuthMiddleware as tt, createGitLabProvider as u, requireAuth as ut, createGitHubProvider as v, SERVICE_IDENTITY as vt, createJwksRoutes as w, parseAggregateSelect as wt, oauthCodeFlowSchema as x, httpMethodToOperation as xt, createLinkedinProvider as y, scopeDataDriver as yt, resolveEmailLinkBase as z };
10810
11045
 
10811
- //# sourceMappingURL=auth-5Et5mnUA.js.map
11046
+ //# sourceMappingURL=auth-C74dqR2B.js.map