@rebasepro/server 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/auth/api-keys/api-key-middleware.d.ts +1 -1
  2. package/dist/auth/jwt.d.ts +1 -1
  3. package/dist/auth/rls-scope.d.ts +1 -1
  4. package/dist/{auth-DIKS1rsI.js → auth-5Et5mnUA.js} +141 -32
  5. package/dist/auth-5Et5mnUA.js.map +1 -0
  6. package/dist/boot/boot.d.ts +17 -1
  7. package/dist/boot/env.d.ts +1 -0
  8. package/dist/boot/provision.d.ts +1 -1
  9. package/dist/boot/schema-stamp.d.ts +173 -0
  10. package/dist/boot/version-skew.d.ts +27 -3
  11. package/dist/{contract-routes-DDNj4_J1.js → contract-routes-DZ-LBpSL.js} +2 -2
  12. package/dist/contract-routes-DZ-LBpSL.js.map +1 -0
  13. package/dist/cron/define-cron.d.ts +1 -1
  14. package/dist/{cron-store-BBGvOA-9.js → cron-store-DfH_4Cd9.js} +2 -2
  15. package/dist/{cron-store-BBGvOA-9.js.map → cron-store-DfH_4Cd9.js.map} +1 -1
  16. package/dist/ddl-bootstrap-Cywoj8Ta.js.map +1 -1
  17. package/dist/email/index.d.ts +1 -0
  18. package/dist/email/templates.d.ts +21 -5
  19. package/dist/email/types.d.ts +14 -0
  20. package/dist/functions/define-function.d.ts +1 -1
  21. package/dist/index.es.js +386 -35
  22. package/dist/index.es.js.map +1 -1
  23. package/dist/init/surfaces.d.ts +8 -1
  24. package/dist/{jobs-BOEOIGAm.js → jobs-CyOKXXlu.js} +2 -2
  25. package/dist/{jobs-BOEOIGAm.js.map → jobs-CyOKXXlu.js.map} +1 -1
  26. package/dist/jwt-DxH9fLPt.js.map +1 -1
  27. package/dist/{openapi-generator-DLiiGD9X.js → openapi-generator-BCKJRUS4.js} +2 -2
  28. package/dist/{openapi-generator-DLiiGD9X.js.map → openapi-generator-BCKJRUS4.js.map} +1 -1
  29. package/dist/singleton.d.ts +1 -1
  30. package/dist/{src-B-E7RjdN.js → src-BPfYOeN4.js} +3 -3
  31. package/dist/src-BPfYOeN4.js.map +1 -0
  32. package/dist/src-CrCxd8km.js.map +1 -1
  33. package/package.json +5 -5
  34. package/dist/auth-DIKS1rsI.js.map +0 -1
  35. package/dist/contract-routes-DDNj4_J1.js.map +0 -1
  36. package/dist/src-B-E7RjdN.js.map +0 -1
@@ -17,7 +17,7 @@
17
17
  * injected `default_admin` policies; a non-admin key (roles `["service"]`,
18
18
  * uid `api-key:<id>`) only sees rows that a policy explicitly grants to the
19
19
  * `service` role or to the public. Owner-style policies
20
- * (`owner_id = auth.uid()`) never match an API key's synthetic uid.
20
+ * (`owner_id = rebase.uid()`) never match an API key's synthetic uid.
21
21
  *
22
22
  * @module
23
23
  */
