better-supabase 0.3.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 (211) hide show
  1. package/README.md +9 -6
  2. package/bin/better-supabase.js +4 -0
  3. package/dist/{base64-CQeB1eSf.js → base64-D2gsioCY.js} +2 -2
  4. package/dist/channel-FQW-CnGR.d.ts +70 -0
  5. package/dist/claims-BAf6c7is.js +48 -0
  6. package/dist/cli/_format-Cax5EsM9.js +101 -0
  7. package/dist/cli/bin.d.ts +1 -0
  8. package/dist/cli/bin.js +48 -0
  9. package/dist/cli/codemod-DqV7612m.js +388 -0
  10. package/dist/cli/command-CK14fP91.js +502 -0
  11. package/dist/cli/compare-BmIWVEui.js +9 -0
  12. package/dist/cli/config-CPVpXP5I.js +1965 -0
  13. package/dist/cli/config-dtoekM0v.js +2 -0
  14. package/dist/cli/db-DOBPt660.js +52 -0
  15. package/dist/cli/diff-SjkO5rNr.js +752 -0
  16. package/dist/cli/dist-DBPdlPYK.js +1112 -0
  17. package/dist/cli/dist-DreZ24Sz.js +129 -0
  18. package/dist/cli/dist-xn4wlboc.js +732 -0
  19. package/dist/cli/doctor-L7Z8mYTx.js +2863 -0
  20. package/dist/cli/env-DmKv_G4A.js +108 -0
  21. package/dist/cli/env-jIFUADLY.js +511 -0
  22. package/dist/cli/errors-4v4DTtda.js +56 -0
  23. package/dist/cli/fastest-levenshtein-CFN-lNDv.js +120 -0
  24. package/dist/cli/gen-BJc9vdyu.js +2 -0
  25. package/dist/cli/gen-C2osRcr5.js +2204 -0
  26. package/dist/cli/index.d.ts +493 -0
  27. package/dist/cli/index.js +184 -0
  28. package/dist/cli/init-Saf5-R9T.js +550 -0
  29. package/dist/cli/introspect--H7oGmcd.js +81 -0
  30. package/dist/cli/io-k41_z-jT.js +24 -0
  31. package/dist/cli/json5-ChRycquj.js +787 -0
  32. package/dist/cli/jsonc-BUniUpOk.js +2 -0
  33. package/dist/cli/jsonc-D5Fu67jw.js +45 -0
  34. package/dist/cli/keys-ByKHnvP9.js +83 -0
  35. package/dist/cli/ohash-D7bG_EOb.js +277 -0
  36. package/dist/cli/openapi-Cp5Fyta-.js +66 -0
  37. package/dist/cli/project-B_GeXyHh.js +70 -0
  38. package/dist/cli/prompts-BNcNfAve.js +1142 -0
  39. package/dist/cli/prompts-BTYQGunc.js +2 -0
  40. package/dist/cli/read-sets-BfMQcKy8.js +289 -0
  41. package/dist/cli/rolldown-runtime-BPOCksWG.js +24 -0
  42. package/dist/cli/run-BKR2W_79.js +351 -0
  43. package/dist/cli/seed-CUlqWh-8.js +98 -0
  44. package/dist/cli/skills-LJJIhLFR.js +137 -0
  45. package/dist/cli/sql-DzPbfngx.js +348 -0
  46. package/dist/cli/style-C0NFdJRL.js +29 -0
  47. package/dist/cli/toml-DB-MNnFs.js +424 -0
  48. package/dist/cli/version-BDJuO0UR.js +4 -0
  49. package/dist/cli/yaml-CYCbJ6et.js +2252 -0
  50. package/dist/client/index.d.ts +2 -2
  51. package/dist/client/index.js +15 -22
  52. package/dist/clone-BfgmLU90.js +21 -0
  53. package/dist/{compiler-2zqGzxVZ.d.ts → compiler-BXH_aZ7N.d.ts} +2 -2
  54. package/dist/config/index.d.ts +4 -4
  55. package/dist/config/index.js +5 -5
  56. package/dist/{config-DOsGYSiT.js → config-DreERlKo.js} +1 -1
  57. package/dist/{config-C1CYcm6o.d.ts → config-T_ejwyd3.d.ts} +176 -11
  58. package/dist/{define-DUbydmJa.d.ts → define-Bp_BZa5h.d.ts} +37 -29
  59. package/dist/{define-DF341GDj.js → define-cNKzqwL_.js} +356 -89
  60. package/dist/edge/index.d.ts +20 -13
  61. package/dist/edge/index.js +55 -26
  62. package/dist/{entitlements-DvJXoI02.d.ts → entitlements-DUxU4oHe.d.ts} +2 -2
  63. package/dist/env/index.d.ts +2 -2
  64. package/dist/env/index.js +34 -1
  65. package/dist/{errors-Bl3rkOzo.js → errors-DtwXa6b8.js} +2 -1
  66. package/dist/events/index.d.ts +3 -2
  67. package/dist/events/index.js +69 -16
  68. package/dist/events-aIO7vsIk.d.ts +230 -0
  69. package/dist/executor-BunPOO-k.d.ts +402 -0
  70. package/dist/{executor-B26lgbHD.d.ts → executor-Cghdstwu.d.ts} +1 -2
  71. package/dist/{executor-C57jx2pn.js → executor-XycESQPH.js} +3 -3
  72. package/dist/hono/index.d.ts +27 -23
  73. package/dist/hono/index.js +46 -11
  74. package/dist/impersonation-VfaGc4Q_.js +41 -0
  75. package/dist/{index-BNsABq7P.d.ts → index-BGdgBPdf.d.ts} +66 -25
  76. package/dist/{index-qriRM9cZ.d.ts → index-C7f8cOBM.d.ts} +4 -4
  77. package/dist/{index-Dy6kuF0P.d.ts → index-CLg87OBW.d.ts} +6 -1
  78. package/dist/{index-GV77NpRP.d.ts → index-DOESKG27.d.ts} +34 -9
  79. package/dist/{index-CriFFJ_p.d.ts → index-DgILJRXj.d.ts} +43 -17
  80. package/dist/{index-UJ-LUxtI.d.ts → index-DmXSxbLi.d.ts} +14 -14
  81. package/dist/{index-Cf-D2jk1.d.ts → index-Dxn2a5cw.d.ts} +2 -2
  82. package/dist/{index-CTTMS6_1.d.ts → index-SaTsEfGP.d.ts} +86 -9
  83. package/dist/{index-CADlNHK-.d.ts → index-fag8VJZ2.d.ts} +5 -6
  84. package/dist/index.d.ts +34 -17
  85. package/dist/index.js +15 -14
  86. package/dist/jobs/index.d.ts +147 -95
  87. package/dist/jobs/index.js +773 -250
  88. package/dist/{json-schema--aOXAGfU.js → json-schema-m1fiwKgN.js} +23 -2
  89. package/dist/kit-events-CTFDfSwb.js +60 -0
  90. package/dist/kit-transport-B82k1djG.d.ts +33 -0
  91. package/dist/kit-transport-DJlHQUBU.js +55 -0
  92. package/dist/{config-Be4GwsqI.js → kits-CGLLrNPd.js} +38 -10
  93. package/dist/list/index.d.ts +1 -1
  94. package/dist/list/index.js +3 -3
  95. package/dist/{live-D8rV1Ldw.js → live-CdJBOc4f.js} +13 -9
  96. package/dist/{live-QE3UPbGm.d.ts → live-D9W2M5aO.d.ts} +16 -15
  97. package/dist/lookup-BaP5bI27.js +18 -0
  98. package/dist/mcp/index.d.ts +39 -22
  99. package/dist/mcp/index.js +39 -25
  100. package/dist/{mfa-x3V3vGh-.d.ts → mfa-CusDgPaD.d.ts} +5 -1
  101. package/dist/next/image/index.js +6 -3
  102. package/dist/next/index.d.ts +60 -36
  103. package/dist/next/index.js +167 -50
  104. package/dist/notifications/index.d.ts +162 -0
  105. package/dist/notifications/index.js +264 -0
  106. package/dist/openapi/index.d.ts +1 -1
  107. package/dist/openapi/index.js +6 -6
  108. package/dist/orgs/index.d.ts +132 -0
  109. package/dist/orgs/index.js +245 -0
  110. package/dist/orpc/index.d.ts +37 -20
  111. package/dist/orpc/index.js +24 -11
  112. package/dist/otel/index.d.ts +24 -5
  113. package/dist/otel/index.js +124 -64
  114. package/dist/permdock-sql-C2Inc6Xc.d.ts +29 -0
  115. package/dist/permdock-sql-DgOfeixi.js +51 -0
  116. package/dist/plugin-DwARMuQp.js +59 -0
  117. package/dist/{plugin-DVRB8qNE.d.ts → plugin-o144f322.d.ts} +17 -5
  118. package/dist/plugins/actor/index.d.ts +5 -2
  119. package/dist/plugins/actor/index.js +22 -8
  120. package/dist/plugins/rules/index.d.ts +2 -2
  121. package/dist/plugins/rules/index.js +52 -17
  122. package/dist/plugins/soft-delete/index.d.ts +3 -3
  123. package/dist/plugins/soft-delete/index.js +14 -5
  124. package/dist/plugins/tenant/index.d.ts +3 -3
  125. package/dist/plugins/tenant/index.js +45 -21
  126. package/dist/plugins/timestamps/index.d.ts +1 -1
  127. package/dist/plugins/timestamps/index.js +4 -3
  128. package/dist/plugins/validation/index.d.ts +2 -2
  129. package/dist/plugins/validation/index.js +26 -6
  130. package/dist/{pool-B9qsLyFR.d.ts → pool-C9voM4Kw.d.ts} +24 -6
  131. package/dist/postgres/index.d.ts +5 -5
  132. package/dist/postgres/index.js +20 -10
  133. package/dist/{postgrest-DmBlevh-.d.ts → postgrest-CruYKAvV.d.ts} +1 -1
  134. package/dist/{problem-CGt1ADix.js → problem-BW1KhzSG.js} +2 -1
  135. package/dist/{problem-Z2RTQjZD.d.ts → problem-Blxe8S31.d.ts} +1 -1
  136. package/dist/query/index.d.ts +2 -2
  137. package/dist/query/index.js +3 -3
  138. package/dist/{query-BdKLTPlj.js → query-CCbo44lG.js} +18 -18
  139. package/dist/queue-BYUCjr59.d.ts +252 -0
  140. package/dist/react/index.d.ts +8 -7
  141. package/dist/react/index.js +4 -3
  142. package/dist/react/server.d.ts +16 -5
  143. package/dist/react/server.js +12 -4
  144. package/dist/react/session.d.ts +10 -4
  145. package/dist/react/session.js +11 -3
  146. package/dist/{react-DNBYNazu.js → react-C_s7kiNz.js} +138 -46
  147. package/dist/{read-set-D0qBcboM.js → read-set-DQFt1N_Y.js} +71 -15
  148. package/dist/realtime/index.d.ts +3 -3
  149. package/dist/realtime/index.js +14 -12
  150. package/dist/{index-kpgopLNv.d.ts → refresh-BTrQ_ZTp.d.ts} +6 -3
  151. package/dist/{resolve-BRciX2jK.d.ts → resolve-C15TJq77.d.ts} +22 -11
  152. package/dist/{resource-D30rp0GN.js → resource-CRxxwkQq.js} +24 -9
  153. package/dist/{resource-CwVgiI5q.d.ts → resource-D8iqWMr-.d.ts} +6 -6
  154. package/dist/respond-D2OQl62-.d.ts +371 -0
  155. package/dist/{result-DWXatkd6.js → result-BKiliPY7.js} +2 -2
  156. package/dist/server/index.d.ts +17 -15
  157. package/dist/server/index.js +16 -22
  158. package/dist/server-Csx2sRjg.js +655 -0
  159. package/dist/{shared-_6-uPL7E.js → shared-D1dRUrAg.js} +19 -4
  160. package/dist/signers-Cc6r98SW.d.ts +162 -0
  161. package/dist/{simplify-LakqK1vH.js → simplify-_0oUKqCd.js} +27 -95
  162. package/dist/sql/index.d.ts +247 -10
  163. package/dist/sql/index.js +8591 -780
  164. package/dist/{sql-BIgk12ew.js → sql-Coh6n4cC.js} +11 -3
  165. package/dist/ssr/index.d.ts +4 -4
  166. package/dist/ssr/index.js +1 -2
  167. package/dist/{standard-FUi3Z3-7.js → standard-D7r6dMUu.js} +6 -3
  168. package/dist/{standard-eudBiRk1.d.ts → standard-J4YgMOLm.d.ts} +1 -1
  169. package/dist/{stats-D78UQ8Yu.js → stats-Bqob3WqT.js} +34 -9
  170. package/dist/storage/index.d.ts +4 -4
  171. package/dist/storage/index.js +174 -27
  172. package/dist/support-DT-OhSxD.d.ts +88 -0
  173. package/dist/support-Dk6vdmEw.js +291 -0
  174. package/dist/support-view-Da5peKFg.d.ts +17 -0
  175. package/dist/support-view-g4YLBeqX.js +18 -0
  176. package/dist/{tables-Dhha3BXM.js → tables-Cb-3_VWc.js} +14 -0
  177. package/dist/{permdock-sql-Bta_G0v-.js → template-TxrGI5jR.js} +2 -31
  178. package/dist/{temporal-BwshQk0W.js → temporal-CVlIV2DZ.js} +17 -4
  179. package/dist/{temporal-required-CMxeSVPo.js → temporal-required-CAt4Dc9Z.js} +2 -2
  180. package/dist/testing/index.d.ts +113 -24
  181. package/dist/testing/index.js +375 -47
  182. package/dist/{types-DONv9SHN.d.ts → types-Dnf_wRDz.d.ts} +26 -3
  183. package/dist/user-change-CHFEUro1.js +49 -0
  184. package/dist/{webhooks-CAIFQSTM.js → verify-Ce49ETPz.js} +38 -19
  185. package/dist/{version-BfpG2gks.js → version-DM_Roffb.js} +1 -1
  186. package/dist/{view-3Po_PVlr.d.ts → view-BEf0diC5.d.ts} +10 -5
  187. package/dist/{mfa-BxoR1RkN.js → view-Bq64ki8-.js} +210 -64
  188. package/dist/webhooks/index.d.ts +136 -5
  189. package/dist/webhooks/index.js +588 -2
  190. package/dist/wire-D2dT86vi.js +35 -0
  191. package/package.json +50 -10
  192. package/schemas/config-v1.json +131 -1
  193. package/schemas/doctor-report-v1.json +10 -0
  194. package/skills/better-supabase/SKILL.md +60 -10
  195. package/skills/better-supabase/references/plugins.md +13 -14
  196. package/skills/better-supabase/references/troubleshooting.md +25 -4
  197. package/skills/better-supabase-api/SKILL.md +74 -19
  198. package/skills/better-supabase-api/references/adapters.md +48 -27
  199. package/skills/better-supabase-auth/SKILL.md +149 -0
  200. package/skills/better-supabase-auth/references/permdock.md +100 -0
  201. package/skills/better-supabase-testing/SKILL.md +58 -4
  202. package/dist/claims-CoTobNC-.js +0 -25
  203. package/dist/events-Do8jXh4D.d.ts +0 -84
  204. package/dist/executor-DynmIWhq.d.ts +0 -195
  205. package/dist/invalidate-DbxuL2jL.js +0 -18
  206. package/dist/permdock-sql-DImJULY-.d.ts +0 -17
  207. package/dist/plugin-BqR6wKMB.js +0 -23
  208. package/dist/respond-C44EZAJo.js +0 -399
  209. package/dist/respond-DAkPIdTH.d.ts +0 -175
  210. package/dist/result-c2OYgCV2.d.ts +0 -203
  211. package/dist/view-wZ-9qIUz.js +0 -45
