better-supabase 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/README.md +9 -6
  2. package/bin/better-supabase.js +4 -0
  3. package/dist/{base64-CQeB1eSf.js → base64-D2gsioCY.js} +2 -2
  4. package/dist/channel-FQW-CnGR.d.ts +70 -0
  5. package/dist/claims-BAf6c7is.js +48 -0
  6. package/dist/cli/_format-Cax5EsM9.js +101 -0
  7. package/dist/cli/bin.d.ts +1 -0
  8. package/dist/cli/bin.js +48 -0
  9. package/dist/cli/codemod-DqV7612m.js +388 -0
  10. package/dist/cli/command-CK14fP91.js +502 -0
  11. package/dist/cli/compare-BmIWVEui.js +9 -0
  12. package/dist/cli/config-CPVpXP5I.js +1965 -0
  13. package/dist/cli/config-dtoekM0v.js +2 -0
  14. package/dist/cli/db-DOBPt660.js +52 -0
  15. package/dist/cli/diff-SjkO5rNr.js +752 -0
  16. package/dist/cli/dist-DBPdlPYK.js +1112 -0
  17. package/dist/cli/dist-DreZ24Sz.js +129 -0
  18. package/dist/cli/dist-xn4wlboc.js +732 -0
  19. package/dist/cli/doctor-L7Z8mYTx.js +2863 -0
  20. package/dist/cli/env-DmKv_G4A.js +108 -0
  21. package/dist/cli/env-jIFUADLY.js +511 -0
  22. package/dist/cli/errors-4v4DTtda.js +56 -0
  23. package/dist/cli/fastest-levenshtein-CFN-lNDv.js +120 -0
  24. package/dist/cli/gen-BJc9vdyu.js +2 -0
  25. package/dist/cli/gen-C2osRcr5.js +2204 -0
  26. package/dist/cli/index.d.ts +493 -0
  27. package/dist/cli/index.js +184 -0
  28. package/dist/cli/init-Saf5-R9T.js +550 -0
  29. package/dist/cli/introspect--H7oGmcd.js +81 -0
  30. package/dist/cli/io-k41_z-jT.js +24 -0
  31. package/dist/cli/json5-ChRycquj.js +787 -0
  32. package/dist/cli/jsonc-BUniUpOk.js +2 -0
  33. package/dist/cli/jsonc-D5Fu67jw.js +45 -0
  34. package/dist/cli/keys-ByKHnvP9.js +83 -0
  35. package/dist/cli/ohash-D7bG_EOb.js +277 -0
  36. package/dist/cli/openapi-Cp5Fyta-.js +66 -0
  37. package/dist/cli/project-B_GeXyHh.js +70 -0
  38. package/dist/cli/prompts-BNcNfAve.js +1142 -0
  39. package/dist/cli/prompts-BTYQGunc.js +2 -0
  40. package/dist/cli/read-sets-BfMQcKy8.js +289 -0
  41. package/dist/cli/rolldown-runtime-BPOCksWG.js +24 -0
  42. package/dist/cli/run-BKR2W_79.js +351 -0
  43. package/dist/cli/seed-CUlqWh-8.js +98 -0
  44. package/dist/cli/skills-LJJIhLFR.js +137 -0
  45. package/dist/cli/sql-DzPbfngx.js +348 -0
  46. package/dist/cli/style-C0NFdJRL.js +29 -0
  47. package/dist/cli/toml-DB-MNnFs.js +424 -0
  48. package/dist/cli/version-BDJuO0UR.js +4 -0
  49. package/dist/cli/yaml-CYCbJ6et.js +2252 -0
  50. package/dist/client/index.d.ts +2 -2
  51. package/dist/client/index.js +15 -22
  52. package/dist/clone-BfgmLU90.js +21 -0
  53. package/dist/{compiler-2zqGzxVZ.d.ts → compiler-BXH_aZ7N.d.ts} +2 -2
  54. package/dist/config/index.d.ts +4 -4
  55. package/dist/config/index.js +5 -5
  56. package/dist/{config-DOsGYSiT.js → config-DreERlKo.js} +1 -1
  57. package/dist/{config-C1CYcm6o.d.ts → config-T_ejwyd3.d.ts} +176 -11
  58. package/dist/{define-DUbydmJa.d.ts → define-Bp_BZa5h.d.ts} +37 -29
  59. package/dist/{define-DF341GDj.js → define-cNKzqwL_.js} +356 -89
  60. package/dist/edge/index.d.ts +20 -13
  61. package/dist/edge/index.js +55 -26
  62. package/dist/{entitlements-DvJXoI02.d.ts → entitlements-DUxU4oHe.d.ts} +2 -2
  63. package/dist/env/index.d.ts +2 -2
  64. package/dist/env/index.js +34 -1
  65. package/dist/{errors-Bl3rkOzo.js → errors-DtwXa6b8.js} +2 -1
  66. package/dist/events/index.d.ts +3 -2
  67. package/dist/events/index.js +69 -16
  68. package/dist/events-aIO7vsIk.d.ts +230 -0
  69. package/dist/executor-BunPOO-k.d.ts +402 -0
  70. package/dist/{executor-B26lgbHD.d.ts → executor-Cghdstwu.d.ts} +1 -2
  71. package/dist/{executor-C57jx2pn.js → executor-XycESQPH.js} +3 -3
  72. package/dist/hono/index.d.ts +27 -23
  73. package/dist/hono/index.js +46 -11
  74. package/dist/impersonation-VfaGc4Q_.js +41 -0
  75. package/dist/{index-BNsABq7P.d.ts → index-BGdgBPdf.d.ts} +66 -25
  76. package/dist/{index-qriRM9cZ.d.ts → index-C7f8cOBM.d.ts} +4 -4
  77. package/dist/{index-Dy6kuF0P.d.ts → index-CLg87OBW.d.ts} +6 -1
  78. package/dist/{index-GV77NpRP.d.ts → index-DOESKG27.d.ts} +34 -9
  79. package/dist/{index-CriFFJ_p.d.ts → index-DgILJRXj.d.ts} +43 -17
  80. package/dist/{index-UJ-LUxtI.d.ts → index-DmXSxbLi.d.ts} +14 -14
  81. package/dist/{index-Cf-D2jk1.d.ts → index-Dxn2a5cw.d.ts} +2 -2
  82. package/dist/{index-CTTMS6_1.d.ts → index-SaTsEfGP.d.ts} +86 -9
  83. package/dist/{index-CADlNHK-.d.ts → index-fag8VJZ2.d.ts} +5 -6
  84. package/dist/index.d.ts +34 -17
  85. package/dist/index.js +15 -14
  86. package/dist/jobs/index.d.ts +147 -95
  87. package/dist/jobs/index.js +773 -250
  88. package/dist/{json-schema--aOXAGfU.js → json-schema-m1fiwKgN.js} +23 -2
  89. package/dist/kit-events-CTFDfSwb.js +60 -0
  90. package/dist/kit-transport-B82k1djG.d.ts +33 -0
  91. package/dist/kit-transport-DJlHQUBU.js +55 -0
  92. package/dist/{config-Be4GwsqI.js → kits-CGLLrNPd.js} +38 -10
  93. package/dist/list/index.d.ts +1 -1
  94. package/dist/list/index.js +3 -3
  95. package/dist/{live-D8rV1Ldw.js → live-CdJBOc4f.js} +13 -9
  96. package/dist/{live-QE3UPbGm.d.ts → live-D9W2M5aO.d.ts} +16 -15
  97. package/dist/lookup-BaP5bI27.js +18 -0
  98. package/dist/mcp/index.d.ts +39 -22
  99. package/dist/mcp/index.js +39 -25
  100. package/dist/{mfa-x3V3vGh-.d.ts → mfa-CusDgPaD.d.ts} +5 -1
  101. package/dist/next/image/index.js +6 -3
  102. package/dist/next/index.d.ts +60 -36
  103. package/dist/next/index.js +167 -50
  104. package/dist/notifications/index.d.ts +162 -0
  105. package/dist/notifications/index.js +264 -0
  106. package/dist/openapi/index.d.ts +1 -1
  107. package/dist/openapi/index.js +6 -6
  108. package/dist/orgs/index.d.ts +132 -0
  109. package/dist/orgs/index.js +245 -0
  110. package/dist/orpc/index.d.ts +37 -20
  111. package/dist/orpc/index.js +24 -11
  112. package/dist/otel/index.d.ts +24 -5
  113. package/dist/otel/index.js +124 -64
  114. package/dist/permdock-sql-C2Inc6Xc.d.ts +29 -0
  115. package/dist/permdock-sql-DgOfeixi.js +51 -0
  116. package/dist/plugin-DwARMuQp.js +59 -0
  117. package/dist/{plugin-DVRB8qNE.d.ts → plugin-o144f322.d.ts} +17 -5
  118. package/dist/plugins/actor/index.d.ts +5 -2
  119. package/dist/plugins/actor/index.js +22 -8
  120. package/dist/plugins/rules/index.d.ts +2 -2
  121. package/dist/plugins/rules/index.js +52 -17
  122. package/dist/plugins/soft-delete/index.d.ts +3 -3
  123. package/dist/plugins/soft-delete/index.js +14 -5
  124. package/dist/plugins/tenant/index.d.ts +3 -3
  125. package/dist/plugins/tenant/index.js +45 -21
  126. package/dist/plugins/timestamps/index.d.ts +1 -1
  127. package/dist/plugins/timestamps/index.js +4 -3
  128. package/dist/plugins/validation/index.d.ts +2 -2
  129. package/dist/plugins/validation/index.js +26 -6
  130. package/dist/{pool-B9qsLyFR.d.ts → pool-C9voM4Kw.d.ts} +24 -6
  131. package/dist/postgres/index.d.ts +5 -5
  132. package/dist/postgres/index.js +20 -10
  133. package/dist/{postgrest-DmBlevh-.d.ts → postgrest-CruYKAvV.d.ts} +1 -1
  134. package/dist/{problem-CGt1ADix.js → problem-BW1KhzSG.js} +2 -1
  135. package/dist/{problem-Z2RTQjZD.d.ts → problem-Blxe8S31.d.ts} +1 -1
  136. package/dist/query/index.d.ts +2 -2
  137. package/dist/query/index.js +3 -3
  138. package/dist/{query-BdKLTPlj.js → query-CCbo44lG.js} +18 -18
  139. package/dist/queue-BYUCjr59.d.ts +252 -0
  140. package/dist/react/index.d.ts +8 -7
  141. package/dist/react/index.js +4 -3
  142. package/dist/react/server.d.ts +16 -5
  143. package/dist/react/server.js +12 -4
  144. package/dist/react/session.d.ts +10 -4
  145. package/dist/react/session.js +11 -3
  146. package/dist/{react-DNBYNazu.js → react-C_s7kiNz.js} +138 -46
  147. package/dist/{read-set-D0qBcboM.js → read-set-DQFt1N_Y.js} +71 -15
  148. package/dist/realtime/index.d.ts +3 -3
  149. package/dist/realtime/index.js +14 -12
  150. package/dist/{index-kpgopLNv.d.ts → refresh-BTrQ_ZTp.d.ts} +6 -3
  151. package/dist/{resolve-BRciX2jK.d.ts → resolve-C15TJq77.d.ts} +22 -11
  152. package/dist/{resource-D30rp0GN.js → resource-CRxxwkQq.js} +24 -9
  153. package/dist/{resource-CwVgiI5q.d.ts → resource-D8iqWMr-.d.ts} +6 -6
  154. package/dist/respond-D2OQl62-.d.ts +371 -0
  155. package/dist/{result-DWXatkd6.js → result-BKiliPY7.js} +2 -2
  156. package/dist/server/index.d.ts +17 -15
  157. package/dist/server/index.js +16 -22
  158. package/dist/server-Csx2sRjg.js +655 -0
  159. package/dist/{shared-_6-uPL7E.js → shared-D1dRUrAg.js} +19 -4
  160. package/dist/signers-Cc6r98SW.d.ts +162 -0
  161. package/dist/{simplify-LakqK1vH.js → simplify-_0oUKqCd.js} +27 -95
  162. package/dist/sql/index.d.ts +247 -10
  163. package/dist/sql/index.js +8591 -780
  164. package/dist/{sql-BIgk12ew.js → sql-Coh6n4cC.js} +11 -3
  165. package/dist/ssr/index.d.ts +4 -4
  166. package/dist/ssr/index.js +1 -2
  167. package/dist/{standard-FUi3Z3-7.js → standard-D7r6dMUu.js} +6 -3
  168. package/dist/{standard-eudBiRk1.d.ts → standard-J4YgMOLm.d.ts} +1 -1
  169. package/dist/{stats-D78UQ8Yu.js → stats-Bqob3WqT.js} +34 -9
  170. package/dist/storage/index.d.ts +4 -4
  171. package/dist/storage/index.js +174 -27
  172. package/dist/support-DT-OhSxD.d.ts +88 -0
  173. package/dist/support-Dk6vdmEw.js +291 -0
  174. package/dist/support-view-Da5peKFg.d.ts +17 -0
  175. package/dist/support-view-g4YLBeqX.js +18 -0
  176. package/dist/{tables-Dhha3BXM.js → tables-Cb-3_VWc.js} +14 -0
  177. package/dist/{permdock-sql-Bta_G0v-.js → template-TxrGI5jR.js} +2 -31
  178. package/dist/{temporal-BwshQk0W.js → temporal-CVlIV2DZ.js} +17 -4
  179. package/dist/{temporal-required-CMxeSVPo.js → temporal-required-CAt4Dc9Z.js} +2 -2
  180. package/dist/testing/index.d.ts +113 -24
  181. package/dist/testing/index.js +375 -47
  182. package/dist/{types-DONv9SHN.d.ts → types-Dnf_wRDz.d.ts} +26 -3
  183. package/dist/user-change-CHFEUro1.js +49 -0
  184. package/dist/{webhooks-CAIFQSTM.js → verify-Ce49ETPz.js} +38 -19
  185. package/dist/{version-BfpG2gks.js → version-DM_Roffb.js} +1 -1
  186. package/dist/{view-3Po_PVlr.d.ts → view-BEf0diC5.d.ts} +10 -5
  187. package/dist/{mfa-BxoR1RkN.js → view-Bq64ki8-.js} +210 -64
  188. package/dist/webhooks/index.d.ts +136 -5
  189. package/dist/webhooks/index.js +588 -2
  190. package/dist/wire-D2dT86vi.js +35 -0
  191. package/package.json +50 -10
  192. package/schemas/config-v1.json +131 -1
  193. package/schemas/doctor-report-v1.json +10 -0
  194. package/skills/better-supabase/SKILL.md +60 -10
  195. package/skills/better-supabase/references/plugins.md +13 -14
  196. package/skills/better-supabase/references/troubleshooting.md +25 -4
  197. package/skills/better-supabase-api/SKILL.md +74 -19
  198. package/skills/better-supabase-api/references/adapters.md +48 -27
  199. package/skills/better-supabase-auth/SKILL.md +149 -0
  200. package/skills/better-supabase-auth/references/permdock.md +100 -0
  201. package/skills/better-supabase-testing/SKILL.md +58 -4
  202. package/dist/claims-CoTobNC-.js +0 -25
  203. package/dist/events-Do8jXh4D.d.ts +0 -84
  204. package/dist/executor-DynmIWhq.d.ts +0 -195
  205. package/dist/invalidate-DbxuL2jL.js +0 -18
  206. package/dist/permdock-sql-DImJULY-.d.ts +0 -17
  207. package/dist/plugin-BqR6wKMB.js +0 -23
  208. package/dist/respond-C44EZAJo.js +0 -399
  209. package/dist/respond-DAkPIdTH.d.ts +0 -175
  210. package/dist/result-c2OYgCV2.d.ts +0 -203
  211. package/dist/view-wZ-9qIUz.js +0 -45
