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.
- package/README.md +2 -0
- package/dist/channel-FQW-CnGR.d.ts +70 -0
- package/dist/cli/bin.js +1 -1
- package/dist/cli/codemod-DqV7612m.js +388 -0
- package/dist/cli/{config-D9R0o1LH.js → config-CPVpXP5I.js} +78 -8
- package/dist/cli/{config-SSKSTUl2.js → config-dtoekM0v.js} +1 -1
- package/dist/cli/{read-sets-CMa29t9a.js → diff-SjkO5rNr.js} +1 -220
- package/dist/cli/{dist-CFgGknmZ.js → dist-DBPdlPYK.js} +4 -4
- package/dist/cli/{dist-B1MvLv-M.js → dist-DreZ24Sz.js} +1 -1
- package/dist/cli/{dist-Du7VnEUG.js → dist-xn4wlboc.js} +112 -1
- package/dist/cli/{doctor-BOym3mbL.js → doctor-L7Z8mYTx.js} +531 -50
- package/dist/cli/{env-BZ0uy8XK.js → env-DmKv_G4A.js} +1 -1
- package/dist/cli/gen-BJc9vdyu.js +2 -0
- package/dist/cli/{gen-Ccn_p90L.js → gen-C2osRcr5.js} +283 -87
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +4 -4
- package/dist/cli/{init-ClnxRYBF.js → init-Saf5-R9T.js} +2 -2
- package/dist/cli/{introspect-DxqfkVIv.js → introspect--H7oGmcd.js} +2 -2
- package/dist/cli/{openapi-8WHjxiTJ.js → openapi-Cp5Fyta-.js} +1 -1
- package/dist/cli/read-sets-BfMQcKy8.js +289 -0
- package/dist/cli/{run-CP20LKk_.js → run-BKR2W_79.js} +16 -12
- package/dist/cli/{seed-5J6eu_Xg.js → seed-CUlqWh-8.js} +1 -1
- package/dist/cli/sql-DzPbfngx.js +348 -0
- package/dist/cli/{version-5mW_8sY1.js → version-BDJuO0UR.js} +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +3 -10
- package/dist/clone-BfgmLU90.js +21 -0
- package/dist/{compiler-DB3aSFOA.d.ts → compiler-BXH_aZ7N.d.ts} +2 -2
- package/dist/config/index.d.ts +4 -4
- package/dist/config/index.js +4 -4
- package/dist/{config-CbvFmGD8.js → config-DreERlKo.js} +1 -1
- package/dist/{config-C1CYcm6o.d.ts → config-T_ejwyd3.d.ts} +176 -11
- package/dist/{define-CRS2MRV3.d.ts → define-Bp_BZa5h.d.ts} +11 -7
- package/dist/{define-Bh0AqTik.js → define-cNKzqwL_.js} +202 -16
- package/dist/edge/index.d.ts +16 -5
- package/dist/edge/index.js +10 -7
- package/dist/{entitlements-Dm-FHpNH.d.ts → entitlements-DUxU4oHe.d.ts} +1 -1
- package/dist/env/index.d.ts +2 -2
- package/dist/env/index.js +34 -1
- package/dist/{errors-L0xK41y_.js → errors-DtwXa6b8.js} +1 -0
- package/dist/events/index.d.ts +3 -2
- package/dist/events/index.js +65 -12
- package/dist/events-aIO7vsIk.d.ts +230 -0
- package/dist/{result-CqEsbgYs.d.ts → executor-BunPOO-k.d.ts} +200 -1
- package/dist/{executor-CZ7ImRiD.d.ts → executor-Cghdstwu.d.ts} +1 -2
- package/dist/{executor-CVt6dD0B.js → executor-XycESQPH.js} +3 -3
- package/dist/hono/index.d.ts +17 -6
- package/dist/hono/index.js +40 -5
- package/dist/impersonation-VfaGc4Q_.js +41 -0
- package/dist/{index-BBdvo-5d.d.ts → index-BGdgBPdf.d.ts} +49 -6
- package/dist/{index-BhHEC04r.d.ts → index-C7f8cOBM.d.ts} +2 -2
- package/dist/{index-Dy6kuF0P.d.ts → index-CLg87OBW.d.ts} +6 -1
- package/dist/{index-D5IfUIDh.d.ts → index-DOESKG27.d.ts} +33 -8
- package/dist/{index-DFmOpBGC.d.ts → index-DgILJRXj.d.ts} +35 -9
- package/dist/{index-RlLXd8dL.d.ts → index-DmXSxbLi.d.ts} +4 -4
- package/dist/{index-Cf-D2jk1.d.ts → index-Dxn2a5cw.d.ts} +2 -2
- package/dist/{index-528Ugmyp.d.ts → index-SaTsEfGP.d.ts} +76 -8
- package/dist/{index-BAy4tsAP.d.ts → index-fag8VJZ2.d.ts} +3 -4
- package/dist/index.d.ts +30 -13
- package/dist/index.js +10 -9
- package/dist/jobs/index.d.ts +147 -101
- package/dist/jobs/index.js +744 -237
- package/dist/{json-schema-DP6V7cbr.js → json-schema-m1fiwKgN.js} +22 -1
- 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-BpxDfnbg.js → kits-CGLLrNPd.js} +37 -9
- package/dist/list/index.d.ts +1 -1
- package/dist/{live-BHakSWWo.d.ts → live-D9W2M5aO.d.ts} +6 -5
- package/dist/mcp/index.d.ts +25 -8
- package/dist/mcp/index.js +23 -14
- package/dist/{mfa-C7MNQ1Ob.d.ts → mfa-CusDgPaD.d.ts} +5 -1
- package/dist/next/index.d.ts +31 -10
- package/dist/next/index.js +136 -27
- 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 +2 -2
- package/dist/orgs/index.d.ts +132 -0
- package/dist/orgs/index.js +245 -0
- package/dist/orpc/index.d.ts +28 -8
- package/dist/orpc/index.js +16 -3
- package/dist/otel/index.d.ts +21 -2
- package/dist/otel/index.js +50 -9
- 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-bNhvJwRh.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 +15 -10
- 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 +2 -2
- package/dist/plugins/tenant/index.js +38 -20
- 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-DFwEQwmN.d.ts → pool-C9voM4Kw.d.ts} +17 -6
- package/dist/postgres/index.d.ts +5 -5
- package/dist/postgres/index.js +20 -11
- package/dist/{postgrest-Bc-0Xw62.d.ts → postgrest-CruYKAvV.d.ts} +1 -1
- package/dist/{problem-BihjPR0t.js → problem-BW1KhzSG.js} +2 -1
- package/dist/{problem-U8UEKccv.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-BMMSWbI7.js → query-CCbo44lG.js} +2 -2
- 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 +14 -3
- package/dist/react/server.js +10 -2
- package/dist/react/session.d.ts +7 -1
- package/dist/react/session.js +9 -1
- package/dist/{react-CQHMaESR.js → react-C_s7kiNz.js} +103 -11
- package/dist/{read-set-FWK8qQ58.js → read-set-DQFt1N_Y.js} +22 -1
- package/dist/realtime/index.d.ts +3 -3
- package/dist/realtime/index.js +5 -4
- package/dist/{refresh-BwYkyNOQ.d.ts → refresh-BTrQ_ZTp.d.ts} +1 -1
- package/dist/{resolve-BAqQcwMW.d.ts → resolve-C15TJq77.d.ts} +11 -5
- package/dist/{resource-My_y-Syh.js → resource-CRxxwkQq.js} +22 -7
- package/dist/{resource-BJcaIfxi.d.ts → resource-D8iqWMr-.d.ts} +5 -5
- package/dist/respond-D2OQl62-.d.ts +371 -0
- package/dist/{result-Bj6yt33D.js → result-BKiliPY7.js} +1 -1
- package/dist/server/index.d.ts +14 -12
- package/dist/server/index.js +11 -17
- package/dist/{server-BA75B0mT.js → server-Csx2sRjg.js} +251 -34
- package/dist/{shared-DnoCdMDr.js → shared-D1dRUrAg.js} +16 -2
- package/dist/signers-Cc6r98SW.d.ts +162 -0
- package/dist/{simplify-Cqvfr9dj.js → simplify-_0oUKqCd.js} +4 -115
- package/dist/sql/index.d.ts +247 -10
- package/dist/sql/index.js +8590 -779
- package/dist/{sql-B34HU2DI.js → sql-Coh6n4cC.js} +9 -2
- package/dist/ssr/index.d.ts +4 -4
- package/dist/ssr/index.js +1 -2
- package/dist/{standard-BRyBGTtp.js → standard-D7r6dMUu.js} +6 -3
- package/dist/{standard-BsaCYQJc.d.ts → standard-J4YgMOLm.d.ts} +1 -1
- package/dist/{stats-C_YxA2r1.js → stats-Bqob3WqT.js} +4 -4
- package/dist/storage/index.d.ts +4 -4
- package/dist/storage/index.js +155 -26
- 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/{permdock-sql-C4KFzLc9.js → template-TxrGI5jR.js} +2 -31
- package/dist/{temporal-BwshQk0W.js → temporal-CVlIV2DZ.js} +17 -4
- package/dist/{temporal-required-CBNHT6ni.js → temporal-required-CAt4Dc9Z.js} +2 -2
- package/dist/testing/index.d.ts +100 -10
- package/dist/testing/index.js +336 -8
- package/dist/{types-DONv9SHN.d.ts → types-Dnf_wRDz.d.ts} +26 -3
- package/dist/user-change-CHFEUro1.js +49 -0
- package/dist/{webhooks-Bh_Jf_-l.js → verify-Ce49ETPz.js} +5 -5
- package/dist/{version-Bmf7_FzM.js → version-DM_Roffb.js} +1 -1
- package/dist/{view-B64o81wp.d.ts → view-BEf0diC5.d.ts} +8 -3
- package/dist/{mfa-byfjruR9.js → view-Bq64ki8-.js} +95 -38
- package/dist/webhooks/index.d.ts +135 -4
- package/dist/webhooks/index.js +588 -2
- package/dist/wire-D2dT86vi.js +35 -0
- package/package.json +16 -8
- package/schemas/config-v1.json +131 -1
- package/schemas/doctor-report-v1.json +10 -0
- package/skills/better-supabase/SKILL.md +9 -6
- package/skills/better-supabase/references/plugins.md +6 -3
- package/skills/better-supabase/references/troubleshooting.md +1 -0
- package/skills/better-supabase-api/SKILL.md +9 -2
- package/skills/better-supabase-api/references/adapters.md +11 -8
- package/skills/better-supabase-auth/SKILL.md +3 -0
- package/skills/better-supabase-auth/references/permdock.md +56 -11
- package/dist/cli/gen-D5cLCULx.js +0 -2
- package/dist/cli/sql-D4VbqmXi.js +0 -199
- package/dist/events-B21UOhvP.d.ts +0 -84
- package/dist/executor-B5IoUxLE.d.ts +0 -195
- package/dist/invalidate-CgxmRamI.js +0 -24
- package/dist/permdock-sql-DImJULY-.d.ts +0 -17
- package/dist/plugin-BqR6wKMB.js +0 -23
- package/dist/respond-DgIbdrt8.d.ts +0 -194
- package/dist/view-BrJI2LKp.js +0 -45
package/dist/edge/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { n as dbError } from "../errors-
|
|
2
|
-
import { a as problemResponse } from "../problem-
|
|
3
|
-
import {
|
|
4
|
-
import { t as defineResource } from "../resource-
|
|
1
|
+
import { n as dbError } from "../errors-DtwXa6b8.js";
|
|
2
|
+
import { a as problemResponse } from "../problem-BW1KhzSG.js";
|
|
3
|
+
import { d as guard, n as createServer, p as respond, r as extendServer, u as defaultExpose } from "../server-Csx2sRjg.js";
|
|
4
|
+
import { t as defineResource } from "../resource-CRxxwkQq.js";
|
|
5
5
|
//#region src/edge/index.ts
|
|
6
6
|
const SUPABASE_CORS_HEADERS = [
|
|
7
7
|
"authorization",
|
|
@@ -56,7 +56,7 @@ function createEdge(betterSupabase, options = {}) {
|
|
|
56
56
|
const cors = options.cors === true ? {} : options.cors === false ? void 0 : options.cors;
|
|
57
57
|
const corsHeaders = cors ? corsFor(cors) : void 0;
|
|
58
58
|
const serve = (run, handlerOptions, early) => {
|
|
59
|
-
return async (request) => {
|
|
59
|
+
return async (request, _env, executionContext) => {
|
|
60
60
|
const extra = corsHeaders?.(request);
|
|
61
61
|
if (cors && request.method === "OPTIONS") return new Response(null, {
|
|
62
62
|
status: 204,
|
|
@@ -79,7 +79,10 @@ function createEdge(betterSupabase, options = {}) {
|
|
|
79
79
|
expose
|
|
80
80
|
});
|
|
81
81
|
}
|
|
82
|
-
response = ctx.
|
|
82
|
+
response = ctx.apply(response);
|
|
83
|
+
const { events } = betterSupabase;
|
|
84
|
+
const waitUntil = options.waitUntil ?? executionContext?.waitUntil.bind(executionContext);
|
|
85
|
+
if (events.pending && waitUntil) waitUntil(events.settled());
|
|
83
86
|
return extra ? withHeaders(response, extra) : response;
|
|
84
87
|
};
|
|
85
88
|
};
|
|
@@ -107,7 +110,7 @@ function createEdge(betterSupabase, options = {}) {
|
|
|
107
110
|
try {
|
|
108
111
|
key = id === void 0 || id === "" ? void 0 : decodeURIComponent(id);
|
|
109
112
|
} catch {
|
|
110
|
-
return problemResponse(dbError("
|
|
113
|
+
return problemResponse(dbError("invalid_input", `Malformed id in ${pathname}`), { instance: pathname });
|
|
111
114
|
}
|
|
112
115
|
routes.set(request, {
|
|
113
116
|
handler,
|
package/dist/env/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as EnvResult, c as
|
|
2
|
-
export { BetterSupabaseEnv, ENV_VARIABLES, EnvIssue, EnvOptions, EnvResult, EnvSource, EnvValidationError, PublicEnv, envSchema, loadEnv, parseEnv, publicEnv, toServerEnv };
|
|
1
|
+
import { a as EnvResult, c as InlineJwks, d as loadEnv, f as parseEnv, i as EnvOptions, l as PublicEnv, m as toServerEnv, n as ENV_VARIABLES, o as EnvSource, p as publicEnv, r as EnvIssue, s as EnvValidationError, t as BetterSupabaseEnv, u as envSchema } from "../index-CLg87OBW.js";
|
|
2
|
+
export { BetterSupabaseEnv, ENV_VARIABLES, EnvIssue, EnvOptions, EnvResult, EnvSource, EnvValidationError, InlineJwks, PublicEnv, envSchema, loadEnv, parseEnv, publicEnv, toServerEnv };
|
package/dist/env/index.js
CHANGED
|
@@ -13,6 +13,7 @@ const ENV_VARIABLES = {
|
|
|
13
13
|
secretKey: ["SUPABASE_SECRET_KEY"],
|
|
14
14
|
dbUrl: ["SUPABASE_DB_URL", "DATABASE_URL"],
|
|
15
15
|
jwksUrl: ["SUPABASE_JWKS_URL"],
|
|
16
|
+
jwks: ["SUPABASE_JWKS"],
|
|
16
17
|
readUrl: ["SUPABASE_READ_URL"]
|
|
17
18
|
};
|
|
18
19
|
const LOOPBACK = /^(localhost|127(?:\.\d{1,3}){3}|\[::1\])$/;
|
|
@@ -57,6 +58,35 @@ function preferred(keys) {
|
|
|
57
58
|
if (!keys) return void 0;
|
|
58
59
|
return keys["default"] ?? Object.values(keys)[0];
|
|
59
60
|
}
|
|
61
|
+
function isJwk(value) {
|
|
62
|
+
return typeof value === "object" && value !== null && "kty" in value && typeof value.kty === "string";
|
|
63
|
+
}
|
|
64
|
+
/** At least one key, each an object with a string `kty`, the shape the verifier reads. */
|
|
65
|
+
function isJwkList(value) {
|
|
66
|
+
return Array.isArray(value) && value.length > 0 && value.every(isJwk);
|
|
67
|
+
}
|
|
68
|
+
/** `SUPABASE_JWKS` as `@supabase/server` reads it: `{"keys":[...]}` or `[...]`. */
|
|
69
|
+
function checkJwks(raw, issues) {
|
|
70
|
+
let parsed;
|
|
71
|
+
try {
|
|
72
|
+
parsed = JSON.parse(raw);
|
|
73
|
+
} catch {
|
|
74
|
+
issues.push({
|
|
75
|
+
variables: ENV_VARIABLES.jwks,
|
|
76
|
+
message: "must be JSON: {\"keys\":[...]} or [...]"
|
|
77
|
+
});
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const keys = typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? "keys" in parsed ? parsed.keys : void 0 : parsed;
|
|
81
|
+
if (!isJwkList(keys)) {
|
|
82
|
+
issues.push({
|
|
83
|
+
variables: ENV_VARIABLES.jwks,
|
|
84
|
+
message: "must hold at least one JSON Web Key with a kty"
|
|
85
|
+
});
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
return { keys };
|
|
89
|
+
}
|
|
60
90
|
function checkUrl(value, variables, issues) {
|
|
61
91
|
let url;
|
|
62
92
|
try {
|
|
@@ -132,6 +162,8 @@ function parseEnv(source, options = {}) {
|
|
|
132
162
|
});
|
|
133
163
|
const rawJwks = first(source, ENV_VARIABLES.jwksUrl);
|
|
134
164
|
const jwksOverride = rawJwks ? checkUrl(rawJwks, ENV_VARIABLES.jwksUrl, issues) : void 0;
|
|
165
|
+
const rawInline = first(source, ENV_VARIABLES.jwks);
|
|
166
|
+
const jwks = rawInline ? checkJwks(rawInline, issues) : void 0;
|
|
135
167
|
const rawRead = first(source, ENV_VARIABLES.readUrl);
|
|
136
168
|
const readUrl = rawRead ? checkUrl(rawRead, ENV_VARIABLES.readUrl, issues) : void 0;
|
|
137
169
|
if (issues.length > 0 || !url || !publishableKey) return {
|
|
@@ -146,6 +178,7 @@ function parseEnv(source, options = {}) {
|
|
|
146
178
|
url: base,
|
|
147
179
|
publishableKey,
|
|
148
180
|
jwksUrl: jwksOverride ?? new URL(`${base}/auth/v1/.well-known/jwks.json`),
|
|
181
|
+
...jwks ? { jwks } : {},
|
|
149
182
|
...secretKey ? {
|
|
150
183
|
secretKey,
|
|
151
184
|
secretKeys: {
|
|
@@ -197,7 +230,7 @@ function toServerEnv(env) {
|
|
|
197
230
|
url: env.url,
|
|
198
231
|
publishableKeys: { default: env.publishableKey },
|
|
199
232
|
secretKeys: env.secretKeys ?? (env.secretKey ? { default: env.secretKey } : {}),
|
|
200
|
-
jwks: env.jwksUrl
|
|
233
|
+
jwks: env.jwks ?? env.jwksUrl
|
|
201
234
|
};
|
|
202
235
|
}
|
|
203
236
|
//#endregion
|
package/dist/events/index.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { C as kitEventAttributes, S as WebhookEventData, _ as KitEventType, b as OrgEventData, d as InvitationEventData, f as KIT_ATTRIBUTES, g as KitEventPattern, h as KitEventMeta, m as KitEventMap, p as KitEvent, v as KitEventsMatching, w as onKitEvent, x as SupportEventData, y as NotificationEventData } from "../events-aIO7vsIk.js";
|
|
2
|
+
import { _ as toHttp, a as ForwardOptions, c as ROW_EVENT_TYPES, d as forwardMutations, f as fromHttp, g as toCloudEvents, h as kitCloudEvent, i as ForwardKitOptions, l as RowEventData, m as isCloudEvent, n as CloudEventOptions, o as HttpMode, p as httpSink, r as EventSink, s as HttpSinkOptions, t as CloudEvent, u as forwardKitEvents } from "../index-DOESKG27.js";
|
|
3
|
+
export { CloudEvent, CloudEventOptions, EventSink, ForwardKitOptions, ForwardOptions, HttpMode, HttpSinkOptions, type InvitationEventData, KIT_ATTRIBUTES, type KitEvent, type KitEventMap, type KitEventMeta, type KitEventPattern, type KitEventType, type KitEventsMatching, type NotificationEventData, type OrgEventData, ROW_EVENT_TYPES, RowEventData, type SupportEventData, type WebhookEventData, forwardKitEvents, forwardMutations, fromHttp, httpSink, isCloudEvent, kitCloudEvent, kitEventAttributes, onKitEvent, toCloudEvents, toHttp };
|
package/dist/events/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { n as nowInstant } from "../temporal-
|
|
1
|
+
import { n as nowInstant } from "../temporal-CVlIV2DZ.js";
|
|
2
|
+
import { i as onKitEvent, r as kitEventAttributes, t as KIT_ATTRIBUTES } from "../kit-events-CTFDfSwb.js";
|
|
2
3
|
//#region src/events/index.ts
|
|
3
4
|
const ROW_EVENT_TYPES = {
|
|
4
5
|
insert: "dev.better-supabase.row.created",
|
|
5
6
|
upsert: "dev.better-supabase.row.upserted",
|
|
6
7
|
update: "dev.better-supabase.row.updated",
|
|
7
|
-
delete: "dev.better-supabase.row.deleted"
|
|
8
|
+
delete: "dev.better-supabase.row.deleted",
|
|
9
|
+
softDelete: "dev.better-supabase.row.softdeleted"
|
|
8
10
|
};
|
|
9
11
|
function subjectOf(meta, table, row) {
|
|
10
12
|
const key = meta?.tables[table]?.primaryKey;
|
|
@@ -15,13 +17,19 @@ function subjectOf(meta, table, row) {
|
|
|
15
17
|
}
|
|
16
18
|
/**
|
|
17
19
|
* One CloudEvent per mutated row: `dev.better-supabase.row.created` with the
|
|
18
|
-
* app-cased row
|
|
19
|
-
* `partitionkey
|
|
20
|
+
* app-cased row and the actor in `data`, the primary key as `subject` and the
|
|
21
|
+
* tenant as `partitionkey`. Writes that return no rows (soft
|
|
22
|
+
* deletes, `returning: false`) send one event per known primary key, with the
|
|
23
|
+
* key as `row`.
|
|
20
24
|
*/
|
|
21
25
|
function toCloudEvents(notice, options) {
|
|
22
|
-
const
|
|
26
|
+
const intent = notice.intent ?? notice.kind;
|
|
27
|
+
const type = options.typePrefix ? `${options.typePrefix}.row.${ROW_EVENT_TYPES[intent].split(".").at(-1)}` : ROW_EVENT_TYPES[intent];
|
|
23
28
|
const time = (options.now ?? nowInstant)().toString();
|
|
24
|
-
|
|
29
|
+
const tenant = notice.tenant ?? notice.context.tenant;
|
|
30
|
+
const rows = notice.rows.length > 0 ? notice.rows : notice.keys ?? [];
|
|
31
|
+
const actorId = notice.context.actor?.id;
|
|
32
|
+
return rows.map((row) => {
|
|
25
33
|
const subject = subjectOf(options.meta, notice.table, row);
|
|
26
34
|
return {
|
|
27
35
|
specversion: "1.0",
|
|
@@ -33,16 +41,18 @@ function toCloudEvents(notice, options) {
|
|
|
33
41
|
datacontenttype: "application/json",
|
|
34
42
|
data: {
|
|
35
43
|
table: notice.table,
|
|
36
|
-
row
|
|
44
|
+
row,
|
|
45
|
+
...actorId ? { actorId } : {}
|
|
37
46
|
},
|
|
38
|
-
...
|
|
39
|
-
...notice.context.actor?.id ? { actorid: notice.context.actor.id } : {}
|
|
47
|
+
...tenant ? { partitionkey: tenant } : {}
|
|
40
48
|
};
|
|
41
49
|
});
|
|
42
50
|
}
|
|
43
51
|
/**
|
|
44
52
|
* Sends a CloudEvent for every mutation to `sink`. Returns a function that
|
|
45
|
-
* stops forwarding.
|
|
53
|
+
* stops forwarding. Sends in flight are tracked on `betterSupabase.events`
|
|
54
|
+
* (`settled()`), which the Next adapter hands to `after()` and the edge
|
|
55
|
+
* entry to `waitUntil`. Use an outbox (SQL kit) when events must not be lost.
|
|
46
56
|
*/
|
|
47
57
|
function forwardMutations(betterSupabase, sink, options) {
|
|
48
58
|
return betterSupabase.events.on("mutation", (notice) => {
|
|
@@ -58,7 +68,50 @@ function forwardMutations(betterSupabase, sink, options) {
|
|
|
58
68
|
}))(error, events);
|
|
59
69
|
};
|
|
60
70
|
try {
|
|
61
|
-
Promise.resolve(sink.send(events)).catch(report);
|
|
71
|
+
betterSupabase.events.track(Promise.resolve(sink.send(events)).catch(report));
|
|
72
|
+
} catch (error) {
|
|
73
|
+
report(error);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* A kit event as a CloudEvent: `dev.better-supabase.support.started` with
|
|
79
|
+
* the event data and the actor (`actorId`) as `data` and the tenant as
|
|
80
|
+
* `partitionkey`.
|
|
81
|
+
*/
|
|
82
|
+
function kitCloudEvent(event, options) {
|
|
83
|
+
return {
|
|
84
|
+
specversion: "1.0",
|
|
85
|
+
id: options.id?.() ?? crypto.randomUUID(),
|
|
86
|
+
source: options.source,
|
|
87
|
+
type: `${options.typePrefix ?? "dev.better-supabase"}.${event.type}`,
|
|
88
|
+
...event.subject ? { subject: event.subject } : {},
|
|
89
|
+
time: (options.now?.() ?? event.time).toString(),
|
|
90
|
+
datacontenttype: "application/json",
|
|
91
|
+
data: event.actorId ? {
|
|
92
|
+
...event.data,
|
|
93
|
+
actorId: event.actorId
|
|
94
|
+
} : event.data,
|
|
95
|
+
...event.tenant ? { partitionkey: event.tenant } : {}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Sends a CloudEvent for every kit event (`support.*`, `org.*`, ...) to
|
|
100
|
+
* `sink`. Returns a function that stops forwarding. Like
|
|
101
|
+
* `forwardMutations`, sends are tracked on `betterSupabase.events`.
|
|
102
|
+
*/
|
|
103
|
+
function forwardKitEvents(betterSupabase, sink, options) {
|
|
104
|
+
const types = options.types;
|
|
105
|
+
return betterSupabase.events.on("kit", (event) => {
|
|
106
|
+
if (types && !types.some((pattern) => pattern.endsWith(".*") ? event.type.startsWith(pattern.slice(0, -1)) : pattern === event.type)) return;
|
|
107
|
+
const events = [kitCloudEvent(event, options)];
|
|
108
|
+
const report = (error) => {
|
|
109
|
+
(options.onError ?? ((cause) => {
|
|
110
|
+
betterSupabase.events.logger.error("event sink failed", { cause });
|
|
111
|
+
}))(error, events);
|
|
112
|
+
};
|
|
113
|
+
try {
|
|
114
|
+
betterSupabase.events.track(Promise.resolve(sink.send(events)).catch(report));
|
|
62
115
|
} catch (error) {
|
|
63
116
|
report(error);
|
|
64
117
|
}
|
|
@@ -137,4 +190,4 @@ function httpSink(url, options = {}) {
|
|
|
137
190
|
} };
|
|
138
191
|
}
|
|
139
192
|
//#endregion
|
|
140
|
-
export { ROW_EVENT_TYPES, forwardMutations, fromHttp, httpSink, isCloudEvent, toCloudEvents, toHttp };
|
|
193
|
+
export { KIT_ATTRIBUTES, ROW_EVENT_TYPES, forwardKitEvents, forwardMutations, fromHttp, httpSink, isCloudEvent, kitCloudEvent, kitEventAttributes, onKitEvent, toCloudEvents, toHttp };
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/// <reference lib="esnext.temporal" />
|
|
2
|
+
import { U as Operation, _ as DbError } from "./executor-BunPOO-k.js";
|
|
3
|
+
import { g as RequestContext, l as MutationIntent, u as MutationKind } from "./plugin-o144f322.js";
|
|
4
|
+
//#region src/core/logger.d.ts
|
|
5
|
+
type LogFields = Readonly<Record<string, unknown>>;
|
|
6
|
+
/**
|
|
7
|
+
* Where better-supabase reports problems it swallows on purpose: event
|
|
8
|
+
* handlers, `afterMutation` hooks, sinks and cache adapters that throw.
|
|
9
|
+
* Structured loggers (pino, consola, OpenTelemetry logs) fit directly.
|
|
10
|
+
*/
|
|
11
|
+
interface Logger {
|
|
12
|
+
debug(message: string, fields?: LogFields): void;
|
|
13
|
+
info(message: string, fields?: LogFields): void;
|
|
14
|
+
warn(message: string, fields?: LogFields): void;
|
|
15
|
+
error(message: string, fields?: LogFields): void;
|
|
16
|
+
}
|
|
17
|
+
/** The default: `console`, with a `better-supabase:` prefix. */
|
|
18
|
+
declare const consoleLogger: Logger;
|
|
19
|
+
declare const silentLogger: Logger;
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/core/kit-events.d.ts
|
|
22
|
+
/** `support.*`: support sessions, where a platform admin views the app as a user. */
|
|
23
|
+
interface SupportEventData {
|
|
24
|
+
/** Absent on `support.denied`. */
|
|
25
|
+
readonly sessionId?: string;
|
|
26
|
+
readonly adminId: string;
|
|
27
|
+
readonly targetUserId: string;
|
|
28
|
+
readonly reason?: string;
|
|
29
|
+
readonly readOnly?: boolean;
|
|
30
|
+
readonly expiresAt?: Temporal.Instant;
|
|
31
|
+
/** `support.denied`: which check refused it. */
|
|
32
|
+
readonly denial?: "authorize" | "permission" | "policy" | "store";
|
|
33
|
+
/** `support.ended`: who or what ended it. */
|
|
34
|
+
readonly endedBy?: "admin" | "expired" | "revoked";
|
|
35
|
+
}
|
|
36
|
+
/** `org.*`: organizations and their members. */
|
|
37
|
+
interface OrgEventData {
|
|
38
|
+
readonly organizationId: string;
|
|
39
|
+
/** The member the event is about. */
|
|
40
|
+
readonly userId?: string;
|
|
41
|
+
readonly role?: string;
|
|
42
|
+
readonly previousRole?: string;
|
|
43
|
+
}
|
|
44
|
+
/** `invitation.*`. The token is never part of the event. */
|
|
45
|
+
interface InvitationEventData {
|
|
46
|
+
readonly invitationId: string;
|
|
47
|
+
/** Absent for a platform-scope invitation. */
|
|
48
|
+
readonly organizationId?: string;
|
|
49
|
+
readonly email?: string;
|
|
50
|
+
readonly role?: string;
|
|
51
|
+
}
|
|
52
|
+
/** `notification.*`. */
|
|
53
|
+
interface NotificationEventData {
|
|
54
|
+
readonly notificationId: string;
|
|
55
|
+
readonly type: string;
|
|
56
|
+
readonly recipientIds?: readonly string[];
|
|
57
|
+
readonly channel?: string;
|
|
58
|
+
readonly error?: string;
|
|
59
|
+
}
|
|
60
|
+
/** `webhook.*`: outgoing webhooks. */
|
|
61
|
+
interface WebhookEventData {
|
|
62
|
+
readonly endpointId: string;
|
|
63
|
+
readonly deliveryId?: string;
|
|
64
|
+
readonly eventType?: string;
|
|
65
|
+
/** The HTTP status of the attempt, when there was a response. */
|
|
66
|
+
readonly status?: number;
|
|
67
|
+
readonly attempt?: number;
|
|
68
|
+
readonly error?: string;
|
|
69
|
+
}
|
|
70
|
+
/** Every kit event type and its data. */
|
|
71
|
+
interface KitEventMap {
|
|
72
|
+
"support.started": SupportEventData;
|
|
73
|
+
"support.ended": SupportEventData;
|
|
74
|
+
"support.denied": SupportEventData;
|
|
75
|
+
"org.created": OrgEventData;
|
|
76
|
+
"org.updated": OrgEventData;
|
|
77
|
+
"org.deleted": OrgEventData;
|
|
78
|
+
"org.member_added": OrgEventData;
|
|
79
|
+
"org.member_removed": OrgEventData;
|
|
80
|
+
"org.member_left": OrgEventData;
|
|
81
|
+
"org.role_changed": OrgEventData;
|
|
82
|
+
"org.ownership_transferred": OrgEventData;
|
|
83
|
+
"org.switched": OrgEventData;
|
|
84
|
+
"invitation.created": InvitationEventData;
|
|
85
|
+
"invitation.resent": InvitationEventData;
|
|
86
|
+
"invitation.accepted": InvitationEventData;
|
|
87
|
+
"invitation.declined": InvitationEventData;
|
|
88
|
+
"invitation.revoked": InvitationEventData;
|
|
89
|
+
"notification.created": NotificationEventData;
|
|
90
|
+
"notification.delivered": NotificationEventData;
|
|
91
|
+
"notification.failed": NotificationEventData;
|
|
92
|
+
"webhook.delivered": WebhookEventData;
|
|
93
|
+
"webhook.failed": WebhookEventData;
|
|
94
|
+
"webhook.disabled": WebhookEventData;
|
|
95
|
+
}
|
|
96
|
+
type KitEventType = keyof KitEventMap;
|
|
97
|
+
/** Where an event came from, beyond its data. */
|
|
98
|
+
interface KitEventMeta {
|
|
99
|
+
/** e.g. `organizations/<id>`; becomes the CloudEvents `subject`. */
|
|
100
|
+
readonly subject?: string;
|
|
101
|
+
readonly tenant?: string;
|
|
102
|
+
/** The user who caused it. */
|
|
103
|
+
readonly actorId?: string;
|
|
104
|
+
readonly context?: RequestContext;
|
|
105
|
+
}
|
|
106
|
+
/** A kit event as `betterSupabase.on('kit', handler)` receives it. */
|
|
107
|
+
type KitEvent<K extends KitEventType = KitEventType> = { readonly [T in K]: KitEventMeta & {
|
|
108
|
+
readonly type: T;
|
|
109
|
+
/** A copy: changing it never changes what the kit does (invariant 5). */
|
|
110
|
+
readonly data: KitEventMap[T];
|
|
111
|
+
readonly time: Temporal.Instant;
|
|
112
|
+
}; }[K];
|
|
113
|
+
/** A type, or a `prefix.*` pattern such as `support.*`. */
|
|
114
|
+
type KitEventPattern = KitEventType | `${KitEventType extends `${infer P}.${string}` ? P : never}.*`;
|
|
115
|
+
/** The event types a pattern matches. */
|
|
116
|
+
type KitEventsMatching<P extends KitEventPattern> = P extends `${infer Prefix}.*` ? Extract<KitEventType, `${Prefix}.${string}`> : Extract<P, KitEventType>;
|
|
117
|
+
/**
|
|
118
|
+
* Fixed OpenTelemetry attribute names for kit work, so dashboards and
|
|
119
|
+
* queries can rely on them.
|
|
120
|
+
*/
|
|
121
|
+
declare const KIT_ATTRIBUTES: {
|
|
122
|
+
readonly event: "better_supabase.kit.event";
|
|
123
|
+
readonly tenant: "better_supabase.tenant";
|
|
124
|
+
readonly actor: "better_supabase.actor.id";
|
|
125
|
+
readonly supportSessionId: "better_supabase.support.session_id";
|
|
126
|
+
readonly orgId: "better_supabase.org.id";
|
|
127
|
+
readonly invitationId: "better_supabase.invitation.id";
|
|
128
|
+
readonly notificationType: "better_supabase.notification.type";
|
|
129
|
+
readonly webhookEndpointId: "better_supabase.webhook.endpoint_id";
|
|
130
|
+
};
|
|
131
|
+
/** The `KIT_ATTRIBUTES` an event carries. */
|
|
132
|
+
declare function kitEventAttributes(event: KitEvent): Record<string, string>;
|
|
133
|
+
/**
|
|
134
|
+
* Listens to the kit events that match `pattern` (`'support.started'` or
|
|
135
|
+
* `'support.*'`). Returns a function that stops listening.
|
|
136
|
+
*/
|
|
137
|
+
declare function onKitEvent<P extends KitEventPattern>(betterSupabase: {
|
|
138
|
+
readonly events: EventHub;
|
|
139
|
+
}, pattern: P, handler: (event: KitEvent<KitEventsMatching<P>>) => void): () => void;
|
|
140
|
+
//#endregion
|
|
141
|
+
//#region src/core/events.d.ts
|
|
142
|
+
interface QueryEvent {
|
|
143
|
+
readonly table: string;
|
|
144
|
+
readonly operation: Operation["kind"];
|
|
145
|
+
readonly ok: boolean;
|
|
146
|
+
readonly durationMs: number;
|
|
147
|
+
readonly rows: number;
|
|
148
|
+
/**
|
|
149
|
+
* An unbounded read returned `maxRows` rows: PostgREST's `db-max-rows`
|
|
150
|
+
* probably cut it short. Add `limit` or use `paginate`.
|
|
151
|
+
*/
|
|
152
|
+
readonly truncated: boolean;
|
|
153
|
+
}
|
|
154
|
+
interface MutationNotice {
|
|
155
|
+
readonly table: string;
|
|
156
|
+
readonly kind: MutationKind;
|
|
157
|
+
/** See `MutationEvent.intent`. */
|
|
158
|
+
readonly intent?: MutationIntent;
|
|
159
|
+
/** A copy of the returned rows: changing it never changes the result. */
|
|
160
|
+
readonly rows: readonly Readonly<Record<string, unknown>>[];
|
|
161
|
+
/** See `MutationEvent.keys`. */
|
|
162
|
+
readonly keys?: readonly Readonly<Record<string, unknown>>[];
|
|
163
|
+
/** See `MutationEvent.tenant`. */
|
|
164
|
+
readonly tenant?: string;
|
|
165
|
+
readonly context: RequestContext;
|
|
166
|
+
}
|
|
167
|
+
/** A successful RPC registered with `betterSupabase.defineRpc(name, { invalidates })`. */
|
|
168
|
+
interface RpcNotice {
|
|
169
|
+
readonly name: string;
|
|
170
|
+
/** App keys of the tables the function changes. */
|
|
171
|
+
readonly invalidates: readonly string[];
|
|
172
|
+
readonly context: RequestContext;
|
|
173
|
+
}
|
|
174
|
+
interface ErrorEvent {
|
|
175
|
+
readonly table?: string;
|
|
176
|
+
readonly error: DbError;
|
|
177
|
+
}
|
|
178
|
+
interface AuthEvent {
|
|
179
|
+
readonly source: "bearer" | "cookie" | "none";
|
|
180
|
+
readonly ok: boolean;
|
|
181
|
+
readonly userId?: string;
|
|
182
|
+
/**
|
|
183
|
+
* Why there is no user: the anon reason (`expired`, `signed_out`, ...) or
|
|
184
|
+
* the invalid reason (`token`, `claims`, `actor`).
|
|
185
|
+
*/
|
|
186
|
+
readonly reason?: "none" | "expired" | "signed_out" | "refresh_failed" | "token" | "claims" | "actor";
|
|
187
|
+
/** The user's `source` as the resolver set it, e.g. a custom resolver's name. */
|
|
188
|
+
readonly rawSource?: string;
|
|
189
|
+
}
|
|
190
|
+
interface RefreshEvent {
|
|
191
|
+
readonly ok: boolean;
|
|
192
|
+
readonly shared: boolean;
|
|
193
|
+
readonly durationMs: number;
|
|
194
|
+
}
|
|
195
|
+
interface BetterSupabaseEvents {
|
|
196
|
+
query: QueryEvent;
|
|
197
|
+
mutation: MutationNotice;
|
|
198
|
+
rpc: RpcNotice;
|
|
199
|
+
error: ErrorEvent;
|
|
200
|
+
auth: AuthEvent;
|
|
201
|
+
refresh: RefreshEvent;
|
|
202
|
+
/** Support sessions, organizations, invitations, notifications and webhooks. */
|
|
203
|
+
kit: KitEvent;
|
|
204
|
+
}
|
|
205
|
+
type EventName = keyof BetterSupabaseEvents;
|
|
206
|
+
type EventHandler<K extends EventName> = (event: BetterSupabaseEvents[K]) => void;
|
|
207
|
+
/**
|
|
208
|
+
* Observers only: a handler that throws is reported to the logger and never
|
|
209
|
+
* changes a result.
|
|
210
|
+
*/
|
|
211
|
+
declare class EventHub {
|
|
212
|
+
#private;
|
|
213
|
+
readonly logger: Logger;
|
|
214
|
+
constructor(logger?: Logger);
|
|
215
|
+
on<K extends EventName>(name: K, handler: EventHandler<K>): () => void;
|
|
216
|
+
has(name: EventName): boolean;
|
|
217
|
+
/**
|
|
218
|
+
* Keeps async work a handler started (a sink send) until it settles, so
|
|
219
|
+
* adapters can hand it to `after()` or `waitUntil`. Its outcome is ignored:
|
|
220
|
+
* handlers report their own failures.
|
|
221
|
+
*/
|
|
222
|
+
track(work: PromiseLike<unknown>): void;
|
|
223
|
+
/** Whether tracked work is still running. */
|
|
224
|
+
get pending(): boolean;
|
|
225
|
+
/** Resolves once the work tracked so far has settled. Never rejects. */
|
|
226
|
+
settled(): Promise<void>;
|
|
227
|
+
emit<K extends EventName>(name: K, event: BetterSupabaseEvents[K]): void;
|
|
228
|
+
}
|
|
229
|
+
//#endregion
|
|
230
|
+
export { kitEventAttributes as C, consoleLogger as D, Logger as E, silentLogger as O, WebhookEventData as S, LogFields as T, KitEventType as _, EventHub as a, OrgEventData as b, QueryEvent as c, InvitationEventData as d, KIT_ATTRIBUTES as f, KitEventPattern as g, KitEventMeta as h, EventHandler as i, RefreshEvent as l, KitEventMap as m, BetterSupabaseEvents as n, EventName as o, KitEvent as p, ErrorEvent as r, MutationNotice as s, AuthEvent as t, RpcNotice as u, KitEventsMatching as v, onKitEvent as w, SupportEventData as x, NotificationEventData as y };
|