better-supabase 0.4.0 → 0.5.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 (179) hide show
  1. package/README.md +2 -0
  2. package/dist/channel-FQW-CnGR.d.ts +70 -0
  3. package/dist/cli/bin.js +1 -1
  4. package/dist/cli/codemod-DqV7612m.js +388 -0
  5. package/dist/cli/{config-D9R0o1LH.js → config-CPVpXP5I.js} +78 -8
  6. package/dist/cli/{config-SSKSTUl2.js → config-dtoekM0v.js} +1 -1
  7. package/dist/cli/{read-sets-CMa29t9a.js → diff-SjkO5rNr.js} +1 -220
  8. package/dist/cli/{dist-CFgGknmZ.js → dist-DBPdlPYK.js} +4 -4
  9. package/dist/cli/{dist-B1MvLv-M.js → dist-DreZ24Sz.js} +1 -1
  10. package/dist/cli/{dist-Du7VnEUG.js → dist-xn4wlboc.js} +112 -1
  11. package/dist/cli/{doctor-BOym3mbL.js → doctor-L7Z8mYTx.js} +531 -50
  12. package/dist/cli/{env-BZ0uy8XK.js → env-DmKv_G4A.js} +1 -1
  13. package/dist/cli/gen-BJc9vdyu.js +2 -0
  14. package/dist/cli/{gen-Ccn_p90L.js → gen-C2osRcr5.js} +283 -87
  15. package/dist/cli/index.d.ts +1 -1
  16. package/dist/cli/index.js +4 -4
  17. package/dist/cli/{init-ClnxRYBF.js → init-Saf5-R9T.js} +2 -2
  18. package/dist/cli/{introspect-DxqfkVIv.js → introspect--H7oGmcd.js} +2 -2
  19. package/dist/cli/{openapi-8WHjxiTJ.js → openapi-Cp5Fyta-.js} +1 -1
  20. package/dist/cli/read-sets-BfMQcKy8.js +289 -0
  21. package/dist/cli/{run-CP20LKk_.js → run-BKR2W_79.js} +16 -12
  22. package/dist/cli/{seed-5J6eu_Xg.js → seed-CUlqWh-8.js} +1 -1
  23. package/dist/cli/sql-DzPbfngx.js +348 -0
  24. package/dist/cli/{version-5mW_8sY1.js → version-BDJuO0UR.js} +1 -1
  25. package/dist/client/index.d.ts +1 -1
  26. package/dist/client/index.js +3 -10
  27. package/dist/clone-BfgmLU90.js +21 -0
  28. package/dist/{compiler-DB3aSFOA.d.ts → compiler-BXH_aZ7N.d.ts} +2 -2
  29. package/dist/config/index.d.ts +4 -4
  30. package/dist/config/index.js +4 -4
  31. package/dist/{config-CbvFmGD8.js → config-DreERlKo.js} +1 -1
  32. package/dist/{config-C1CYcm6o.d.ts → config-T_ejwyd3.d.ts} +176 -11
  33. package/dist/{define-CRS2MRV3.d.ts → define-Bp_BZa5h.d.ts} +11 -7
  34. package/dist/{define-Bh0AqTik.js → define-cNKzqwL_.js} +202 -16
  35. package/dist/edge/index.d.ts +16 -5
  36. package/dist/edge/index.js +10 -7
  37. package/dist/{entitlements-Dm-FHpNH.d.ts → entitlements-DUxU4oHe.d.ts} +1 -1
  38. package/dist/env/index.d.ts +2 -2
  39. package/dist/env/index.js +34 -1
  40. package/dist/{errors-L0xK41y_.js → errors-DtwXa6b8.js} +1 -0
  41. package/dist/events/index.d.ts +3 -2
  42. package/dist/events/index.js +65 -12
  43. package/dist/events-aIO7vsIk.d.ts +230 -0
  44. package/dist/{result-CqEsbgYs.d.ts → executor-BunPOO-k.d.ts} +200 -1
  45. package/dist/{executor-CZ7ImRiD.d.ts → executor-Cghdstwu.d.ts} +1 -2
  46. package/dist/{executor-CVt6dD0B.js → executor-XycESQPH.js} +3 -3
  47. package/dist/hono/index.d.ts +17 -6
  48. package/dist/hono/index.js +40 -5
  49. package/dist/impersonation-VfaGc4Q_.js +41 -0
  50. package/dist/{index-BBdvo-5d.d.ts → index-BGdgBPdf.d.ts} +49 -6
  51. package/dist/{index-BhHEC04r.d.ts → index-C7f8cOBM.d.ts} +2 -2
  52. package/dist/{index-Dy6kuF0P.d.ts → index-CLg87OBW.d.ts} +6 -1
  53. package/dist/{index-D5IfUIDh.d.ts → index-DOESKG27.d.ts} +33 -8
  54. package/dist/{index-DFmOpBGC.d.ts → index-DgILJRXj.d.ts} +35 -9
  55. package/dist/{index-RlLXd8dL.d.ts → index-DmXSxbLi.d.ts} +4 -4
  56. package/dist/{index-Cf-D2jk1.d.ts → index-Dxn2a5cw.d.ts} +2 -2
  57. package/dist/{index-528Ugmyp.d.ts → index-SaTsEfGP.d.ts} +76 -8
  58. package/dist/{index-BAy4tsAP.d.ts → index-fag8VJZ2.d.ts} +3 -4
  59. package/dist/index.d.ts +30 -13
  60. package/dist/index.js +10 -9
  61. package/dist/jobs/index.d.ts +147 -101
  62. package/dist/jobs/index.js +744 -237
  63. package/dist/{json-schema-DP6V7cbr.js → json-schema-m1fiwKgN.js} +22 -1
  64. package/dist/kit-events-CTFDfSwb.js +60 -0
  65. package/dist/kit-transport-B82k1djG.d.ts +33 -0
  66. package/dist/kit-transport-DJlHQUBU.js +55 -0
  67. package/dist/{config-BpxDfnbg.js → kits-CGLLrNPd.js} +37 -9
  68. package/dist/list/index.d.ts +1 -1
  69. package/dist/{live-BHakSWWo.d.ts → live-D9W2M5aO.d.ts} +6 -5
  70. package/dist/mcp/index.d.ts +25 -8
  71. package/dist/mcp/index.js +23 -14
  72. package/dist/{mfa-C7MNQ1Ob.d.ts → mfa-CusDgPaD.d.ts} +5 -1
  73. package/dist/next/index.d.ts +31 -10
  74. package/dist/next/index.js +136 -27
  75. package/dist/notifications/index.d.ts +162 -0
  76. package/dist/notifications/index.js +264 -0
  77. package/dist/openapi/index.d.ts +1 -1
  78. package/dist/openapi/index.js +2 -2
  79. package/dist/orgs/index.d.ts +132 -0
  80. package/dist/orgs/index.js +245 -0
  81. package/dist/orpc/index.d.ts +28 -8
  82. package/dist/orpc/index.js +16 -3
  83. package/dist/otel/index.d.ts +21 -2
  84. package/dist/otel/index.js +50 -9
  85. package/dist/permdock-sql-C2Inc6Xc.d.ts +29 -0
  86. package/dist/permdock-sql-DgOfeixi.js +51 -0
  87. package/dist/plugin-DwARMuQp.js +59 -0
  88. package/dist/{plugin-bNhvJwRh.d.ts → plugin-o144f322.d.ts} +17 -5
  89. package/dist/plugins/actor/index.d.ts +5 -2
  90. package/dist/plugins/actor/index.js +22 -8
  91. package/dist/plugins/rules/index.d.ts +2 -2
  92. package/dist/plugins/rules/index.js +15 -10
  93. package/dist/plugins/soft-delete/index.d.ts +3 -3
  94. package/dist/plugins/soft-delete/index.js +14 -5
  95. package/dist/plugins/tenant/index.d.ts +2 -2
  96. package/dist/plugins/tenant/index.js +38 -20
  97. package/dist/plugins/timestamps/index.d.ts +1 -1
  98. package/dist/plugins/timestamps/index.js +4 -3
  99. package/dist/plugins/validation/index.d.ts +2 -2
  100. package/dist/plugins/validation/index.js +26 -6
  101. package/dist/{pool-DFwEQwmN.d.ts → pool-C9voM4Kw.d.ts} +17 -6
  102. package/dist/postgres/index.d.ts +5 -5
  103. package/dist/postgres/index.js +20 -11
  104. package/dist/{postgrest-Bc-0Xw62.d.ts → postgrest-CruYKAvV.d.ts} +1 -1
  105. package/dist/{problem-BihjPR0t.js → problem-BW1KhzSG.js} +2 -1
  106. package/dist/{problem-U8UEKccv.d.ts → problem-Blxe8S31.d.ts} +1 -1
  107. package/dist/query/index.d.ts +2 -2
  108. package/dist/query/index.js +3 -3
  109. package/dist/{query-BMMSWbI7.js → query-CCbo44lG.js} +2 -2
  110. package/dist/queue-BYUCjr59.d.ts +252 -0
  111. package/dist/react/index.d.ts +8 -7
  112. package/dist/react/index.js +4 -3
  113. package/dist/react/server.d.ts +14 -3
  114. package/dist/react/server.js +10 -2
  115. package/dist/react/session.d.ts +7 -1
  116. package/dist/react/session.js +9 -1
  117. package/dist/{react-CQHMaESR.js → react-C_s7kiNz.js} +103 -11
  118. package/dist/{read-set-FWK8qQ58.js → read-set-DQFt1N_Y.js} +22 -1
  119. package/dist/realtime/index.d.ts +3 -3
  120. package/dist/realtime/index.js +5 -4
  121. package/dist/{refresh-BwYkyNOQ.d.ts → refresh-BTrQ_ZTp.d.ts} +1 -1
  122. package/dist/{resolve-BAqQcwMW.d.ts → resolve-C15TJq77.d.ts} +11 -5
  123. package/dist/{resource-My_y-Syh.js → resource-CRxxwkQq.js} +22 -7
  124. package/dist/{resource-BJcaIfxi.d.ts → resource-D8iqWMr-.d.ts} +5 -5
  125. package/dist/respond-D2OQl62-.d.ts +371 -0
  126. package/dist/{result-Bj6yt33D.js → result-BKiliPY7.js} +1 -1
  127. package/dist/server/index.d.ts +14 -12
  128. package/dist/server/index.js +11 -17
  129. package/dist/{server-BA75B0mT.js → server-Csx2sRjg.js} +251 -34
  130. package/dist/{shared-DnoCdMDr.js → shared-D1dRUrAg.js} +16 -2
  131. package/dist/signers-Cc6r98SW.d.ts +162 -0
  132. package/dist/{simplify-Cqvfr9dj.js → simplify-_0oUKqCd.js} +4 -115
  133. package/dist/sql/index.d.ts +247 -10
  134. package/dist/sql/index.js +8590 -779
  135. package/dist/{sql-B34HU2DI.js → sql-Coh6n4cC.js} +9 -2
  136. package/dist/ssr/index.d.ts +4 -4
  137. package/dist/ssr/index.js +1 -2
  138. package/dist/{standard-BRyBGTtp.js → standard-D7r6dMUu.js} +6 -3
  139. package/dist/{standard-BsaCYQJc.d.ts → standard-J4YgMOLm.d.ts} +1 -1
  140. package/dist/{stats-C_YxA2r1.js → stats-Bqob3WqT.js} +4 -4
  141. package/dist/storage/index.d.ts +4 -4
  142. package/dist/storage/index.js +155 -26
  143. package/dist/support-DT-OhSxD.d.ts +88 -0
  144. package/dist/support-Dk6vdmEw.js +291 -0
  145. package/dist/support-view-Da5peKFg.d.ts +17 -0
  146. package/dist/support-view-g4YLBeqX.js +18 -0
  147. package/dist/{permdock-sql-C4KFzLc9.js → template-TxrGI5jR.js} +2 -31
  148. package/dist/{temporal-BwshQk0W.js → temporal-CVlIV2DZ.js} +17 -4
  149. package/dist/{temporal-required-CBNHT6ni.js → temporal-required-CAt4Dc9Z.js} +2 -2
  150. package/dist/testing/index.d.ts +100 -10
  151. package/dist/testing/index.js +336 -8
  152. package/dist/{types-DONv9SHN.d.ts → types-Dnf_wRDz.d.ts} +26 -3
  153. package/dist/user-change-CHFEUro1.js +49 -0
  154. package/dist/{webhooks-Bh_Jf_-l.js → verify-Ce49ETPz.js} +5 -5
  155. package/dist/{version-Bmf7_FzM.js → version-DM_Roffb.js} +1 -1
  156. package/dist/{view-B64o81wp.d.ts → view-BEf0diC5.d.ts} +8 -3
  157. package/dist/{mfa-byfjruR9.js → view-Bq64ki8-.js} +95 -38
  158. package/dist/webhooks/index.d.ts +135 -4
  159. package/dist/webhooks/index.js +588 -2
  160. package/dist/wire-D2dT86vi.js +35 -0
  161. package/package.json +16 -8
  162. package/schemas/config-v1.json +131 -1
  163. package/schemas/doctor-report-v1.json +10 -0
  164. package/skills/better-supabase/SKILL.md +9 -6
  165. package/skills/better-supabase/references/plugins.md +6 -3
  166. package/skills/better-supabase/references/troubleshooting.md +1 -0
  167. package/skills/better-supabase-api/SKILL.md +9 -2
  168. package/skills/better-supabase-api/references/adapters.md +11 -8
  169. package/skills/better-supabase-auth/SKILL.md +3 -0
  170. package/skills/better-supabase-auth/references/permdock.md +56 -11
  171. package/dist/cli/gen-D5cLCULx.js +0 -2
  172. package/dist/cli/sql-D4VbqmXi.js +0 -199
  173. package/dist/events-B21UOhvP.d.ts +0 -84
  174. package/dist/executor-B5IoUxLE.d.ts +0 -195
  175. package/dist/invalidate-CgxmRamI.js +0 -24
  176. package/dist/permdock-sql-DImJULY-.d.ts +0 -17
  177. package/dist/plugin-BqR6wKMB.js +0 -23
  178. package/dist/respond-DgIbdrt8.d.ts +0 -194
  179. package/dist/view-BrJI2LKp.js +0 -45
