better-supabase 0.0.0 → 0.2.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 (157) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +88 -1
  3. package/bin/better-supabase.js +4 -0
  4. package/dist/casing-da0uRTqt.js +14 -0
  5. package/dist/claims-CoTobNC-.js +25 -0
  6. package/dist/cli/bin.d.ts +1 -0
  7. package/dist/cli/bin.js +15 -0
  8. package/dist/cli/index.d.ts +226 -0
  9. package/dist/cli/index.js +3 -0
  10. package/dist/cli-DrSgDOQC.js +6646 -0
  11. package/dist/client/index.d.ts +2 -0
  12. package/dist/client/index.js +112 -0
  13. package/dist/compiler-CZ9nIuqy.d.ts +14 -0
  14. package/dist/config/index.d.ts +3 -0
  15. package/dist/config/index.js +3 -0
  16. package/dist/config-ClDXmVtY.js +277 -0
  17. package/dist/define-BXMYD9AY.js +1071 -0
  18. package/dist/define-e3zI4_4g.d.ts +679 -0
  19. package/dist/edge/index.d.ts +43 -0
  20. package/dist/edge/index.js +103 -0
  21. package/dist/entitlements-CTEBm7IB.js +20 -0
  22. package/dist/entitlements-Dng-ctuT.d.ts +42 -0
  23. package/dist/env/index.d.ts +2 -0
  24. package/dist/env/index.js +2 -0
  25. package/dist/env-BEToab0I.js +204 -0
  26. package/dist/errors-Bl3rkOzo.js +196 -0
  27. package/dist/events/index.d.ts +2 -0
  28. package/dist/events/index.js +2 -0
  29. package/dist/events-BuzTgn2D.js +135 -0
  30. package/dist/events-DOt3nSO-.d.ts +84 -0
  31. package/dist/executor-BYXQ9AGn.d.ts +25 -0
  32. package/dist/executor-CGdNQ27g.d.ts +195 -0
  33. package/dist/executor-DeppvirK.js +89 -0
  34. package/dist/guards-BnzDvFrz.js +10 -0
  35. package/dist/hono/index.d.ts +45 -0
  36. package/dist/hono/index.js +70 -0
  37. package/dist/index-Bw1bwtC0.d.ts +144 -0
  38. package/dist/index-CBDufH1M.d.ts +120 -0
  39. package/dist/index-CG69s15v.d.ts +38 -0
  40. package/dist/index-Ciu5Xlml.d.ts +74 -0
  41. package/dist/index-D4v7sncu.d.ts +62 -0
  42. package/dist/index-DOsjFvMf.d.ts +618 -0
  43. package/dist/index-DbxFr7XG.d.ts +76 -0
  44. package/dist/index-DuP14VHo.d.ts +216 -0
  45. package/dist/index-Dy6kuF0P.d.ts +71 -0
  46. package/dist/index-K-3WM2Hp.d.ts +117 -0
  47. package/dist/index.d.ts +110 -0
  48. package/dist/index.js +43 -0
  49. package/dist/invalidate-DbxuL2jL.js +18 -0
  50. package/dist/jobs/index.d.ts +173 -0
  51. package/dist/jobs/index.js +478 -0
  52. package/dist/json-schema-BpYqkCKf.js +182 -0
  53. package/dist/lint/index.d.ts +75 -0
  54. package/dist/lint/index.js +167 -0
  55. package/dist/list/index.d.ts +2 -0
  56. package/dist/list/index.js +410 -0
  57. package/dist/live-D8rV1Ldw.js +168 -0
  58. package/dist/live-DJyZMD6w.d.ts +191 -0
  59. package/dist/logger-glM28NVN.js +21 -0
  60. package/dist/mcp/index.d.ts +142 -0
  61. package/dist/mcp/index.js +463 -0
  62. package/dist/mfa-CpLfJSde.js +541 -0
  63. package/dist/mfa-TVia554c.d.ts +44 -0
  64. package/dist/next/image/index.d.ts +34 -0
  65. package/dist/next/image/index.js +40 -0
  66. package/dist/next/index.d.ts +210 -0
  67. package/dist/next/index.js +427 -0
  68. package/dist/openapi/index.d.ts +2 -0
  69. package/dist/openapi/index.js +332 -0
  70. package/dist/orpc/index.d.ts +46 -0
  71. package/dist/orpc/index.js +50 -0
  72. package/dist/otel/index.d.ts +40 -0
  73. package/dist/otel/index.js +168 -0
  74. package/dist/path-Cv__-OTj.d.ts +17 -0
  75. package/dist/permdock-sql-CghlHPuH.js +117 -0
  76. package/dist/plugin-BqR6wKMB.js +23 -0
  77. package/dist/plugin-C4TR9J0Z.d.ts +118 -0
  78. package/dist/plugins/actor/index.d.ts +13 -0
  79. package/dist/plugins/actor/index.js +43 -0
  80. package/dist/plugins/rules/index.d.ts +89 -0
  81. package/dist/plugins/rules/index.js +167 -0
  82. package/dist/plugins/soft-delete/index.d.ts +38 -0
  83. package/dist/plugins/soft-delete/index.js +69 -0
  84. package/dist/plugins/tenant/index.d.ts +49 -0
  85. package/dist/plugins/tenant/index.js +75 -0
  86. package/dist/plugins/timestamps/index.d.ts +9 -0
  87. package/dist/plugins/timestamps/index.js +37 -0
  88. package/dist/plugins/validation/index.d.ts +24 -0
  89. package/dist/plugins/validation/index.js +59 -0
  90. package/dist/pool-R2m_DkXi.d.ts +37 -0
  91. package/dist/postgres/index.d.ts +28 -0
  92. package/dist/postgres/index.js +79 -0
  93. package/dist/postgrest-cAEQ_IQN.d.ts +43 -0
  94. package/dist/problem-BTkTtRV9.js +97 -0
  95. package/dist/problem-DlgB0lYl.d.ts +47 -0
  96. package/dist/query/index.d.ts +2 -0
  97. package/dist/query/index.js +3 -0
  98. package/dist/query-ChvDbsDE.js +186 -0
  99. package/dist/react/index.d.ts +7 -0
  100. package/dist/react/index.js +5 -0
  101. package/dist/react/server.d.ts +13 -0
  102. package/dist/react/server.js +25 -0
  103. package/dist/react/session.d.ts +2 -0
  104. package/dist/react/session.js +3 -0
  105. package/dist/react-C83BdBGt.js +242 -0
  106. package/dist/read-set-4lI3W_gZ.js +216 -0
  107. package/dist/realtime/index.d.ts +4 -0
  108. package/dist/realtime/index.js +268 -0
  109. package/dist/resolve-CjHILk2w.d.ts +170 -0
  110. package/dist/resource-BHEWmQJK.js +175 -0
  111. package/dist/resource-CgZa3YG_.d.ts +65 -0
  112. package/dist/respond-DMcbQwfN.js +383 -0
  113. package/dist/respond-Dne1ARSa.d.ts +169 -0
  114. package/dist/result-CTkCZ6JA.d.ts +198 -0
  115. package/dist/result-DWXatkd6.js +103 -0
  116. package/dist/scope-CDHcS7dE.js +90 -0
  117. package/dist/seed-CzecODcJ.js +101 -0
  118. package/dist/server/index.d.ts +42 -0
  119. package/dist/server/index.js +68 -0
  120. package/dist/session-C3LxnQ0e.d.ts +22 -0
  121. package/dist/session-CqEwzVup.js +20 -0
  122. package/dist/shared-_6-uPL7E.js +57 -0
  123. package/dist/simplify-bwJpLnaS.js +665 -0
  124. package/dist/spec-pins-C3oc-Tdp.js +20 -0
  125. package/dist/spec-pins-CqorWDnO.d.ts +20 -0
  126. package/dist/sql-DTjA1LPj.js +260 -0
  127. package/dist/ssr/index.d.ts +5 -0
  128. package/dist/ssr/index.js +4 -0
  129. package/dist/standard-CyhRzctb.d.ts +7 -0
  130. package/dist/standard-FUi3Z3-7.js +21 -0
  131. package/dist/stats-C5W-rBfR.js +566 -0
  132. package/dist/storage/index.d.ts +5 -0
  133. package/dist/storage/index.js +2 -0
  134. package/dist/storage-DuGn38fr.js +527 -0
  135. package/dist/tables-rZxRxNPW.js +59 -0
  136. package/dist/template-mbvlysOo.d.ts +6 -0
  137. package/dist/testing/index.d.ts +325 -0
  138. package/dist/testing/index.js +819 -0
  139. package/dist/types-6cYK9pbJ.js +29 -0
  140. package/dist/types-C83v5SQz.d.ts +248 -0
  141. package/dist/version-Cqq1m3UY.js +4 -0
  142. package/dist/view-D1s0wa0T.d.ts +49 -0
  143. package/dist/view-aOYizTeE.js +36 -0
  144. package/dist/webhooks/index.d.ts +152 -0
  145. package/dist/webhooks/index.js +2 -0
  146. package/dist/webhooks-DMipjhSP.js +139 -0
  147. package/package.json +250 -17
  148. package/schemas/config-v1.json +373 -0
  149. package/schemas/doctor-report-v1.json +106 -0
  150. package/schemas/snapshot-v2.json +1006 -0
  151. package/skills/better-supabase/SKILL.md +84 -0
  152. package/skills/better-supabase/references/plugins.md +65 -0
  153. package/skills/better-supabase/references/troubleshooting.md +39 -0
  154. package/skills/better-supabase-api/SKILL.md +67 -0
  155. package/skills/better-supabase-api/references/adapters.md +108 -0
  156. package/skills/better-supabase-testing/SKILL.md +66 -0
  157. package/index.js +0 -1
