better-supabase 0.2.0 → 0.4.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 +37 -33
- package/dist/base64-D2gsioCY.js +42 -0
- package/dist/claims-BAf6c7is.js +48 -0
- package/dist/cli/_format-Cax5EsM9.js +101 -0
- package/dist/cli/bin.js +39 -6
- package/dist/cli/command-CK14fP91.js +502 -0
- package/dist/cli/compare-BmIWVEui.js +9 -0
- package/dist/cli/config-D9R0o1LH.js +1895 -0
- package/dist/cli/config-SSKSTUl2.js +2 -0
- package/dist/cli/db-DOBPt660.js +52 -0
- package/dist/cli/dist-B1MvLv-M.js +129 -0
- package/dist/cli/dist-CFgGknmZ.js +1112 -0
- package/dist/cli/dist-Du7VnEUG.js +621 -0
- package/dist/cli/doctor-BOym3mbL.js +2382 -0
- package/dist/cli/env-BZ0uy8XK.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-Ccn_p90L.js +2008 -0
- package/dist/cli/gen-D5cLCULx.js +2 -0
- package/dist/cli/index.d.ts +295 -28
- package/dist/cli/index.js +184 -3
- package/dist/cli/init-ClnxRYBF.js +550 -0
- package/dist/cli/introspect-DxqfkVIv.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-8WHjxiTJ.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-CMa29t9a.js +971 -0
- package/dist/cli/rolldown-runtime-BPOCksWG.js +24 -0
- package/dist/cli/run-CP20LKk_.js +347 -0
- package/dist/cli/seed-5J6eu_Xg.js +98 -0
- package/dist/cli/skills-LJJIhLFR.js +137 -0
- package/dist/cli/sql-D4VbqmXi.js +199 -0
- package/dist/cli/style-C0NFdJRL.js +29 -0
- package/dist/cli/toml-DB-MNnFs.js +424 -0
- package/dist/{version-Cqq1m3UY.js → cli/version-5mW_8sY1.js} +1 -1
- package/dist/cli/yaml-CYCbJ6et.js +2252 -0
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.js +15 -25
- package/dist/{compiler-CZ9nIuqy.d.ts → compiler-DB3aSFOA.d.ts} +2 -2
- package/dist/config/index.d.ts +4 -3
- package/dist/config/index.js +5 -3
- package/dist/config-BpxDfnbg.js +129 -0
- package/dist/{index-DOsjFvMf.d.ts → config-C1CYcm6o.d.ts} +49 -49
- package/dist/{config-ClDXmVtY.js → config-CbvFmGD8.js} +14 -137
- package/dist/{define-BXMYD9AY.js → define-Bh0AqTik.js} +221 -120
- package/dist/{define-e3zI4_4g.d.ts → define-CRS2MRV3.d.ts} +36 -31
- package/dist/edge/index.d.ts +9 -13
- package/dist/edge/index.js +51 -25
- package/dist/{entitlements-Dng-ctuT.d.ts → entitlements-Dm-FHpNH.d.ts} +2 -2
- package/dist/env/index.js +203 -1
- package/dist/{errors-Bl3rkOzo.js → errors-L0xK41y_.js} +1 -1
- package/dist/events/index.d.ts +1 -1
- package/dist/events/index.js +139 -1
- package/dist/{events-DOt3nSO-.d.ts → events-B21UOhvP.d.ts} +4 -4
- package/dist/{executor-CGdNQ27g.d.ts → executor-B5IoUxLE.d.ts} +2 -2
- package/dist/{executor-DeppvirK.js → executor-CVt6dD0B.js} +9 -4
- package/dist/{executor-BYXQ9AGn.d.ts → executor-CZ7ImRiD.d.ts} +2 -2
- package/dist/hono/index.d.ts +17 -24
- package/dist/hono/index.js +11 -11
- package/dist/{index-DuP14VHo.d.ts → index-528Ugmyp.d.ts} +30 -10
- package/dist/{index-Bw1bwtC0.d.ts → index-BAy4tsAP.d.ts} +28 -15
- package/dist/{index-CBDufH1M.d.ts → index-BBdvo-5d.d.ts} +24 -26
- package/dist/{index-DbxFr7XG.d.ts → index-BhHEC04r.d.ts} +9 -4
- package/dist/index-Cf-D2jk1.d.ts +50 -0
- package/dist/{index-Ciu5Xlml.d.ts → index-D5IfUIDh.d.ts} +6 -5
- package/dist/{index-K-3WM2Hp.d.ts → index-DFmOpBGC.d.ts} +13 -13
- package/dist/{index-D4v7sncu.d.ts → index-RlLXd8dL.d.ts} +13 -13
- package/dist/index.d.ts +17 -17
- package/dist/index.js +23 -14
- package/dist/{invalidate-DbxuL2jL.js → invalidate-CgxmRamI.js} +7 -1
- package/dist/jobs/index.d.ts +33 -26
- package/dist/jobs/index.js +44 -22
- package/dist/{json-schema-BpYqkCKf.js → json-schema-DP6V7cbr.js} +8 -3
- package/dist/lint/index.js +1 -1
- package/dist/list/index.d.ts +2 -2
- package/dist/list/index.js +131 -96
- package/dist/{live-DJyZMD6w.d.ts → live-BHakSWWo.d.ts} +26 -16
- package/dist/{live-D8rV1Ldw.js → live-CdJBOc4f.js} +13 -9
- package/dist/lookup-BaP5bI27.js +18 -0
- package/dist/mcp/index.d.ts +34 -27
- package/dist/mcp/index.js +59 -25
- package/dist/{mfa-TVia554c.d.ts → mfa-C7MNQ1Ob.d.ts} +1 -1
- package/dist/{mfa-CpLfJSde.js → mfa-byfjruR9.js} +190 -46
- package/dist/next/image/index.js +6 -3
- package/dist/next/index.d.ts +64 -48
- package/dist/next/index.js +63 -50
- package/dist/openapi/index.d.ts +1 -1
- package/dist/openapi/index.js +61 -38
- package/dist/orpc/index.d.ts +18 -21
- package/dist/orpc/index.js +12 -12
- package/dist/otel/index.d.ts +5 -5
- package/dist/otel/index.js +84 -63
- package/dist/{permdock-sql-CghlHPuH.js → permdock-sql-C4KFzLc9.js} +12 -7
- package/dist/permdock-sql-DImJULY-.d.ts +17 -0
- package/dist/{plugin-C4TR9J0Z.d.ts → plugin-bNhvJwRh.d.ts} +5 -4
- package/dist/plugins/actor/index.d.ts +1 -1
- package/dist/plugins/actor/index.js +1 -1
- package/dist/plugins/rules/index.d.ts +2 -2
- package/dist/plugins/rules/index.js +46 -16
- package/dist/plugins/soft-delete/index.d.ts +3 -3
- package/dist/plugins/soft-delete/index.js +4 -4
- package/dist/plugins/tenant/index.d.ts +3 -3
- package/dist/plugins/tenant/index.js +10 -4
- package/dist/plugins/timestamps/index.d.ts +1 -1
- package/dist/plugins/timestamps/index.js +2 -2
- package/dist/plugins/validation/index.d.ts +2 -2
- package/dist/plugins/validation/index.js +3 -3
- package/dist/pool-DFwEQwmN.d.ts +78 -0
- package/dist/postgres/index.d.ts +10 -5
- package/dist/postgres/index.js +37 -16
- package/dist/{postgrest-cAEQ_IQN.d.ts → postgrest-Bc-0Xw62.d.ts} +1 -1
- package/dist/{problem-BTkTtRV9.js → problem-BihjPR0t.js} +11 -5
- package/dist/{problem-DlgB0lYl.d.ts → problem-U8UEKccv.d.ts} +6 -4
- package/dist/query/index.d.ts +2 -2
- package/dist/query/index.js +2 -2
- package/dist/{query-ChvDbsDE.js → query-BMMSWbI7.js} +19 -19
- package/dist/react/index.d.ts +7 -7
- package/dist/react/index.js +2 -2
- package/dist/react/server.d.ts +6 -6
- package/dist/react/server.js +3 -3
- package/dist/react/session.d.ts +21 -2
- package/dist/react/session.js +19 -1
- package/dist/{react-C83BdBGt.js → react-CQHMaESR.js} +59 -55
- package/dist/{read-set-4lI3W_gZ.js → read-set-FWK8qQ58.js} +51 -16
- package/dist/realtime/index.d.ts +4 -3
- package/dist/realtime/index.js +19 -17
- package/dist/{index-CG69s15v.d.ts → refresh-BwYkyNOQ.d.ts} +7 -3
- package/dist/{resolve-CjHILk2w.d.ts → resolve-BAqQcwMW.d.ts} +17 -11
- package/dist/{resource-CgZa3YG_.d.ts → resource-BJcaIfxi.d.ts} +17 -8
- package/dist/{resource-BHEWmQJK.js → resource-My_y-Syh.js} +43 -21
- package/dist/{respond-Dne1ARSa.d.ts → respond-DgIbdrt8.d.ts} +39 -14
- package/dist/{result-DWXatkd6.js → result-Bj6yt33D.js} +2 -2
- package/dist/{result-CTkCZ6JA.d.ts → result-CqEsbgYs.d.ts} +16 -11
- package/dist/server/index.d.ts +38 -13
- package/dist/server/index.js +43 -14
- package/dist/{respond-DMcbQwfN.js → server-BA75B0mT.js} +151 -96
- package/dist/{shared-_6-uPL7E.js → shared-DnoCdMDr.js} +4 -3
- package/dist/{simplify-bwJpLnaS.js → simplify-Cqvfr9dj.js} +76 -39
- package/dist/sql/index.d.ts +139 -0
- package/dist/sql/index.js +1983 -0
- package/dist/{sql-DTjA1LPj.js → sql-B34HU2DI.js} +74 -17
- package/dist/ssr/index.d.ts +4 -4
- package/dist/ssr/index.js +2 -2
- package/dist/{standard-FUi3Z3-7.js → standard-BRyBGTtp.js} +2 -2
- package/dist/{standard-CyhRzctb.d.ts → standard-BsaCYQJc.d.ts} +1 -1
- package/dist/{stats-C5W-rBfR.js → stats-C_YxA2r1.js} +40 -14
- package/dist/storage/index.d.ts +4 -3
- package/dist/storage/index.js +548 -1
- package/dist/{tables-rZxRxNPW.js → tables-Cb-3_VWc.js} +19 -5
- package/dist/temporal-BwshQk0W.js +27 -0
- package/dist/temporal-required-CBNHT6ni.js +19 -0
- package/dist/testing/index.d.ts +25 -24
- package/dist/testing/index.js +165 -66
- package/dist/{types-C83v5SQz.d.ts → types-DONv9SHN.d.ts} +14 -11
- package/dist/version-Bmf7_FzM.js +4 -0
- package/dist/{view-D1s0wa0T.d.ts → view-B64o81wp.d.ts} +36 -6
- package/dist/{view-aOYizTeE.js → view-BrJI2LKp.js} +11 -2
- package/dist/webhooks/index.d.ts +19 -17
- package/dist/webhooks/index.js +1 -1
- package/dist/{webhooks-DMipjhSP.js → webhooks-Bh_Jf_-l.js} +45 -24
- package/package.json +50 -22
- package/schemas/config-v1.json +31 -1
- package/schemas/doctor-report-v1.json +17 -1
- package/schemas/snapshot-v2.json +35 -0
- package/skills/better-supabase/SKILL.md +77 -18
- package/skills/better-supabase/references/plugins.md +21 -22
- package/skills/better-supabase/references/troubleshooting.md +38 -18
- package/skills/better-supabase-api/SKILL.md +66 -18
- package/skills/better-supabase-api/references/adapters.md +64 -41
- package/skills/better-supabase-auth/SKILL.md +146 -0
- package/skills/better-supabase-auth/references/permdock.md +55 -0
- package/skills/better-supabase-testing/SKILL.md +68 -11
- package/dist/casing-da0uRTqt.js +0 -14
- package/dist/claims-CoTobNC-.js +0 -25
- package/dist/cli-DrSgDOQC.js +0 -6646
- package/dist/env-BEToab0I.js +0 -204
- package/dist/events-BuzTgn2D.js +0 -135
- package/dist/pool-R2m_DkXi.d.ts +0 -37
- package/dist/seed-CzecODcJ.js +0 -101
- package/dist/session-C3LxnQ0e.d.ts +0 -22
- package/dist/session-CqEwzVup.js +0 -20
- package/dist/storage-DuGn38fr.js +0 -527
|
@@ -1,7 +1,30 @@
|
|
|
1
|
-
import { p as DbError } from "./result-
|
|
2
|
-
import { n as AmrEntry, s as Impersonator, t as Aal } from "./mfa-
|
|
3
|
-
import { i as InvalidReason, r as AuthState } from "./resolve-
|
|
1
|
+
import { p as DbError } from "./result-CqEsbgYs.js";
|
|
2
|
+
import { n as AmrEntry, s as Impersonator, t as Aal } from "./mfa-C7MNQ1Ob.js";
|
|
3
|
+
import { i as InvalidReason, r as AuthState } from "./resolve-BAqQcwMW.js";
|
|
4
4
|
import { JWTClaims, UserClaims } from "@supabase/server";
|
|
5
|
+
//#region src/auth/actor.d.ts
|
|
6
|
+
/** One level of an RFC 8693 `act` chain: who acts, and who acted before it. */
|
|
7
|
+
interface ActClaim {
|
|
8
|
+
readonly sub: string;
|
|
9
|
+
readonly act?: ActClaim;
|
|
10
|
+
readonly [claim: string]: unknown;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The app acting for the user: the outermost `sub` of an RFC 8693 `act`
|
|
14
|
+
* chain (prior actors stay on `chain`), else the OAuth `client_id` of a
|
|
15
|
+
* Supabase OAuth server token.
|
|
16
|
+
*/
|
|
17
|
+
interface SessionActor {
|
|
18
|
+
readonly id: string;
|
|
19
|
+
readonly kind: "oauth-client";
|
|
20
|
+
readonly chain?: ActClaim;
|
|
21
|
+
}
|
|
22
|
+
/** What the user delegated to the actor: the token's OAuth `scope`. */
|
|
23
|
+
interface SessionDelegation {
|
|
24
|
+
readonly scopes: readonly string[];
|
|
25
|
+
readonly chain?: ActClaim;
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
5
28
|
//#region src/auth/view.d.ts
|
|
6
29
|
/**
|
|
7
30
|
* The verified caller as plain data: no token, no clients. Safe to return
|
|
@@ -12,13 +35,13 @@ type AuthSession<C = unknown, P = unknown> = {
|
|
|
12
35
|
readonly user: UserClaims;
|
|
13
36
|
/**
|
|
14
37
|
* The verified JWT payload, including custom access token hook claims,
|
|
15
|
-
* typed and validated by `
|
|
38
|
+
* typed and validated by `betterSupabase.claims(schema)`.
|
|
16
39
|
*/
|
|
17
40
|
readonly claims: JWTClaims & C;
|
|
18
41
|
/** Seconds since epoch, from the token's `exp`. */
|
|
19
42
|
readonly expiresAt: number | null;
|
|
20
43
|
/**
|
|
21
|
-
* `user_metadata` parsed by `
|
|
44
|
+
* `user_metadata` parsed by `betterSupabase.userMetadata(schema)`, for display.
|
|
22
45
|
* Absent without a schema or when the metadata fails it. Users
|
|
23
46
|
* write this data with `auth.updateUser()`: never base access on it.
|
|
24
47
|
*/
|
|
@@ -29,6 +52,13 @@ type AuthSession<C = unknown, P = unknown> = {
|
|
|
29
52
|
readonly amr: readonly AmrEntry[];
|
|
30
53
|
/** Set when an admin acts as this user (the `act` claim), for a banner. */
|
|
31
54
|
readonly impersonator?: Impersonator;
|
|
55
|
+
/**
|
|
56
|
+
* The OAuth client or agent acting for the user (`client_id`, or the
|
|
57
|
+
* outermost `sub` of the `act` chain), as PermDock's `actorOf` reads it.
|
|
58
|
+
*/
|
|
59
|
+
readonly actor?: SessionActor;
|
|
60
|
+
/** The scopes the user granted `actor`, plus its `act` chain. Set only with `actor`. */
|
|
61
|
+
readonly delegation?: SessionDelegation;
|
|
32
62
|
} | {
|
|
33
63
|
readonly kind: "service";
|
|
34
64
|
readonly keyName: string;
|
|
@@ -46,4 +76,4 @@ type AnonReason = Extract<AuthState, {
|
|
|
46
76
|
/** Drops the token from an `AuthState`, leaving only serializable fields. */
|
|
47
77
|
declare function toSession<C, P>(auth: AuthState<C, P>): AuthSession<C, P>;
|
|
48
78
|
//#endregion
|
|
49
|
-
export { toSession as n, AuthSession as t };
|
|
79
|
+
export { SessionDelegation as a, SessionActor as i, toSession as n, ActClaim as r, AuthSession as t };
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import { g as
|
|
1
|
+
import { _ as delegationOf, g as actorOf, n as amrOf, t as aalOf, y as impersonatorOf } from "./mfa-byfjruR9.js";
|
|
2
2
|
//#region src/auth/view.ts
|
|
3
3
|
/** Drops the token from an `AuthState`, leaving only serializable fields. */
|
|
4
4
|
function toSession(auth) {
|
|
5
5
|
switch (auth.kind) {
|
|
6
6
|
case "user": {
|
|
7
7
|
const impersonator = impersonatorOf(auth.claims);
|
|
8
|
+
const outcome = actorOf(auth.claims);
|
|
9
|
+
const actor = outcome.ok ? outcome.actor : void 0;
|
|
10
|
+
const scopes = actor ? delegationOf(auth.claims)?.scopes : void 0;
|
|
11
|
+
const delegation = actor && (scopes || actor.chain) ? {
|
|
12
|
+
scopes: scopes ?? [],
|
|
13
|
+
...actor.chain ? { chain: actor.chain } : {}
|
|
14
|
+
} : void 0;
|
|
8
15
|
return {
|
|
9
16
|
kind: "user",
|
|
10
17
|
user: auth.user,
|
|
@@ -13,7 +20,9 @@ function toSession(auth) {
|
|
|
13
20
|
...auth.profile === void 0 ? {} : { profile: auth.profile },
|
|
14
21
|
aal: aalOf(auth.claims),
|
|
15
22
|
amr: amrOf(auth.claims),
|
|
16
|
-
...impersonator ? { impersonator } : {}
|
|
23
|
+
...impersonator ? { impersonator } : {},
|
|
24
|
+
...actor ? { actor } : {},
|
|
25
|
+
...delegation ? { delegation } : {}
|
|
17
26
|
};
|
|
18
27
|
}
|
|
19
28
|
case "service": return {
|
package/dist/webhooks/index.d.ts
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
/// <reference lib="esnext.temporal" />
|
|
2
|
+
import { N as TableKey, O as Row, n as AnyModels, t as AnyFunctions } from "../types-DONv9SHN.js";
|
|
3
|
+
import { o as Result } from "../result-CqEsbgYs.js";
|
|
4
|
+
import { t as BetterSupabase } from "../define-CRS2MRV3.js";
|
|
5
|
+
//#region src/webhooks/verify.d.ts
|
|
6
|
+
interface VerifiedWebhook<T = unknown> {
|
|
6
7
|
readonly id: string;
|
|
7
|
-
readonly timestamp:
|
|
8
|
+
readonly timestamp: Temporal.Instant;
|
|
8
9
|
readonly payload: T;
|
|
9
10
|
readonly body: string;
|
|
10
11
|
}
|
|
11
|
-
|
|
12
|
+
interface VerifyOptions {
|
|
12
13
|
/** Allowed clock skew in seconds. Defaults to 300. */
|
|
13
14
|
readonly tolerance?: number;
|
|
14
|
-
readonly now?: () =>
|
|
15
|
+
readonly now?: () => Temporal.Instant;
|
|
15
16
|
}
|
|
16
|
-
|
|
17
|
+
type WebhookInput = Request | {
|
|
17
18
|
readonly headers: Headers | Readonly<Record<string, string>>;
|
|
18
19
|
readonly body: string;
|
|
19
20
|
};
|
|
@@ -23,7 +24,7 @@ export declare function timingSafeEqual(a: string, b: string): boolean;
|
|
|
23
24
|
export declare function signWebhook(secret: string, message: {
|
|
24
25
|
readonly id: string;
|
|
25
26
|
readonly body: string;
|
|
26
|
-
readonly timestamp?:
|
|
27
|
+
readonly timestamp?: Temporal.Instant;
|
|
27
28
|
}): Promise<Record<string, string>>;
|
|
28
29
|
/**
|
|
29
30
|
* Verifies a Standard Webhooks signature (`webhook-id`, `webhook-timestamp`,
|
|
@@ -41,13 +42,13 @@ interface HookUser {
|
|
|
41
42
|
readonly user_metadata?: Readonly<Record<string, unknown>>;
|
|
42
43
|
readonly [key: string]: unknown;
|
|
43
44
|
}
|
|
44
|
-
|
|
45
|
+
interface AuthHookError {
|
|
45
46
|
readonly error: {
|
|
46
47
|
readonly http_code: number;
|
|
47
48
|
readonly message: string;
|
|
48
49
|
};
|
|
49
50
|
}
|
|
50
|
-
|
|
51
|
+
interface AuthHooks {
|
|
51
52
|
custom_access_token: {
|
|
52
53
|
input: {
|
|
53
54
|
readonly user_id: string;
|
|
@@ -116,7 +117,7 @@ export interface AuthHooks {
|
|
|
116
117
|
output: Record<never, never>;
|
|
117
118
|
};
|
|
118
119
|
}
|
|
119
|
-
|
|
120
|
+
type AuthHookKind = keyof AuthHooks;
|
|
120
121
|
/** Rejects a hook with a status and message Supabase Auth passes to the client. */
|
|
121
122
|
export declare function hookError(status: number, message: string): AuthHookError;
|
|
122
123
|
/**
|
|
@@ -130,14 +131,14 @@ export declare function hookError(status: number, message: string): AuthHookErro
|
|
|
130
131
|
* ```
|
|
131
132
|
*/
|
|
132
133
|
export declare function authHook<K extends AuthHookKind>(_kind: K, secret: string | readonly string[], handler: (payload: AuthHooks[K]["input"], webhook: VerifiedWebhook<AuthHooks[K]["input"]>) => AuthHooks[K]["output"] | AuthHookError | Promise<AuthHooks[K]["output"] | AuthHookError>, options?: VerifyOptions): (request: Request) => Promise<Response>;
|
|
133
|
-
|
|
134
|
+
interface DatabaseWebhookPayload {
|
|
134
135
|
readonly type: "INSERT" | "UPDATE" | "DELETE";
|
|
135
136
|
readonly table: string;
|
|
136
137
|
readonly schema: string;
|
|
137
138
|
readonly record: Readonly<Record<string, unknown>> | null;
|
|
138
139
|
readonly old_record: Readonly<Record<string, unknown>> | null;
|
|
139
140
|
}
|
|
140
|
-
|
|
141
|
+
interface DatabaseChange<R> {
|
|
141
142
|
readonly type: "INSERT" | "UPDATE" | "DELETE";
|
|
142
143
|
readonly table: string;
|
|
143
144
|
readonly record: R | null;
|
|
@@ -148,5 +149,6 @@ export declare function isDatabaseWebhook(value: unknown): value is DatabaseWebh
|
|
|
148
149
|
* Reads a database webhook payload for `table` as an app-cased change.
|
|
149
150
|
* Returns `null` for other tables or payloads that aren't database webhooks.
|
|
150
151
|
*/
|
|
151
|
-
export declare function databaseChange<M extends AnyModels, D, F extends AnyFunctions, E, T extends TableKey<M>>(
|
|
152
|
-
//#endregion
|
|
152
|
+
export declare function databaseChange<M extends AnyModels, D, F extends AnyFunctions, E, T extends TableKey<M>>(betterSupabase: BetterSupabase<M, D, F, E>, table: T, payload: unknown): DatabaseChange<Row<M, T>> | null;
|
|
153
|
+
//#endregion
|
|
154
|
+
export type { AuthHookError, AuthHookKind, AuthHooks, DatabaseChange, DatabaseWebhookPayload, VerifiedWebhook, VerifyOptions, WebhookInput };
|
package/dist/webhooks/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
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-
|
|
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-Bh_Jf_-l.js";
|
|
2
2
|
export { authHook, databaseChange, hookError, isDatabaseWebhook, signWebhook, timingSafeEqual, verifySharedSecret, verifyWebhook };
|
|
@@ -1,26 +1,45 @@
|
|
|
1
|
-
import { n as dbError } from "./errors-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { n as dbError } from "./errors-L0xK41y_.js";
|
|
2
|
+
import { n as nowInstant, r as optionalTemporal } from "./temporal-BwshQk0W.js";
|
|
3
|
+
import { n as temporalMissing } from "./temporal-required-CBNHT6ni.js";
|
|
4
|
+
import { i as ok, n as err } from "./result-Bj6yt33D.js";
|
|
5
|
+
import { i as toBase64, r as fromBase64 } from "./base64-D2gsioCY.js";
|
|
6
|
+
import { s as toApp } from "./shared-DnoCdMDr.js";
|
|
7
|
+
//#region src/webhooks/verify.ts
|
|
8
|
+
const encoder = /* @__PURE__ */ new TextEncoder();
|
|
6
9
|
/** Accepts `whsec_…`, `v1,whsec_…` (Supabase Auth hooks) or raw base64. */
|
|
7
10
|
function secretBytes(secret) {
|
|
8
11
|
const raw = secret.replace(/^v1,/, "").replace(/^whsec_/, "");
|
|
9
12
|
try {
|
|
10
|
-
return
|
|
13
|
+
return fromBase64(raw);
|
|
11
14
|
} catch {
|
|
12
15
|
throw new TypeError("Webhook secret is not valid base64");
|
|
13
16
|
}
|
|
14
17
|
}
|
|
18
|
+
/** Secrets come from configuration, so a handful of keys suffice. */
|
|
19
|
+
const keys = /* @__PURE__ */ new Map();
|
|
20
|
+
function keyFor(secret) {
|
|
21
|
+
let key = keys.get(secret);
|
|
22
|
+
if (!key) {
|
|
23
|
+
key = crypto.subtle.importKey("raw", secretBytes(secret), {
|
|
24
|
+
name: "HMAC",
|
|
25
|
+
hash: "SHA-256"
|
|
26
|
+
}, false, ["sign", "verify"]);
|
|
27
|
+
if (keys.size < 16) {
|
|
28
|
+
keys.set(secret, key);
|
|
29
|
+
key.catch(() => keys.delete(secret));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return key;
|
|
33
|
+
}
|
|
15
34
|
async function signature(secret, content) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
35
|
+
return toBase64(new Uint8Array(await crypto.subtle.sign("HMAC", await keyFor(secret), encoder.encode(content))));
|
|
36
|
+
}
|
|
37
|
+
function decodeSignature(value) {
|
|
38
|
+
try {
|
|
39
|
+
return fromBase64(value);
|
|
40
|
+
} catch {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
24
43
|
}
|
|
25
44
|
/** Constant-time string comparison. */
|
|
26
45
|
function timingSafeEqual(a, b) {
|
|
@@ -37,7 +56,7 @@ function header(headers, name) {
|
|
|
37
56
|
const unauthorized = (code, message) => dbError("unauthorized", message, { code });
|
|
38
57
|
/** Headers for a signed Standard Webhooks request. */
|
|
39
58
|
async function signWebhook(secret, message) {
|
|
40
|
-
const timestamp = String(Math.floor((message.timestamp ??
|
|
59
|
+
const timestamp = String(Math.floor((message.timestamp ?? nowInstant()).epochMilliseconds / 1e3));
|
|
41
60
|
return {
|
|
42
61
|
"webhook-id": message.id,
|
|
43
62
|
"webhook-timestamp": timestamp,
|
|
@@ -51,29 +70,31 @@ async function signWebhook(secret, message) {
|
|
|
51
70
|
*/
|
|
52
71
|
async function verifyWebhook(input, secrets, options = {}) {
|
|
53
72
|
const headers = input.headers;
|
|
54
|
-
const body = input instanceof Request ? await input.text() : input.body;
|
|
55
73
|
const id = header(headers, "webhook-id");
|
|
56
74
|
const timestamp = header(headers, "webhook-timestamp");
|
|
57
75
|
const signatures = header(headers, "webhook-signature");
|
|
58
76
|
if (!id || !timestamp || !signatures) return err(unauthorized("WEBHOOK_MISSING_HEADERS", "Missing webhook signature headers"));
|
|
59
77
|
const seconds = Number(timestamp);
|
|
60
78
|
if (!/^\d+$/.test(timestamp)) return err(unauthorized("WEBHOOK_INVALID_TIMESTAMP", "Invalid webhook timestamp"));
|
|
61
|
-
const
|
|
79
|
+
const namespace = optionalTemporal();
|
|
80
|
+
if (namespace === void 0) return err(temporalMissing());
|
|
81
|
+
const now = (options.now?.() ?? namespace.Now.instant()).epochMilliseconds / 1e3;
|
|
62
82
|
const tolerance = options.tolerance ?? 300;
|
|
63
83
|
if (seconds < now - tolerance) return err(unauthorized("WEBHOOK_TIMESTAMP_TOO_OLD", "Webhook timestamp is too old"));
|
|
64
84
|
if (seconds > now + tolerance) return err(unauthorized("WEBHOOK_TIMESTAMP_TOO_NEW", "Webhook timestamp is in the future"));
|
|
65
|
-
const
|
|
66
|
-
const
|
|
85
|
+
const body = input instanceof Request ? await input.text() : input.body;
|
|
86
|
+
const content = encoder.encode(`${id}.${timestamp}.${body}`);
|
|
87
|
+
const offered = signatures.split(" ").map((entry) => entry.split(",")).filter(([version, value]) => version === "v1" && value).flatMap(([, value]) => decodeSignature(value) ?? []);
|
|
67
88
|
let valid = false;
|
|
68
89
|
for (const secret of typeof secrets === "string" ? [secrets] : secrets) {
|
|
69
|
-
const
|
|
70
|
-
for (const candidate of offered) if (
|
|
90
|
+
const key = await keyFor(secret);
|
|
91
|
+
for (const candidate of offered) if (await crypto.subtle.verify("HMAC", key, candidate, content)) valid = true;
|
|
71
92
|
}
|
|
72
93
|
if (!valid) return err(unauthorized("WEBHOOK_INVALID_SIGNATURE", "Invalid webhook signature"));
|
|
73
94
|
try {
|
|
74
95
|
return ok({
|
|
75
96
|
id,
|
|
76
|
-
timestamp:
|
|
97
|
+
timestamp: namespace.Instant.fromEpochMilliseconds(seconds * 1e3),
|
|
77
98
|
payload: JSON.parse(body),
|
|
78
99
|
body
|
|
79
100
|
});
|
|
@@ -125,8 +146,8 @@ function isDatabaseWebhook(value) {
|
|
|
125
146
|
* Reads a database webhook payload for `table` as an app-cased change.
|
|
126
147
|
* Returns `null` for other tables or payloads that aren't database webhooks.
|
|
127
148
|
*/
|
|
128
|
-
function databaseChange(
|
|
129
|
-
const meta =
|
|
149
|
+
function databaseChange(betterSupabase, table, payload) {
|
|
150
|
+
const meta = betterSupabase.meta.tables[table];
|
|
130
151
|
if (!meta || !isDatabaseWebhook(payload) || payload.table !== meta.name || payload.schema !== meta.schema) return null;
|
|
131
152
|
return {
|
|
132
153
|
type: payload.type,
|
package/package.json
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "better-supabase",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Strongly typed Supabase: generated schema, typed repositories, plugins, auth glue
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Strongly typed Supabase: generated schema, typed repositories, plugins, auth glue, framework integrations and the better-supabase CLI.",
|
|
5
5
|
"keywords": [
|
|
6
|
+
"cli",
|
|
7
|
+
"codegen",
|
|
6
8
|
"hono",
|
|
7
9
|
"mcp",
|
|
8
10
|
"nextjs",
|
|
@@ -47,6 +49,10 @@
|
|
|
47
49
|
"types": "./dist/config/index.d.ts",
|
|
48
50
|
"default": "./dist/config/index.js"
|
|
49
51
|
},
|
|
52
|
+
"./cli": {
|
|
53
|
+
"types": "./dist/cli/index.d.ts",
|
|
54
|
+
"default": "./dist/cli/index.js"
|
|
55
|
+
},
|
|
50
56
|
"./client": {
|
|
51
57
|
"types": "./dist/client/index.d.ts",
|
|
52
58
|
"default": "./dist/client/index.js"
|
|
@@ -160,14 +166,14 @@
|
|
|
160
166
|
"types": "./dist/plugins/validation/index.d.ts",
|
|
161
167
|
"default": "./dist/plugins/validation/index.js"
|
|
162
168
|
},
|
|
169
|
+
"./sql": {
|
|
170
|
+
"types": "./dist/sql/index.d.ts",
|
|
171
|
+
"default": "./dist/sql/index.js"
|
|
172
|
+
},
|
|
163
173
|
"./testing": {
|
|
164
174
|
"types": "./dist/testing/index.d.ts",
|
|
165
175
|
"default": "./dist/testing/index.js"
|
|
166
176
|
},
|
|
167
|
-
"./cli": {
|
|
168
|
-
"types": "./dist/cli/index.d.ts",
|
|
169
|
-
"default": "./dist/cli/index.js"
|
|
170
|
-
},
|
|
171
177
|
"./schemas/*.json": "./schemas/*.json",
|
|
172
178
|
"./package.json": "./package.json"
|
|
173
179
|
},
|
|
@@ -177,53 +183,67 @@
|
|
|
177
183
|
},
|
|
178
184
|
"dependencies": {
|
|
179
185
|
"@standard-schema/spec": "1.1.0",
|
|
180
|
-
"@supabase/middleware": "0.
|
|
181
|
-
"@supabase/postgrest-
|
|
182
|
-
"@supabase/
|
|
186
|
+
"@supabase/middleware": "1.0.0",
|
|
187
|
+
"@supabase/postgrest-js": "2.117.2",
|
|
188
|
+
"@supabase/postgrest-typegen": "0.3.1",
|
|
189
|
+
"@supabase/server": "1.9.0",
|
|
183
190
|
"@supabase/ssr": "0.12.7"
|
|
184
191
|
},
|
|
185
192
|
"devDependencies": {
|
|
186
193
|
"@arethetypeswrong/cli": "0.18.5",
|
|
187
194
|
"@better-supabase/ox-config": "0.0.0",
|
|
188
195
|
"@better-supabase/typescript-config": "0.0.0",
|
|
189
|
-
"@
|
|
196
|
+
"@clack/prompts": "1.8.1",
|
|
197
|
+
"@effect/platform-node": "4.0.0",
|
|
190
198
|
"@opentelemetry/api": "1.9.1",
|
|
191
199
|
"@orpc/server": "2.0.0-beta.40",
|
|
192
|
-
"@supabase/config": "0.
|
|
200
|
+
"@supabase/config": "0.11.1",
|
|
193
201
|
"@supabase/supabase-js": "2.117.2",
|
|
202
|
+
"@t3-oss/env-core": "0.13.11",
|
|
194
203
|
"@tanstack/query-core": "5.104.0",
|
|
195
204
|
"@tanstack/react-query": "5.104.0",
|
|
196
|
-
"@types/node": "
|
|
205
|
+
"@types/node": "26.6.3",
|
|
197
206
|
"@types/pg": "8.23.1",
|
|
198
207
|
"@types/react": "19.3.0",
|
|
199
208
|
"@types/react-dom": "19.3.0",
|
|
200
209
|
"@valibot/to-json-schema": "1.8.0",
|
|
201
|
-
"@vitest/coverage-v8": "5.0.
|
|
202
|
-
"
|
|
203
|
-
"
|
|
204
|
-
"
|
|
210
|
+
"@vitest/coverage-v8": "5.0.3",
|
|
211
|
+
"ajv": "8.20.0",
|
|
212
|
+
"ajv-formats": "3.0.1",
|
|
213
|
+
"c12": "4.0.0-rc.2",
|
|
214
|
+
"citty": "0.2.2",
|
|
215
|
+
"diff": "9.0.0",
|
|
216
|
+
"effect": "4.0.0",
|
|
217
|
+
"fastest-levenshtein": "1.0.16",
|
|
218
|
+
"hono": "4.13.12",
|
|
219
|
+
"next": "16.3.8",
|
|
205
220
|
"oxfmt": "0.71.0",
|
|
206
221
|
"oxlint": "1.86.0",
|
|
207
|
-
"pg": "8.23.
|
|
222
|
+
"pg": "8.23.1",
|
|
208
223
|
"publint": "0.3.24",
|
|
209
224
|
"react": "19.3.0",
|
|
210
225
|
"react-dom": "19.3.0",
|
|
226
|
+
"smol-toml": "1.9.0",
|
|
227
|
+
"temporal-polyfill": "1.0.5",
|
|
228
|
+
"tinyexec": "1.3.1",
|
|
211
229
|
"tsdown": "0.23.0",
|
|
212
230
|
"typescript": "7.0.2",
|
|
213
231
|
"valibot": "1.5.0",
|
|
214
|
-
"vitest": "5.0.
|
|
232
|
+
"vitest": "5.0.3",
|
|
215
233
|
"zod": "4.6.5"
|
|
216
234
|
},
|
|
217
235
|
"peerDependencies": {
|
|
218
236
|
"@opentelemetry/api": ">=1.9",
|
|
219
237
|
"@orpc/server": ">=2.0.0-0",
|
|
220
|
-
"@supabase/config": ">=0.9.0 <0.
|
|
238
|
+
"@supabase/config": ">=0.9.0 <0.12",
|
|
221
239
|
"@supabase/supabase-js": "^2.116.0",
|
|
222
240
|
"@tanstack/query-core": "^5",
|
|
223
241
|
"hono": ">=4",
|
|
224
242
|
"next": ">=16",
|
|
243
|
+
"oxfmt": ">=0.66",
|
|
225
244
|
"pg": ">=8",
|
|
226
|
-
"react": "^19"
|
|
245
|
+
"react": "^19",
|
|
246
|
+
"temporal-polyfill": ">=1"
|
|
227
247
|
},
|
|
228
248
|
"peerDependenciesMeta": {
|
|
229
249
|
"@opentelemetry/api": {
|
|
@@ -244,11 +264,17 @@
|
|
|
244
264
|
"next": {
|
|
245
265
|
"optional": true
|
|
246
266
|
},
|
|
267
|
+
"oxfmt": {
|
|
268
|
+
"optional": true
|
|
269
|
+
},
|
|
247
270
|
"pg": {
|
|
248
271
|
"optional": true
|
|
249
272
|
},
|
|
250
273
|
"react": {
|
|
251
274
|
"optional": true
|
|
275
|
+
},
|
|
276
|
+
"temporal-polyfill": {
|
|
277
|
+
"optional": true
|
|
252
278
|
}
|
|
253
279
|
},
|
|
254
280
|
"engines": {
|
|
@@ -258,8 +284,10 @@
|
|
|
258
284
|
"bench": "node tests/next/next.bench.ts",
|
|
259
285
|
"build": "tsdown",
|
|
260
286
|
"check:publish": "publint && pnpm pack --out pack.tgz && attw --profile esm-only pack.tgz && rm -f pack.tgz",
|
|
287
|
+
"gen:fixtures": "node scripts/gen-fixtures.ts",
|
|
261
288
|
"lint": "oxlint",
|
|
262
|
-
"test": "vitest run --coverage",
|
|
263
|
-
"
|
|
289
|
+
"test": "vitest run --project unit --coverage",
|
|
290
|
+
"transit": "true",
|
|
291
|
+
"typecheck": "tsc --noEmit"
|
|
264
292
|
}
|
|
265
293
|
}
|
package/schemas/config-v1.json
CHANGED
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"type": "object",
|
|
68
68
|
"additionalProperties": false,
|
|
69
69
|
"properties": {
|
|
70
|
-
"timestamptz": { "enum": ["string", "
|
|
70
|
+
"timestamptz": { "enum": ["string", "instant"], "default": "string" },
|
|
71
71
|
"int8": { "enum": ["number", "string", "bigint"], "default": "number" },
|
|
72
72
|
"numeric": { "enum": ["number", "string"], "default": "number" }
|
|
73
73
|
}
|
|
@@ -283,6 +283,36 @@
|
|
|
283
283
|
"description": "The tenant id column of that table.",
|
|
284
284
|
"type": "string",
|
|
285
285
|
"default": "id"
|
|
286
|
+
},
|
|
287
|
+
"permdock": {
|
|
288
|
+
"description": "With a PermDock manifest, read PermDock's memberships for this scope instead of better_supabase.memberships; false keeps the kit's table.",
|
|
289
|
+
"oneOf": [
|
|
290
|
+
{ "const": false },
|
|
291
|
+
{
|
|
292
|
+
"type": "object",
|
|
293
|
+
"additionalProperties": false,
|
|
294
|
+
"properties": {
|
|
295
|
+
"scope": { "type": "string", "default": "organization" }
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
]
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
"permdock": {
|
|
303
|
+
"description": "Where PermDock's JSON outputs are.",
|
|
304
|
+
"type": "object",
|
|
305
|
+
"additionalProperties": false,
|
|
306
|
+
"properties": {
|
|
307
|
+
"manifest": {
|
|
308
|
+
"description": "What `permdock supabase inspect --out` writes.",
|
|
309
|
+
"type": "string",
|
|
310
|
+
"default": "permdock.manifest.json"
|
|
311
|
+
},
|
|
312
|
+
"catalog": {
|
|
313
|
+
"description": "What `permdock catalog` writes.",
|
|
314
|
+
"type": "string",
|
|
315
|
+
"default": "permissions.catalog.json"
|
|
286
316
|
}
|
|
287
317
|
}
|
|
288
318
|
},
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://unpkg.com/better-supabase/schemas/doctor-report-v1.json",
|
|
4
4
|
"title": "better-supabase doctor report",
|
|
5
|
-
"description": "Output of `better-supabase doctor --
|
|
5
|
+
"description": "Output of `better-supabase doctor --json`.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
8
|
"required": ["$schema", "version", "tool", "summary", "findings"],
|
|
@@ -42,6 +42,12 @@
|
|
|
42
42
|
"BS106",
|
|
43
43
|
"BS107",
|
|
44
44
|
"BS108",
|
|
45
|
+
"BS109",
|
|
46
|
+
"BS110",
|
|
47
|
+
"BS111",
|
|
48
|
+
"BS112",
|
|
49
|
+
"BS113",
|
|
50
|
+
"BS114",
|
|
45
51
|
"BS200",
|
|
46
52
|
"BS204",
|
|
47
53
|
"BS205",
|
|
@@ -52,6 +58,15 @@
|
|
|
52
58
|
"BS210",
|
|
53
59
|
"BS211",
|
|
54
60
|
"BS212",
|
|
61
|
+
"BS213",
|
|
62
|
+
"BS214",
|
|
63
|
+
"BS215",
|
|
64
|
+
"BS216",
|
|
65
|
+
"BS217",
|
|
66
|
+
"BS218",
|
|
67
|
+
"BS219",
|
|
68
|
+
"BS220",
|
|
69
|
+
"BS221",
|
|
55
70
|
"BS301",
|
|
56
71
|
"BS302",
|
|
57
72
|
"BS303",
|
|
@@ -65,6 +80,7 @@
|
|
|
65
80
|
"BS405",
|
|
66
81
|
"BS406",
|
|
67
82
|
"BS407",
|
|
83
|
+
"BS408",
|
|
68
84
|
"BS501",
|
|
69
85
|
"BS502"
|
|
70
86
|
]
|
package/schemas/snapshot-v2.json
CHANGED
|
@@ -690,6 +690,12 @@
|
|
|
690
690
|
"additionalProperties": {
|
|
691
691
|
"type": "string"
|
|
692
692
|
}
|
|
693
|
+
},
|
|
694
|
+
"execute": {
|
|
695
|
+
"type": "array",
|
|
696
|
+
"items": {
|
|
697
|
+
"type": "string"
|
|
698
|
+
}
|
|
693
699
|
}
|
|
694
700
|
}
|
|
695
701
|
}
|
|
@@ -901,6 +907,12 @@
|
|
|
901
907
|
},
|
|
902
908
|
"partial": {
|
|
903
909
|
"type": "boolean"
|
|
910
|
+
},
|
|
911
|
+
"method": {
|
|
912
|
+
"type": "string"
|
|
913
|
+
},
|
|
914
|
+
"predicate": {
|
|
915
|
+
"type": ["string", "null"]
|
|
904
916
|
}
|
|
905
917
|
}
|
|
906
918
|
}
|
|
@@ -996,6 +1008,29 @@
|
|
|
996
1008
|
}
|
|
997
1009
|
}
|
|
998
1010
|
},
|
|
1011
|
+
"columnGrants": {
|
|
1012
|
+
"description": "Column-level insert and update grants to anon and authenticated. Absent in snapshots taken before they were introspected.",
|
|
1013
|
+
"type": "array",
|
|
1014
|
+
"items": {
|
|
1015
|
+
"type": "object",
|
|
1016
|
+
"additionalProperties": false,
|
|
1017
|
+
"required": ["column", "role", "privileges"],
|
|
1018
|
+
"properties": {
|
|
1019
|
+
"column": {
|
|
1020
|
+
"type": "string"
|
|
1021
|
+
},
|
|
1022
|
+
"role": {
|
|
1023
|
+
"type": "string"
|
|
1024
|
+
},
|
|
1025
|
+
"privileges": {
|
|
1026
|
+
"type": "array",
|
|
1027
|
+
"items": {
|
|
1028
|
+
"type": "string"
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
},
|
|
999
1034
|
"large": {
|
|
1000
1035
|
"description": "Postgres estimates at least 10,000 rows; the unbounded-read lint rule reads it.",
|
|
1001
1036
|
"const": true
|