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.
- package/README.md +9 -6
- package/bin/better-supabase.js +4 -0
- package/dist/{base64-CQeB1eSf.js → base64-D2gsioCY.js} +2 -2
- package/dist/channel-FQW-CnGR.d.ts +70 -0
- package/dist/claims-BAf6c7is.js +48 -0
- package/dist/cli/_format-Cax5EsM9.js +101 -0
- package/dist/cli/bin.d.ts +1 -0
- package/dist/cli/bin.js +48 -0
- package/dist/cli/codemod-DqV7612m.js +388 -0
- package/dist/cli/command-CK14fP91.js +502 -0
- package/dist/cli/compare-BmIWVEui.js +9 -0
- package/dist/cli/config-CPVpXP5I.js +1965 -0
- package/dist/cli/config-dtoekM0v.js +2 -0
- package/dist/cli/db-DOBPt660.js +52 -0
- package/dist/cli/diff-SjkO5rNr.js +752 -0
- package/dist/cli/dist-DBPdlPYK.js +1112 -0
- package/dist/cli/dist-DreZ24Sz.js +129 -0
- package/dist/cli/dist-xn4wlboc.js +732 -0
- package/dist/cli/doctor-L7Z8mYTx.js +2863 -0
- package/dist/cli/env-DmKv_G4A.js +108 -0
- package/dist/cli/env-jIFUADLY.js +511 -0
- package/dist/cli/errors-4v4DTtda.js +56 -0
- package/dist/cli/fastest-levenshtein-CFN-lNDv.js +120 -0
- package/dist/cli/gen-BJc9vdyu.js +2 -0
- package/dist/cli/gen-C2osRcr5.js +2204 -0
- package/dist/cli/index.d.ts +493 -0
- package/dist/cli/index.js +184 -0
- package/dist/cli/init-Saf5-R9T.js +550 -0
- package/dist/cli/introspect--H7oGmcd.js +81 -0
- package/dist/cli/io-k41_z-jT.js +24 -0
- package/dist/cli/json5-ChRycquj.js +787 -0
- package/dist/cli/jsonc-BUniUpOk.js +2 -0
- package/dist/cli/jsonc-D5Fu67jw.js +45 -0
- package/dist/cli/keys-ByKHnvP9.js +83 -0
- package/dist/cli/ohash-D7bG_EOb.js +277 -0
- package/dist/cli/openapi-Cp5Fyta-.js +66 -0
- package/dist/cli/project-B_GeXyHh.js +70 -0
- package/dist/cli/prompts-BNcNfAve.js +1142 -0
- package/dist/cli/prompts-BTYQGunc.js +2 -0
- package/dist/cli/read-sets-BfMQcKy8.js +289 -0
- package/dist/cli/rolldown-runtime-BPOCksWG.js +24 -0
- package/dist/cli/run-BKR2W_79.js +351 -0
- package/dist/cli/seed-CUlqWh-8.js +98 -0
- package/dist/cli/skills-LJJIhLFR.js +137 -0
- package/dist/cli/sql-DzPbfngx.js +348 -0
- package/dist/cli/style-C0NFdJRL.js +29 -0
- package/dist/cli/toml-DB-MNnFs.js +424 -0
- package/dist/cli/version-BDJuO0UR.js +4 -0
- package/dist/cli/yaml-CYCbJ6et.js +2252 -0
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.js +15 -22
- package/dist/clone-BfgmLU90.js +21 -0
- package/dist/{compiler-2zqGzxVZ.d.ts → compiler-BXH_aZ7N.d.ts} +2 -2
- package/dist/config/index.d.ts +4 -4
- package/dist/config/index.js +5 -5
- package/dist/{config-DOsGYSiT.js → config-DreERlKo.js} +1 -1
- package/dist/{config-C1CYcm6o.d.ts → config-T_ejwyd3.d.ts} +176 -11
- package/dist/{define-DUbydmJa.d.ts → define-Bp_BZa5h.d.ts} +37 -29
- package/dist/{define-DF341GDj.js → define-cNKzqwL_.js} +356 -89
- package/dist/edge/index.d.ts +20 -13
- package/dist/edge/index.js +55 -26
- package/dist/{entitlements-DvJXoI02.d.ts → entitlements-DUxU4oHe.d.ts} +2 -2
- package/dist/env/index.d.ts +2 -2
- package/dist/env/index.js +34 -1
- package/dist/{errors-Bl3rkOzo.js → errors-DtwXa6b8.js} +2 -1
- package/dist/events/index.d.ts +3 -2
- package/dist/events/index.js +69 -16
- package/dist/events-aIO7vsIk.d.ts +230 -0
- package/dist/executor-BunPOO-k.d.ts +402 -0
- package/dist/{executor-B26lgbHD.d.ts → executor-Cghdstwu.d.ts} +1 -2
- package/dist/{executor-C57jx2pn.js → executor-XycESQPH.js} +3 -3
- package/dist/hono/index.d.ts +27 -23
- package/dist/hono/index.js +46 -11
- package/dist/impersonation-VfaGc4Q_.js +41 -0
- package/dist/{index-BNsABq7P.d.ts → index-BGdgBPdf.d.ts} +66 -25
- package/dist/{index-qriRM9cZ.d.ts → index-C7f8cOBM.d.ts} +4 -4
- package/dist/{index-Dy6kuF0P.d.ts → index-CLg87OBW.d.ts} +6 -1
- package/dist/{index-GV77NpRP.d.ts → index-DOESKG27.d.ts} +34 -9
- package/dist/{index-CriFFJ_p.d.ts → index-DgILJRXj.d.ts} +43 -17
- package/dist/{index-UJ-LUxtI.d.ts → index-DmXSxbLi.d.ts} +14 -14
- package/dist/{index-Cf-D2jk1.d.ts → index-Dxn2a5cw.d.ts} +2 -2
- package/dist/{index-CTTMS6_1.d.ts → index-SaTsEfGP.d.ts} +86 -9
- package/dist/{index-CADlNHK-.d.ts → index-fag8VJZ2.d.ts} +5 -6
- package/dist/index.d.ts +34 -17
- package/dist/index.js +15 -14
- package/dist/jobs/index.d.ts +147 -95
- package/dist/jobs/index.js +773 -250
- package/dist/{json-schema--aOXAGfU.js → json-schema-m1fiwKgN.js} +23 -2
- package/dist/kit-events-CTFDfSwb.js +60 -0
- package/dist/kit-transport-B82k1djG.d.ts +33 -0
- package/dist/kit-transport-DJlHQUBU.js +55 -0
- package/dist/{config-Be4GwsqI.js → kits-CGLLrNPd.js} +38 -10
- package/dist/list/index.d.ts +1 -1
- package/dist/list/index.js +3 -3
- package/dist/{live-D8rV1Ldw.js → live-CdJBOc4f.js} +13 -9
- package/dist/{live-QE3UPbGm.d.ts → live-D9W2M5aO.d.ts} +16 -15
- package/dist/lookup-BaP5bI27.js +18 -0
- package/dist/mcp/index.d.ts +39 -22
- package/dist/mcp/index.js +39 -25
- package/dist/{mfa-x3V3vGh-.d.ts → mfa-CusDgPaD.d.ts} +5 -1
- package/dist/next/image/index.js +6 -3
- package/dist/next/index.d.ts +60 -36
- package/dist/next/index.js +167 -50
- package/dist/notifications/index.d.ts +162 -0
- package/dist/notifications/index.js +264 -0
- package/dist/openapi/index.d.ts +1 -1
- package/dist/openapi/index.js +6 -6
- package/dist/orgs/index.d.ts +132 -0
- package/dist/orgs/index.js +245 -0
- package/dist/orpc/index.d.ts +37 -20
- package/dist/orpc/index.js +24 -11
- package/dist/otel/index.d.ts +24 -5
- package/dist/otel/index.js +124 -64
- package/dist/permdock-sql-C2Inc6Xc.d.ts +29 -0
- package/dist/permdock-sql-DgOfeixi.js +51 -0
- package/dist/plugin-DwARMuQp.js +59 -0
- package/dist/{plugin-DVRB8qNE.d.ts → plugin-o144f322.d.ts} +17 -5
- package/dist/plugins/actor/index.d.ts +5 -2
- package/dist/plugins/actor/index.js +22 -8
- package/dist/plugins/rules/index.d.ts +2 -2
- package/dist/plugins/rules/index.js +52 -17
- package/dist/plugins/soft-delete/index.d.ts +3 -3
- package/dist/plugins/soft-delete/index.js +14 -5
- package/dist/plugins/tenant/index.d.ts +3 -3
- package/dist/plugins/tenant/index.js +45 -21
- package/dist/plugins/timestamps/index.d.ts +1 -1
- package/dist/plugins/timestamps/index.js +4 -3
- package/dist/plugins/validation/index.d.ts +2 -2
- package/dist/plugins/validation/index.js +26 -6
- package/dist/{pool-B9qsLyFR.d.ts → pool-C9voM4Kw.d.ts} +24 -6
- package/dist/postgres/index.d.ts +5 -5
- package/dist/postgres/index.js +20 -10
- package/dist/{postgrest-DmBlevh-.d.ts → postgrest-CruYKAvV.d.ts} +1 -1
- package/dist/{problem-CGt1ADix.js → problem-BW1KhzSG.js} +2 -1
- package/dist/{problem-Z2RTQjZD.d.ts → problem-Blxe8S31.d.ts} +1 -1
- package/dist/query/index.d.ts +2 -2
- package/dist/query/index.js +3 -3
- package/dist/{query-BdKLTPlj.js → query-CCbo44lG.js} +18 -18
- package/dist/queue-BYUCjr59.d.ts +252 -0
- package/dist/react/index.d.ts +8 -7
- package/dist/react/index.js +4 -3
- package/dist/react/server.d.ts +16 -5
- package/dist/react/server.js +12 -4
- package/dist/react/session.d.ts +10 -4
- package/dist/react/session.js +11 -3
- package/dist/{react-DNBYNazu.js → react-C_s7kiNz.js} +138 -46
- package/dist/{read-set-D0qBcboM.js → read-set-DQFt1N_Y.js} +71 -15
- package/dist/realtime/index.d.ts +3 -3
- package/dist/realtime/index.js +14 -12
- package/dist/{index-kpgopLNv.d.ts → refresh-BTrQ_ZTp.d.ts} +6 -3
- package/dist/{resolve-BRciX2jK.d.ts → resolve-C15TJq77.d.ts} +22 -11
- package/dist/{resource-D30rp0GN.js → resource-CRxxwkQq.js} +24 -9
- package/dist/{resource-CwVgiI5q.d.ts → resource-D8iqWMr-.d.ts} +6 -6
- package/dist/respond-D2OQl62-.d.ts +371 -0
- package/dist/{result-DWXatkd6.js → result-BKiliPY7.js} +2 -2
- package/dist/server/index.d.ts +17 -15
- package/dist/server/index.js +16 -22
- package/dist/server-Csx2sRjg.js +655 -0
- package/dist/{shared-_6-uPL7E.js → shared-D1dRUrAg.js} +19 -4
- package/dist/signers-Cc6r98SW.d.ts +162 -0
- package/dist/{simplify-LakqK1vH.js → simplify-_0oUKqCd.js} +27 -95
- package/dist/sql/index.d.ts +247 -10
- package/dist/sql/index.js +8591 -780
- package/dist/{sql-BIgk12ew.js → sql-Coh6n4cC.js} +11 -3
- package/dist/ssr/index.d.ts +4 -4
- package/dist/ssr/index.js +1 -2
- package/dist/{standard-FUi3Z3-7.js → standard-D7r6dMUu.js} +6 -3
- package/dist/{standard-eudBiRk1.d.ts → standard-J4YgMOLm.d.ts} +1 -1
- package/dist/{stats-D78UQ8Yu.js → stats-Bqob3WqT.js} +34 -9
- package/dist/storage/index.d.ts +4 -4
- package/dist/storage/index.js +174 -27
- package/dist/support-DT-OhSxD.d.ts +88 -0
- package/dist/support-Dk6vdmEw.js +291 -0
- package/dist/support-view-Da5peKFg.d.ts +17 -0
- package/dist/support-view-g4YLBeqX.js +18 -0
- package/dist/{tables-Dhha3BXM.js → tables-Cb-3_VWc.js} +14 -0
- package/dist/{permdock-sql-Bta_G0v-.js → template-TxrGI5jR.js} +2 -31
- package/dist/{temporal-BwshQk0W.js → temporal-CVlIV2DZ.js} +17 -4
- package/dist/{temporal-required-CMxeSVPo.js → temporal-required-CAt4Dc9Z.js} +2 -2
- package/dist/testing/index.d.ts +113 -24
- package/dist/testing/index.js +375 -47
- package/dist/{types-DONv9SHN.d.ts → types-Dnf_wRDz.d.ts} +26 -3
- package/dist/user-change-CHFEUro1.js +49 -0
- package/dist/{webhooks-CAIFQSTM.js → verify-Ce49ETPz.js} +38 -19
- package/dist/{version-BfpG2gks.js → version-DM_Roffb.js} +1 -1
- package/dist/{view-3Po_PVlr.d.ts → view-BEf0diC5.d.ts} +10 -5
- package/dist/{mfa-BxoR1RkN.js → view-Bq64ki8-.js} +210 -64
- package/dist/webhooks/index.d.ts +136 -5
- package/dist/webhooks/index.js +588 -2
- package/dist/wire-D2dT86vi.js +35 -0
- package/package.json +50 -10
- package/schemas/config-v1.json +131 -1
- package/schemas/doctor-report-v1.json +10 -0
- package/skills/better-supabase/SKILL.md +60 -10
- package/skills/better-supabase/references/plugins.md +13 -14
- package/skills/better-supabase/references/troubleshooting.md +25 -4
- package/skills/better-supabase-api/SKILL.md +74 -19
- package/skills/better-supabase-api/references/adapters.md +48 -27
- package/skills/better-supabase-auth/SKILL.md +149 -0
- package/skills/better-supabase-auth/references/permdock.md +100 -0
- package/skills/better-supabase-testing/SKILL.md +58 -4
- package/dist/claims-CoTobNC-.js +0 -25
- package/dist/events-Do8jXh4D.d.ts +0 -84
- package/dist/executor-DynmIWhq.d.ts +0 -195
- package/dist/invalidate-DbxuL2jL.js +0 -18
- package/dist/permdock-sql-DImJULY-.d.ts +0 -17
- package/dist/plugin-BqR6wKMB.js +0 -23
- package/dist/respond-C44EZAJo.js +0 -399
- package/dist/respond-DAkPIdTH.d.ts +0 -175
- package/dist/result-c2OYgCV2.d.ts +0 -203
- package/dist/view-wZ-9qIUz.js +0 -45
package/dist/webhooks/index.js
CHANGED
|
@@ -1,2 +1,588 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
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 };
|