@@ -0,0 +1,191 @@
1
+ import { A as SchemaMeta, N as TableKey, O as Row, n as AnyModels, t as AnyFunctions, x as PermdockTopicPolicy } from "./types-C83v5SQz.js";
2
+ import { b as ValidationIssue, o as Result, p as DbError, t as AsyncResult } from "./result-CTkCZ6JA.js";
3
+ import { Z as QuerySpec, t as BetterSupabase } from "./define-e3zI4_4g.js";
4
+ import { n as TemplateValues, t as TemplateParams } from "./template-mbvlysOo.js";
5
+ import { RealtimeChannel, SupabaseClient } from "@supabase/supabase-js";
6
+ import { StandardSchemaV1 } from "@standard-schema/spec";
7
+ //#region src/realtime/index.d.ts
8
+ type EventSchemas = Readonly<Record<string, StandardSchemaV1>>;
9
+ interface TopicOptions<E extends EventSchemas> {
10
+ /** Name used for generated policies and functions. Defaults to the template's literal parts. */
11
+ readonly name?: string;
12
+ /** Payload schemas per event. Incoming and outgoing payloads are validated. */
13
+ readonly events?: E;
14
+ /** Private channels are authorized by `realtime.messages` policies. Defaults to `true`. */
15
+ readonly private?: boolean;
16
+ /**
17
+ * Tenant check for the generated policy. Defaults to `{orgId}` against
18
+ * `tenant_id` / `app_metadata.tenant_id` when the template has `{orgId}`.
19
+ */
20
+ readonly tenant?: false | {
21
+ readonly param?: string;
22
+ readonly claim?: string | readonly string[];
23
+ readonly sql?: string;
24
+ };
25
+ /** Owner check. Defaults to `{userId}` against `auth.uid()` when the template has `{userId}`. */
26
+ readonly owner?: false | {
27
+ readonly param?: string;
28
+ };
29
+ /**
30
+ * Authorize with PermDock's SQL helpers instead of the tenant claim:
31
+ * `permitted_<scope>_ids(receive)` on the scope segment (or
32
+ * `permdock_has` for `scope: 'global'`), and `send` for broadcasting.
33
+ *
34
+ * The helpers check role and scope only. Use this just for permissions
35
+ * whose grants have no row conditions beyond the scope: for a permission
36
+ * with row conditions (e.g. `authorId = principal.id`) every member of the
37
+ * scope may join. Leave those to the policies `permdock rls generate` writes.
38
+ */
39
+ readonly permdock?: PermdockTopicPolicy;
40
+ /** Let clients broadcast on the topic, not only receive. Defaults to `false`. */
41
+ readonly send?: boolean;
42
+ /** Also authorize presence. Defaults to `false`. */
43
+ readonly presence?: boolean;
44
+ }
45
+ interface TopicMessage {
46
+ readonly event: string;
47
+ readonly payload: unknown;
48
+ readonly topic: string;
49
+ }
50
+ type Output<S> = S extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<S> : unknown;
51
+ type Input<S> = S extends StandardSchemaV1 ? StandardSchemaV1.InferInput<S> : unknown;
52
+ type TopicHandlers<E extends EventSchemas> = [keyof E] extends [never] ? {
53
+ readonly [event: string]: ((payload: unknown, message: TopicMessage) => void) | undefined;
54
+ } : { readonly [K in keyof E & string]?: (payload: Output<E[K]>, message: TopicMessage) => void; } & {
55
+ readonly "*"?: (payload: unknown, message: TopicMessage) => void;
56
+ };
57
+ type TopicEvent<E extends EventSchemas> = [keyof E] extends [never] ? string : keyof E & string;
58
+ type TopicPayload<E extends EventSchemas, K extends string> = [keyof E] extends [never] ? unknown : Input<E[K]>;
59
+ type SubscriptionStatus = "joining" | "subscribed" | "closed" | "error";
60
+ interface SubscribeOptions {
61
+ /** Receive your own broadcasts. */
62
+ readonly self?: boolean;
63
+ readonly onStatus?: (status: SubscriptionStatus, error?: Error) => void;
64
+ /** Called when a payload fails its event schema. The handler is skipped. */
65
+ readonly onInvalid?: (message: TopicMessage, issues: readonly ValidationIssue[]) => void;
66
+ }
67
+ interface Subscription extends Disposable, AsyncDisposable {
68
+ readonly topic: string;
69
+ readonly channel: RealtimeChannel;
70
+ /** Resolves once joined; rejects if the join is refused or times out. */
71
+ readonly ready: Promise<void>;
72
+ unsubscribe(): Promise<void>;
73
+ }
74
+ type RealtimeClient = Pick<SupabaseClient, "channel" | "removeChannel" | "realtime">;
75
+ interface TriggerOptions<M extends AnyModels, T extends keyof M, P extends string> {
76
+ /** Column that fills each placeholder. */
77
+ readonly values: { readonly [K in TemplateParams<P>]: Extract<keyof Row<M, T>, string>; };
78
+ /** Defaults to all three. */
79
+ readonly events?: readonly ("insert" | "update" | "delete")[];
80
+ /** Schema for the generated function. Defaults to `public`. */
81
+ readonly functionSchema?: string;
82
+ }
83
+ interface RowChange<R> {
84
+ readonly operation: "INSERT" | "UPDATE" | "DELETE";
85
+ readonly table: string;
86
+ readonly record: R | null;
87
+ readonly oldRecord: Partial<R> | null;
88
+ }
89
+ interface Topic<P extends string, E extends EventSchemas> {
90
+ readonly template: P;
91
+ readonly name: string;
92
+ readonly params: readonly TemplateParams<P>[];
93
+ readonly private: boolean;
94
+ topic(values: TemplateValues<P>): string;
95
+ match(topic: string): TemplateValues<P> | null;
96
+ /** `realtime.messages` policies for this topic. Idempotent. */
97
+ sql(): string;
98
+ /** A trigger that broadcasts row changes (`realtime.broadcast_changes`) to this topic. */
99
+ 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;
100
+ subscribe(client: RealtimeClient, values: TemplateValues<P>, handlers: TopicHandlers<E>, options?: SubscribeOptions): Subscription;
101
+ /** Sends over HTTP, without joining the channel. */
102
+ send<K extends TopicEvent<E>>(client: RealtimeClient, values: TemplateValues<P>, event: K, payload: TopicPayload<E, K>): AsyncResult<void>;
103
+ }
104
+ /**
105
+ * A typed Realtime broadcast topic: names from a template, RLS policies for
106
+ * private channels, row-change triggers, and disposable subscriptions.
107
+ *
108
+ * ```ts
109
+ * export const notifications = defineTopic('org:{orgId}:notifications:{userId}', {
110
+ * events: { created: v.object({ id: v.string(), title: v.string() }) },
111
+ * });
112
+ * using sub = notifications.subscribe(supabase, { orgId, userId }, { created: (n) => toast(n.title) });
113
+ * ```
114
+ */
115
+ declare function defineTopic<const P extends string, const E extends EventSchemas = Record<never, never>>(template: P, options?: TopicOptions<E>): Topic<P, E>;
116
+ /**
117
+ * Reads a `realtime.broadcast_changes` message as an app-cased row change.
118
+ * Returns `null` for messages about other tables.
119
+ */
120
+ 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;
121
+ //#endregion
122
+ //#region src/realtime/live.d.ts
123
+ /** What `liveQuery` needs from `sb`: metadata and the tables a spec reads. */
124
+ interface LiveSource {
125
+ readonly meta: SchemaMeta;
126
+ tablesOf(spec: QuerySpec): string[];
127
+ }
128
+ interface LiveQueryOptions {
129
+ /** Called once per burst of changes, with the app keys of the tables that changed. */
130
+ readonly onChange: (tables: readonly string[]) => void;
131
+ /** Tenant value for tables scoped by a tenant column (`realtime-tables` topics). */
132
+ readonly tenant?: string;
133
+ /** Waits this long after the last change before calling `onChange`. Defaults to 100 ms. */
134
+ readonly debounceMs?: number;
135
+ readonly onStatus?: (status: SubscriptionStatus, error?: Error) => void;
136
+ }
137
+ interface LiveSubscription extends Disposable, AsyncDisposable {
138
+ /** App keys of the watched tables. */
139
+ readonly tables: readonly string[];
140
+ /** Tables the query reads that don't broadcast changes (not in `realtime.tables`). */
141
+ readonly unwatched: readonly string[];
142
+ readonly ready: Promise<void>;
143
+ unsubscribe(): Promise<void>;
144
+ }
145
+ /**
146
+ * A count from the server plus the spec to keep it live on the client:
147
+ * `next.liveCount(spec)` makes one, `useLiveCount(seed)` reads it. `count`
148
+ * is `null` when the server read failed; the client then fetches it.
149
+ */
150
+ interface LiveCountSeed<T extends string = string> {
151
+ readonly spec: QuerySpec<T, "count", number>;
152
+ readonly count: number | null;
153
+ }
154
+ /** Topic a `realtime-tables` trigger broadcasts on: `bs:t:<schema>.<table>[:<tenant>]`. */
155
+ declare function liveTopic(meta: SchemaMeta, table: string, tenant?: string): string;
156
+ /**
157
+ * Calls `onChange` when a table the spec reads changes, so you can refetch.
158
+ * Needs the `realtime-tables` SQL module and the tables in
159
+ * `realtime.tables`. Live queries on the same client share one channel per
160
+ * table, so any number of them can run at once.
161
+ *
162
+ * ```ts
163
+ * using live = liveQuery(sb, supabase, spec, { onChange: () => refetch() });
164
+ * ```
165
+ */
166
+ declare function liveQuery(sb: LiveSource, client: RealtimeClient, spec: QuerySpec | readonly string[], options: LiveQueryOptions): LiveSubscription;
167
+ /** What `liveCount` runs the spec with: a `db` from `sb.connect()` or `createBrowser()`. */
168
+ interface CountRunner {
169
+ $run(spec: QuerySpec): PromiseLike<Result<unknown>>;
170
+ }
171
+ interface LiveCountOptions extends Omit<LiveQueryOptions, "onChange"> {
172
+ readonly onCount: (count: number) => void;
173
+ /** A failed refetch; the previous count stays valid. */
174
+ readonly onError?: (error: DbError) => void;
175
+ /** Count right away. Defaults to `true`; pass `false` when you have a seed. */
176
+ readonly immediate?: boolean;
177
+ }
178
+ /**
179
+ * Keeps a count current: runs the `count` spec (a HEAD request) now, after
180
+ * each debounced change to a table it reads, and after the channel rejoins.
181
+ * Responses that arrive out of order are dropped.
182
+ *
183
+ * ```ts
184
+ * using live = liveCount(sb, supabase, db, sb.spec.notes.count(), {
185
+ * onCount: (count) => render(count),
186
+ * });
187
+ * ```
188
+ */
189
+ declare function liveCount(sb: LiveSource, client: RealtimeClient, db: CountRunner, spec: QuerySpec<string, "count", number>, options: LiveCountOptions): LiveSubscription;
190
+ //#endregion
191
+ 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,21 @@
1
+ //#region src/core/logger.ts
2
+ const log = (method) => (message, fields) => {
3
+ if (fields === void 0) console[method](`better-supabase: ${message}`);
4
+ else console[method](`better-supabase: ${message}`, fields);
5
+ };
6
+ /** The default: `console`, with a `better-supabase:` prefix. */
7
+ const consoleLogger = {
8
+ debug: log("debug"),
9
+ info: log("info"),
10
+ warn: log("warn"),
11
+ error: log("error")
12
+ };
13
+ const noop = () => {};
14
+ const silentLogger = {
15
+ debug: noop,
16
+ info: noop,
17
+ warn: noop,
18
+ error: noop
19
+ };
20
+ //#endregion
21
+ export { silentLogger as n, consoleLogger as t };
@@ -0,0 +1,142 @@
1
+ import { N as TableKey, n as AnyModels, t as AnyFunctions } from "../types-C83v5SQz.js";
2
+ import { t as BetterSupabase } from "../define-e3zI4_4g.js";
3
+ import { d as ServerContext, f as ServerOptions, l as BetterServer, n as GuardOptions } from "../respond-Dne1ARSa.js";
4
+ import { i as ResourceRouteOptions } from "../resource-CgZa3YG_.js";
5
+ import { StandardSchemaV1 } from "@standard-schema/spec";
6
+ //#region src/mcp/index.d.ts
7
+ export declare const MCP_PROTOCOL_VERSION: string;
8
+ type Json = Readonly<Record<string, unknown>>;
9
+ /** MCP tool annotations (hints for clients, not guarantees). */
10
+ export interface ToolAnnotations {
11
+ readonly title?: string;
12
+ readonly readOnlyHint?: boolean;
13
+ readonly destructiveHint?: boolean;
14
+ readonly idempotentHint?: boolean;
15
+ readonly openWorldHint?: boolean;
16
+ }
17
+ /** A tool as listed by `tools/list`. */
18
+ export interface ToolInfo {
19
+ readonly name: string;
20
+ readonly title?: string;
21
+ readonly description?: string;
22
+ readonly inputSchema: Json;
23
+ readonly outputSchema?: Json;
24
+ readonly annotations?: ToolAnnotations;
25
+ }
26
+ export interface ToolContext<M extends AnyModels, F extends AnyFunctions, E> extends ServerContext<M, F, E> {
27
+ readonly request: Request;
28
+ readonly signal: AbortSignal;
29
+ }
30
+ export interface McpTool<M extends AnyModels, F extends AnyFunctions, E> {
31
+ readonly info: ToolInfo;
32
+ /** Validates arguments; its output is what `run` receives. */
33
+ readonly input?: StandardSchemaV1;
34
+ /** Opaque data for the `authorize` and `visible` hooks. Never sent to clients. */
35
+ readonly meta?: unknown;
36
+ run(args: never, ctx: ToolContext<M, F, E>): unknown;
37
+ }
38
+ /** What the `authorize` and `visible` hooks see of a tool. */
39
+ export interface ToolRef {
40
+ readonly info: ToolInfo;
41
+ /** The tool's `meta`, e.g. a PermDock permission. `undefined` for table tools. */
42
+ readonly meta: unknown;
43
+ }
44
+ /** The outcome of `authorize`. `scopes` turns a refusal into an OAuth challenge. */
45
+ export type ToolDecision = {
46
+ readonly allowed: true;
47
+ } | {
48
+ readonly allowed: false;
49
+ readonly reason?: string;
50
+ /** OAuth scopes the call needs, sent in an `insufficient_scope` challenge. */
51
+ readonly scopes?: readonly string[];
52
+ };
53
+ export interface ToolDefinition<M extends AnyModels, F extends AnyFunctions, E, I> {
54
+ readonly name: string;
55
+ readonly title?: string;
56
+ readonly description: string;
57
+ /**
58
+ * Validator for the arguments. Its JSON Schema comes from Standard JSON
59
+ * Schema (zod 4, arktype, valibot) unless `inputSchema` is given.
60
+ */
61
+ readonly input?: StandardSchemaV1<unknown, I>;
62
+ readonly inputSchema?: Json;
63
+ readonly outputSchema?: Json;
64
+ readonly annotations?: ToolAnnotations;
65
+ /** Opaque data for the `authorize` and `visible` hooks. Never sent to clients. */
66
+ readonly meta?: unknown;
67
+ run(args: I, ctx: ToolContext<M, F, E>): unknown;
68
+ }
69
+ export type ToolResources<M extends AnyModels> = { readonly [T in TableKey<M>]?: ResourceRouteOptions<M, T> | true; };
70
+ export interface McpOptions<M extends AnyModels, F extends AnyFunctions, E> extends ServerOptions, GuardOptions {
71
+ readonly name: string;
72
+ readonly version: string;
73
+ readonly title?: string;
74
+ /** Sent to clients on `initialize`, e.g. how the tools fit together. */
75
+ readonly instructions?: string;
76
+ /** Tables exposed as tools: `<table>_list`, `_get`, `_create`, `_update`, `_delete`. */
77
+ readonly resources?: ToolResources<M>;
78
+ readonly tools?: readonly McpTool<M, F, E>[];
79
+ /**
80
+ * Canonical URL of this MCP server (RFC 9728 `resource`). Defaults to the
81
+ * request URL without query.
82
+ */
83
+ readonly resource?: string | ((request: Request) => string);
84
+ /** Defaults to Supabase Auth of `env.url` (`<url>/auth/v1`). */
85
+ readonly authorizationServers?: readonly string[];
86
+ /**
87
+ * Scopes the tools need, published as RFC 9728 `scopes_supported` and in
88
+ * `insufficient_scope` challenges. `offline_access` is left out: refresh
89
+ * tokens are between the client and the authorization server.
90
+ */
91
+ readonly scopes?: readonly string[];
92
+ /** Origins allowed to call the server (DNS rebinding protection). Defaults to any. */
93
+ readonly allowedOrigins?: readonly string[];
94
+ /** Custom jsonb types, as in `createOpenApi`. */
95
+ readonly json?: Readonly<Record<string, unknown>>;
96
+ /** Include internal error messages in tool results. Defaults to `NODE_ENV === 'development'`. */
97
+ readonly exposeErrors?: boolean;
98
+ /**
99
+ * Decides each `tools/call` after the arguments are validated and before
100
+ * `run`. `args` are the validated arguments (raw for table tools, which
101
+ * validate in the repository). A refusal is a tool error; a refusal with
102
+ * `scopes` is a 403 `insufficient_scope` challenge naming them.
103
+ */
104
+ readonly authorize?: (ctx: ToolContext<M, F, E>, tool: ToolRef, args: unknown) => ToolDecision | Promise<ToolDecision>;
105
+ /**
106
+ * Whether the caller sees a tool in `tools/list`. A hidden tool is called
107
+ * like an unknown one. Lists are cached per token (`cacheScope: 'private'`).
108
+ */
109
+ readonly visible?: (ctx: ToolContext<M, F, E>, tool: ToolRef) => boolean | Promise<boolean>;
110
+ }
111
+ export interface BetterMcp<M extends AnyModels, F extends AnyFunctions, E> extends BetterServer<M, F, E> {
112
+ /** Everything in one handler: the MCP endpoint and `/.well-known/oauth-protected-resource`. */
113
+ readonly fetch: (request: Request) => Promise<Response>;
114
+ /** The Streamable HTTP endpoint (POST JSON-RPC). */
115
+ readonly handler: (request: Request) => Promise<Response>;
116
+ /** RFC 9728 protected resource metadata. */
117
+ metadata(request: Request): Response;
118
+ readonly tools: readonly ToolInfo[];
119
+ /** Adds a tool typed against this app's repositories. */
120
+ tool<I = Record<string, unknown>>(definition: ToolDefinition<M, F, E, I>): this;
121
+ /** Calls a tool directly, e.g. from tests or another transport. */
122
+ call(name: string, args: unknown, ctx: ToolContext<M, F, E>): Promise<ToolResult>;
123
+ }
124
+ export interface ToolResult {
125
+ readonly content: readonly {
126
+ readonly type: "text";
127
+ readonly text: string;
128
+ }[];
129
+ readonly structuredContent?: Json;
130
+ readonly isError?: boolean;
131
+ }
132
+ /** A custom tool. Pass it in `createMcp({ tools: [...] })`. */
133
+ 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>;
134
+ /**
135
+ * An MCP server (Streamable HTTP, stateless) whose tools run as the caller:
136
+ * the Bearer token from the MCP client is verified and every query goes
137
+ * through RLS. Answers 401 with RFC 9728 metadata so clients can sign in with
138
+ * Supabase Auth's OAuth server.
139
+ */
140
+ 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>;
141
+ //#endregion
142
+ export type { GuardOptions, ResourceRouteOptions };