@@ -1,10 +1,12 @@
1
- import { n as dbError } from "../errors-L0xK41y_.js";
2
- import { a as problemResponse, i as isProblem, n as PROBLEM_TYPE_BASE, o as toProblem, r as fromProblem, t as PROBLEM_CONTENT_TYPE } from "../problem-BihjPR0t.js";
3
- import { a as guard, c as settle, o as isResult, s as respond, t as createServer, u as PRIMARY_COOKIE } from "../server-BA75B0mT.js";
4
- import { a as clientIp, c as AUTH_CACHE_HEADERS, d as readSession, h as refreshSession, i as authContext, m as writeSession, n as amrOf, p as sessionCookieName, r as checkAal, s as resolveAuth, t as aalOf, y as impersonatorOf } from "../mfa-byfjruR9.js";
5
- import { n as postgresExecutor } from "../executor-CVt6dD0B.js";
6
- import { t as toSession } from "../view-BrJI2LKp.js";
7
- import { t as defineResource } from "../resource-My_y-Syh.js";
1
+ import { n as dbError } from "../errors-DtwXa6b8.js";
2
+ import { a as problemResponse, i as isProblem, n as PROBLEM_TYPE_BASE, o as toProblem, r as fromProblem, t as PROBLEM_CONTENT_TYPE } from "../problem-BW1KhzSG.js";
3
+ import { n as impersonatorOf, r as userContext } from "../impersonation-VfaGc4Q_.js";
4
+ import { t as supportOf } from "../support-view-g4YLBeqX.js";
5
+ import { c as supportCookie, d as guard, f as isResult, l as supportCookieValue, m as settle, n as createServer, o as SUPPORT_COOKIE, p as respond, s as clearSupportCookie, t as TENANT_HEADER, v as PRIMARY_COOKIE } from "../server-Csx2sRjg.js";
6
+ import { _ as writeSession, a as checkAal, d as AUTH_CACHE_HEADERS, g as sessionCookieName, i as amrOf, m as readSession, n as toSession, o as authContext, r as aalOf, s as clientIp, u as resolveAuth, v as refreshSession } from "../view-Bq64ki8-.js";
7
+ import { n as sqlSupportStore, r as supportClaims, t as supportSessions } from "../support-Dk6vdmEw.js";
8
+ import { n as postgresExecutor } from "../executor-XycESQPH.js";
9
+ import { t as defineResource } from "../resource-CRxxwkQq.js";
8
10
  import { defineMiddleware } from "@supabase/middleware";