@@ -25,7 +25,7 @@ export interface JwtConfig {
25
25
  export interface AccessTokenPayload {
26
26
  /**
27
27
  * The user's id — the same spelling the domain model, the auth adapters and
28
- * the RLS layer (`auth.uid()`) all use. Tokens minted before this rename
28
+ * the RLS layer (`rebase.uid()`) all use. Tokens minted before this rename
29
29
  * carry `uid` instead, and older external IdPs may send `sub`;
30
30
  * {@link verifyAccessToken} accepts all three and normalises to this.
31
31
  */
@@ -25,7 +25,7 @@ import type { DataDriver } from "@rebasepro/types";
25
25
  *
26
26
  * - It passes the default policies through their `rolesOverlap(['admin'])`
27
27
  * arm — the same arm an application user holding the `admin` role passes.
28
- * - `policy.serverContext()` compiles to `auth.uid() IS NULL` and is therefore
28
+ * - `policy.serverContext()` compiles to `rebase.uid() IS NULL` and is therefore
29
29
  * **false** for it. A collection with `disableDefaultPolicies: true` whose
30
30
  * only rule is `serverContext()` denies these writes (42501) and returns
31
31
  * zero rows for these reads.
@@ -2,7 +2,7 @@ import { createRequire as __createRequire } from "module";
2
2
  import process from "process";
3
3
  __createRequire(import.meta.url);
4
4
  import { i as __toESM, n as __exportAll } from "./rolldown-runtime-DSJWtz9O.js";
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-B-E7RjdN.js";
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
7
  import { n as createDdlBootstrapper, o as revokeInternalTableSql, s as isSQLAdmin } from "./ddl-bootstrap-Cywoj8Ta.js";
8
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";
@@ -589,7 +589,7 @@ var import_jsonwebtoken = /* @__PURE__ */ __toESM(require_jsonwebtoken(), 1);
589
589
  *
590
590
  * - It passes the default policies through their `rolesOverlap(['admin'])`
591
591
  * arm — the same arm an application user holding the `admin` role passes.
592
- * - `policy.serverContext()` compiles to `auth.uid() IS NULL` and is therefore
592
+ * - `policy.serverContext()` compiles to `rebase.uid() IS NULL` and is therefore
593
593
  * **false** for it. A collection with `disableDefaultPolicies: true` whose
594
594
  * only rule is `serverContext()` denies these writes (42501) and returns
595
595
  * zero rows for these reads.
@@ -710,7 +710,7 @@ function extractBearerToken(authHeader) {
710
710
  * injected `default_admin` policies; a non-admin key (roles `["service"]`,
711
711
  * uid `api-key:<id>`) only sees rows that a policy explicitly grants to the
712
712
  * `service` role or to the public. Owner-style policies
713
- * (`owner_id = auth.uid()`) never match an API key's synthetic uid.
713
+ * (`owner_id = rebase.uid()`) never match an API key's synthetic uid.
714
714
  *
715
715
  * @module
716
716
  */
@@ -1561,9 +1561,90 @@ var styles = {
1561
1561
  `)
1562
1562
  };
1563
1563
  /**
1564
+ * The app name every template falls back to when `email.appName` is unset.
1565
+ */
1566
+ var DEFAULT_APP_NAME = "Rebase";
1567
+ /**
1568
+ * The Rebase mark, hosted. Mail clients do not render SVG (Gmail strips it
1569
+ * outright), so this is the PNG rather than `logo.svg`, and it is an absolute
1570
+ * URL rather than a data URI because Gmail blocks those in `<img src>` too.
1571
+ */
1572
+ var REBASE_LOGO_URL = "https://rebase.pro/img/logo_small.png";
1573
+ /**
1574
+ * Whether a configured logo can actually be fetched by a mail client.
1575
+ *
1576
+ * A relative path, a `data:` URI or a `file:` URL all produce a broken image in
1577
+ * the recipient's inbox rather than an error anyone would see, so an unusable
1578
+ * value renders no logo at all instead. This is deliberately not a boot-time
1579
+ * assert like `assertEmailLinkBases`: a dead link makes the mail useless, a
1580
+ * missing logo only makes it plain.
1581
+ */
1582
+ function isMailableImageUrl(url) {
1583
+ if (!url) return false;
1584
+ try {
1585
+ const { protocol } = new URL(url);
1586
+ return protocol === "https:" || protocol === "http:";
1587
+ } catch {
1588
+ return false;
1589
+ }
1590
+ }
1591
+ /**
1592
+ * Whether this config still carries the stock app name — i.e. nobody has
1593
+ * branded this install.
1594
+ *
1595
+ * Testing against the *value* rather than against `undefined` is deliberate and
1596
+ * load-bearing: `APP_NAME` is declared `z.string().default("Rebase")` in the
1597
+ * cloud, in the demo app and in the eject template, so `appName` arrives here as
1598
+ * the literal string "Rebase" even when the operator set nothing. Keying the
1599
+ * fallback on `appName === undefined` would mean the logo never appeared in any
1600
+ * Rebase-run product.
1601
+ */
1602
+ function isUnbranded(appName) {
1603
+ return !appName || appName === DEFAULT_APP_NAME;
1604
+ }
1605
+ /**
1606
+ * Resolve the branding a template should render from the email config.
1607
+ *
1608
+ * The fallback is asymmetric on purpose. `appName` falls back to "Rebase"
1609
+ * because an unconfigured app has no better name to show. The *logo* is stricter:
1610
+ * an app that named itself "Acme" but set no `logoUrl` gets no logo at all,
1611
+ * because the alternative is mailing Acme's users a Rebase mark from Acme's
1612
+ * domain. Only an install that has not renamed itself is treated as Rebase's own.
1613
+ */
1614
+ function resolveEmailBranding(config) {
1615
+ const logoUrl = config?.logoUrl ?? (isUnbranded(config?.appName) ? REBASE_LOGO_URL : void 0);
1616
+ return {
1617
+ appName: config?.appName || DEFAULT_APP_NAME,
1618
+ logoUrl: isMailableImageUrl(logoUrl) ? logoUrl : void 0
1619
+ };
1620
+ }
1621
+ /**
1622
+ * The logo block that sits above the card in every default template.
1623
+ *
1624
+ * `width`/`height` are attributes as well as CSS because Outlook's Word renderer
1625
+ * ignores the style block. `alt` carries the app name, for screen readers and
1626
+ * for the very common case of a client that blocks remote images on a first
1627
+ * email from an unknown sender.
1628
+ *
1629
+ * The type styling on the `<img>` is what that blocked case falls back to: alt
1630
+ * text inherits it, and at the body's 16px "Rebase" overflows a 48px box and
1631
+ * renders as "Rebas". 12px plus `height: auto` — CSS the clients that block
1632
+ * images do read, while Outlook still sizes from the attributes — lets the name
1633
+ * through intact.
1634
+ */
1635
+ function renderHeader(appName, logoUrl) {
1636
+ if (!logoUrl) return raw("");
1637
+ return html`
1638
+ <div style="text-align: center; margin-bottom: 24px;">
1639
+ <img src="${logoUrl}" width="48" height="48" alt="${appName}"
1640
+ style="display: inline-block; width: 48px; height: auto; border: 0; outline: none; text-decoration: none; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; font-size: 12px; color: #64748b;">
1641
+ </div>
1642
+ `;
1643
+ }
1644
+ /**
1564
1645
  * Default password reset email template
1565
1646
  */
1566
- function getPasswordResetTemplate(resetUrl, user, appName = "Rebase") {
1647
+ function getPasswordResetTemplate(resetUrl, user, appName = DEFAULT_APP_NAME, logoUrl) {
1567
1648
  const greeting = getGreeting(user);
1568
1649
  const subject = `Reset your ${appName} password`;
1569
1650
  const body = html`
@@ -1575,7 +1656,7 @@ function getPasswordResetTemplate(resetUrl, user, appName = "Rebase") {
1575
1656
  <title>${subject}</title>
1576
1657
  </head>
1577
1658
  <body style="margin: 0; padding: 0; background-color: #f8fafc;">
1578
- <div style="${styles.container}">
1659
+ <div style="${styles.container}">${renderHeader(appName, logoUrl)}
1579
1660
  <div style="${styles.card}">
1580
1661
  <h1 style="${styles.heading}">Reset Your Password</h1>
1581
1662
 
@@ -1638,7 +1719,7 @@ Your password will remain unchanged.
1638
1719
  /**
1639
1720
  * Default email verification template
1640
1721
  */
1641
- function getEmailVerificationTemplate(verifyUrl, user, appName = "Rebase") {
1722
+ function getEmailVerificationTemplate(verifyUrl, user, appName = DEFAULT_APP_NAME, logoUrl) {
1642
1723
  const greeting = getGreeting(user);
1643
1724
  const subject = `Verify your ${appName} email address`;
1644
1725
  const body = html`
@@ -1650,7 +1731,7 @@ function getEmailVerificationTemplate(verifyUrl, user, appName = "Rebase") {
1650
1731
  <title>${subject}</title>
1651
1732
  </head>
1652
1733
  <body style="margin: 0; padding: 0; background-color: #f8fafc;">
1653
- <div style="${styles.container}">
1734
+ <div style="${styles.container}">${renderHeader(appName, logoUrl)}
1654
1735
  <div style="${styles.card}">
1655
1736
  <h1 style="${styles.heading}">Verify Your Email</h1>
1656
1737
 
@@ -1704,7 +1785,7 @@ If you didn't create an account with ${appName}, you can safely ignore this emai
1704
1785
  * Default user invitation email template
1705
1786
  * Sent when an admin creates a new user account
1706
1787
  */
1707
- function getUserInvitationTemplate(setPasswordUrl, user, appName = "Rebase") {
1788
+ function getUserInvitationTemplate(setPasswordUrl, user, appName = DEFAULT_APP_NAME, logoUrl) {
1708
1789
  const greeting = getGreeting(user);
1709
1790
  const subject = `You've been invited to ${appName}`;
1710
1791
  const body = html`
@@ -1716,7 +1797,7 @@ function getUserInvitationTemplate(setPasswordUrl, user, appName = "Rebase") {
1716
1797
  <title>${subject}</title>
1717
1798
  </head>
1718
1799
  <body style="margin: 0; padding: 0; background-color: #f8fafc;">
1719
- <div style="${styles.container}">
1800
+ <div style="${styles.container}">${renderHeader(appName, logoUrl)}
1720
1801
  <div style="${styles.card}">
1721
1802
  <h1 style="${styles.heading}">Welcome to ${appName}!</h1>
1722
1803
 
@@ -1778,7 +1859,7 @@ If you weren't expecting this invitation, you can safely ignore this email.
1778
1859
  * Default welcome email template
1779
1860
  * Sent automatically when a new user registers
1780
1861
  */
1781
- function getWelcomeEmailTemplate(user, appName = "Rebase", loginUrl) {
1862
+ function getWelcomeEmailTemplate(user, appName = DEFAULT_APP_NAME, loginUrl, logoUrl) {
1782
1863
  const greeting = getGreeting(user);
1783
1864
  const url = loginUrl || "";
1784
1865
  const subject = `¡Bienvenido/a a ${appName}!`;
@@ -1791,7 +1872,7 @@ function getWelcomeEmailTemplate(user, appName = "Rebase", loginUrl) {
1791
1872
  <title>${subject}</title>
1792
1873
  </head>
1793
1874
  <body style="margin: 0; padding: 0; background-color: #f8fafc;">
1794
- <div style="${styles.container}">
1875
+ <div style="${styles.container}">${renderHeader(appName, logoUrl)}
1795
1876
  <div style="${styles.card}">
1796
1877
  <h1 style="${styles.heading}">¡Bienvenido/a a ${appName}!</h1>
1797
1878
 
@@ -1851,7 +1932,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
1851
1932
  /**
1852
1933
  * Default magic link email template
1853
1934
  */
1854
- function getMagicLinkTemplate(magicLinkUrl, user, appName = "Rebase") {
1935
+ function getMagicLinkTemplate(magicLinkUrl, user, appName = DEFAULT_APP_NAME, logoUrl) {
1855
1936
  const greeting = getGreeting(user);
1856
1937
  const subject = `Sign in to ${appName}`;
1857
1938
  const body = html`
@@ -1863,7 +1944,7 @@ function getMagicLinkTemplate(magicLinkUrl, user, appName = "Rebase") {
1863
1944
  <title>${subject}</title>
1864
1945
  </head>
1865
1946
  <body style="margin: 0; padding: 0; background-color: #f8fafc;">
1866
- <div style="${styles.container}">
1947
+ <div style="${styles.container}">${renderHeader(appName, logoUrl)}
1867
1948
  <div style="${styles.card}">
1868
1949
  <h1 style="${styles.heading}">Sign In to ${appName}</h1>
1869
1950
 
@@ -2054,7 +2135,7 @@ async function finalizeAdminUserCreation(entity, clearPassword, ctx) {
2054
2135
  const expiresAt = new Date(Date.now() + 1440 * 60 * 1e3);
2055
2136
  await ctx.authRepo.createPasswordResetToken(entity.id, tokenHash, expiresAt);
2056
2137
  const setPasswordUrl = `${ctx.emailConfig?.resetPasswordUrl || ""}/reset-password?token=${token}`;
2057
- const appName = ctx.emailConfig?.appName || "Rebase";
2138
+ const { appName, logoUrl } = resolveEmailBranding(ctx.emailConfig);
2058
2139
  const templateFn = ctx.emailConfig?.templates?.userInvitation;
2059
2140
  const emailContent = templateFn ? templateFn(setPasswordUrl, {
2060
2141
  email: entity.values.email,
@@ -2062,7 +2143,7 @@ async function finalizeAdminUserCreation(entity, clearPassword, ctx) {
2062
2143
  }) : getUserInvitationTemplate(setPasswordUrl, {
2063
2144
  email: entity.values.email,
2064
2145
  displayName: entity.values.displayName
2065
- }, appName);
2146
+ }, appName, logoUrl);
2066
2147
  await ctx.emailService.send({
2067
2148
  to: entity.values.email,
2068
2149
  subject: emailContent.subject,
@@ -2633,6 +2714,36 @@ var DEFAULT_FUNCTIONS_ANONYMOUS_LIMIT = 3e3;
2633
2714
  function createDataRateLimiter(config = {}) {
2634
2715
  const { windowMs = 900 * 1e3, apiKey: apiKeyLimit = 1e3, user: userLimit = 1e3, anonymous: anonLimit = 300, store = new MemoryRateLimitStore(windowMs) } = config;
2635
2716
  const trustedProxyHops = resolveTrustedProxyHops(config.trustedProxyHops);
2717
+ /**
2718
+ * Who this request is, for bucketing only.
2719
+ *
2720
+ * `c.get("user")` is the answer whenever an auth middleware has already run.
2721
+ * On the storage router it has not: the limiter is registered before
2722
+ * `route("/")`, and the JWT middlewares live inside those routes, so a
2723
+ * signed-in caller reached here indistinguishable from an anonymous one and
2724
+ * was bucketed `ip:` at the anonymous allowance. Everyone behind one NAT
2725
+ * shared 300 requests per window, and the admin panel — which mints a
2726
+ * download token per file — spent them on a single page of thumbnails.
2727
+ *
2728
+ * Reading the token here rather than moving the limiter is deliberate. The
2729
+ * limiter has to run *before* the routes to guard them, and pre-resolving
2730
+ * the user into the context instead would change authorization: the storage
2731
+ * adapter path enforces on `c.get("user")` when its own `verifyRequest`
2732
+ * finds nobody, so seeding that key would let a Rebase-signed JWT satisfy a
2733
+ * deployment that delegates auth to Firebase or Clerk. Nothing here writes
2734
+ * to the context; an identity that fails to verify simply buckets by IP,
2735
+ * exactly as before.
2736
+ */
2737
+ const identify = (c) => {
2738
+ const user = c.get("user");
2739
+ if (user?.uid) return isAnonymousUid(user.uid) ? void 0 : user.uid;
2740
+ if (!isJwtConfigured()) return void 0;
2741
+ const token = extractBearerToken(c.req.header("authorization"));
2742
+ if (token === void 0) return void 0;
2743
+ const payload = verifyAccessToken(token);
2744
+ if (!payload?.uid || isAnonymousUid(payload.uid)) return void 0;
2745
+ return payload.uid;
2746
+ };
2636
2747
  return createRateLimiter({
2637
2748
  windowMs,
2638
2749
  store,
@@ -2640,16 +2751,14 @@ function createDataRateLimiter(config = {}) {
2640
2751
  keyGenerator: (c) => {
2641
2752
  const key = c.get("apiKey");
2642
2753
  if (key) return `api-key:${key.id}`;
2643
- const user = c.get("user");
2644
- if (user?.uid && !isAnonymousUid(user.uid)) return `user:${user.uid}`;
2754
+ const uid = identify(c);
2755
+ if (uid) return `user:${uid}`;
2645
2756
  return `ip:${defaultKeyGenerator(c, trustedProxyHops)}`;
2646
2757
  },
2647
2758
  resolveLimit: (c) => {
2648
2759
  const key = c.get("apiKey");
2649
2760
  if (key) return key.rate_limit ?? apiKeyLimit;
2650
- const user = c.get("user");
2651
- if (user?.uid && !isAnonymousUid(user.uid)) return userLimit;
2652
- return anonLimit;
2761
+ return identify(c) ? userLimit : anonLimit;
2653
2762
  }
2654
2763
  });
2655
2764
  }
@@ -7627,7 +7736,7 @@ function mountMagicLinkRoutes(deps) {
7627
7736
  const expiresAt = getMagicLinkExpiry();
7628
7737
  await authRepo.createMagicLinkToken(user.id, tokenHash, expiresAt);
7629
7738
  const magicLinkUrl = `${resolveEmailLinkBase(emailConfig, "magicLink")}/auth/magic-link?token=${token}`;
7630
- const appName = emailConfig?.appName || "Rebase";
7739
+ const { appName, logoUrl } = resolveEmailBranding(emailConfig);
7631
7740
  const templateFn = emailConfig?.templates?.magicLink;
7632
7741
  const emailContent = templateFn ? templateFn(magicLinkUrl, {
7633
7742
  email: user.email,
@@ -7635,7 +7744,7 @@ function mountMagicLinkRoutes(deps) {
7635
7744
  }) : getMagicLinkTemplate(magicLinkUrl, {
7636
7745
  email: user.email,
7637
7746
  displayName: user.displayName
7638
- }, appName);
7747
+ }, appName, logoUrl);
7639
7748
  try {
7640
7749
  await emailService.send({
7641
7750
  to: user.email,
@@ -7849,10 +7958,10 @@ function createAuthRoutes(config) {
7849
7958
  */
7850
7959
  function sendWelcomeEmail(user) {
7851
7960
  if (!isEmailConfigured()) return;
7852
- const appName = emailConfig?.appName || "Rebase";
7961
+ const { appName, logoUrl } = resolveEmailBranding(emailConfig);
7853
7962
  const loginUrl = resolveEmailLinkBase(emailConfig, "resetPassword");
7854
7963
  const templateFn = emailConfig?.templates?.welcomeEmail;
7855
- const emailContent = templateFn ? templateFn(user, appName) : getWelcomeEmailTemplate(user, appName, loginUrl ? `${loginUrl}/app` : void 0);
7964
+ const emailContent = templateFn ? templateFn(user, appName) : getWelcomeEmailTemplate(user, appName, loginUrl ? `${loginUrl}/app` : void 0, logoUrl);
7856
7965
  emailService.send({
7857
7966
  to: user.email,
7858
7967
  subject: emailContent.subject,
@@ -8123,7 +8232,7 @@ function createAuthRoutes(config) {
8123
8232
  const expiresAt = getPasswordResetExpiry();
8124
8233
  await authRepo.createPasswordResetToken(user.id, tokenHash, expiresAt);
8125
8234
  const resetUrl = `${resolveEmailLinkBase(emailConfig, "resetPassword")}/reset-password?token=${token}`;
8126
- const appName = emailConfig?.appName || "Rebase";
8235
+ const { appName, logoUrl } = resolveEmailBranding(emailConfig);
8127
8236
  const templateFn = emailConfig?.templates?.passwordReset;
8128
8237
  const emailContent = templateFn ? templateFn(resetUrl, {
8129
8238
  email: user.email,
@@ -8131,7 +8240,7 @@ function createAuthRoutes(config) {
8131
8240
  }) : getPasswordResetTemplate(resetUrl, {
8132
8241
  email: user.email,
8133
8242
  displayName: user.displayName
8134
- }, appName);
8243
+ }, appName, logoUrl);
8135
8244
  try {
8136
8245
  await emailService.send({
8137
8246
  to: user.email,
@@ -8215,7 +8324,7 @@ function createAuthRoutes(config) {
8215
8324
  const token = generateSecureToken();
8216
8325
  await authRepo.setVerificationToken(user.id, hashToken(token));
8217
8326
  const verifyUrl = `${resolveEmailLinkBase(emailConfig, "verifyEmail")}/verify-email?token=${token}`;
8218
- const appName = emailConfig?.appName || "Rebase";
8327
+ const { appName, logoUrl } = resolveEmailBranding(emailConfig);
8219
8328
  const templateFn = emailConfig?.templates?.emailVerification;
8220
8329
  const emailContent = templateFn ? templateFn(verifyUrl, {
8221
8330
  email: user.email,
@@ -8223,7 +8332,7 @@ function createAuthRoutes(config) {
8223
8332
  }) : getEmailVerificationTemplate(verifyUrl, {
8224
8333
  email: user.email,
8225
8334
  displayName: user.displayName
8226
- }, appName);
8335
+ }, appName, logoUrl);
8227
8336
  await emailService.send({
8228
8337
  to: user.email,
8229
8338
  subject: emailContent.subject,
@@ -8432,7 +8541,7 @@ function createResetPasswordRoute(config) {
8432
8541
  const expiresAt = new Date(Date.now() + 3600 * 1e3);
8433
8542
  await authRepo.createPasswordResetToken(existing.id, tokenHash, expiresAt);
8434
8543
  const setPasswordUrl = `${emailConfig?.resetPasswordUrl || ""}/reset-password?token=${token}`;
8435
- const appName = emailConfig?.appName || "Rebase";
8544
+ const { appName, logoUrl } = resolveEmailBranding(emailConfig);
8436
8545
  const templateFn = emailConfig?.templates?.passwordReset;
8437
8546
  const emailContent = templateFn ? templateFn(setPasswordUrl, {
8438
8547
  email: existing.email,
@@ -8440,7 +8549,7 @@ function createResetPasswordRoute(config) {
8440
8549
  }) : getPasswordResetTemplate(setPasswordUrl, {
8441
8550
  email: existing.email,
8442
8551
  displayName: existing.displayName
8443
- }, appName);
8552
+ }, appName, logoUrl);
8444
8553
  await emailService.send({
8445
8554
  to: existing.email,
8446
8555
  subject: emailContent.subject,
@@ -10697,6 +10806,6 @@ var auth_exports = /* @__PURE__ */ __exportAll({
10697
10806
  verifyPassword: () => verifyPassword
10698
10807
  });
10699
10808
  //#endregion
10700
- export { createAuthMiddleware as $, _coercedNumber as A, validatePasswordStrength as B, providerVerifiedEmail as C, resolveListLimitParam as Ct, _enum as D, ZodNumber as E, createEmailService as F, getPasswordResetTemplate as G, generateSecurePassword as H, assertEmailLinkBases as I, RawHtml as J, getUserInvitationTemplate as K, resolveEmailLinkBase as L, createDataRateLimiter as M, MemoryRateLimitStore as N, object as O, SMTPEmailService as P, createAdapterAuthMiddleware as Q, resolveAuthHooks as R, pkceTokenParams as S, parseQueryOptions as St, createBuiltinAuthAdapter as T, isPublicStoragePath as Tt, getEmailVerificationTemplate as U, verifyPassword as V, getMagicLinkTemplate as W, html as X, escapeHtml as Y, raw as Z, verifyOidcIdToken as _, httpMethodToOperation as _t, resolveRateLimitStoreKind as a, queryTokenAuth as at, createGoogleProvider as b, parseAggregateSelect as bt, createSlackProvider as c, createApiKeyPreAuth as ct, createDiscordProvider as d, isApiKeyToken as dt, createRequireAuth as et, createTwitterProvider as f, validateApiKey as ft, tryVerifyOidcIdToken as g, scopeDataDriver as gt, createMicrosoftProvider as h, SERVICE_IDENTITY as ht, createApiKeyStore as i, publicObjectAuth as it, DEFAULT_FUNCTIONS_ANONYMOUS_LIMIT as j, string as k, createBitbucketProvider as l, createFunctionApiKeyGuard as lt, createAppleProvider as m, safeCompare as mt, createCustomAuthAdapter as n, fileTokenAuth as nt, createSqlRateLimitStore as o, requireAdmin as ot, createFacebookProvider as p, extractBearerToken as pt, getWelcomeEmailTemplate as q, createApiKeyRoutes as r, optionalAuth as rt, createSpotifyProvider as s, requireAuth as st, auth_exports as t, extractUserFromToken as tt, createGitLabProvider as u, createStorageApiKeyGuard as ut, createGitHubProvider as v, isOperationAllowed as vt, createJwksRoutes as w, PUBLIC_STORAGE_PREFIX as wt, oauthCodeFlowSchema as x, parseGroupBy as xt, createLinkedinProvider as y, orderByEntriesToTuples as yt, hashPassword as z };
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 };
10701
10810
 
10702
- //# sourceMappingURL=auth-DIKS1rsI.js.map
10811
+ //# sourceMappingURL=auth-5Et5mnUA.js.map