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.
Files changed (190) hide show
  1. package/README.md +37 -33
  2. package/dist/base64-D2gsioCY.js +42 -0
  3. package/dist/claims-BAf6c7is.js +48 -0
  4. package/dist/cli/_format-Cax5EsM9.js +101 -0
  5. package/dist/cli/bin.js +39 -6
  6. package/dist/cli/command-CK14fP91.js +502 -0
  7. package/dist/cli/compare-BmIWVEui.js +9 -0
  8. package/dist/cli/config-D9R0o1LH.js +1895 -0
  9. package/dist/cli/config-SSKSTUl2.js +2 -0
  10. package/dist/cli/db-DOBPt660.js +52 -0
  11. package/dist/cli/dist-B1MvLv-M.js +129 -0
  12. package/dist/cli/dist-CFgGknmZ.js +1112 -0
  13. package/dist/cli/dist-Du7VnEUG.js +621 -0
  14. package/dist/cli/doctor-BOym3mbL.js +2382 -0
  15. package/dist/cli/env-BZ0uy8XK.js +108 -0
  16. package/dist/cli/env-jIFUADLY.js +511 -0
  17. package/dist/cli/errors-4v4DTtda.js +56 -0
  18. package/dist/cli/fastest-levenshtein-CFN-lNDv.js +120 -0
  19. package/dist/cli/gen-Ccn_p90L.js +2008 -0
  20. package/dist/cli/gen-D5cLCULx.js +2 -0
  21. package/dist/cli/index.d.ts +295 -28
  22. package/dist/cli/index.js +184 -3
  23. package/dist/cli/init-ClnxRYBF.js +550 -0
  24. package/dist/cli/introspect-DxqfkVIv.js +81 -0
  25. package/dist/cli/io-k41_z-jT.js +24 -0
  26. package/dist/cli/json5-ChRycquj.js +787 -0
  27. package/dist/cli/jsonc-BUniUpOk.js +2 -0
  28. package/dist/cli/jsonc-D5Fu67jw.js +45 -0
  29. package/dist/cli/keys-ByKHnvP9.js +83 -0
  30. package/dist/cli/ohash-D7bG_EOb.js +277 -0
  31. package/dist/cli/openapi-8WHjxiTJ.js +66 -0
  32. package/dist/cli/project-B_GeXyHh.js +70 -0
  33. package/dist/cli/prompts-BNcNfAve.js +1142 -0
  34. package/dist/cli/prompts-BTYQGunc.js +2 -0
  35. package/dist/cli/read-sets-CMa29t9a.js +971 -0
  36. package/dist/cli/rolldown-runtime-BPOCksWG.js +24 -0
  37. package/dist/cli/run-CP20LKk_.js +347 -0
  38. package/dist/cli/seed-5J6eu_Xg.js +98 -0
  39. package/dist/cli/skills-LJJIhLFR.js +137 -0
  40. package/dist/cli/sql-D4VbqmXi.js +199 -0
  41. package/dist/cli/style-C0NFdJRL.js +29 -0
  42. package/dist/cli/toml-DB-MNnFs.js +424 -0
  43. package/dist/{version-Cqq1m3UY.js → cli/version-5mW_8sY1.js} +1 -1
  44. package/dist/cli/yaml-CYCbJ6et.js +2252 -0
  45. package/dist/client/index.d.ts +2 -2
  46. package/dist/client/index.js +15 -25
  47. package/dist/{compiler-CZ9nIuqy.d.ts → compiler-DB3aSFOA.d.ts} +2 -2
  48. package/dist/config/index.d.ts +4 -3
  49. package/dist/config/index.js +5 -3
  50. package/dist/config-BpxDfnbg.js +129 -0
  51. package/dist/{index-DOsjFvMf.d.ts → config-C1CYcm6o.d.ts} +49 -49
  52. package/dist/{config-ClDXmVtY.js → config-CbvFmGD8.js} +14 -137
  53. package/dist/{define-BXMYD9AY.js → define-Bh0AqTik.js} +221 -120
  54. package/dist/{define-e3zI4_4g.d.ts → define-CRS2MRV3.d.ts} +36 -31
  55. package/dist/edge/index.d.ts +9 -13
  56. package/dist/edge/index.js +51 -25
  57. package/dist/{entitlements-Dng-ctuT.d.ts → entitlements-Dm-FHpNH.d.ts} +2 -2
  58. package/dist/env/index.js +203 -1
  59. package/dist/{errors-Bl3rkOzo.js → errors-L0xK41y_.js} +1 -1
  60. package/dist/events/index.d.ts +1 -1
  61. package/dist/events/index.js +139 -1
  62. package/dist/{events-DOt3nSO-.d.ts → events-B21UOhvP.d.ts} +4 -4
  63. package/dist/{executor-CGdNQ27g.d.ts → executor-B5IoUxLE.d.ts} +2 -2
  64. package/dist/{executor-DeppvirK.js → executor-CVt6dD0B.js} +9 -4
  65. package/dist/{executor-BYXQ9AGn.d.ts → executor-CZ7ImRiD.d.ts} +2 -2
  66. package/dist/hono/index.d.ts +17 -24
  67. package/dist/hono/index.js +11 -11
  68. package/dist/{index-DuP14VHo.d.ts → index-528Ugmyp.d.ts} +30 -10
  69. package/dist/{index-Bw1bwtC0.d.ts → index-BAy4tsAP.d.ts} +28 -15
  70. package/dist/{index-CBDufH1M.d.ts → index-BBdvo-5d.d.ts} +24 -26
  71. package/dist/{index-DbxFr7XG.d.ts → index-BhHEC04r.d.ts} +9 -4
  72. package/dist/index-Cf-D2jk1.d.ts +50 -0
  73. package/dist/{index-Ciu5Xlml.d.ts → index-D5IfUIDh.d.ts} +6 -5
  74. package/dist/{index-K-3WM2Hp.d.ts → index-DFmOpBGC.d.ts} +13 -13
  75. package/dist/{index-D4v7sncu.d.ts → index-RlLXd8dL.d.ts} +13 -13
  76. package/dist/index.d.ts +17 -17
  77. package/dist/index.js +23 -14
  78. package/dist/{invalidate-DbxuL2jL.js → invalidate-CgxmRamI.js} +7 -1
  79. package/dist/jobs/index.d.ts +33 -26
  80. package/dist/jobs/index.js +44 -22
  81. package/dist/{json-schema-BpYqkCKf.js → json-schema-DP6V7cbr.js} +8 -3
  82. package/dist/lint/index.js +1 -1
  83. package/dist/list/index.d.ts +2 -2
  84. package/dist/list/index.js +131 -96
  85. package/dist/{live-DJyZMD6w.d.ts → live-BHakSWWo.d.ts} +26 -16
  86. package/dist/{live-D8rV1Ldw.js → live-CdJBOc4f.js} +13 -9
  87. package/dist/lookup-BaP5bI27.js +18 -0
  88. package/dist/mcp/index.d.ts +34 -27
  89. package/dist/mcp/index.js +59 -25
  90. package/dist/{mfa-TVia554c.d.ts → mfa-C7MNQ1Ob.d.ts} +1 -1
  91. package/dist/{mfa-CpLfJSde.js → mfa-byfjruR9.js} +190 -46
  92. package/dist/next/image/index.js +6 -3
  93. package/dist/next/index.d.ts +64 -48
  94. package/dist/next/index.js +63 -50
  95. package/dist/openapi/index.d.ts +1 -1
  96. package/dist/openapi/index.js +61 -38
  97. package/dist/orpc/index.d.ts +18 -21
  98. package/dist/orpc/index.js +12 -12
  99. package/dist/otel/index.d.ts +5 -5
  100. package/dist/otel/index.js +84 -63
  101. package/dist/{permdock-sql-CghlHPuH.js → permdock-sql-C4KFzLc9.js} +12 -7
  102. package/dist/permdock-sql-DImJULY-.d.ts +17 -0
  103. package/dist/{plugin-C4TR9J0Z.d.ts → plugin-bNhvJwRh.d.ts} +5 -4
  104. package/dist/plugins/actor/index.d.ts +1 -1
  105. package/dist/plugins/actor/index.js +1 -1
  106. package/dist/plugins/rules/index.d.ts +2 -2
  107. package/dist/plugins/rules/index.js +46 -16
  108. package/dist/plugins/soft-delete/index.d.ts +3 -3
  109. package/dist/plugins/soft-delete/index.js +4 -4
  110. package/dist/plugins/tenant/index.d.ts +3 -3
  111. package/dist/plugins/tenant/index.js +10 -4
  112. package/dist/plugins/timestamps/index.d.ts +1 -1
  113. package/dist/plugins/timestamps/index.js +2 -2
  114. package/dist/plugins/validation/index.d.ts +2 -2
  115. package/dist/plugins/validation/index.js +3 -3
  116. package/dist/pool-DFwEQwmN.d.ts +78 -0
  117. package/dist/postgres/index.d.ts +10 -5
  118. package/dist/postgres/index.js +37 -16
  119. package/dist/{postgrest-cAEQ_IQN.d.ts → postgrest-Bc-0Xw62.d.ts} +1 -1
  120. package/dist/{problem-BTkTtRV9.js → problem-BihjPR0t.js} +11 -5
  121. package/dist/{problem-DlgB0lYl.d.ts → problem-U8UEKccv.d.ts} +6 -4
  122. package/dist/query/index.d.ts +2 -2
  123. package/dist/query/index.js +2 -2
  124. package/dist/{query-ChvDbsDE.js → query-BMMSWbI7.js} +19 -19
  125. package/dist/react/index.d.ts +7 -7
  126. package/dist/react/index.js +2 -2
  127. package/dist/react/server.d.ts +6 -6
  128. package/dist/react/server.js +3 -3
  129. package/dist/react/session.d.ts +21 -2
  130. package/dist/react/session.js +19 -1
  131. package/dist/{react-C83BdBGt.js → react-CQHMaESR.js} +59 -55
  132. package/dist/{read-set-4lI3W_gZ.js → read-set-FWK8qQ58.js} +51 -16
  133. package/dist/realtime/index.d.ts +4 -3
  134. package/dist/realtime/index.js +19 -17
  135. package/dist/{index-CG69s15v.d.ts → refresh-BwYkyNOQ.d.ts} +7 -3
  136. package/dist/{resolve-CjHILk2w.d.ts → resolve-BAqQcwMW.d.ts} +17 -11
  137. package/dist/{resource-CgZa3YG_.d.ts → resource-BJcaIfxi.d.ts} +17 -8
  138. package/dist/{resource-BHEWmQJK.js → resource-My_y-Syh.js} +43 -21
  139. package/dist/{respond-Dne1ARSa.d.ts → respond-DgIbdrt8.d.ts} +39 -14
  140. package/dist/{result-DWXatkd6.js → result-Bj6yt33D.js} +2 -2
  141. package/dist/{result-CTkCZ6JA.d.ts → result-CqEsbgYs.d.ts} +16 -11
  142. package/dist/server/index.d.ts +38 -13
  143. package/dist/server/index.js +43 -14
  144. package/dist/{respond-DMcbQwfN.js → server-BA75B0mT.js} +151 -96
  145. package/dist/{shared-_6-uPL7E.js → shared-DnoCdMDr.js} +4 -3
  146. package/dist/{simplify-bwJpLnaS.js → simplify-Cqvfr9dj.js} +76 -39
  147. package/dist/sql/index.d.ts +139 -0
  148. package/dist/sql/index.js +1983 -0
  149. package/dist/{sql-DTjA1LPj.js → sql-B34HU2DI.js} +74 -17
  150. package/dist/ssr/index.d.ts +4 -4
  151. package/dist/ssr/index.js +2 -2
  152. package/dist/{standard-FUi3Z3-7.js → standard-BRyBGTtp.js} +2 -2
  153. package/dist/{standard-CyhRzctb.d.ts → standard-BsaCYQJc.d.ts} +1 -1
  154. package/dist/{stats-C5W-rBfR.js → stats-C_YxA2r1.js} +40 -14
  155. package/dist/storage/index.d.ts +4 -3
  156. package/dist/storage/index.js +548 -1
  157. package/dist/{tables-rZxRxNPW.js → tables-Cb-3_VWc.js} +19 -5
  158. package/dist/temporal-BwshQk0W.js +27 -0
  159. package/dist/temporal-required-CBNHT6ni.js +19 -0
  160. package/dist/testing/index.d.ts +25 -24
  161. package/dist/testing/index.js +165 -66
  162. package/dist/{types-C83v5SQz.d.ts → types-DONv9SHN.d.ts} +14 -11
  163. package/dist/version-Bmf7_FzM.js +4 -0
  164. package/dist/{view-D1s0wa0T.d.ts → view-B64o81wp.d.ts} +36 -6
  165. package/dist/{view-aOYizTeE.js → view-BrJI2LKp.js} +11 -2
  166. package/dist/webhooks/index.d.ts +19 -17
  167. package/dist/webhooks/index.js +1 -1
  168. package/dist/{webhooks-DMipjhSP.js → webhooks-Bh_Jf_-l.js} +45 -24
  169. package/package.json +50 -22
  170. package/schemas/config-v1.json +31 -1
  171. package/schemas/doctor-report-v1.json +17 -1
  172. package/schemas/snapshot-v2.json +35 -0
  173. package/skills/better-supabase/SKILL.md +77 -18
  174. package/skills/better-supabase/references/plugins.md +21 -22
  175. package/skills/better-supabase/references/troubleshooting.md +38 -18
  176. package/skills/better-supabase-api/SKILL.md +66 -18
  177. package/skills/better-supabase-api/references/adapters.md +64 -41
  178. package/skills/better-supabase-auth/SKILL.md +146 -0
  179. package/skills/better-supabase-auth/references/permdock.md +55 -0
  180. package/skills/better-supabase-testing/SKILL.md +68 -11
  181. package/dist/casing-da0uRTqt.js +0 -14
  182. package/dist/claims-CoTobNC-.js +0 -25
  183. package/dist/cli-DrSgDOQC.js +0 -6646
  184. package/dist/env-BEToab0I.js +0 -204
  185. package/dist/events-BuzTgn2D.js +0 -135
  186. package/dist/pool-R2m_DkXi.d.ts +0 -37
  187. package/dist/seed-CzecODcJ.js +0 -101
  188. package/dist/session-C3LxnQ0e.d.ts +0 -22
  189. package/dist/session-CqEwzVup.js +0 -20
  190. package/dist/storage-DuGn38fr.js +0 -527