9
11
  //#region src/server/middleware.ts
10
12
  /** Repository context from a `@supabase/server` context. */
@@ -12,15 +14,7 @@ function contextFromSupabase(ctx) {
12
14
  switch (ctx.authMode) {
13
15
  case "user":
14
16
  if (!ctx.userClaims) break;
15
- return {
16
- actor: {
17
- id: ctx.userClaims.id,
18
- kind: "user",
19
- ...ctx.userClaims.role === void 0 ? {} : { role: ctx.userClaims.role },
20
- ...ctx.userClaims.email === void 0 ? {} : { email: ctx.userClaims.email }
21
- },
22
- claims: ctx.jwtClaims ?? {}
23
- };
17
+ return userContext(ctx.userClaims, ctx.jwtClaims ?? {});
24
18
  case "secret": return {
25
19
  actor: {
26
20
  id: "service",
@@ -94,4 +88,4 @@ async function checkSession(sql, auth) {
94
88
  return row?.active === true ? void 0 : revoked;
95
89
  }
96
90
  //#endregion
97
- export { AUTH_CACHE_HEADERS, PRIMARY_COOKIE, PROBLEM_CONTENT_TYPE, PROBLEM_TYPE_BASE, aalOf, amrOf, authContext, checkAal, checkSession, clientIp, contextFromSupabase, createServer, defineResource, fromProblem, guard, impersonatorOf, isProblem, isResult, problemResponse, readSession, refreshSession, resolveAuth, respond, sessionCookieName, settle, toProblem, toSession, withBetterPostgres, withBetterSupabase, writeSession };
91
+ export { AUTH_CACHE_HEADERS, PRIMARY_COOKIE, PROBLEM_CONTENT_TYPE, PROBLEM_TYPE_BASE, SUPPORT_COOKIE, TENANT_HEADER, aalOf, amrOf, authContext, checkAal, checkSession, clearSupportCookie, clientIp, contextFromSupabase, createServer, defineResource, fromProblem, guard, impersonatorOf, isProblem, isResult, problemResponse, readSession, refreshSession, resolveAuth, respond, sessionCookieName, settle, sqlSupportStore, supportClaims, supportCookie, supportCookieValue, supportOf, supportSessions, toProblem, toSession, withBetterPostgres, withBetterSupabase, writeSession };
@@ -1,9 +1,10 @@
1
- import { n as dbError, o as isDbError, r as dbErrorOf } from "./errors-L0xK41y_.js";
2
- import { i as ok, n as err, o as toDbError, t as AsyncResult } from "./result-Bj6yt33D.js";
3
- import { a as postgrestExecutor, n as StatsRecorder } from "./stats-C_YxA2r1.js";
4
- import { a as problemResponse } from "./problem-BihjPR0t.js";
1
+ import { n as dbError, o as isDbError, r as dbErrorOf } from "./errors-DtwXa6b8.js";
2
+ import { i as ok, n as err, o as toDbError, t as AsyncResult } from "./result-BKiliPY7.js";
3
+ import { a as postgrestExecutor, n as StatsRecorder } from "./stats-Bqob3WqT.js";
4
+ import { a as problemResponse } from "./problem-BW1KhzSG.js";
5
+ import { r as userContext, t as actClaim } from "./impersonation-VfaGc4Q_.js";
5
6
  import { loadEnv } from "./env/index.js";
6
- import { _ as delegationOf, g as actorOf, i as authContext, o as prefetchJwks, r as checkAal, s as resolveAuth, v as actClaim } from "./mfa-byfjruR9.js";
7
+ import { a as checkAal, b as delegationOf, c as prefetchJwks, l as rememberVerified, o as authContext, t as isAnonymousUser, u as resolveAuth, y as actorOf } from "./view-Bq64ki8-.js";
7
8
  import { parseCookieHeader, serializeCookieHeader } from "@supabase/ssr";
8
9
  import { createClient } from "@supabase/supabase-js";
9
10
  import { PostgrestClient } from "@supabase/postgrest-js";
@@ -34,14 +35,35 @@ function pinnedUntil(cookies) {
34
35
  const until = Number(value);
35
36
  return value !== void 0 && Number.isFinite(until) ? until : 0;
36
37
  }
37
- /** A `Set-Cookie` value keeping the next requests on the primary for `pinMs`. */
38
- function primaryCookie(pinMs, now = Date.now()) {
39
- return serializeCookieHeader(PRIMARY_COOKIE, String(now + pinMs), {
38
+ /** Options of the `bs-primary-until` cookie, for frameworks that set cookies themselves. */
39
+ function primaryCookieOptions(pinMs) {
40
+ return {
40
41
  path: "/",
41
42
  maxAge: Math.max(1, Math.ceil(pinMs / 1e3)),
42
43
  httpOnly: true,
43
44
  sameSite: "lax"
44
- });
45
+ };
46
+ }
47
+ /** A `Set-Cookie` value keeping the next requests on the primary for `pinMs`. */
48
+ function primaryCookie(pinMs, now = Date.now()) {
49
+ return serializeCookieHeader(PRIMARY_COOKIE, String(now + pinMs), primaryCookieOptions(pinMs));
50
+ }
51
+ /**
52
+ * `response` with the `bs-primary-until` cookie when the request wrote, so the
53
+ * caller's next requests read their own writes. A response with immutable
54
+ * headers is copied.
55
+ */
56
+ function withPrimaryPin(response, replica, pinMs) {
57
+ if (!replica?.wrote) return response;
58
+ const cookie = primaryCookie(pinMs);
59
+ try {
60
+ response.headers.append("set-cookie", cookie);
61
+ return response;
62
+ } catch {
63
+ const copy = new Response(response.body, response);
64
+ copy.headers.append("set-cookie", cookie);
65
+ return copy;
66
+ }
45
67
  }
46
68
  /**
47
69
  * Reads go to `replica` until the context is pinned; writes, and every read
@@ -101,7 +123,7 @@ function deleteAccount(betterSupabase, service, userId, options = {}) {
101
123
  const param = bucket.owner;
102
124
  if (!param) continue;
103
125
  options.signal?.throwIfAborted();
104
- const storage = bucket.connect(client);
126
+ const storage = bucket.connect(client, { allTenants: true });
105
127
  const listed = await storage.list({ [param]: userId }, options.signal ? { signal: options.signal } : {});
106
128
  if (!listed.ok) return listed;
107
129
  const paths = listed.data.map((object) => object.path).filter((path) => {
@@ -140,6 +162,21 @@ function deleteAccount(betterSupabase, service, userId, options = {}) {
140
162
  });
141
163
  }
142
164
  //#endregion
165
+ //#region src/core/json.ts
166
+ /** A `JSON.stringify` replacer: `bigint` values become decimal strings, as the wire codec writes them. */
167
+ function jsonReplacer(_key, value) {
168
+ return typeof value === "bigint" ? value.toString() : value;
169
+ }
170
+ /** `Response.json` that writes `bigint` values as decimal strings instead of throwing. */
171
+ function jsonResponse(data, init = {}) {
172
+ const headers = new Headers(init.headers);
173
+ if (!headers.has("content-type")) headers.set("content-type", "application/json");
174
+ return new Response(JSON.stringify(data, jsonReplacer), {
175
+ ...init,
176
+ headers
177
+ });
178
+ }
179
+ //#endregion
143
180
  //#region src/server/respond.ts
144
181
  /** The scopes a delegated user token lacks; empty for the user's own session. */
145
182
  function missingScopes(auth, required) {
@@ -155,6 +192,7 @@ function guard(auth, allow = ["user"], aal = "aal1", scopes = []) {
155
192
  if (allow.includes(auth.kind)) {
156
193
  const denied = checkAal(auth, aal);
157
194
  if (denied || auth.kind !== "user") return denied;
195
+ if (isAnonymousUser(auth.claims) && !allow.includes("anonymous") && !allow.includes("anon")) return dbError("forbidden", "Sign up to continue", { code: "ANONYMOUS_USER" });
158
196
  const missing = missingScopes(auth, scopes);
159
197
  return missing.length > 0 ? dbError("forbidden", `The token lacks the scope ${missing.join(" ")}`, {
160
198
  code: "INSUFFICIENT_SCOPE",
@@ -202,7 +240,7 @@ async function respond(run, options = {}) {
202
240
  if (!settled.ok) return problemResponse(settled.error, options);
203
241
  if (settled.data instanceof Response) return settled.data;
204
242
  if (settled.data === void 0) return new Response(null, { status: 204 });
205
- return Response.json(settled.data, { status: options.status ?? 200 });
243
+ return jsonResponse(settled.data, { status: options.status ?? 200 });
206
244
  }
207
245
  /** Prefetching the JWKS would add a network call to every test that builds a server. */
208
246
  function defaultPrefetchJwks() {
@@ -212,7 +250,56 @@ function defaultExpose() {
212
250
  return typeof process !== "undefined" && process.env["NODE_ENV"] === "development";
213
251
  }
214
252
  //#endregion
253
+ //#region src/auth/support-cookie.ts
254
+ /** The cookie that carries the support session id. */
255
+ const SUPPORT_COOKIE = "bs-support";
256
+ /** `Set-Cookie` for a session: HttpOnly, SameSite=Lax, gone when the session expires. */
257
+ function supportCookie(session, options = {}, now = Date.now()) {
258
+ const maxAge = Math.max(0, Math.ceil((session.expiresAt.epochMilliseconds - now) / 1e3));
259
+ return cookieLine(options, encodeURIComponent(session.id), maxAge);
260
+ }
261
+ /** `Set-Cookie` that removes the support cookie. */
262
+ function clearSupportCookie(options = {}) {
263
+ return cookieLine(options, "", 0);
264
+ }
265
+ function cookieLine(options, value, maxAge) {
266
+ return [
267
+ `${options.name ?? "bs-support"}=${value}`,
268
+ `Path=${options.path ?? "/"}`,
269
+ `Max-Age=${String(maxAge)}`,
270
+ "HttpOnly",
271
+ "SameSite=Lax",
272
+ ...options.secure === false ? [] : ["Secure"]
273
+ ].join("; ");
274
+ }
275
+ /** The support session id in a `Cookie` header, or `undefined`. */
276
+ function supportCookieValue(cookieHeader, name = SUPPORT_COOKIE) {
277
+ if (!cookieHeader) return void 0;
278
+ for (const part of cookieHeader.split(";")) {
279
+ const index = part.indexOf("=");
280
+ if (index === -1 || part.slice(0, index).trim() !== name) continue;
281
+ const value = decodeURIComponent(part.slice(index + 1).trim());
282
+ return value === "" ? void 0 : value;
283
+ }
284
+ }
285
+ //#endregion
286
+ //#region src/server/support-off.ts
287
+ const NOT_CONFIGURED = "Support sessions need createServer(betterSupabase, { support: supportSessions({ store }) })";
288
+ const notConfigured = () => AsyncResult.err(dbError("unexpected", NOT_CONFIGURED));
289
+ /** `bs.support` without `ServerOptions.support`: every call fails. */
290
+ const supportOff = {
291
+ start: notConfigured,
292
+ stop: notConfigured,
293
+ revoke: notConfigured,
294
+ current: () => Promise.resolve(void 0),
295
+ list: notConfigured,
296
+ sessionIdOf: () => void 0,
297
+ clearCookie: () => clearSupportCookie()
298
+ };
299
+ //#endregion
215
300
  //#region src/server/server.ts
301
+ /** The header `current_tenant_id()` reads over the Data API. */
302
+ const TENANT_HEADER = "x-bs-tenant";
216
303
  const STATELESS = {
217
304
  persistSession: false,
218
305
  autoRefreshToken: false,
@@ -252,15 +339,24 @@ function createServer(betterSupabase, options = {}) {
252
339
  if (!key) throw new TypeError("admin() needs SUPABASE_SECRET_KEY; it is not set");
253
340
  return key;
254
341
  };
342
+ const customFetch = options.fetch ? { fetch: options.fetch } : void 0;
343
+ const sharedGlobal = customFetch ? { global: customFetch } : {};
255
344
  const serviceClient = () => {
256
- adminClient ??= createClient(env().url, secretKey(), { auth: STATELESS });
345
+ adminClient ??= createClient(env().url, secretKey(), {
346
+ auth: STATELESS,
347
+ ...sharedGlobal
348
+ });
257
349
  return adminClient;
258
350
  };
259
351
  const readUrl = () => options.readUrl === false ? void 0 : options.readUrl?.replace(/\/+$/, "") ?? env().readUrl;
260
352
  /** Clients for the primary share the service and anon clients; others don't. */
261
353
  const supabaseAt = (url, auth, headers = {}) => {
262
- const global = Object.keys(headers).length > 0 ? { global: { headers } } : void 0;
263
- const shared = !global && url === env().url;
354
+ const custom = Object.keys(headers).length > 0;
355
+ const global = custom ? { global: {
356
+ headers,
357
+ ...customFetch
358
+ } } : sharedGlobal;
359
+ const shared = !custom && url === env().url;
264
360
  switch (auth.kind) {
265
361
  case "user": return createClient(url, env().publishableKey, {
266
362
  accessToken: () => Promise.resolve(auth.token),
@@ -276,7 +372,10 @@ function createServer(betterSupabase, options = {}) {
276
372
  auth: STATELESS,
277
373
  ...global
278
374
  });
279
- anonClient ??= createClient(url, env().publishableKey, { auth: STATELESS });
375
+ anonClient ??= createClient(url, env().publishableKey, {
376
+ auth: STATELESS,
377
+ ...sharedGlobal
378
+ });
280
379
  return anonClient;
281
380
  default: return auth;
282
381
  }
@@ -290,11 +389,14 @@ function createServer(betterSupabase, options = {}) {
290
389
  const restAt = (url, auth, headers = {}) => {
291
390
  if (auth.kind !== "user") return supabaseAt(url, auth, headers);
292
391
  const key = env().publishableKey;
293
- return new PostgrestClient(new URL("rest/v1", url.endsWith("/") ? url : `${url}/`).href, { headers: {
294
- ...headers,
295
- apikey: key,
296
- Authorization: `Bearer ${auth.token}`
297
- } });
392
+ return new PostgrestClient(new URL("rest/v1", url.endsWith("/") ? url : `${url}/`).href, {
393
+ headers: {
394
+ ...headers,
395
+ apikey: key,
396
+ Authorization: `Bearer ${auth.token}`
397
+ },
398
+ ...customFetch
399
+ });
298
400
  };
299
401
  /** `$client` stays the full Supabase client, built when first read. */
300
402
  const withLazyClient = (db, client) => Object.defineProperty(db, "$client", {
@@ -311,14 +413,101 @@ function createServer(betterSupabase, options = {}) {
311
413
  });
312
414
  return withLazyClient(db, () => supabase ??= supabaseFor(auth));
313
415
  };
314
- const sqlFor = (claims, context, stats) => {
416
+ const sqlFor = (claims, context, stats, session) => {
315
417
  if (!options.postgres) throw new TypeError("Direct Postgres access needs createServer(betterSupabase, { postgres: createPostgres() })");
316
- return betterSupabase.connect(options.postgres.executorFor(claims), context, stats ? { stats } : {});
418
+ return betterSupabase.connect(session ? options.postgres.executorFor(claims, session) : options.postgres.executorFor(claims), context, stats ? { stats } : {});
419
+ };
420
+ /** `ServerOptions.tenant` outside `context()`, where it can't be awaited. */
421
+ const syncTenant = (request, auth) => {
422
+ const tenant = options.tenant?.(request, auth);
423
+ if (tenant !== void 0 && typeof tenant !== "string") throw new TypeError("ServerOptions.tenant returned a promise: use context(request), or pass { tenant } to contextFromResolution");
424
+ return tenant;
425
+ };
426
+ const pinMs = options.replicas?.pinMs ?? 5e3;
427
+ if (options.support && !options.postgres) throw new TypeError("Support sessions need createServer(betterSupabase, { postgres: createPostgres(), support })");
428
+ const support = options.support ? options.support.create(betterSupabase.events, options.auth?.claims ?? betterSupabase.claimsSchema) : supportOff;
429
+ /**
430
+ * The target's context in a support session. Queries run over direct
431
+ * Postgres with the target's claims plus `act`, read-only unless the
432
+ * session allows writes; nothing reaches Supabase with the admin's token.
433
+ */
434
+ const supportContextFor = (resolution, active, parent, tenant) => {
435
+ const { session, claims } = active;
436
+ const record = (key) => {
437
+ const value = claims[key];
438
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? Object.fromEntries(Object.entries(value)) : void 0;
439
+ };
440
+ const appMetadata = record("app_metadata");
441
+ const userMetadata = record("user_metadata");
442
+ const user = {
443
+ id: session.targetUserId,
444
+ role: typeof claims["role"] === "string" ? claims["role"] : "authenticated",
445
+ ...typeof claims["email"] === "string" ? { email: claims["email"] } : {},
446
+ ...appMetadata ? { appMetadata } : {},
447
+ ...userMetadata ? { userMetadata } : {}
448
+ };
449
+ const auth = {
450
+ kind: "user",
451
+ token: "",
452
+ claims,
453
+ user,
454
+ source: "support",
455
+ expiresAt: Math.floor(session.expiresAt.epochMilliseconds / 1e3)
456
+ };
457
+ const scope = tenant ?? session.tenant;
458
+ const base = userContext(user, claims);
459
+ const context = scope === void 0 ? base : {
460
+ ...base,
461
+ tenant: scope
462
+ };
463
+ const sessionOptions = {
464
+ ...session.readOnly ? { readOnly: true } : {},
465
+ ...scope === void 0 ? {} : { settings: { "better_supabase.tenant": scope } }
466
+ };
467
+ const recorder = new StatsRecorder(parent);
468
+ const sqlClaims = claims;
469
+ const unavailable = () => {
470
+ throw new TypeError("ctx.supabase and ctx.db.$client are not available in a support session; ctx.db and ctx.sql run as the target");
471
+ };
472
+ let db;
473
+ let sql;
474
+ return {
475
+ auth,
476
+ resolution,
477
+ support: active,
478
+ get supabase() {
479
+ return unavailable();
480
+ },
481
+ get db() {
482
+ db ??= Object.defineProperty(sqlFor(sqlClaims, context, recorder, sessionOptions), "$client", {
483
+ get: unavailable,
484
+ enumerable: true,
485
+ configurable: true
486
+ });
487
+ return db;
488
+ },
489
+ get sql() {
490
+ sql ??= sqlFor(sqlClaims, context, recorder, sessionOptions);
491
+ return sql;
492
+ },
493
+ replica: void 0,
494
+ stats: () => recorder.snapshot(),
495
+ apply: (response) => resolution.apply(response)
496
+ };
317
497
  };
318
498
  /** Clients and repositories are built on first access: most scopes use one of them. */
319
- const contextFor = (resolution, headers, parent, until = 0) => {
499
+ const contextFor = (resolution, requestHeaders, parent, until = 0, tenant, active) => {
500
+ if (active) return supportContextFor(resolution, active, parent, tenant);
320
501
  const { auth } = resolution;
321
- const context = authContext(auth);
502
+ const context = tenant === void 0 ? authContext(auth) : {
503
+ ...authContext(auth),
504
+ tenant
505
+ };
506
+ const headers = tenant === void 0 ? requestHeaders : {
507
+ ...requestHeaders,
508
+ [TENANT_HEADER]: tenant
509
+ };
510
+ const session = tenant === void 0 ? void 0 : { settings: { "better_supabase.tenant": tenant } };
322
511
  const recorder = new StatsRecorder(parent);
323
512
  let supabase;
324
513
  let db;
@@ -354,13 +543,14 @@ function createServer(betterSupabase, options = {}) {
354
543
  },
355
544
  get sql() {
356
545
  if (!options.postgres || !sqlClaims) return;
357
- sql ??= sqlFor(sqlClaims, context, recorder);
546
+ sql ??= sqlFor(sqlClaims, context, recorder, session);
358
547
  return sql;
359
548
  },
360
- stats: () => recorder.snapshot()
549
+ stats: () => recorder.snapshot(),
550
+ apply: (response) => withPrimaryPin(resolution.apply(response), replica, pinMs)
361
551
  };
362
552
  };
363
- const fromResolution = (resolution, request, contextOptions) => contextFor(resolution, options.headers?.(request) ?? {}, contextOptions.stats, contextOptions.pinnedUntil ?? (readUrl() ? pinnedUntil(resolution.requestCookies) : 0));
553
+ const fromResolution = (resolution, request, contextOptions, tenant) => contextFor(resolution, options.headers?.(request) ?? {}, contextOptions.stats, contextOptions.pinnedUntil ?? (readUrl() ? pinnedUntil(resolution.requestCookies) : 0), tenant, contextOptions.support);
364
554
  const resolve = async (request, resolveOptions = {}) => {
365
555
  const claims = options.auth?.claims ?? betterSupabase.claimsSchema;
366
556
  const userMetadata = betterSupabase.userMetadataSchema;
@@ -371,6 +561,7 @@ function createServer(betterSupabase, options = {}) {
371
561
  ...userMetadata ? { userMetadata } : {},
372
562
  env: env(),
373
563
  ...resolveOptions.refresh === void 0 ? {} : { refresh: resolveOptions.refresh },
564
+ ...resolveOptions.cookies === void 0 ? {} : { cookies: resolveOptions.cookies },
374
565
  onRefresh: (event) => {
375
566
  options.auth?.onRefresh?.(event);
376
567
  betterSupabase.events.emit("refresh", event);
@@ -381,12 +572,16 @@ function createServer(betterSupabase, options = {}) {
381
572
  betterSupabase.events.emit("auth", {
382
573
  source: auth.kind === "user" ? auth.source === "cookie" ? "cookie" : "bearer" : auth.kind === "service" ? "bearer" : "none",
383
574
  ok: auth.kind !== "invalid",
384
- ...auth.kind === "user" ? { userId: auth.user.id } : {}
575
+ ...auth.kind === "user" ? {
576
+ userId: auth.user.id,
577
+ rawSource: auth.source
578
+ } : {},
579
+ ...auth.kind === "anon" || auth.kind === "invalid" ? { reason: auth.reason } : {}
385
580
  });
386
581
  }
387
582
  return resolution;
388
583
  };
389
- return {
584
+ const server = {
390
585
  get env() {
391
586
  return env();
392
587
  },
@@ -399,12 +594,19 @@ function createServer(betterSupabase, options = {}) {
399
594
  headers: {},
400
595
  requestCookies: [],
401
596
  apply: (response) => response
402
- }, {}, contextOptions.stats, contextOptions.pinnedUntil),
597
+ }, {}, contextOptions.stats, contextOptions.pinnedUntil, contextOptions.tenant, contextOptions.support),
403
598
  async context(request, contextOptions = {}) {
404
- const resolution = await resolve(request, { refresh: contextOptions.refresh ?? false });
405
- return fromResolution(resolution, request, contextOptions);
599
+ const resolution = await resolve(request, {
600
+ refresh: contextOptions.refresh ?? false,
601
+ ...contextOptions.cookies === void 0 ? {} : { cookies: contextOptions.cookies }
602
+ });
603
+ const [tenant, active] = await Promise.all([contextOptions.tenant ?? options.tenant?.(request, resolution.auth), contextOptions.support ?? support.current(request, resolution.auth)]);
604
+ return fromResolution(resolution, request, active ? {
605
+ ...contextOptions,
606
+ support: active
607
+ } : contextOptions, tenant);
406
608
  },
407
- contextFromResolution: (resolution, request, contextOptions = {}) => fromResolution(resolution, request, contextOptions),
609
+ contextFromResolution: (resolution, request, contextOptions = {}) => fromResolution(resolution, request, contextOptions, contextOptions.tenant ?? syncTenant(request, resolution.auth)),
408
610
  admin: (context = {}) => betterSupabase.connect(serviceClient(), {
409
611
  actor: {
410
612
  id: "service",
@@ -414,6 +616,7 @@ function createServer(betterSupabase, options = {}) {
414
616
  claims: { role: "service_role" },
415
617
  ...context
416
618
  }),
619
+ support,
417
620
  deleteAccount: (userId, deleteOptions) => deleteAccount(betterSupabase, serviceClient, userId, deleteOptions),
418
621
  actingAs: (userId, claims = {}, impersonation) => {
419
622
  const full = {
@@ -433,6 +636,20 @@ function createServer(betterSupabase, options = {}) {
433
636
  });
434
637
  }
435
638
  };
639
+ verifiedSeeders.set(server, (verified) => rememberVerified({
640
+ ...options.auth,
641
+ env: env()
642
+ }, verified));
643
+ return server;
644
+ }
645
+ const verifiedSeeders = /* @__PURE__ */ new WeakMap();
646
+ /**
647
+ * Hands `server` a bearer token another layer verified for this request
648
+ * (`@supabase/server`'s `withSupabase`), so `context()` doesn't verify it
649
+ * again. See `rememberVerified` for when it is accepted.
650
+ */
651
+ function rememberVerifiedFor(server, verified) {
652
+ return verifiedSeeders.get(server)?.(verified) ?? false;
436
653
  }
437
654
  //#endregion
438
- export { guard as a, settle as c, primaryCookie as d, defaultExpose as i, DEFAULT_PIN_MS as l, extendServer as n, isResult as o, withExtra as r, respond as s, createServer as t, PRIMARY_COOKIE as u };
655
+ export { DEFAULT_PIN_MS as _, withExtra as a, withPrimaryPin as b, supportCookie as c, guard as d, isResult as f, jsonResponse as g, jsonReplacer as h, rememberVerifiedFor as i, supportCookieValue as l, settle as m, createServer as n, SUPPORT_COOKIE as o, respond as p, extendServer as r, clearSupportCookie as s, TENANT_HEADER as t, defaultExpose as u, PRIMARY_COOKIE as v, primaryCookieOptions as y };
@@ -1,4 +1,4 @@
1
- import { n as dbError, t as DbException } from "./errors-L0xK41y_.js";
1
+ import { n as dbError, t as DbException } from "./errors-DtwXa6b8.js";
2
2
  import { t as lookupOf } from "./lookup-BaP5bI27.js";
3
3
  //#region src/plugins/shared.ts
4
4
  /** Database column name for an app column, or `undefined` if it is unknown. */
@@ -35,6 +35,20 @@ function withDefault(row, column, value) {
35
35
  [column]: value
36
36
  };
37
37
  }
38
+ /**
39
+ * Refuses caller-supplied values for columns a plugin fills (timestamps,
40
+ * actor, soft delete) unless the call passes `{ override: true }`. Returns
41
+ * whether the caller may keep its own values.
42
+ */
43
+ function guardManaged(op, columns, plugin, options) {
44
+ if (options["override"] === true) return true;
45
+ const rows = op.kind === "insert" ? op.rows : op.kind === "update" ? [op.set] : [];
46
+ for (const column of columns) {
47
+ if (column === void 0) continue;
48
+ if (rows.some((row) => column in row)) throw new DbException(dbError("invalid_request", `"${column}" on ${op.table.key} is filled by ${plugin}(); pass { override: true } to set it yourself`, { table: op.table.key }));
49
+ }
50
+ return false;
51
+ }
38
52
  /** Upserts that may update existing rows must not overwrite "created" columns. */
39
53
  function insertsOnly(op) {
40
54
  return !op.onConflict || op.onConflict.action === "ignore";
@@ -55,4 +69,4 @@ function toDb(table, row) {
55
69
  return out;
56
70
  }
57
71
  //#endregion
58
- export { isNotNull as a, toDb as c, insertsOnly as i, withDefault as l, equals as n, isNull as o, forbidden as r, toApp as s, dbName as t };
72
+ export { insertsOnly as a, toApp as c, guardManaged as i, toDb as l, equals as n, isNotNull as o, forbidden as r, isNull as s, dbName as t, withDefault as u };
@@ -0,0 +1,162 @@
1
+ /// <reference lib="esnext.temporal" />
2
+ import { t as KitTransport } from "./kit-transport-B82k1djG.js";
3
+ //#region src/webhooks/url-policy.d.ts
4
+ /** Decides whether a webhook may be sent to `url`. Rejecting counts as `false`. */
5
+ type AllowUrl = (url: URL) => boolean | Promise<boolean>;
6
+ /** The addresses a host name resolves to. */
7
+ type ResolveHost = (hostname: string) => Promise<readonly string[]>;
8
+ interface PublicUrlOptions {
9
+ /**
10
+ * Resolves host names so each address can be checked. Defaults to
11
+ * `node:dns` where the runtime has it (Node, Bun, Deno); pass one on other
12
+ * runtimes, or `false` to check only the URL.
13
+ */
14
+ readonly resolve?: ResolveHost | false;
15
+ /** Allow `http:` URLs, e.g. for local development. */
16
+ readonly allowHttp?: boolean;
17
+ /** Host names that skip every check, e.g. an internal receiver you trust. */
18
+ readonly allowHosts?: readonly string[];
19
+ }
20
+ /**
21
+ * Whether an IP address is reachable on the public internet: not loopback,
22
+ * private, link-local (cloud metadata), carrier-grade NAT, multicast or
23
+ * reserved. Returns `false` for anything that isn't an IP address.
24
+ */
25
+ declare function isPublicAddress(address: string): boolean;
26
+ /**
27
+ * The default `allowUrl`: HTTPS only, no credentials in the URL, no local
28
+ * host names, and every address the host resolves to must be public. The
29
+ * delivery transport calls it again for each redirect.
30
+ *
31
+ * DNS can change between this check and the connection. Route webhooks
32
+ * through an egress proxy when that matters.
33
+ */
34
+ declare function publicUrl(options?: PublicUrlOptions): AllowUrl;
35
+ //#endregion
36
+ //#region src/webhooks/http.d.ts
37
+ interface WebhookRequest {
38
+ readonly url: string;
39
+ readonly headers: Readonly<Record<string, string>>;
40
+ readonly body: string;
41
+ readonly signal?: AbortSignal;
42
+ }
43
+ interface WebhookResponse {
44
+ readonly status: number;
45
+ readonly body: string;
46
+ /** Seconds from the `Retry-After` header, when the receiver sent one. */
47
+ readonly retryAfter?: number;
48
+ }
49
+ /**
50
+ * Sends one signed webhook request. Throw `WebhookPolicyError` when the
51
+ * request must never be retried (a blocked URL); any other error retries.
52
+ */
53
+ interface WebhookTransport {
54
+ readonly apiVersion: 1;
55
+ readonly name: string;
56
+ send(request: WebhookRequest): Promise<WebhookResponse>;
57
+ }
58
+ /** A delivery that must not be retried, e.g. a URL `allowUrl` rejected. */
59
+ declare class WebhookPolicyError extends Error {
60
+ override readonly name = "WebhookPolicyError";
61
+ }
62
+ interface FetchTransportOptions {
63
+ readonly fetch?: typeof fetch;
64
+ /** Checked before the first request and before each redirect. */
65
+ readonly allowUrl?: AllowUrl;
66
+ /**
67
+ * Defaults to 0: a redirect fails the delivery without a retry. Each hop
68
+ * you allow is checked with `allowUrl` first.
69
+ */
70
+ readonly maxRedirects?: number;
71
+ /** Per request. Defaults to 10 seconds. */
72
+ readonly timeoutMs?: number;
73
+ /** The response body bytes kept; the rest is not read. Defaults to 64 KiB. */
74
+ readonly maxResponseBytes?: number;
75
+ }
76
+ /** Sends webhooks with `fetch`, following redirects only to allowed URLs. */
77
+ declare function fetchTransport(options?: FetchTransportOptions): WebhookTransport;
78
+ //#endregion
79
+ //#region src/webhooks/secrets.d.ts
80
+ interface RotateSecretOptions {
81
+ /** How long older secrets keep signing, e.g. `'24 hours'` (the default). */
82
+ readonly overlap?: string;
83
+ /** Use this secret instead of a generated one, e.g. when migrating. */
84
+ readonly secret?: string;
85
+ }
86
+ /**
87
+ * Where endpoint signing secrets live. `sqlSecretStore` (Vault or a
88
+ * column, set in `kits.webhooks-out.options.secretStorage`) is the default;
89
+ * implement it for a KMS or a secrets manager.
90
+ */
91
+ interface WebhookSecretStore {
92
+ readonly apiVersion: 1;
93
+ /** The live secrets of an endpoint, newest first. */
94
+ secrets(endpointId: string): Promise<readonly string[]>;
95
+ /** Creates a new secret and returns it once. */
96
+ rotate?(endpointId: string, options?: RotateSecretOptions): Promise<string>;
97
+ }
98
+ /** The kit's `webhook_secrets` and `rotate_webhook_secret` functions. */
99
+ declare function sqlSecretStore(transport: KitTransport, options?: {
100
+ readonly schema?: string;
101
+ }): WebhookSecretStore;
102
+ //#endregion
103
+ //#region src/webhooks/signers.d.ts
104
+ interface WebhookSignInput {
105
+ /** The delivery id, the same on every retry so receivers can deduplicate. */
106
+ readonly id: string;
107
+ readonly body: string;
108
+ readonly timestamp: Temporal.Instant;
109
+ /** The endpoint's live secrets, newest first. Sign with each while rotating. */
110
+ readonly secrets: readonly string[];
111
+ }
112
+ /**
113
+ * Adds the signature headers to an outgoing webhook. `standardWebhooks()`
114
+ * is the default; `hmacSigner()` covers custom header and payload formats.
115
+ */
116
+ interface WebhookSigner {
117
+ readonly apiVersion: 1;
118
+ readonly name: string;
119
+ sign(input: WebhookSignInput): Readonly<Record<string, string>> | Promise<Readonly<Record<string, string>>>;
120
+ }
121
+ interface StandardWebhooksOptions {
122
+ /** Header names, for receivers that expect a prefix such as `svix-`. */
123
+ readonly headers?: {
124
+ readonly id?: string;
125
+ readonly timestamp?: string;
126
+ readonly signature?: string;
127
+ };
128
+ }
129
+ /**
130
+ * Signs with the [Standard Webhooks](https://www.standardwebhooks.com/)
131
+ * scheme: `webhook-id`, `webhook-timestamp` and one `v1,` signature per
132
+ * secret in `webhook-signature`. `verifyWebhook` checks it.
133
+ */
134
+ declare function standardWebhooks(options?: StandardWebhooksOptions): WebhookSigner;
135
+ interface HmacSignerOptions {
136
+ readonly name?: string;
137
+ /** e.g. `x-acme-signature`. */
138
+ readonly signatureHeader: string;
139
+ /** Sends the epoch-seconds timestamp in this header. */
140
+ readonly timestampHeader?: string;
141
+ /** Sends the delivery id in this header. */
142
+ readonly idHeader?: string;
143
+ /** The signed text. Defaults to `${timestamp}.${body}`. */
144
+ readonly content?: (input: {
145
+ readonly id: string;
146
+ readonly timestamp: string;
147
+ readonly body: string;
148
+ }) => string;
149
+ /** Defaults to `hex`. */
150
+ readonly encoding?: "hex" | "base64";
151
+ /** Written before each signature. Defaults to `v1=`. */
152
+ readonly prefix?: string;
153
+ /** Joins the signatures of several secrets. Defaults to `,`. */
154
+ readonly separator?: string;
155
+ }
156
+ /**
157
+ * HMAC-SHA256 with the secret's UTF-8 bytes as the key, for receivers that
158
+ * verify an existing format, e.g. `v1=<hex>` over `timestamp.body`.
159
+ */
160
+ declare function hmacSigner(options: HmacSignerOptions): WebhookSigner;
161
+ //#endregion
162
+ export { PublicUrlOptions as _, hmacSigner as a, publicUrl as b, WebhookSecretStore as c, WebhookPolicyError as d, WebhookRequest as f, AllowUrl as g, fetchTransport as h, WebhookSigner as i, sqlSecretStore as l, WebhookTransport as m, StandardWebhooksOptions as n, standardWebhooks as o, WebhookResponse as p, WebhookSignInput as r, RotateSecretOptions as s, HmacSignerOptions as t, FetchTransportOptions as u, ResolveHost as v, isPublicAddress as y };