@@ -1,2 +1,588 @@
1
- import { a as signWebhook, c as verifyWebhook, i as isDatabaseWebhook, n as databaseChange, o as timingSafeEqual, r as hookError, s as verifySharedSecret, t as authHook } from "../webhooks-CAIFQSTM.js";
2
- export { authHook, databaseChange, hookError, isDatabaseWebhook, signWebhook, timingSafeEqual, verifySharedSecret, verifyWebhook };
1
+ import { c as mapDbError, n as dbError } from "../errors-DtwXa6b8.js";
2
+ import { t as temporal } from "../temporal-required-CAt4Dc9Z.js";
3
+ import { i as ok, n as err, o as toDbError, t as AsyncResult } from "../result-BKiliPY7.js";
4
+ import { i as toBase64 } from "../base64-D2gsioCY.js";
5
+ import { a as problemResponse } from "../problem-BW1KhzSG.js";
6
+ import { n as emitKitEvent } from "../kit-events-CTFDfSwb.js";
7
+ import { a as signWebhook, c as verifyWebhook, i as isDatabaseWebhook, n as databaseChange, o as timingSafeEqual, r as hookError, s as verifySharedSecret, t as authHook } from "../verify-Ce49ETPz.js";
8
+ import { n as rpcTransport, r as sqlTransport, t as rawError } from "../kit-transport-DJlHQUBU.js";
9
+ //#region src/webhooks/http.ts
10
+ /** A delivery that must not be retried, e.g. a URL `allowUrl` rejected. */
11
+ var WebhookPolicyError = class extends Error {
12
+ name = "WebhookPolicyError";
13
+ };
14
+ const REDIRECTS = /* @__PURE__ */ new Set([
15
+ 301,
16
+ 302,
17
+ 303,
18
+ 307,
19
+ 308
20
+ ]);
21
+ /** `Retry-After` as seconds from now: delta-seconds or an HTTP date. */
22
+ function parseRetryAfter(value, now = Date.now()) {
23
+ if (value === null) return void 0;
24
+ const text = value.trim();
25
+ if (/^\d+$/.test(text)) return Number(text);
26
+ const at = Date.parse(text);
27
+ if (Number.isNaN(at)) return void 0;
28
+ return Math.max(0, Math.ceil((at - now) / 1e3));
29
+ }
30
+ /** Reads at most `limit` bytes of the body, then cancels the stream. */
31
+ async function readCapped(response, limit) {
32
+ if (!response.body) return "";
33
+ const reader = response.body.getReader();
34
+ const decoder = new TextDecoder();
35
+ let text = "";
36
+ let left = limit;
37
+ try {
38
+ while (left > 0) {
39
+ const { done, value } = await reader.read();
40
+ if (done) return text + decoder.decode();
41
+ const chunk = value.byteLength > left ? value.subarray(0, left) : value;
42
+ left -= chunk.byteLength;
43
+ text += decoder.decode(chunk, { stream: true });
44
+ }
45
+ return text + decoder.decode();
46
+ } finally {
47
+ await reader.cancel().catch(() => void 0);
48
+ }
49
+ }
50
+ async function allowed(allowUrl, url) {
51
+ try {
52
+ return await allowUrl(url);
53
+ } catch {
54
+ return false;
55
+ }
56
+ }
57
+ /** Sends webhooks with `fetch`, following redirects only to allowed URLs. */
58
+ function fetchTransport(options = {}) {
59
+ const send = options.fetch ?? globalThis.fetch;
60
+ const maxRedirects = options.maxRedirects ?? 0;
61
+ const timeoutMs = options.timeoutMs ?? 1e4;
62
+ const maxResponseBytes = options.maxResponseBytes ?? 65536;
63
+ return {
64
+ apiVersion: 1,
65
+ name: "fetch",
66
+ async send(request) {
67
+ const timeout = AbortSignal.timeout(timeoutMs);
68
+ const signal = request.signal ? AbortSignal.any([request.signal, timeout]) : timeout;
69
+ let url = new URL(request.url);
70
+ for (let hop = 0;; hop++) {
71
+ if (options.allowUrl && !await allowed(options.allowUrl, url)) throw new WebhookPolicyError(`Endpoint URL is not allowed: ${url.host}`);
72
+ const response = await send(url, {
73
+ method: "POST",
74
+ headers: request.headers,
75
+ body: request.body,
76
+ redirect: "manual",
77
+ signal
78
+ });
79
+ if (!REDIRECTS.has(response.status)) {
80
+ const retryAfter = parseRetryAfter(response.headers.get("retry-after"));
81
+ return {
82
+ status: response.status,
83
+ body: await readCapped(response, maxResponseBytes),
84
+ ...retryAfter === void 0 ? {} : { retryAfter }
85
+ };
86
+ }
87
+ await response.body?.cancel();
88
+ const location = response.headers.get("location");
89
+ if (!location) throw new WebhookPolicyError(`Redirect ${String(response.status)} without a location`);
90
+ if (hop >= maxRedirects) throw new WebhookPolicyError(maxRedirects === 0 ? `Redirect ${String(response.status)} not followed (maxRedirects is 0)` : `More than ${String(maxRedirects)} redirects`);
91
+ url = new URL(location, url);
92
+ }
93
+ }
94
+ };
95
+ }
96
+ //#endregion
97
+ //#region src/webhooks/secrets.ts
98
+ /** The kit's `webhook_secrets` and `rotate_webhook_secret` functions. */
99
+ function sqlSecretStore(transport, options = {}) {
100
+ const schema = options.schema ?? "better_supabase";
101
+ return {
102
+ apiVersion: 1,
103
+ async secrets(endpointId) {
104
+ const value = await transport.call(schema, "webhook_secrets", { endpoint: endpointId });
105
+ return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
106
+ },
107
+ async rotate(endpointId, rotateOptions = {}) {
108
+ const value = await transport.call(schema, "rotate_webhook_secret", {
109
+ endpoint: endpointId,
110
+ overlap: rotateOptions.overlap,
111
+ secret: rotateOptions.secret
112
+ });
113
+ return String(value);
114
+ }
115
+ };
116
+ }
117
+ //#endregion
118
+ //#region src/webhooks/signers.ts
119
+ function needSecrets(secrets) {
120
+ if (secrets.length === 0) throw new TypeError("The endpoint has no signing secret");
121
+ }
122
+ /**
123
+ * Signs with the [Standard Webhooks](https://www.standardwebhooks.com/)
124
+ * scheme: `webhook-id`, `webhook-timestamp` and one `v1,` signature per
125
+ * secret in `webhook-signature`. `verifyWebhook` checks it.
126
+ */
127
+ function standardWebhooks(options = {}) {
128
+ const names = {
129
+ id: options.headers?.id ?? "webhook-id",
130
+ timestamp: options.headers?.timestamp ?? "webhook-timestamp",
131
+ signature: options.headers?.signature ?? "webhook-signature"
132
+ };
133
+ return {
134
+ apiVersion: 1,
135
+ name: "standard-webhooks",
136
+ async sign({ id, body, timestamp, secrets }) {
137
+ needSecrets(secrets);
138
+ const signed = await Promise.all(secrets.map((secret) => signWebhook(secret, {
139
+ id,
140
+ body,
141
+ timestamp
142
+ })));
143
+ return {
144
+ [names.id]: id,
145
+ [names.timestamp]: signed[0]["webhook-timestamp"],
146
+ [names.signature]: signed.map((headers) => headers["webhook-signature"]).join(" ")
147
+ };
148
+ }
149
+ };
150
+ }
151
+ const encoder = /* @__PURE__ */ new TextEncoder();
152
+ const hex = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
153
+ /**
154
+ * HMAC-SHA256 with the secret's UTF-8 bytes as the key, for receivers that
155
+ * verify an existing format, e.g. `v1=<hex>` over `timestamp.body`.
156
+ */
157
+ function hmacSigner(options) {
158
+ const content = options.content ?? ((input) => `${input.timestamp}.${input.body}`);
159
+ const encode = options.encoding === "base64" ? toBase64 : hex;
160
+ const prefix = options.prefix ?? "v1=";
161
+ return {
162
+ apiVersion: 1,
163
+ name: options.name ?? "hmac",
164
+ async sign({ id, body, timestamp, secrets }) {
165
+ needSecrets(secrets);
166
+ const seconds = String(Math.floor(timestamp.epochMilliseconds / 1e3));
167
+ const message = encoder.encode(content({
168
+ id,
169
+ timestamp: seconds,
170
+ body
171
+ }));
172
+ const signatures = await Promise.all(secrets.map(async (secret) => {
173
+ const key = await crypto.subtle.importKey("raw", encoder.encode(secret), {
174
+ name: "HMAC",
175
+ hash: "SHA-256"
176
+ }, false, ["sign"]);
177
+ const mac = await crypto.subtle.sign("HMAC", key, message);
178
+ return `${prefix}${encode(new Uint8Array(mac))}`;
179
+ }));
180
+ return {
181
+ [options.signatureHeader]: signatures.join(options.separator ?? ","),
182
+ ...options.timestampHeader ? { [options.timestampHeader]: seconds } : {},
183
+ ...options.idHeader ? { [options.idHeader]: id } : {}
184
+ };
185
+ }
186
+ };
187
+ }
188
+ //#endregion
189
+ //#region src/webhooks/url-policy.ts
190
+ const PRIVATE_V4 = [
191
+ [0, 8],
192
+ [167772160, 8],
193
+ [1681915904, 10],
194
+ [2130706432, 8],
195
+ [2851995648, 16],
196
+ [2886729728, 12],
197
+ [3221225472, 24],
198
+ [3221225984, 24],
199
+ [3232235520, 16],
200
+ [3323068416, 15],
201
+ [3325256704, 24],
202
+ [3405803776, 24],
203
+ [3758096384, 4],
204
+ [4026531840, 4]
205
+ ];
206
+ function v4(address) {
207
+ const parts = address.split(".");
208
+ if (parts.length !== 4) return void 0;
209
+ let value = 0;
210
+ for (const part of parts) {
211
+ if (!/^\d{1,3}$/.test(part)) return void 0;
212
+ const octet = Number(part);
213
+ if (octet > 255) return void 0;
214
+ value = value * 256 + octet;
215
+ }
216
+ return value;
217
+ }
218
+ const publicV4 = (value) => !PRIVATE_V4.some(([base, bits]) => Math.floor(value / 2 ** (32 - bits)) === Math.floor(base / 2 ** (32 - bits)));
219
+ function v6(address) {
220
+ let text = address.toLowerCase();
221
+ const zone = text.indexOf("%");
222
+ if (zone !== -1) text = text.slice(0, zone);
223
+ if (text.includes(".")) {
224
+ const colon = text.lastIndexOf(":");
225
+ const embedded = v4(text.slice(colon + 1));
226
+ if (embedded === void 0) return void 0;
227
+ const high = Math.floor(embedded / 65536).toString(16);
228
+ const low = (embedded % 65536).toString(16);
229
+ text = `${text.slice(0, colon + 1)}${high}:${low}`;
230
+ }
231
+ const halves = text.split("::");
232
+ if (halves.length > 2) return void 0;
233
+ const groups = (part) => part === "" ? [] : part.split(":");
234
+ const head = groups(halves[0]);
235
+ const rest = halves.length === 2 ? groups(halves[1]) : [];
236
+ if (![...head, ...rest].every((group) => /^[0-9a-f]{1,4}$/.test(group))) return void 0;
237
+ const known = head.length + rest.length;
238
+ if (halves.length === 1 ? known !== 8 : known > 7) return void 0;
239
+ return [
240
+ ...head,
241
+ ...Array.from({ length: 8 - known }, () => "0"),
242
+ ...rest
243
+ ].map((group) => Number.parseInt(group, 16));
244
+ }
245
+ function publicV6(groups) {
246
+ const [first = 0] = groups;
247
+ const zeros = (from, to) => groups.slice(from, to).every((group) => group === 0);
248
+ const embedded = groups[6] * 65536 + groups[7];
249
+ if (zeros(0, 8)) return false;
250
+ if (zeros(0, 7) && groups[7] === 1) return false;
251
+ if (zeros(0, 5) && groups[5] === 65535) return publicV4(embedded);
252
+ if (first === 100 && groups[1] === 65435 && zeros(2, 6)) return publicV4(embedded);
253
+ if (zeros(0, 6)) return false;
254
+ if (first === 100 && groups[1] === 65435 && groups[2] === 1) return false;
255
+ if (first === 8194) return publicV4(groups[1] * 65536 + groups[2]);
256
+ if (first === 8193 && groups[1] === 0) return false;
257
+ if ((first & 65024) === 64512) return false;
258
+ if ((first & 65472) === 65152) return false;
259
+ if ((first & 65472) === 65216) return false;
260
+ if ((first & 65280) === 65280) return false;
261
+ if (first === 8193 && groups[1] === 3512) return false;
262
+ if (first === 256 && zeros(1, 4)) return false;
263
+ return true;
264
+ }
265
+ /**
266
+ * Whether an IP address is reachable on the public internet: not loopback,
267
+ * private, link-local (cloud metadata), carrier-grade NAT, multicast or
268
+ * reserved. Returns `false` for anything that isn't an IP address.
269
+ */
270
+ function isPublicAddress(address) {
271
+ const ipv4 = v4(address);
272
+ if (ipv4 !== void 0) return publicV4(ipv4);
273
+ const ipv6 = v6(address.replaceAll(/^\[|\]$/g, ""));
274
+ return ipv6 !== void 0 && publicV6(ipv6);
275
+ }
276
+ const isAddress = (host) => v4(host) !== void 0 || v6(host) !== void 0;
277
+ const LOCAL_HOST = /(^|\.)(localhost|local|internal|home\.arpa)$/;
278
+ /** `node:dns` without importing it, so this entry stays runtime-neutral. */
279
+ function builtinResolver() {
280
+ const dns = globalThis.process?.getBuiltinModule?.("node:dns");
281
+ if (typeof dns !== "object" || dns === null || !("promises" in dns)) return void 0;
282
+ const { promises } = dns;
283
+ return async (hostname) => (await promises.lookup(hostname, {
284
+ all: true,
285
+ verbatim: true
286
+ })).map((entry) => entry.address);
287
+ }
288
+ /**
289
+ * The default `allowUrl`: HTTPS only, no credentials in the URL, no local
290
+ * host names, and every address the host resolves to must be public. The
291
+ * delivery transport calls it again for each redirect.
292
+ *
293
+ * DNS can change between this check and the connection. Route webhooks
294
+ * through an egress proxy when that matters.
295
+ */
296
+ function publicUrl(options = {}) {
297
+ const resolve = options.resolve === false ? void 0 : options.resolve ?? builtinResolver();
298
+ if (options.resolve === void 0 && !resolve) throw new TypeError("publicUrl() needs a resolve option on runtimes without node:dns, or resolve: false to skip the DNS check");
299
+ const trusted = new Set(options.allowHosts?.map((host) => host.toLowerCase()));
300
+ return async (url) => {
301
+ const host = url.hostname.toLowerCase().replaceAll(/^\[|\]$/g, "");
302
+ if (trusted.has(host)) return true;
303
+ if (url.protocol !== "https:" && !(options.allowHttp && url.protocol === "http:")) return false;
304
+ if (url.username || url.password) return false;
305
+ if (isAddress(host)) return isPublicAddress(host);
306
+ if (LOCAL_HOST.test(host) || !host.includes(".")) return false;
307
+ if (!resolve) return true;
308
+ const addresses = await resolve(host);
309
+ return addresses.length > 0 && addresses.every(isPublicAddress);
310
+ };
311
+ }
312
+ //#endregion
313
+ //#region src/webhooks/outgoing.ts
314
+ const DEFAULT_SCHEMA = "better_supabase";
315
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
316
+ const textOf = (value) => value === null || value === void 0 ? null : String(value);
317
+ function toDelivery(row) {
318
+ return {
319
+ id: String(row["id"]),
320
+ endpointId: String(row["endpoint_id"]),
321
+ url: String(row["url"]),
322
+ type: String(row["type"]),
323
+ payload: row["payload"] ?? {},
324
+ attempt: Number(row["attempt"] ?? 1),
325
+ tenant: textOf(row["tenant"]),
326
+ eventId: textOf(row["event_id"]),
327
+ runId: textOf(row["run_id"]),
328
+ createdAt: typeof row["created_at"] === "string" ? temporal().Instant.from(row["created_at"]) : temporal().Now.instant()
329
+ };
330
+ }
331
+ const SCHEDULE = [
332
+ 5,
333
+ 300,
334
+ 1800,
335
+ 7200,
336
+ 18e3,
337
+ 36e3
338
+ ];
339
+ const defaultBackoff = (attempt) => Math.round(Math.random() * SCHEDULE[Math.min(Math.max(attempt, 1), SCHEDULE.length) - 1]);
340
+ const MAX_RETRY_AFTER = 86400;
341
+ const defaultRetryable = (status) => status === 408 || status === 429 || status >= 500;
342
+ const errorText = (cause) => cause instanceof Error ? cause.message : String(cause);
343
+ function createWebhooks(options) {
344
+ const { transport } = options;
345
+ const schema = options.schema ?? DEFAULT_SCHEMA;
346
+ const mappers = options.errorMappers ?? [];
347
+ const store = options.secrets ?? sqlSecretStore(transport, { schema });
348
+ const signer = options.signer ?? standardWebhooks();
349
+ const maxAttempts = options.retry?.maxAttempts ?? 8;
350
+ const backoff = options.retry?.backoff ?? defaultBackoff;
351
+ const retryable = options.retry?.retryable ?? defaultRetryable;
352
+ const bodyLimit = options.responseBodyLimit ?? 2e3;
353
+ let http = options.http;
354
+ const httpTransport = () => http ??= fetchTransport({ allowUrl: options.allowUrl ?? publicUrl() });
355
+ const call = (fn, args) => transport.call(schema, fn, args);
356
+ function run(fn, then) {
357
+ return AsyncResult.from(async () => {
358
+ try {
359
+ return ok(then(await fn()));
360
+ } catch (cause) {
361
+ const raw = rawError(cause);
362
+ return err(raw ? mapDbError(raw, mappers) : toDbError(cause));
363
+ }
364
+ });
365
+ }
366
+ function emit(type, delivery, extra = {}) {
367
+ if (!options.events) return;
368
+ emitKitEvent(options.events, type, {
369
+ endpointId: delivery.endpointId,
370
+ deliveryId: delivery.id,
371
+ eventType: delivery.type,
372
+ attempt: delivery.attempt,
373
+ ...extra
374
+ }, {
375
+ subject: `webhooks/${delivery.endpointId}`,
376
+ ...delivery.tenant ? { tenant: delivery.tenant } : {},
377
+ ...options.context ? { context: options.context } : {}
378
+ });
379
+ }
380
+ async function complete(delivery, status, detail) {
381
+ const retryAfter = Math.min(detail.response?.retryAfter ?? 0, MAX_RETRY_AFTER);
382
+ const retryAt = status === "retrying" ? temporal().Now.instant().add({ seconds: Math.max(backoff(delivery.attempt), retryAfter) }).toString() : void 0;
383
+ return call("complete_webhook_delivery", {
384
+ delivery: delivery.id,
385
+ outcome: {
386
+ status,
387
+ attempt: delivery.attempt,
388
+ ...retryAt ? { retry_at: retryAt } : {},
389
+ ...detail.response ? {
390
+ response_status: detail.response.status,
391
+ response_body: detail.response.body.slice(0, bodyLimit)
392
+ } : {},
393
+ ...detail.durationMs === void 0 ? {} : { duration_ms: detail.durationMs },
394
+ ...detail.error === void 0 ? {} : { error: detail.error.slice(0, bodyLimit) }
395
+ }
396
+ });
397
+ }
398
+ const failure = (delivery) => delivery.attempt >= maxAttempts ? "dead" : "retrying";
399
+ async function attempt(delivery) {
400
+ if (options.shouldDeliver) {
401
+ let send;
402
+ try {
403
+ send = await options.shouldDeliver(delivery);
404
+ } catch (cause) {
405
+ return {
406
+ outcome: "canceled",
407
+ error: errorText(cause)
408
+ };
409
+ }
410
+ if (!send) return {
411
+ outcome: "canceled",
412
+ error: "Canceled by shouldDeliver"
413
+ };
414
+ }
415
+ let body;
416
+ let headers;
417
+ try {
418
+ const secrets = await store.secrets(delivery.endpointId);
419
+ if (secrets.length === 0) return {
420
+ outcome: "dead",
421
+ error: "The endpoint has no signing secret"
422
+ };
423
+ body = JSON.stringify(options.transform ? await options.transform(delivery) : {
424
+ type: delivery.type,
425
+ timestamp: delivery.createdAt.toString(),
426
+ data: delivery.payload
427
+ });
428
+ headers = {
429
+ "content-type": "application/json",
430
+ "user-agent": "better-supabase-webhooks",
431
+ ...options.headers?.(delivery),
432
+ ...await signer.sign({
433
+ id: delivery.id,
434
+ body,
435
+ timestamp: temporal().Now.instant(),
436
+ secrets
437
+ })
438
+ };
439
+ } catch (cause) {
440
+ return {
441
+ outcome: failure(delivery),
442
+ error: errorText(cause)
443
+ };
444
+ }
445
+ const started = Date.now();
446
+ try {
447
+ const response = await httpTransport().send({
448
+ url: delivery.url,
449
+ headers,
450
+ body
451
+ });
452
+ const durationMs = Date.now() - started;
453
+ if (response.status >= 200 && response.status < 300) return {
454
+ outcome: "succeeded",
455
+ response,
456
+ durationMs
457
+ };
458
+ return {
459
+ outcome: retryable(response.status) ? failure(delivery) : "dead",
460
+ response,
461
+ durationMs,
462
+ error: `HTTP ${String(response.status)}`
463
+ };
464
+ } catch (cause) {
465
+ return {
466
+ outcome: cause instanceof WebhookPolicyError ? "dead" : failure(delivery),
467
+ durationMs: Date.now() - started,
468
+ error: errorText(cause)
469
+ };
470
+ }
471
+ }
472
+ async function deliver(deliverOptions = {}) {
473
+ const counts = {
474
+ succeeded: 0,
475
+ retrying: 0,
476
+ dead: 0,
477
+ canceled: 0,
478
+ disabled: 0
479
+ };
480
+ const deadline = deliverOptions.budgetMs === void 0 ? Number.POSITIVE_INFINITY : Date.now() + deliverOptions.budgetMs;
481
+ const batch = deliverOptions.batch ?? 25;
482
+ const concurrency = Math.max(1, deliverOptions.concurrency ?? 10);
483
+ async function handle(delivery) {
484
+ const result = await attempt(delivery);
485
+ const state = await complete(delivery, result.outcome, result);
486
+ if (state === "stale") return;
487
+ switch (result.outcome) {
488
+ case "succeeded":
489
+ counts.succeeded += 1;
490
+ emit("webhook.delivered", delivery, { status: result.response?.status ?? 200 });
491
+ break;
492
+ case "retrying":
493
+ case "dead":
494
+ if (result.outcome === "retrying") counts.retrying += 1;
495
+ else counts.dead += 1;
496
+ emit("webhook.failed", delivery, {
497
+ ...result.response ? { status: result.response.status } : {},
498
+ ...result.error ? { error: result.error } : {}
499
+ });
500
+ break;
501
+ case "canceled":
502
+ counts.canceled += 1;
503
+ break;
504
+ default: {
505
+ const unknown = result.outcome;
506
+ throw new TypeError(`Unknown outcome ${String(unknown)}`);
507
+ }
508
+ }
509
+ if (state === "disabled") {
510
+ counts.disabled += 1;
511
+ emit("webhook.disabled", delivery);
512
+ }
513
+ }
514
+ while (Date.now() < deadline) {
515
+ const claimed = await call("claim_webhook_deliveries", {
516
+ max_items: batch,
517
+ lease: deliverOptions.lease ?? "2 minutes",
518
+ max_attempts: maxAttempts
519
+ });
520
+ const queue = (Array.isArray(claimed) ? claimed : []).filter(isRecord).map(toDelivery);
521
+ const size = queue.length;
522
+ await Promise.all(Array.from({ length: Math.min(concurrency, size) }, async () => {
523
+ for (let next = queue.shift(); next; next = queue.shift()) await handle(next);
524
+ }));
525
+ if (size < batch) break;
526
+ }
527
+ return counts;
528
+ }
529
+ return {
530
+ publish: (event) => run(() => call("publish_webhook_event", {
531
+ event_type: event.type,
532
+ payload: event.data ?? {},
533
+ tenant: event.tenant ?? null,
534
+ event_id: event.id ?? null
535
+ }), Number),
536
+ dispatch: (input) => run(() => call("dispatch_webhook", {
537
+ endpoint: input.endpointId,
538
+ event_type: input.type,
539
+ payload: input.data ?? {},
540
+ run_id: input.runId ?? null,
541
+ event_id: input.eventId ?? null
542
+ }), String),
543
+ redeliver: (deliveryId) => run(() => call("redeliver_webhook", { delivery: deliveryId }), String),
544
+ rotateSecret(endpointId, rotateOptions) {
545
+ if (!store.rotate) return AsyncResult.err(dbError("unexpected", "The secret store can't rotate secrets"));
546
+ const rotate = store.rotate.bind(store);
547
+ return run(() => rotate(endpointId, rotateOptions), String);
548
+ },
549
+ deliver,
550
+ deliverRoute(routeOptions) {
551
+ const secret = routeOptions.secret;
552
+ if (!secret) throw new TypeError("deliverRoute needs a secret, such as process.env.CRON_SECRET");
553
+ return async (request) => {
554
+ const instance = new URL(request.url).pathname;
555
+ if (request.method !== "GET" && request.method !== "POST") return new Response(null, {
556
+ status: 405,
557
+ headers: { allow: "GET, POST" }
558
+ });
559
+ if (!verifySharedSecret(request, secret)) return problemResponse(dbError("unauthorized", "The deliver route needs its bearer secret"), { instance });
560
+ try {
561
+ return Response.json(await deliver({
562
+ budgetMs: 5e4,
563
+ ...routeOptions
564
+ }));
565
+ } catch (cause) {
566
+ routeOptions.onError?.(cause);
567
+ return problemResponse(toDbError(cause), { instance });
568
+ }
569
+ };
570
+ },
571
+ sink: (sinkOptions = {}) => {
572
+ const prefix = `${sinkOptions.typePrefix ?? "dev.better-supabase"}.`;
573
+ return { async send(events) {
574
+ for (const event of events) {
575
+ const tenant = event["partitionkey"];
576
+ await call("publish_webhook_event", {
577
+ event_type: event.type.startsWith(prefix) ? event.type.slice(prefix.length) : event.type,
578
+ payload: event.data ?? {},
579
+ tenant: typeof tenant === "string" ? tenant : null,
580
+ event_id: event.id
581
+ });
582
+ }
583
+ } };
584
+ }
585
+ };
586
+ }
587
+ //#endregion
588
+ export { WebhookPolicyError, authHook, createWebhooks, databaseChange, fetchTransport, hmacSigner, hookError, isDatabaseWebhook, isPublicAddress, publicUrl, rpcTransport, signWebhook, sqlSecretStore, sqlTransport, standardWebhooks, timingSafeEqual, verifySharedSecret, verifyWebhook };
@@ -0,0 +1,35 @@
1
+ import { n as dbError, t as DbException } from "./errors-DtwXa6b8.js";
2
+ import { i as temporalText } from "./temporal-CVlIV2DZ.js";
3
+ import { t as temporal } from "./temporal-required-CAt4Dc9Z.js";
4
+ //#region src/ir/wire.ts
5
+ /** Wire form of an app value: Temporal values to ISO text, `bigint` to decimal text. */
6
+ function encodeValue(value) {
7
+ if (typeof value !== "object" || value === null) return typeof value === "bigint" ? value.toString() : value;
8
+ const text = temporalText(value);
9
+ if (text !== void 0) return text;
10
+ if (Array.isArray(value)) return value.map(encodeValue);
11
+ return value;
12
+ }
13
+ /** Postgres timestamps can be `infinity`; Temporal has no such value. */
14
+ function finite(value, type) {
15
+ const text = String(value);
16
+ if (text === "infinity" || text === "-infinity") throw new DbException(dbError("invalid_value", `${text} can't be held by a Temporal.${type}`));
17
+ return text;
18
+ }
19
+ /** App form of one wire scalar for a column with `codec`. */
20
+ function decodeScalar(codec, value) {
21
+ if (value === null || value === void 0) return value;
22
+ switch (codec) {
23
+ case "instant": return temporal().Instant.from(finite(value, "Instant"));
24
+ case "plainDateTime": return temporal().PlainDateTime.from(finite(value, "PlainDateTime"));
25
+ case "bigint": return BigInt(value);
26
+ case "string": return String(value);
27
+ default: return codec;
28
+ }
29
+ }
30
+ /** App form of a wire value for a column with `codec`. */
31
+ function decodeValue(codec, value) {
32
+ return Array.isArray(value) ? value.map((item) => decodeScalar(codec, item)) : decodeScalar(codec, value);
33
+ }
34
+ //#endregion
35
+ export { decodeValue as n, encodeValue as r, decodeScalar as t };