@@ -1,7 +1,30 @@
1
- import { p as DbError } from "./result-CTkCZ6JA.js";
2
- import { n as AmrEntry, s as Impersonator, t as Aal } from "./mfa-TVia554c.js";
3
- import { i as InvalidReason, r as AuthState } from "./resolve-CjHILk2w.js";
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 `sb.claims(schema)`.
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 `sb.userMetadata(schema)`, for display.
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 impersonatorOf, n as amrOf, t as aalOf } from "./mfa-CpLfJSde.js";
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 {
@@ -1,19 +1,20 @@
1
- import { N as TableKey, O as Row, n as AnyModels, t as AnyFunctions } from "../types-C83v5SQz.js";
2
- import { o as Result } from "../result-CTkCZ6JA.js";
3
- import { t as BetterSupabase } from "../define-e3zI4_4g.js";
4
- //#region src/webhooks/index.d.ts
5
- export interface VerifiedWebhook<T = unknown> {
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: Date;
8
+ readonly timestamp: Temporal.Instant;
8
9
  readonly payload: T;
9
10
  readonly body: string;
10
11
  }
11
- export interface VerifyOptions {
12
+ interface VerifyOptions {
12
13
  /** Allowed clock skew in seconds. Defaults to 300. */
13
14
  readonly tolerance?: number;
14
- readonly now?: () => number;
15
+ readonly now?: () => Temporal.Instant;
15
16
  }
16
- export type WebhookInput = Request | {
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?: Date;
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
- export interface AuthHookError {
45
+ interface AuthHookError {
45
46
  readonly error: {
46
47
  readonly http_code: number;
47
48
  readonly message: string;
48
49
  };
49
50
  }
50
- export interface AuthHooks {
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
- export type AuthHookKind = keyof AuthHooks;
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
- export interface DatabaseWebhookPayload {
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
- export interface DatabaseChange<R> {
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>>(sb: BetterSupabase<M, D, F, E>, table: T, payload: unknown): DatabaseChange<Row<M, T>> | null;
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 };
@@ -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-DMipjhSP.js";
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-Bl3rkOzo.js";
2
- import { i as ok, n as err } from "./result-DWXatkd6.js";
3
- import { s as toApp } from "./shared-_6-uPL7E.js";
4
- //#region src/webhooks/index.ts
5
- const encoder = new TextEncoder();
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 Uint8Array.from(atob(raw), (char) => char.charCodeAt(0));
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
- const key = await crypto.subtle.importKey("raw", secretBytes(secret), {
17
- name: "HMAC",
18
- hash: "SHA-256"
19
- }, false, ["sign"]);
20
- const mac = new Uint8Array(await crypto.subtle.sign("HMAC", key, encoder.encode(content)));
21
- let binary = "";
22
- for (const byte of mac) binary += String.fromCharCode(byte);
23
- return btoa(binary);
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 ?? /* @__PURE__ */ new Date()).getTime() / 1e3));
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 now = (options.now?.() ?? Date.now()) / 1e3;
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 content = `${id}.${timestamp}.${body}`;
66
- const offered = signatures.split(" ").map((entry) => entry.split(",")).filter(([version, value]) => version === "v1" && value).map(([, value]) => value);
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 expected = await signature(secret, content);
70
- for (const candidate of offered) if (timingSafeEqual(candidate, expected)) valid = true;
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: /* @__PURE__ */ new Date(seconds * 1e3),
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(sb, table, payload) {
129
- const meta = sb.meta.tables[table];
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.2.0",
4
- "description": "Strongly typed Supabase: generated schema, typed repositories, plugins, auth glue and framework integrations, with a CLI from day one.",
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.6.0",
181
- "@supabase/postgrest-typegen": "0.2.2",
182
- "@supabase/server": "1.8.1",
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
- "@effect/platform-node": "4.0.0-rc.117",
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.10.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": "24.19.0",
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.2",
202
- "effect": "4.0.0-rc.117",
203
- "hono": "4.13.10",
204
- "next": "16.3.7",
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.0",
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.2",
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.11",
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
- "typecheck": "tsc --noEmit --checkers 4"
289
+ "test": "vitest run --project unit --coverage",
290
+ "transit": "true",
291
+ "typecheck": "tsc --noEmit"
264
292
  }
265
293
  }
@@ -67,7 +67,7 @@
67
67
  "type": "object",
68
68
  "additionalProperties": false,
69
69
  "properties": {
70
- "timestamptz": { "enum": ["string", "date"], "default": "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 --format json`.",
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
  ]
@@ -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