@@ -32,6 +32,18 @@ function scalarKind(column) {
32
32
  if (column.type === "record" || column.type === "void") return { kind: "unknown" };
33
33
  return { kind: "string" };
34
34
  }
35
+ /** App names of the columns that plugins fill from the table's flags. */
36
+ function managedColumns(table) {
37
+ const { timestamps, actor, softDelete } = table.flags;
38
+ return new Set([
39
+ timestamps?.createdAt,
40
+ timestamps?.updatedAt,
41
+ actor?.createdBy,
42
+ actor?.updatedBy,
43
+ actor?.impersonatedBy,
44
+ softDelete
45
+ ].filter((name) => name !== void 0));
46
+ }
35
47
  /**
36
48
  * The generated row type of a field (its element type for arrays). Storage
37
49
  * path columns validate as strings and are cast to their `StoragePath` brand.
@@ -43,6 +55,7 @@ function rowType(table, plan) {
43
55
  /** The fields a variant accepts; read-only columns are left out of writes. */
44
56
  function fieldsFor(table, variant, input) {
45
57
  const fields = [];
58
+ const managed = managedColumns(table);
46
59
  for (const [name, column] of Object.entries(table.columns)) {
47
60
  if (variant === "Insert" && (column.generated || column.insertable === false)) continue;
48
61
  if (variant === "Update" && (column.generated || column.updatable === false)) continue;
@@ -52,7 +65,8 @@ function fieldsFor(table, variant, input) {
52
65
  scalar: scalarKind(column),
53
66
  nullable: column.nullable,
54
67
  optional: variant === "Update" || variant === "Insert" && (column.nullable || column.hasDefault),
55
- customJson: Boolean(column.json && input.config.json[`${table.name}.${column.db}`])
68
+ customJson: Boolean(column.json && input.config.json[`${table.name}.${column.db}`]),
69
+ managed: managed.has(name)
56
70
  });
57
71
  }
58
72
  return fields;
@@ -65,7 +79,7 @@ function variantsFor(table) {
65
79
  ];
66
80
  }
67
81
  function tableEntries(input) {
68
- return Object.entries(input.meta.tables).sort(([a], [b]) => a.localeCompare(b));
82
+ return Object.entries(input.meta.tables).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
69
83
  }
70
84
  function propertyKey(name) {
71
85
  return /^[A-Za-z_$][\w$]*$/.test(name) ? name : JSON.stringify(name);
@@ -130,6 +144,13 @@ function scalar(kind) {
130
144
  }
131
145
  }
132
146
  function property(plan) {
147
+ const schema = nullable(plan);
148
+ return plan.managed ? {
149
+ ...schema,
150
+ readOnly: true
151
+ } : schema;
152
+ }
153
+ function nullable(plan) {
133
154
  let schema = scalar(plan.scalar);
134
155
  if (plan.column.array) schema = {
135
156
  type: "array",
@@ -0,0 +1,60 @@
1
+ import { t as cloneValue } from "./clone-BfgmLU90.js";
2
+ import { n as nowInstant } from "./temporal-CVlIV2DZ.js";
3
+ //#region src/core/kit-events.ts
4
+ /**
5
+ * Fixed OpenTelemetry attribute names for kit work, so dashboards and
6
+ * queries can rely on them.
7
+ */
8
+ const KIT_ATTRIBUTES = {
9
+ event: "better_supabase.kit.event",
10
+ tenant: "better_supabase.tenant",
11
+ actor: "better_supabase.actor.id",
12
+ supportSessionId: "better_supabase.support.session_id",
13
+ orgId: "better_supabase.org.id",
14
+ invitationId: "better_supabase.invitation.id",
15
+ notificationType: "better_supabase.notification.type",
16
+ webhookEndpointId: "better_supabase.webhook.endpoint_id"
17
+ };
18
+ /** The `KIT_ATTRIBUTES` an event carries. */
19
+ function kitEventAttributes(event) {
20
+ const attributes = { [KIT_ATTRIBUTES.event]: event.type };
21
+ if (event.tenant) attributes[KIT_ATTRIBUTES.tenant] = event.tenant;
22
+ if (event.actorId) attributes[KIT_ATTRIBUTES.actor] = event.actorId;
23
+ const data = Object.fromEntries(Object.entries(event.data));
24
+ const add = (key, value) => {
25
+ if (typeof value === "string" && value.length > 0) attributes[key] = value;
26
+ };
27
+ add(KIT_ATTRIBUTES.supportSessionId, data["sessionId"]);
28
+ add(KIT_ATTRIBUTES.orgId, data["organizationId"]);
29
+ add(KIT_ATTRIBUTES.invitationId, data["invitationId"]);
30
+ add(KIT_ATTRIBUTES.notificationType, data["type"]);
31
+ add(KIT_ATTRIBUTES.webhookEndpointId, data["endpointId"]);
32
+ return attributes;
33
+ }
34
+ /**
35
+ * Emits a kit event on `events` with a copy of `data`. Does nothing without
36
+ * a `kit` handler, so kits call it unconditionally.
37
+ */
38
+ function emitKitEvent(events, type, data, meta = {}) {
39
+ if (!events.has("kit")) return;
40
+ const event = {
41
+ ...meta,
42
+ type,
43
+ data: cloneValue(data),
44
+ time: nowInstant()
45
+ };
46
+ events.emit("kit", event);
47
+ }
48
+ const matches = (pattern, type) => pattern.endsWith(".*") ? type.startsWith(pattern.slice(0, -1)) : pattern === type;
49
+ /**
50
+ * Listens to the kit events that match `pattern` (`'support.started'` or
51
+ * `'support.*'`). Returns a function that stops listening.
52
+ */
53
+ function onKitEvent(betterSupabase, pattern, handler) {
54
+ return betterSupabase.events.on("kit", (event) => {
55
+ if (!matches(pattern, event.type)) return;
56
+ handler(event);
57
+ });
58
+ }
59
+ //#endregion
60
+ export { onKitEvent as i, emitKitEvent as n, kitEventAttributes as r, KIT_ATTRIBUTES as t };
@@ -0,0 +1,33 @@
1
+ import { t as SqlClient } from "./executor-Cghdstwu.js";
2
+ //#region src/core/kit-transport.d.ts
3
+ /**
4
+ * Calls a kit function as the current user and resolves with its result.
5
+ * Rejects with the raw database error (a `pg` error or a PostgREST error
6
+ * object), which the kit maps to a `DbError`.
7
+ */
8
+ interface KitTransport {
9
+ call(schema: string, fn: string, args: Readonly<Record<string, unknown>>): Promise<unknown>;
10
+ }
11
+ /**
12
+ * Calls the functions over Postgres, as the user `client` runs as:
13
+ * `sqlTransport(postgres.asUser(ctx.auth.claims))`. Works for any schema.
14
+ */
15
+ declare function sqlTransport(client: SqlClient): KitTransport;
16
+ interface RpcResponse {
17
+ readonly data: unknown;
18
+ readonly error: unknown;
19
+ }
20
+ /** The part of a supabase-js client `rpcTransport` uses. */
21
+ interface RpcClient {
22
+ schema(name: string): {
23
+ rpc(fn: string, args: Record<string, unknown>): PromiseLike<RpcResponse>;
24
+ };
25
+ }
26
+ /**
27
+ * Calls the functions over the Data API with the user's session. The kit
28
+ * schema must be exposed (`[api] schemas` in `config.toml`), or the modules
29
+ * installed in `public` (`kits.<module>.schema`).
30
+ */
31
+ declare function rpcTransport(client: RpcClient): KitTransport;
32
+ //#endregion
33
+ export { sqlTransport as i, RpcClient as n, rpcTransport as r, KitTransport as t };
@@ -0,0 +1,55 @@
1
+ import { r as sqlIdent } from "./template-TxrGI5jR.js";
2
+ //#region src/core/kit-transport.ts
3
+ /**
4
+ * Calls the functions over Postgres, as the user `client` runs as:
5
+ * `sqlTransport(postgres.asUser(ctx.auth.claims))`. Works for any schema.
6
+ */
7
+ function sqlTransport(client) {
8
+ return { async call(schema, fn, args) {
9
+ const entries = Object.entries(args).filter(([, value]) => value !== void 0);
10
+ const params = entries.map(([, value]) => value !== null && typeof value === "object" && !Array.isArray(value) ? JSON.stringify(value) : value);
11
+ const list = entries.map(([name], index) => `${sqlIdent(name)} => $${String(index + 1)}`).join(", ");
12
+ const [row] = await client.queryRaw(`select ${sqlIdent(schema)}.${sqlIdent(fn)}(${list}) as value`, params);
13
+ return row?.value ?? null;
14
+ } };
15
+ }
16
+ /**
17
+ * Calls the functions over the Data API with the user's session. The kit
18
+ * schema must be exposed (`[api] schemas` in `config.toml`), or the modules
19
+ * installed in `public` (`kits.<module>.schema`).
20
+ */
21
+ function rpcTransport(client) {
22
+ return { async call(schema, fn, args) {
23
+ const defined = Object.fromEntries(Object.entries(args).filter(([, value]) => value !== void 0));
24
+ const { data, error } = await client.schema(schema).rpc(fn, defined);
25
+ if (error) {
26
+ const message = typeof error === "object" && "message" in error ? String(error.message) : String(error);
27
+ throw Object.assign(new Error(message), error);
28
+ }
29
+ return data;
30
+ } };
31
+ }
32
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
33
+ /** The Postgres or PostgREST error a transport rejected with, if it is one. */
34
+ function rawError(cause) {
35
+ if (!isRecord(cause)) return void 0;
36
+ const field = (key) => {
37
+ const value = cause[key];
38
+ return typeof value === "string" ? value : void 0;
39
+ };
40
+ if (field("code") === void 0) return void 0;
41
+ const raw = {};
42
+ for (const key of [
43
+ "message",
44
+ "code",
45
+ "details",
46
+ "hint",
47
+ "constraint"
48
+ ]) {
49
+ const value = field(key);
50
+ if (value !== void 0) raw[key] = value;
51
+ }
52
+ return raw;
53
+ }
54
+ //#endregion
55
+ export { rpcTransport as n, sqlTransport as r, rawError as t };
@@ -1,4 +1,4 @@
1
- import { t as DEFAULT_CLAIMS } from "./claims-CoTobNC-.js";
1
+ import { t as DEFAULT_CLAIMS } from "./claims-BAf6c7is.js";
2
2
  //#region src/config/config.ts
3
3
  /** The JSON Schema document for a `json[...].schema` entry. */
4
4
  function resolveJsonSchema(source) {
@@ -9,14 +9,16 @@ function resolveJsonSchema(source) {
9
9
  return jsonSchema.input({ target: "draft-2020-12" });
10
10
  }
11
11
  function entitlementsOf(config = {}) {
12
- const customer = config.customer ?? "organizations.stripe_customer_id";
13
- const dot = customer.lastIndexOf(".");
14
- if (dot <= 0) throw new TypeError(`entitlements.customer must be "table.column", got "${customer}"`);
12
+ const customer = config.customer;
13
+ const dot = customer?.lastIndexOf(".") ?? -1;
14
+ if (customer !== void 0 && dot <= 0) throw new TypeError(`entitlements.customer must be "table.column", got "${customer}"`);
15
15
  return {
16
- table: customer.slice(0, dot),
17
- column: customer.slice(dot + 1),
16
+ ...customer === void 0 ? {} : {
17
+ table: customer.slice(0, dot),
18
+ column: customer.slice(dot + 1)
19
+ },
18
20
  key: config.key ?? "id",
19
- permdock: config.permdock === false ? false : { scope: config.permdock?.scope ?? "organization" }
21
+ permdock: config.permdock === false ? false : config.permdock?.scope === void 0 ? {} : { scope: config.permdock.scope }
20
22
  };
21
23
  }
22
24
  function vectorSearchOf(config = {}) {
@@ -86,7 +88,8 @@ function resolveConfig(config, root) {
86
88
  tenant: pick(config.plugins?.tenant, { column: "organization_id" }),
87
89
  actor: pick(config.plugins?.actor, {
88
90
  createdBy: "created_by",
89
- updatedBy: "updated_by"
91
+ updatedBy: "updated_by",
92
+ impersonatedBy: "impersonated_by"
90
93
  })
91
94
  },
92
95
  claims: {
@@ -95,7 +98,10 @@ function resolveConfig(config, root) {
95
98
  },
96
99
  buckets: config.buckets ?? {},
97
100
  topics: config.topics ?? {},
98
- realtime: { tables: config.realtime?.tables ?? [] },
101
+ realtime: {
102
+ tables: config.realtime?.tables ?? [],
103
+ global: config.realtime?.global ?? []
104
+ },
99
105
  entitlements: entitlementsOf(config.entitlements),
100
106
  permdock: {
101
107
  manifest: config.permdock?.manifest ?? "permdock.manifest.json",
@@ -108,6 +114,7 @@ function resolveConfig(config, root) {
108
114
  testsDir: config.sql?.testsDir ?? "supabase/tests",
109
115
  kit: config.sql?.kit ?? []
110
116
  },
117
+ kits: config.kits ?? {},
111
118
  seed: {
112
119
  entry: config.seed?.entry ?? "supabase/seed.ts",
113
120
  output: config.seed?.output ?? "supabase/seeds/000_better_supabase.sql"
@@ -126,4 +133,25 @@ function resolveConfig(config, root) {
126
133
  };
127
134
  }
128
135
  //#endregion
129
- export { resolveJsonSchema as i, defineConfig as n, resolveConfig as r, CONFIG_SCHEMA_URL as t };
136
+ //#region src/config/kits.ts
137
+ /** URL tenancy: the tenant comes from the request (a slug), not a stored setting. */
138
+ const DEFAULT_ACTIVE_TENANT = "resolver";
139
+ const KIT_SCHEMA = "better_supabase";
140
+ function resolveKitModule(config = {}) {
141
+ return {
142
+ mode: config.mode ?? "managed",
143
+ schema: config.schema ?? KIT_SCHEMA,
144
+ tables: config.tables ?? {},
145
+ columns: config.columns ?? {},
146
+ ...config.idType === void 0 ? {} : { idType: config.idType },
147
+ permissions: config.permissions ?? {},
148
+ options: config.options ?? {},
149
+ hooks: {
150
+ schema: config.hooks?.schema ?? "public",
151
+ functions: config.hooks?.functions ?? {}
152
+ },
153
+ events: config.events ?? true
154
+ };
155
+ }
156
+ //#endregion
157
+ export { resolveConfig as a, defineConfig as i, resolveKitModule as n, resolveJsonSchema as o, CONFIG_SCHEMA_URL as r, DEFAULT_ACTIVE_TENANT as t };
@@ -1,2 +1,2 @@
1
- import { a as ListPage, c as ListParsers, d as ListQueryInput, f as ListSearchParams, g as defineListQuery, h as UNSET, i as ListExtra, l as ListQuery, m as ParserSpec, n as FacetInfo, o as ListPagination, p as OpenApiParameter, r as ListDefinition, s as ListParseResult, t as FacetCounts, u as ListQueryConfig } from "../index-CADlNHK-.js";
1
+ import { a as ListPage, c as ListParsers, d as ListQueryInput, f as ListSearchParams, g as defineListQuery, h as UNSET, i as ListExtra, l as ListQuery, m as ParserSpec, n as FacetInfo, o as ListPagination, p as OpenApiParameter, r as ListDefinition, s as ListParseResult, t as FacetCounts, u as ListQueryConfig } from "../index-fag8VJZ2.js";
2
2
  export { type FacetCounts, type FacetInfo, type ListDefinition, type ListExtra, type ListPage, type ListPagination, type ListParseResult, type ListParsers, type ListQuery, type ListQueryConfig, type ListQueryInput, type ListSearchParams, type OpenApiParameter, type ParserSpec, UNSET, defineListQuery };
@@ -35,7 +35,7 @@ function toInt(raw) {
35
35
  * OpenAPI parameters and a JSON Schema derived from the same definition.
36
36
  *
37
37
  * ```ts
38
- * export const customerList = defineListQuery(sb, 'customers', {
38
+ * export const customerList = defineListQuery(betterSupabase, 'customers', {
39
39
  * search: ['name', 'kvk'],
40
40
  * facets: { status: 'status' },
41
41
  * sorts: { name: { name: 'asc' }, newest: [{ createdAt: 'desc' }, { id: 'asc' }] },
@@ -43,8 +43,8 @@ function toInt(raw) {
43
43
  * });
44
44
  * ```
45
45
  */
46
- function defineListQuery(sb, table, config) {
47
- const meta = sb.meta.tables[table];
46
+ function defineListQuery(betterSupabase, table, config) {
47
+ const meta = betterSupabase.meta.tables[table];
48
48
  if (!meta) throw new TypeError(`defineListQuery: unknown table "${table}"`);
49
49
  const pageSize = config.pageSize ?? 50;
50
50
  const maxPageSize = config.maxPageSize ?? 200;
@@ -64,6 +64,10 @@ function join(client, topic, listener) {
64
64
  left = true;
65
65
  current.listeners.delete(listener);
66
66
  if (current.listeners.size > 0) return;
67
+ await new Promise((resolve) => {
68
+ setTimeout(resolve, 0);
69
+ });
70
+ if (current.listeners.size > 0 || byTopic.get(topic) !== current) return;
67
71
  byTopic.delete(topic);
68
72
  await client.removeChannel(current.channel);
69
73
  }
@@ -76,13 +80,13 @@ function join(client, topic, listener) {
76
80
  * table, so any number of them can run at once.
77
81
  *
78
82
  * ```ts
79
- * using live = liveQuery(sb, supabase, spec, { onChange: () => refetch() });
83
+ * using live = liveQuery(betterSupabase, supabase, spec, { onChange: () => refetch() });
80
84
  * ```
81
85
  */
82
- function liveQuery(sb, client, spec, options) {
83
- const touched = Array.isArray(spec) ? spec : sb.tablesOf(spec);
84
- const tables = touched.filter((table) => sb.meta.realtime?.[table]);
85
- const unwatched = touched.filter((table) => !sb.meta.realtime?.[table]);
86
+ function liveQuery(betterSupabase, client, spec, options) {
87
+ const touched = Array.isArray(spec) ? spec : betterSupabase.tablesOf(spec);
88
+ const tables = touched.filter((table) => betterSupabase.meta.realtime?.[table]);
89
+ const unwatched = touched.filter((table) => !betterSupabase.meta.realtime?.[table]);
86
90
  const debounceMs = options.debounceMs ?? 100;
87
91
  const changed = /* @__PURE__ */ new Set();
88
92
  let timer;
@@ -94,7 +98,7 @@ function liveQuery(sb, client, spec, options) {
94
98
  changed.clear();
95
99
  options.onChange(batch);
96
100
  };
97
- const memberships = tables.map((table) => join(client, liveTopic(sb.meta, table, options.tenant), () => {
101
+ const memberships = tables.map((table) => join(client, liveTopic(betterSupabase.meta, table, options.tenant), () => {
98
102
  changed.add(table);
99
103
  if (timer !== void 0) clearTimeout(timer);
100
104
  timer = setTimeout(flush, debounceMs);
@@ -130,12 +134,12 @@ function liveQuery(sb, client, spec, options) {
130
134
  * Responses that arrive out of order are dropped.
131
135
  *
132
136
  * ```ts
133
- * using live = liveCount(sb, supabase, db, sb.spec.notes.count(), {
137
+ * using live = liveCount(betterSupabase, supabase, db, betterSupabase.spec.notes.count(), {
134
138
  * onCount: (count) => render(count),
135
139
  * });
136
140
  * ```
137
141
  */
138
- function liveCount(sb, client, db, spec, options) {
142
+ function liveCount(betterSupabase, client, db, spec, options) {
139
143
  let latest = 0;
140
144
  let closed = false;
141
145
  const refetch = () => {
@@ -146,7 +150,7 @@ function liveCount(sb, client, db, spec, options) {
146
150
  else options.onError?.(result.error);
147
151
  });
148
152
  };
149
- const live = liveQuery(sb, client, spec, {
153
+ const live = liveQuery(betterSupabase, client, spec, {
150
154
  onChange: refetch,
151
155
  ...options.tenant === void 0 ? {} : { tenant: options.tenant },
152
156
  ...options.debounceMs === void 0 ? {} : { debounceMs: options.debounceMs },
@@ -1,7 +1,7 @@
1
- import { A as SchemaMeta, N as TableKey, O as Row, n as AnyModels, t as AnyFunctions, x as PermdockTopicPolicy } from "./types-DONv9SHN.js";
2
- import { b as ValidationIssue, o as Result, p as DbError, t as AsyncResult } from "./result-c2OYgCV2.js";
3
- import { Z as QuerySpec, t as BetterSupabase } from "./define-DUbydmJa.js";
4
- import { t as PermdockCatalog } from "./permdock-sql-DImJULY-.js";
1
+ import { P as TableKey, S as PermdockTopicPolicy, j as SchemaMeta, k as Row, n as AnyFunctions, r as AnyModels } from "./types-Dnf_wRDz.js";
2
+ import { _ as DbError, a as AsyncResult, u as Result, w as ValidationIssue } from "./executor-BunPOO-k.js";
3
+ import { Z as QuerySpec, t as BetterSupabase } from "./define-Bp_BZa5h.js";
4
+ import { n as PermdockCatalog } from "./permdock-sql-C2Inc6Xc.js";
5
5
  import { n as TemplateValues, t as TemplateParams } from "./template-mbvlysOo.js";
6
6
  import { RealtimeChannel, SupabaseClient } from "@supabase/supabase-js";
7
7
  import { StandardSchemaV1 } from "@standard-schema/spec";
@@ -41,7 +41,8 @@ interface TopicOptions<E extends EventSchemas> {
41
41
  readonly permdock?: PermdockTopicPolicy;
42
42
  /**
43
43
  * PermDock's `permissions.catalog.json`. With it, a `permdock` policy
44
- * naming a permission with `rowConditions: true` throws.
44
+ * naming a permission without `rowConditions: false` (including one the
45
+ * catalog doesn't list) throws.
45
46
  */
46
47
  readonly catalog?: PermdockCatalog;
47
48
  /** Let clients broadcast on the topic, not only receive. Defaults to `false`. */
@@ -78,7 +79,7 @@ interface Subscription extends Disposable, AsyncDisposable {
78
79
  readonly ready: Promise<void>;
79
80
  unsubscribe(): Promise<void>;
80
81
  }
81
- type RealtimeClient = Pick<SupabaseClient, "channel" | "removeChannel" | "realtime">;
82
+ type RealtimeClient = Pick<SupabaseClient, "channel" | "getChannels" | "removeChannel" | "realtime">;
82
83
  interface TriggerOptions<M extends AnyModels, T extends keyof M, P extends string> {
83
84
  /** Column that fills each placeholder. */
84
85
  readonly values: { readonly [K in TemplateParams<P>]: Extract<keyof Row<M, T>, string>; };
@@ -106,7 +107,7 @@ interface Topic<P extends string, E extends EventSchemas> {
106
107
  /** `realtime.messages` policies for this topic. Idempotent. */
107
108
  sql(): string;
108
109
  /** A trigger that broadcasts row changes (`realtime.broadcast_changes`) to this topic. */
109
- triggerSql<M extends AnyModels, D, F extends AnyFunctions, X, T extends TableKey<M>>(sb: BetterSupabase<M, D, F, X>, table: T, options: TriggerOptions<M, T, P>): string;
110
+ triggerSql<M extends AnyModels, D, F extends AnyFunctions, X, T extends TableKey<M>>(betterSupabase: BetterSupabase<M, D, F, X>, table: T, options: TriggerOptions<M, T, P>): string;
110
111
  subscribe(client: RealtimeClient, values: TemplateValues<P>, handlers: TopicHandlers<E>, options?: SubscribeOptions): Subscription;
111
112
  /** Sends over HTTP, without joining the channel. */
112
113
  send<K extends TopicEvent<E>>(client: RealtimeClient, values: TemplateValues<P>, event: K, payload: TopicPayload<E, K>): AsyncResult<void>;
@@ -127,10 +128,10 @@ declare function defineTopic<const P extends string, const E extends EventSchema
127
128
  * Reads a `realtime.broadcast_changes` message as an app-cased row change.
128
129
  * Returns `null` for messages about other tables.
129
130
  */
130
- declare function rowChange<M extends AnyModels, D, F extends AnyFunctions, X, T extends TableKey<M>>(sb: BetterSupabase<M, D, F, X>, table: T, message: TopicMessage): RowChange<Row<M, T>> | null;
131
+ declare function rowChange<M extends AnyModels, D, F extends AnyFunctions, X, T extends TableKey<M>>(betterSupabase: BetterSupabase<M, D, F, X>, table: T, message: TopicMessage): RowChange<Row<M, T>> | null;
131
132
  //#endregion
132
133
  //#region src/realtime/live.d.ts
133
- /** What `liveQuery` needs from `sb`: metadata and the tables a spec reads. */
134
+ /** What `liveQuery` needs from `betterSupabase`: metadata and the tables a spec reads. */
134
135
  interface LiveSource {
135
136
  readonly meta: SchemaMeta;
136
137
  tablesOf(spec: QuerySpec): string[];
@@ -154,7 +155,7 @@ interface LiveSubscription extends Disposable, AsyncDisposable {
154
155
  }
155
156
  /**
156
157
  * A count from the server plus the spec to keep it live on the client:
157
- * `next.liveCount(spec)` makes one, `useLiveCount(seed)` reads it. `count`
158
+ * `bs.liveCount(spec)` makes one, `useLiveCount(seed)` reads it. `count`
158
159
  * is `null` when the server read failed; the client then fetches it.
159
160
  */
160
161
  interface LiveCountSeed<T extends string = string> {
@@ -170,11 +171,11 @@ declare function liveTopic(meta: SchemaMeta, table: string, tenant?: string): st
170
171
  * table, so any number of them can run at once.
171
172
  *
172
173
  * ```ts
173
- * using live = liveQuery(sb, supabase, spec, { onChange: () => refetch() });
174
+ * using live = liveQuery(betterSupabase, supabase, spec, { onChange: () => refetch() });
174
175
  * ```
175
176
  */
176
- declare function liveQuery(sb: LiveSource, client: RealtimeClient, spec: QuerySpec | readonly string[], options: LiveQueryOptions): LiveSubscription;
177
- /** What `liveCount` runs the spec with: a `db` from `sb.connect()` or `createBrowser()`. */
177
+ declare function liveQuery(betterSupabase: LiveSource, client: RealtimeClient, spec: QuerySpec | readonly string[], options: LiveQueryOptions): LiveSubscription;
178
+ /** What `liveCount` runs the spec with: a `db` from `betterSupabase.connect()` or `createClient()`. */
178
179
  interface CountRunner {
179
180
  $run(spec: QuerySpec): PromiseLike<Result<unknown>>;
180
181
  }
@@ -191,11 +192,11 @@ interface LiveCountOptions extends Omit<LiveQueryOptions, "onChange"> {
191
192
  * Responses that arrive out of order are dropped.
192
193
  *
193
194
  * ```ts
194
- * using live = liveCount(sb, supabase, db, sb.spec.notes.count(), {
195
+ * using live = liveCount(betterSupabase, supabase, db, betterSupabase.spec.notes.count(), {
195
196
  * onCount: (count) => render(count),
196
197
  * });
197
198
  * ```
198
199
  */
199
- declare function liveCount(sb: LiveSource, client: RealtimeClient, db: CountRunner, spec: QuerySpec<string, "count", number>, options: LiveCountOptions): LiveSubscription;
200
+ declare function liveCount(betterSupabase: LiveSource, client: RealtimeClient, db: CountRunner, spec: QuerySpec<string, "count", number>, options: LiveCountOptions): LiveSubscription;
200
201
  //#endregion
201
202
  export { defineTopic as C, TriggerOptions as S, TopicEvent as _, LiveSource as a, TopicOptions as b, liveQuery as c, RealtimeClient as d, RowChange as f, Topic as g, SubscriptionStatus as h, LiveQueryOptions as i, liveTopic as l, Subscription as m, LiveCountOptions as n, LiveSubscription as o, SubscribeOptions as p, LiveCountSeed as r, liveCount as s, CountRunner as t, EventSchemas as u, TopicHandlers as v, rowChange as w, TopicPayload as x, TopicMessage as y };
@@ -0,0 +1,18 @@
1
+ //#region src/schema/lookup.ts
2
+ const lookups = /* @__PURE__ */ new WeakMap();
3
+ /** The cached lookup for a table; metadata is immutable after `defineSchema`. */
4
+ function lookupOf(table) {
5
+ let lookup = lookups.get(table);
6
+ if (!lookup) {
7
+ const entries = Object.entries(table.columns);
8
+ lookup = {
9
+ entries,
10
+ byDb: new Map(entries.map((entry) => [entry[1].db, entry])),
11
+ keys: [table.primaryKey, ...Object.values(table.uniqueKeys)]
12
+ };
13
+ lookups.set(table, lookup);
14
+ }
15
+ return lookup;
16
+ }
17
+ //#endregion
18
+ export { lookupOf as t };
@@ -1,7 +1,8 @@
1
- import { N as TableKey, n as AnyModels, t as AnyFunctions } from "../types-DONv9SHN.js";
2
- import { t as BetterSupabase } from "../define-DUbydmJa.js";
3
- import { d as ServerContext, f as ServerOptions, l as BetterServer, n as GuardOptions } from "../respond-DAkPIdTH.js";
4
- import { a as ResourceRouteOptions } from "../resource-CwVgiI5q.js";
1
+ import { P as TableKey, n as AnyFunctions, r as AnyModels } from "../types-Dnf_wRDz.js";
2
+ import { t as BetterSupabase } from "../define-Bp_BZa5h.js";
3
+ import { f as ServerContext, n as GuardOptions, p as ServerOptions, u as BetterServer } from "../respond-D2OQl62-.js";
4
+ import { i as ResourceOperation } from "../index-C7f8cOBM.js";
5
+ import { a as ResourceRouteOptions } from "../resource-D8iqWMr-.js";
5
6
  import { StandardSchemaV1 } from "@standard-schema/spec";
6
7
  //#region src/mcp/mcp.d.ts
7
8
  export declare const MCP_PROTOCOL_VERSION: string;
@@ -23,22 +24,22 @@ interface ToolInfo {
23
24
  readonly outputSchema?: Json;
24
25
  readonly annotations?: ToolAnnotations;
25
26
  }
26
- interface ToolContext<M extends AnyModels, F extends AnyFunctions, E> extends ServerContext<M, F, E> {
27
+ interface ToolContext<M extends AnyModels, F extends AnyFunctions, E, C = unknown, P = unknown> extends ServerContext<M, F, E, C, P> {
27
28
  readonly request: Request;
28
29
  readonly signal: AbortSignal;
29
30
  }
30
- interface McpTool<M extends AnyModels, F extends AnyFunctions, E> {
31
+ interface McpTool<M extends AnyModels, F extends AnyFunctions, E, C = unknown, P = unknown> {
31
32
  readonly info: ToolInfo;
32
33
  /** Validates arguments; its output is what `run` receives. */
33
34
  readonly input?: StandardSchemaV1;
34
35
  /** Opaque data for the `authorize` and `visible` hooks. Never sent to clients. */
35
36
  readonly meta?: unknown;
36
- run(args: never, ctx: ToolContext<M, F, E>): unknown;
37
+ run(args: never, ctx: ToolContext<M, F, E, C, P>): unknown;
37
38
  }
38
39
  /** What the `authorize` and `visible` hooks see of a tool. */
39
40
  interface ToolRef {
40
41
  readonly info: ToolInfo;
41
- /** The tool's `meta`, e.g. a PermDock permission. `undefined` for table tools. */
42
+ /** The tool's `meta`, e.g. a PermDock permission. For table tools, the resource's `meta` for that operation. */
42
43
  readonly meta: unknown;
43
44
  }
44
45
  /** The outcome of `authorize`. `scopes` turns a refusal into an OAuth challenge. */
@@ -50,7 +51,7 @@ type ToolDecision = {
50
51
  /** OAuth scopes the call needs, sent in an `insufficient_scope` challenge. */
51
52
  readonly scopes?: readonly string[];
52
53
  };
53
- interface ToolDefinition<M extends AnyModels, F extends AnyFunctions, E, I> {
54
+ interface ToolDefinition<M extends AnyModels, F extends AnyFunctions, E, I, C = unknown, P = unknown> {
54
55
  readonly name: string;
55
56
  readonly title?: string;
56
57
  readonly description: string;
@@ -64,10 +65,19 @@ interface ToolDefinition<M extends AnyModels, F extends AnyFunctions, E, I> {
64
65
  readonly annotations?: ToolAnnotations;
65
66
  /** Opaque data for the `authorize` and `visible` hooks. Never sent to clients. */
66
67
  readonly meta?: unknown;
67
- run(args: I, ctx: ToolContext<M, F, E>): unknown;
68
+ run(args: I, ctx: ToolContext<M, F, E, C, P>): unknown;
68
69
  }
69
- type ToolResources<M extends AnyModels> = { readonly [T in TableKey<M>]?: ResourceRouteOptions<M, T> | true; };
70
- interface McpOptions<M extends AnyModels, F extends AnyFunctions, E> extends ServerOptions, GuardOptions {
70
+ /** A table's tools: the REST resource options, plus `meta` per operation for `authorize` and `visible`. */
71
+ type ToolResourceOptions<M extends AnyModels, T extends TableKey<M>> = ResourceRouteOptions<M, T> & {
72
+ /**
73
+ * Opaque data per operation, e.g. a PermDock permission, passed to
74
+ * `authorize` and `visible` as the table tool's `meta`. Never sent to
75
+ * clients. An operation without an entry gets `undefined`.
76
+ */
77
+ readonly meta?: { readonly [K in ResourceOperation]?: unknown; };
78
+ };
79
+ type ToolResources<M extends AnyModels> = { readonly [T in TableKey<M>]?: ToolResourceOptions<M, T> | true; };
80
+ interface McpOptions<M extends AnyModels, F extends AnyFunctions, E, C = unknown, P = unknown> extends ServerOptions, Omit<GuardOptions, "scopes"> {
71
81
  readonly name: string;
72
82
  readonly version: string;
73
83
  readonly title?: string;
@@ -75,7 +85,7 @@ interface McpOptions<M extends AnyModels, F extends AnyFunctions, E> extends Ser
75
85
  readonly instructions?: string;
76
86
  /** Tables exposed as tools: `<table>_list`, `_get`, `_create`, `_update`, `_delete`. */
77
87
  readonly resources?: ToolResources<M>;
78
- readonly tools?: readonly McpTool<M, F, E>[];
88
+ readonly tools?: readonly McpTool<M, F, E, C, P>[];
79
89
  /**
80
90
  * Canonical URL of this MCP server (RFC 9728 `resource`). Defaults to the
81
91
  * request URL without query.
@@ -88,7 +98,14 @@ interface McpOptions<M extends AnyModels, F extends AnyFunctions, E> extends Ser
88
98
  * `insufficient_scope` challenges. `offline_access` is left out: refresh
89
99
  * tokens are between the client and the authorization server.
90
100
  */
101
+ readonly advertisedScopes?: readonly string[];
102
+ /** @deprecated Use `advertisedScopes`; `requiredScopes` is what `guard` enforces. */
91
103
  readonly scopes?: readonly string[];
104
+ /**
105
+ * Scopes a delegated token (an OAuth client or an `act` chain) needs to call
106
+ * the server at all. A missing one answers 403 `insufficient_scope`.
107
+ */
108
+ readonly requiredScopes?: readonly string[];
92
109
  /** Origins allowed to call the server (DNS rebinding protection). Defaults to any. */
93
110
  readonly allowedOrigins?: readonly string[];
94
111
  /**
@@ -108,25 +125,25 @@ interface McpOptions<M extends AnyModels, F extends AnyFunctions, E> extends Ser
108
125
  * validate in the repository). A refusal is a tool error; a refusal with
109
126
  * `scopes` is a 403 `insufficient_scope` challenge naming them.
110
127
  */
111
- readonly authorize?: (ctx: ToolContext<M, F, E>, tool: ToolRef, args: unknown) => ToolDecision | Promise<ToolDecision>;
128
+ readonly authorize?: (ctx: ToolContext<M, F, E, C, P>, tool: ToolRef, args: unknown) => ToolDecision | Promise<ToolDecision>;
112
129
  /**
113
130
  * Whether the caller sees a tool in `tools/list`. A hidden tool is called
114
131
  * like an unknown one. Lists are cached per token (`cacheScope: 'private'`).
115
132
  */
116
- readonly visible?: (ctx: ToolContext<M, F, E>, tool: ToolRef) => boolean | Promise<boolean>;
133
+ readonly visible?: (ctx: ToolContext<M, F, E, C, P>, tool: ToolRef) => boolean | Promise<boolean>;
117
134
  }
118
- interface BetterMcp<M extends AnyModels, F extends AnyFunctions, E> extends BetterServer<M, F, E> {
135
+ interface BetterMcp<M extends AnyModels, F extends AnyFunctions, E, C = unknown, P = unknown> extends BetterServer<M, F, E, C, P> {
119
136
  /** Everything in one handler: the MCP endpoint and `/.well-known/oauth-protected-resource`. */
120
137
  readonly fetch: (request: Request) => Promise<Response>;
121
138
  /** The Streamable HTTP endpoint (POST JSON-RPC). */
122
- readonly handler: (request: Request) => Promise<Response>;
139
+ readonly endpoint: (request: Request) => Promise<Response>;
123
140
  /** RFC 9728 protected resource metadata. */
124
141
  metadata(request: Request): Response;
125
142
  readonly tools: readonly ToolInfo[];
126
143
  /** Adds a tool typed against this app's repositories. */
127
- tool<I = Record<string, unknown>>(definition: ToolDefinition<M, F, E, I>): this;
144
+ tool<I = Record<string, unknown>>(definition: ToolDefinition<M, F, E, I, C, P>): this;
128
145
  /** Calls a tool directly, e.g. from tests or another transport. */
129
- call(name: string, args: unknown, ctx: ToolContext<M, F, E>): Promise<ToolResult>;
146
+ call(name: string, args: unknown, ctx: ToolContext<M, F, E, C, P>): Promise<ToolResult>;
130
147
  }
131
148
  interface ToolResult {
132
149
  readonly content: readonly {
@@ -137,13 +154,13 @@ interface ToolResult {
137
154
  readonly isError?: boolean;
138
155
  }
139
156
  /** A custom tool. Pass it in `createMcp({ tools: [...] })`. */
140
- export declare function defineTool<M extends AnyModels, F extends AnyFunctions, E, I = Record<string, unknown>>(definition: ToolDefinition<M, F, E, I>): McpTool<M, F, E>;
157
+ export declare function defineTool<M extends AnyModels, F extends AnyFunctions, E, I = Record<string, unknown>, C = unknown, P = unknown>(definition: ToolDefinition<M, F, E, I, C, P>): McpTool<M, F, E, C, P>;
141
158
  /**
142
159
  * An MCP server (Streamable HTTP, stateless) whose tools run as the caller:
143
160
  * the Bearer token from the MCP client is verified and every query goes
144
161
  * through RLS. Answers 401 with RFC 9728 metadata so clients can sign in with
145
162
  * Supabase Auth's OAuth server.
146
163
  */
147
- export declare function createMcp<M extends AnyModels, D, F extends AnyFunctions, E>(sb: BetterSupabase<M, D, F, E>, options: McpOptions<M, F, E>): BetterMcp<M, F, E>;
164
+ export declare function createMcp<M extends AnyModels, D, F extends AnyFunctions, E, C = unknown, P = unknown>(betterSupabase: BetterSupabase<M, D, F, E, C, P>, options: McpOptions<M, F, E, C, P>): BetterMcp<M, F, E, C, P>;
148
165
  //#endregion
149
- export type { BetterMcp, GuardOptions, McpOptions, McpTool, ResourceRouteOptions, ToolAnnotations, ToolContext, ToolDecision, ToolDefinition, ToolInfo, ToolRef, ToolResources, ToolResult };
166
+ export type { BetterMcp, GuardOptions, McpOptions, McpTool, ResourceRouteOptions, ToolAnnotations, ToolContext, ToolDecision, ToolDefinition, ToolInfo, ToolRef, ToolResourceOptions, ToolResources, ToolResult };