better-supabase 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -31
- package/dist/base64-CQeB1eSf.js +42 -0
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +4 -14
- package/dist/{compiler-CZ9nIuqy.d.ts → compiler-2zqGzxVZ.d.ts} +2 -2
- package/dist/config/index.d.ts +4 -3
- package/dist/config/index.js +5 -3
- package/dist/config-Be4GwsqI.js +129 -0
- package/dist/{index-DOsjFvMf.d.ts → config-C1CYcm6o.d.ts} +49 -49
- package/dist/{config-ClDXmVtY.js → config-DOsGYSiT.js} +14 -137
- package/dist/{define-BXMYD9AY.js → define-DF341GDj.js} +71 -51
- package/dist/{define-e3zI4_4g.d.ts → define-DUbydmJa.d.ts} +10 -9
- package/dist/edge/index.d.ts +4 -4
- package/dist/edge/index.js +4 -4
- package/dist/{entitlements-Dng-ctuT.d.ts → entitlements-DvJXoI02.d.ts} +1 -1
- package/dist/env/index.js +203 -1
- package/dist/events/index.d.ts +1 -1
- package/dist/events/index.js +139 -1
- package/dist/{events-DOt3nSO-.d.ts → events-Do8jXh4D.d.ts} +3 -3
- package/dist/{executor-BYXQ9AGn.d.ts → executor-B26lgbHD.d.ts} +2 -2
- package/dist/{executor-DeppvirK.js → executor-C57jx2pn.js} +7 -2
- package/dist/{executor-CGdNQ27g.d.ts → executor-DynmIWhq.d.ts} +2 -2
- package/dist/hono/index.d.ts +5 -5
- package/dist/hono/index.js +4 -4
- package/dist/{index-CBDufH1M.d.ts → index-BNsABq7P.d.ts} +8 -8
- package/dist/{index-Bw1bwtC0.d.ts → index-CADlNHK-.d.ts} +27 -14
- package/dist/{index-DuP14VHo.d.ts → index-CTTMS6_1.d.ts} +19 -8
- package/dist/index-Cf-D2jk1.d.ts +50 -0
- package/dist/{index-K-3WM2Hp.d.ts → index-CriFFJ_p.d.ts} +4 -4
- package/dist/{index-Ciu5Xlml.d.ts → index-GV77NpRP.d.ts} +5 -4
- package/dist/{index-D4v7sncu.d.ts → index-UJ-LUxtI.d.ts} +3 -3
- package/dist/{index-CG69s15v.d.ts → index-kpgopLNv.d.ts} +4 -3
- package/dist/{index-DbxFr7XG.d.ts → index-qriRM9cZ.d.ts} +7 -2
- package/dist/index.d.ts +12 -12
- package/dist/index.js +16 -7
- package/dist/jobs/index.d.ts +25 -24
- package/dist/jobs/index.js +16 -10
- package/dist/{json-schema-BpYqkCKf.js → json-schema--aOXAGfU.js} +7 -2
- package/dist/lint/index.js +1 -1
- package/dist/list/index.d.ts +2 -2
- package/dist/list/index.js +128 -93
- package/dist/{live-DJyZMD6w.d.ts → live-QE3UPbGm.d.ts} +16 -6
- package/dist/mcp/index.d.ts +24 -17
- package/dist/mcp/index.js +41 -12
- package/dist/{mfa-CpLfJSde.js → mfa-BxoR1RkN.js} +74 -19
- package/dist/{mfa-TVia554c.d.ts → mfa-x3V3vGh-.d.ts} +1 -1
- package/dist/next/index.d.ts +39 -26
- package/dist/next/index.js +28 -23
- package/dist/openapi/index.d.ts +1 -1
- package/dist/openapi/index.js +57 -34
- package/dist/orpc/index.d.ts +6 -6
- package/dist/orpc/index.js +3 -3
- package/dist/otel/index.d.ts +2 -2
- package/dist/otel/index.js +4 -2
- package/dist/{permdock-sql-CghlHPuH.js → permdock-sql-Bta_G0v-.js} +11 -6
- package/dist/permdock-sql-DImJULY-.d.ts +17 -0
- package/dist/{plugin-C4TR9J0Z.d.ts → plugin-DVRB8qNE.d.ts} +5 -4
- package/dist/plugins/actor/index.d.ts +1 -1
- package/dist/plugins/rules/index.d.ts +2 -2
- package/dist/plugins/rules/index.js +4 -4
- package/dist/plugins/soft-delete/index.d.ts +3 -3
- package/dist/plugins/soft-delete/index.js +2 -2
- package/dist/plugins/tenant/index.d.ts +2 -2
- package/dist/plugins/timestamps/index.d.ts +1 -1
- package/dist/plugins/timestamps/index.js +1 -1
- package/dist/plugins/validation/index.d.ts +2 -2
- package/dist/pool-B9qsLyFR.d.ts +71 -0
- package/dist/postgres/index.d.ts +10 -5
- package/dist/postgres/index.js +36 -16
- package/dist/{postgrest-cAEQ_IQN.d.ts → postgrest-DmBlevh-.d.ts} +1 -1
- package/dist/{problem-BTkTtRV9.js → problem-CGt1ADix.js} +10 -4
- package/dist/{problem-DlgB0lYl.d.ts → problem-Z2RTQjZD.d.ts} +6 -4
- package/dist/query/index.d.ts +2 -2
- package/dist/query/index.js +1 -1
- package/dist/{query-ChvDbsDE.js → query-BdKLTPlj.js} +2 -2
- package/dist/react/index.d.ts +7 -7
- package/dist/react/index.js +2 -2
- package/dist/react/server.d.ts +3 -3
- package/dist/react/server.js +1 -1
- package/dist/react/session.d.ts +21 -2
- package/dist/react/session.js +19 -1
- package/dist/{react-C83BdBGt.js → react-DNBYNazu.js} +20 -16
- package/dist/{read-set-4lI3W_gZ.js → read-set-D0qBcboM.js} +3 -3
- package/dist/realtime/index.d.ts +4 -3
- package/dist/realtime/index.js +7 -6
- package/dist/{resolve-CjHILk2w.d.ts → resolve-BRciX2jK.d.ts} +6 -5
- package/dist/{resource-CgZa3YG_.d.ts → resource-CwVgiI5q.d.ts} +16 -7
- package/dist/{resource-BHEWmQJK.js → resource-D30rp0GN.js} +38 -16
- package/dist/{respond-DMcbQwfN.js → respond-C44EZAJo.js} +35 -19
- package/dist/{respond-Dne1ARSa.d.ts → respond-DAkPIdTH.d.ts} +15 -9
- package/dist/{result-CTkCZ6JA.d.ts → result-c2OYgCV2.d.ts} +13 -8
- package/dist/server/index.d.ts +35 -10
- package/dist/server/index.js +38 -9
- package/dist/{simplify-bwJpLnaS.js → simplify-LakqK1vH.js} +10 -16
- package/dist/sql/index.d.ts +139 -0
- package/dist/sql/index.js +1983 -0
- package/dist/{sql-DTjA1LPj.js → sql-BIgk12ew.js} +72 -16
- package/dist/ssr/index.d.ts +4 -4
- package/dist/ssr/index.js +2 -2
- package/dist/{standard-CyhRzctb.d.ts → standard-eudBiRk1.d.ts} +1 -1
- package/dist/{stats-C5W-rBfR.js → stats-D78UQ8Yu.js} +8 -7
- package/dist/storage/index.d.ts +4 -3
- package/dist/storage/index.js +530 -1
- package/dist/{tables-rZxRxNPW.js → tables-Dhha3BXM.js} +5 -5
- package/dist/temporal-BwshQk0W.js +27 -0
- package/dist/temporal-required-CMxeSVPo.js +19 -0
- package/dist/testing/index.d.ts +13 -11
- package/dist/testing/index.js +128 -29
- package/dist/{types-C83v5SQz.d.ts → types-DONv9SHN.d.ts} +14 -11
- package/dist/{version-Cqq1m3UY.js → version-BfpG2gks.js} +1 -1
- package/dist/{view-D1s0wa0T.d.ts → view-3Po_PVlr.d.ts} +34 -4
- package/dist/{view-aOYizTeE.js → view-wZ-9qIUz.js} +11 -2
- package/dist/webhooks/index.d.ts +18 -16
- package/dist/webhooks/index.js +1 -1
- package/dist/{webhooks-DMipjhSP.js → webhooks-CAIFQSTM.js} +11 -9
- package/package.json +24 -28
- package/schemas/config-v1.json +31 -1
- package/schemas/doctor-report-v1.json +17 -1
- package/schemas/snapshot-v2.json +35 -0
- package/skills/better-supabase/SKILL.md +23 -11
- package/skills/better-supabase/references/plugins.md +21 -18
- package/skills/better-supabase/references/troubleshooting.md +15 -15
- package/skills/better-supabase-api/SKILL.md +7 -7
- package/skills/better-supabase-api/references/adapters.md +30 -25
- package/skills/better-supabase-testing/SKILL.md +11 -8
- package/bin/better-supabase.js +0 -4
- package/dist/casing-da0uRTqt.js +0 -14
- package/dist/cli/bin.d.ts +0 -1
- package/dist/cli/bin.js +0 -15
- package/dist/cli/index.d.ts +0 -226
- package/dist/cli/index.js +0 -3
- package/dist/cli-DrSgDOQC.js +0 -6646
- package/dist/env-BEToab0I.js +0 -204
- package/dist/events-BuzTgn2D.js +0 -135
- package/dist/pool-R2m_DkXi.d.ts +0 -37
- package/dist/seed-CzecODcJ.js +0 -101
- package/dist/session-C3LxnQ0e.d.ts +0 -22
- package/dist/session-CqEwzVup.js +0 -20
- package/dist/storage-DuGn38fr.js +0 -527
package/dist/list/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
//#region src/list/
|
|
1
|
+
//#region src/list/list-query.ts
|
|
2
2
|
/** Facet value meaning "not set": filters with `is null`. */
|
|
3
3
|
const UNSET = "__unset__";
|
|
4
4
|
const RESERVED = /* @__PURE__ */ new Set([
|
|
5
5
|
"q",
|
|
6
6
|
"sort",
|
|
7
7
|
"page",
|
|
8
|
+
"after",
|
|
8
9
|
"size"
|
|
9
10
|
]);
|
|
10
11
|
function first(value) {
|
|
@@ -13,8 +14,9 @@ function first(value) {
|
|
|
13
14
|
function all(value) {
|
|
14
15
|
return (typeof value === "string" ? [value] : [...value ?? []]).flatMap((entry) => entry.split(",")).map((entry) => entry.trim()).filter(Boolean);
|
|
15
16
|
}
|
|
17
|
+
const isParamValue = (value) => value === void 0 || typeof value === "string" || Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
16
18
|
function isSearchParams(value) {
|
|
17
|
-
return value instanceof URLSearchParams || !("facets" in value && typeof value.facets === "object");
|
|
19
|
+
return value instanceof URLSearchParams || !("facets" in value && typeof value.facets === "object") && Object.values(value).every(isParamValue);
|
|
18
20
|
}
|
|
19
21
|
function readRecord(input) {
|
|
20
22
|
if (!(input instanceof URLSearchParams)) return input;
|
|
@@ -47,6 +49,7 @@ function defineListQuery(sb, table, config) {
|
|
|
47
49
|
const pageSize = config.pageSize ?? 50;
|
|
48
50
|
const maxPageSize = config.maxPageSize ?? 200;
|
|
49
51
|
const maxSearch = config.maxSearchLength ?? 200;
|
|
52
|
+
const cursor = config.pagination === "cursor";
|
|
50
53
|
const sortKeys = Object.keys(config.sorts);
|
|
51
54
|
const facetEntries = Object.entries(config.facets ?? {});
|
|
52
55
|
for (const [key, column] of facetEntries) {
|
|
@@ -96,6 +99,18 @@ function defineListQuery(sb, table, config) {
|
|
|
96
99
|
message: "Must be a whole number of at least 1",
|
|
97
100
|
path: ["page"]
|
|
98
101
|
});
|
|
102
|
+
else if (cursor && page !== 1) issues.push({
|
|
103
|
+
message: "This list pages with `after`, not `page`",
|
|
104
|
+
path: ["page"]
|
|
105
|
+
});
|
|
106
|
+
let after;
|
|
107
|
+
if (raw.after !== void 0 && raw.after !== "") {
|
|
108
|
+
if (cursor && typeof raw.after === "string") after = raw.after;
|
|
109
|
+
else issues.push({
|
|
110
|
+
message: cursor ? "Must be text" : "This list pages with `page`, not `after`",
|
|
111
|
+
path: ["after"]
|
|
112
|
+
});
|
|
113
|
+
}
|
|
99
114
|
const size = raw.size === void 0 || raw.size === "" ? pageSize : toInt(raw.size);
|
|
100
115
|
if (size === void 0 || !Number.isInteger(size) || size < 1 || size > maxPageSize) issues.push({
|
|
101
116
|
message: `Must be a whole number from 1 to ${String(maxPageSize)}`,
|
|
@@ -121,6 +136,7 @@ function defineListQuery(sb, table, config) {
|
|
|
121
136
|
...q ? { q } : {},
|
|
122
137
|
sort,
|
|
123
138
|
page: page ?? 1,
|
|
139
|
+
...after ? { after } : {},
|
|
124
140
|
size: size ?? pageSize,
|
|
125
141
|
facets: facetValues
|
|
126
142
|
}
|
|
@@ -143,6 +159,7 @@ function defineListQuery(sb, table, config) {
|
|
|
143
159
|
q: first(record["q"]),
|
|
144
160
|
sort: first(record["sort"]),
|
|
145
161
|
page: first(record["page"]),
|
|
162
|
+
after: first(record["after"]),
|
|
146
163
|
size: first(record["size"]),
|
|
147
164
|
facets: facetValues
|
|
148
165
|
});
|
|
@@ -160,6 +177,7 @@ function defineListQuery(sb, table, config) {
|
|
|
160
177
|
q: typed.q,
|
|
161
178
|
sort: typed.sort,
|
|
162
179
|
page: typed.page,
|
|
180
|
+
after: typed.after,
|
|
163
181
|
size: typed.size,
|
|
164
182
|
facets: rawFacets
|
|
165
183
|
});
|
|
@@ -195,12 +213,15 @@ function defineListQuery(sb, table, config) {
|
|
|
195
213
|
}
|
|
196
214
|
function args(query) {
|
|
197
215
|
const filter = where(query);
|
|
216
|
+
const paging = cursor ? { after: query.after ?? null } : {
|
|
217
|
+
page: query.page,
|
|
218
|
+
count: config.count ?? "exact"
|
|
219
|
+
};
|
|
198
220
|
return {
|
|
199
221
|
...filter ? { where: filter } : {},
|
|
200
222
|
orderBy: config.sorts[query.sort],
|
|
201
|
-
page: query.page,
|
|
202
223
|
size: query.size,
|
|
203
|
-
|
|
224
|
+
...paging
|
|
204
225
|
};
|
|
205
226
|
}
|
|
206
227
|
/** Whether a group's value passes the facet's selected values. */
|
|
@@ -249,6 +270,10 @@ function defineListQuery(sb, table, config) {
|
|
|
249
270
|
serialize: (value) => value
|
|
250
271
|
},
|
|
251
272
|
page: intParser,
|
|
273
|
+
after: {
|
|
274
|
+
parse: (value) => value || null,
|
|
275
|
+
serialize: (value) => value
|
|
276
|
+
},
|
|
252
277
|
size: intParser,
|
|
253
278
|
...Object.fromEntries(facets.map((facet) => [facet.key, listParser]))
|
|
254
279
|
};
|
|
@@ -261,8 +286,102 @@ function defineListQuery(sb, table, config) {
|
|
|
261
286
|
uniqueItems: true
|
|
262
287
|
});
|
|
263
288
|
const searchDescription = config.search ? Array.isArray(config.search) ? `Case-insensitive search in ${config.search.join(", ")}.` : "Full-text search (web search syntax)." : void 0;
|
|
289
|
+
const openapi = [
|
|
290
|
+
...searchDescription ? [{
|
|
291
|
+
name: "q",
|
|
292
|
+
in: "query",
|
|
293
|
+
required: false,
|
|
294
|
+
description: searchDescription,
|
|
295
|
+
schema: {
|
|
296
|
+
type: "string",
|
|
297
|
+
maxLength: maxSearch
|
|
298
|
+
}
|
|
299
|
+
}] : [],
|
|
300
|
+
{
|
|
301
|
+
name: "sort",
|
|
302
|
+
in: "query",
|
|
303
|
+
required: false,
|
|
304
|
+
description: `Ordering. Default \`${config.defaultSort}\`.`,
|
|
305
|
+
schema: {
|
|
306
|
+
type: "string",
|
|
307
|
+
enum: sortKeys,
|
|
308
|
+
default: config.defaultSort
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
cursor ? {
|
|
312
|
+
name: "after",
|
|
313
|
+
in: "query",
|
|
314
|
+
required: false,
|
|
315
|
+
description: "`nextCursor` of the previous page. Leave it out for the first page.",
|
|
316
|
+
schema: { type: "string" }
|
|
317
|
+
} : {
|
|
318
|
+
name: "page",
|
|
319
|
+
in: "query",
|
|
320
|
+
required: false,
|
|
321
|
+
description: "1-based page number.",
|
|
322
|
+
schema: {
|
|
323
|
+
type: "integer",
|
|
324
|
+
minimum: 1,
|
|
325
|
+
default: 1
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
name: "size",
|
|
330
|
+
in: "query",
|
|
331
|
+
required: false,
|
|
332
|
+
description: "Page size.",
|
|
333
|
+
schema: {
|
|
334
|
+
type: "integer",
|
|
335
|
+
minimum: 1,
|
|
336
|
+
maximum: maxPageSize,
|
|
337
|
+
default: pageSize
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
...facets.map((facet) => ({
|
|
341
|
+
name: facet.key,
|
|
342
|
+
in: "query",
|
|
343
|
+
required: false,
|
|
344
|
+
description: `Filter on ${facet.column}. Comma-separated; any value matches.${facet.nullable ? ` \`${UNSET}\` matches empty.` : ""}`,
|
|
345
|
+
schema: facetSchema(facet),
|
|
346
|
+
style: "form",
|
|
347
|
+
explode: false
|
|
348
|
+
}))
|
|
349
|
+
];
|
|
350
|
+
const jsonSchema = {
|
|
351
|
+
type: "object",
|
|
352
|
+
additionalProperties: false,
|
|
353
|
+
properties: {
|
|
354
|
+
...searchDescription ? { q: {
|
|
355
|
+
type: "string",
|
|
356
|
+
maxLength: maxSearch,
|
|
357
|
+
description: searchDescription
|
|
358
|
+
} } : {},
|
|
359
|
+
sort: {
|
|
360
|
+
type: "string",
|
|
361
|
+
enum: sortKeys,
|
|
362
|
+
default: config.defaultSort
|
|
363
|
+
},
|
|
364
|
+
...cursor ? { after: { type: "string" } } : { page: {
|
|
365
|
+
type: "integer",
|
|
366
|
+
minimum: 1,
|
|
367
|
+
default: 1
|
|
368
|
+
} },
|
|
369
|
+
size: {
|
|
370
|
+
type: "integer",
|
|
371
|
+
minimum: 1,
|
|
372
|
+
maximum: maxPageSize,
|
|
373
|
+
default: pageSize
|
|
374
|
+
},
|
|
375
|
+
...facets.length > 0 ? { facets: {
|
|
376
|
+
type: "object",
|
|
377
|
+
additionalProperties: false,
|
|
378
|
+
properties: Object.fromEntries(facets.map((facet) => [facet.key, facetSchema(facet)]))
|
|
379
|
+
} } : {}
|
|
380
|
+
}
|
|
381
|
+
};
|
|
264
382
|
return {
|
|
265
383
|
table,
|
|
384
|
+
pagination: config.pagination ?? "offset",
|
|
266
385
|
defaults,
|
|
267
386
|
facets,
|
|
268
387
|
parse,
|
|
@@ -279,7 +398,8 @@ function defineListQuery(sb, table, config) {
|
|
|
279
398
|
if (query.q) params.set("q", query.q);
|
|
280
399
|
if (query.sort && query.sort !== config.defaultSort) params.set("sort", query.sort);
|
|
281
400
|
if (query.page && query.page !== 1) params.set("page", String(query.page));
|
|
282
|
-
if (query.
|
|
401
|
+
if (query.after) params.set("after", query.after);
|
|
402
|
+
if (query.size !== void 0 && query.size > 0 && query.size !== pageSize) params.set("size", String(query.size));
|
|
283
403
|
for (const facet of facets) {
|
|
284
404
|
const values = query.facets?.[facet.key];
|
|
285
405
|
if (values && values.length > 0) params.set(facet.key, values.join(","));
|
|
@@ -297,7 +417,7 @@ function defineListQuery(sb, table, config) {
|
|
|
297
417
|
const page = repository.paginate({
|
|
298
418
|
...extra,
|
|
299
419
|
...base,
|
|
300
|
-
count: count ?? base["count"],
|
|
420
|
+
...cursor ? {} : { count: count ?? base["count"] },
|
|
301
421
|
...combined ? { where: combined } : {}
|
|
302
422
|
});
|
|
303
423
|
if (!config.facetCounts) return page;
|
|
@@ -317,93 +437,8 @@ function defineListQuery(sb, table, config) {
|
|
|
317
437
|
facetCounts: marginals(rows, query)
|
|
318
438
|
})));
|
|
319
439
|
},
|
|
320
|
-
openapi
|
|
321
|
-
|
|
322
|
-
name: "q",
|
|
323
|
-
in: "query",
|
|
324
|
-
required: false,
|
|
325
|
-
description: searchDescription,
|
|
326
|
-
schema: {
|
|
327
|
-
type: "string",
|
|
328
|
-
maxLength: maxSearch
|
|
329
|
-
}
|
|
330
|
-
}] : [],
|
|
331
|
-
{
|
|
332
|
-
name: "sort",
|
|
333
|
-
in: "query",
|
|
334
|
-
required: false,
|
|
335
|
-
description: `Ordering. Default \`${config.defaultSort}\`.`,
|
|
336
|
-
schema: {
|
|
337
|
-
type: "string",
|
|
338
|
-
enum: sortKeys,
|
|
339
|
-
default: config.defaultSort
|
|
340
|
-
}
|
|
341
|
-
},
|
|
342
|
-
{
|
|
343
|
-
name: "page",
|
|
344
|
-
in: "query",
|
|
345
|
-
required: false,
|
|
346
|
-
description: "1-based page number.",
|
|
347
|
-
schema: {
|
|
348
|
-
type: "integer",
|
|
349
|
-
minimum: 1,
|
|
350
|
-
default: 1
|
|
351
|
-
}
|
|
352
|
-
},
|
|
353
|
-
{
|
|
354
|
-
name: "size",
|
|
355
|
-
in: "query",
|
|
356
|
-
required: false,
|
|
357
|
-
description: "Page size.",
|
|
358
|
-
schema: {
|
|
359
|
-
type: "integer",
|
|
360
|
-
minimum: 1,
|
|
361
|
-
maximum: maxPageSize,
|
|
362
|
-
default: pageSize
|
|
363
|
-
}
|
|
364
|
-
},
|
|
365
|
-
...facets.map((facet) => ({
|
|
366
|
-
name: facet.key,
|
|
367
|
-
in: "query",
|
|
368
|
-
required: false,
|
|
369
|
-
description: `Filter on ${facet.column}. Comma-separated; any value matches.${facet.nullable ? ` \`${UNSET}\` matches empty.` : ""}`,
|
|
370
|
-
schema: facetSchema(facet),
|
|
371
|
-
style: "form",
|
|
372
|
-
explode: false
|
|
373
|
-
}))
|
|
374
|
-
],
|
|
375
|
-
jsonSchema: {
|
|
376
|
-
type: "object",
|
|
377
|
-
additionalProperties: false,
|
|
378
|
-
properties: {
|
|
379
|
-
...searchDescription ? { q: {
|
|
380
|
-
type: "string",
|
|
381
|
-
maxLength: maxSearch,
|
|
382
|
-
description: searchDescription
|
|
383
|
-
} } : {},
|
|
384
|
-
sort: {
|
|
385
|
-
type: "string",
|
|
386
|
-
enum: sortKeys,
|
|
387
|
-
default: config.defaultSort
|
|
388
|
-
},
|
|
389
|
-
page: {
|
|
390
|
-
type: "integer",
|
|
391
|
-
minimum: 1,
|
|
392
|
-
default: 1
|
|
393
|
-
},
|
|
394
|
-
size: {
|
|
395
|
-
type: "integer",
|
|
396
|
-
minimum: 1,
|
|
397
|
-
maximum: maxPageSize,
|
|
398
|
-
default: pageSize
|
|
399
|
-
},
|
|
400
|
-
...facets.length > 0 ? { facets: {
|
|
401
|
-
type: "object",
|
|
402
|
-
additionalProperties: false,
|
|
403
|
-
properties: Object.fromEntries(facets.map((facet) => [facet.key, facetSchema(facet)]))
|
|
404
|
-
} } : {}
|
|
405
|
-
}
|
|
406
|
-
}
|
|
440
|
+
openapi,
|
|
441
|
+
jsonSchema
|
|
407
442
|
};
|
|
408
443
|
}
|
|
409
444
|
//#endregion
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { A as SchemaMeta, N as TableKey, O as Row, n as AnyModels, t as AnyFunctions, x as PermdockTopicPolicy } from "./types-
|
|
2
|
-
import { b as ValidationIssue, o as Result, p as DbError, t as AsyncResult } from "./result-
|
|
3
|
-
import { Z as QuerySpec, t as BetterSupabase } from "./define-
|
|
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";
|
|
4
5
|
import { n as TemplateValues, t as TemplateParams } from "./template-mbvlysOo.js";
|
|
5
6
|
import { RealtimeChannel, SupabaseClient } from "@supabase/supabase-js";
|
|
6
7
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
7
|
-
//#region src/realtime/
|
|
8
|
+
//#region src/realtime/topic.d.ts
|
|
8
9
|
type EventSchemas = Readonly<Record<string, StandardSchemaV1>>;
|
|
9
10
|
interface TopicOptions<E extends EventSchemas> {
|
|
10
11
|
/** Name used for generated policies and functions. Defaults to the template's literal parts. */
|
|
@@ -34,9 +35,15 @@ interface TopicOptions<E extends EventSchemas> {
|
|
|
34
35
|
* The helpers check role and scope only. Use this just for permissions
|
|
35
36
|
* whose grants have no row conditions beyond the scope: for a permission
|
|
36
37
|
* with row conditions (e.g. `authorId = principal.id`) every member of the
|
|
37
|
-
* scope
|
|
38
|
+
* scope could join. Pass `catalog` to refuse those keys here;
|
|
39
|
+
* `better-supabase doctor` (BS214) refuses them from the catalog file.
|
|
38
40
|
*/
|
|
39
41
|
readonly permdock?: PermdockTopicPolicy;
|
|
42
|
+
/**
|
|
43
|
+
* PermDock's `permissions.catalog.json`. With it, a `permdock` policy
|
|
44
|
+
* naming a permission with `rowConditions: true` throws.
|
|
45
|
+
*/
|
|
46
|
+
readonly catalog?: PermdockCatalog;
|
|
40
47
|
/** Let clients broadcast on the topic, not only receive. Defaults to `false`. */
|
|
41
48
|
readonly send?: boolean;
|
|
42
49
|
/** Also authorize presence. Defaults to `false`. */
|
|
@@ -77,7 +84,10 @@ interface TriggerOptions<M extends AnyModels, T extends keyof M, P extends strin
|
|
|
77
84
|
readonly values: { readonly [K in TemplateParams<P>]: Extract<keyof Row<M, T>, string>; };
|
|
78
85
|
/** Defaults to all three. */
|
|
79
86
|
readonly events?: readonly ("insert" | "update" | "delete")[];
|
|
80
|
-
/**
|
|
87
|
+
/**
|
|
88
|
+
* Schema for the generated function. Defaults to `better_supabase`, which
|
|
89
|
+
* the Data API does not expose.
|
|
90
|
+
*/
|
|
81
91
|
readonly functionSchema?: string;
|
|
82
92
|
}
|
|
83
93
|
interface RowChange<R> {
|
package/dist/mcp/index.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { N as TableKey, n as AnyModels, t as AnyFunctions } from "../types-
|
|
2
|
-
import { t as BetterSupabase } from "../define-
|
|
3
|
-
import { d as ServerContext, f as ServerOptions, l as BetterServer, n as GuardOptions } from "../respond-
|
|
4
|
-
import {
|
|
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";
|
|
5
5
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
6
|
-
//#region src/mcp/
|
|
6
|
+
//#region src/mcp/mcp.d.ts
|
|
7
7
|
export declare const MCP_PROTOCOL_VERSION: string;
|
|
8
8
|
type Json = Readonly<Record<string, unknown>>;
|
|
9
9
|
/** MCP tool annotations (hints for clients, not guarantees). */
|
|
10
|
-
|
|
10
|
+
interface ToolAnnotations {
|
|
11
11
|
readonly title?: string;
|
|
12
12
|
readonly readOnlyHint?: boolean;
|
|
13
13
|
readonly destructiveHint?: boolean;
|
|
@@ -15,7 +15,7 @@ export interface ToolAnnotations {
|
|
|
15
15
|
readonly openWorldHint?: boolean;
|
|
16
16
|
}
|
|
17
17
|
/** A tool as listed by `tools/list`. */
|
|
18
|
-
|
|
18
|
+
interface ToolInfo {
|
|
19
19
|
readonly name: string;
|
|
20
20
|
readonly title?: string;
|
|
21
21
|
readonly description?: string;
|
|
@@ -23,11 +23,11 @@ export interface ToolInfo {
|
|
|
23
23
|
readonly outputSchema?: Json;
|
|
24
24
|
readonly annotations?: ToolAnnotations;
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
interface ToolContext<M extends AnyModels, F extends AnyFunctions, E> extends ServerContext<M, F, E> {
|
|
27
27
|
readonly request: Request;
|
|
28
28
|
readonly signal: AbortSignal;
|
|
29
29
|
}
|
|
30
|
-
|
|
30
|
+
interface McpTool<M extends AnyModels, F extends AnyFunctions, E> {
|
|
31
31
|
readonly info: ToolInfo;
|
|
32
32
|
/** Validates arguments; its output is what `run` receives. */
|
|
33
33
|
readonly input?: StandardSchemaV1;
|
|
@@ -36,13 +36,13 @@ export interface McpTool<M extends AnyModels, F extends AnyFunctions, E> {
|
|
|
36
36
|
run(args: never, ctx: ToolContext<M, F, E>): unknown;
|
|
37
37
|
}
|
|
38
38
|
/** What the `authorize` and `visible` hooks see of a tool. */
|
|
39
|
-
|
|
39
|
+
interface ToolRef {
|
|
40
40
|
readonly info: ToolInfo;
|
|
41
41
|
/** The tool's `meta`, e.g. a PermDock permission. `undefined` for table tools. */
|
|
42
42
|
readonly meta: unknown;
|
|
43
43
|
}
|
|
44
44
|
/** The outcome of `authorize`. `scopes` turns a refusal into an OAuth challenge. */
|
|
45
|
-
|
|
45
|
+
type ToolDecision = {
|
|
46
46
|
readonly allowed: true;
|
|
47
47
|
} | {
|
|
48
48
|
readonly allowed: false;
|
|
@@ -50,7 +50,7 @@ export type ToolDecision = {
|
|
|
50
50
|
/** OAuth scopes the call needs, sent in an `insufficient_scope` challenge. */
|
|
51
51
|
readonly scopes?: readonly string[];
|
|
52
52
|
};
|
|
53
|
-
|
|
53
|
+
interface ToolDefinition<M extends AnyModels, F extends AnyFunctions, E, I> {
|
|
54
54
|
readonly name: string;
|
|
55
55
|
readonly title?: string;
|
|
56
56
|
readonly description: string;
|
|
@@ -66,8 +66,8 @@ export interface ToolDefinition<M extends AnyModels, F extends AnyFunctions, E,
|
|
|
66
66
|
readonly meta?: unknown;
|
|
67
67
|
run(args: I, ctx: ToolContext<M, F, E>): unknown;
|
|
68
68
|
}
|
|
69
|
-
|
|
70
|
-
|
|
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 {
|
|
71
71
|
readonly name: string;
|
|
72
72
|
readonly version: string;
|
|
73
73
|
readonly title?: string;
|
|
@@ -91,6 +91,13 @@ export interface McpOptions<M extends AnyModels, F extends AnyFunctions, E> exte
|
|
|
91
91
|
readonly scopes?: readonly string[];
|
|
92
92
|
/** Origins allowed to call the server (DNS rebinding protection). Defaults to any. */
|
|
93
93
|
readonly allowedOrigins?: readonly string[];
|
|
94
|
+
/**
|
|
95
|
+
* Hostnames the `Host` header may name, without the port (DNS rebinding
|
|
96
|
+
* protection). List the production, preview and local hosts. Defaults to any.
|
|
97
|
+
*/
|
|
98
|
+
readonly allowedHosts?: readonly string[];
|
|
99
|
+
/** A page that explains how to connect, published as RFC 9728 `resource_documentation`. */
|
|
100
|
+
readonly resourceDocumentation?: string;
|
|
94
101
|
/** Custom jsonb types, as in `createOpenApi`. */
|
|
95
102
|
readonly json?: Readonly<Record<string, unknown>>;
|
|
96
103
|
/** Include internal error messages in tool results. Defaults to `NODE_ENV === 'development'`. */
|
|
@@ -108,7 +115,7 @@ export interface McpOptions<M extends AnyModels, F extends AnyFunctions, E> exte
|
|
|
108
115
|
*/
|
|
109
116
|
readonly visible?: (ctx: ToolContext<M, F, E>, tool: ToolRef) => boolean | Promise<boolean>;
|
|
110
117
|
}
|
|
111
|
-
|
|
118
|
+
interface BetterMcp<M extends AnyModels, F extends AnyFunctions, E> extends BetterServer<M, F, E> {
|
|
112
119
|
/** Everything in one handler: the MCP endpoint and `/.well-known/oauth-protected-resource`. */
|
|
113
120
|
readonly fetch: (request: Request) => Promise<Response>;
|
|
114
121
|
/** The Streamable HTTP endpoint (POST JSON-RPC). */
|
|
@@ -121,7 +128,7 @@ export interface BetterMcp<M extends AnyModels, F extends AnyFunctions, E> exten
|
|
|
121
128
|
/** Calls a tool directly, e.g. from tests or another transport. */
|
|
122
129
|
call(name: string, args: unknown, ctx: ToolContext<M, F, E>): Promise<ToolResult>;
|
|
123
130
|
}
|
|
124
|
-
|
|
131
|
+
interface ToolResult {
|
|
125
132
|
readonly content: readonly {
|
|
126
133
|
readonly type: "text";
|
|
127
134
|
readonly text: string;
|
|
@@ -139,4 +146,4 @@ export declare function defineTool<M extends AnyModels, F extends AnyFunctions,
|
|
|
139
146
|
*/
|
|
140
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>;
|
|
141
148
|
//#endregion
|
|
142
|
-
export type { GuardOptions, ResourceRouteOptions };
|
|
149
|
+
export type { BetterMcp, GuardOptions, McpOptions, McpTool, ResourceRouteOptions, ToolAnnotations, ToolContext, ToolDecision, ToolDefinition, ToolInfo, ToolRef, ToolResources, ToolResult };
|
package/dist/mcp/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { n as dbError } from "../errors-Bl3rkOzo.js";
|
|
2
|
+
import { t as base64ToText } from "../base64-CQeB1eSf.js";
|
|
2
3
|
import { t as validate } from "../standard-FUi3Z3-7.js";
|
|
3
|
-
import { o as toProblem } from "../problem-
|
|
4
|
+
import { o as toProblem } from "../problem-CGt1ADix.js";
|
|
4
5
|
import { t as SPEC_PINS } from "../spec-pins-C3oc-Tdp.js";
|
|
5
|
-
import { t as buildJsonSchema } from "../json-schema
|
|
6
|
-
import { a as settle, c as withExtra, n as guard, o as createServer, s as extendServer, t as defaultExpose } from "../respond-
|
|
7
|
-
import { t as defineResource } from "../resource-
|
|
6
|
+
import { t as buildJsonSchema } from "../json-schema--aOXAGfU.js";
|
|
7
|
+
import { a as settle, c as withExtra, n as guard, o as createServer, s as extendServer, t as defaultExpose } from "../respond-C44EZAJo.js";
|
|
8
|
+
import { t as defineResource } from "../resource-D30rp0GN.js";
|
|
8
9
|
import { fromSupabaseUrl, unauthorizedResponse } from "@supabase/server/oauth-protected-resource";
|
|
9
|
-
//#region src/mcp/
|
|
10
|
+
//#region src/mcp/mcp.ts
|
|
10
11
|
const MCP_PROTOCOL_VERSION = SPEC_PINS.mcp;
|
|
11
12
|
const WELL_KNOWN = "/.well-known/oauth-protected-resource";
|
|
12
13
|
/** Revisions before 2026-07-28 open with an `initialize` handshake. */
|
|
@@ -16,6 +17,16 @@ const LEGACY_VERSIONS = [
|
|
|
16
17
|
"2025-03-26"
|
|
17
18
|
];
|
|
18
19
|
const SUPPORTED_VERSIONS = [MCP_PROTOCOL_VERSION, ...LEGACY_VERSIONS];
|
|
20
|
+
/** The `Host` header's hostname is in the list; a missing or malformed header is not. */
|
|
21
|
+
function hostAllowed(request, allowed) {
|
|
22
|
+
const host = request.headers.get("host");
|
|
23
|
+
if (!host) return false;
|
|
24
|
+
try {
|
|
25
|
+
return allowed.includes(new URL(`http://${host}`).hostname);
|
|
26
|
+
} catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
19
30
|
/** A custom tool. Pass it in `createMcp({ tools: [...] })`. */
|
|
20
31
|
function defineTool(definition) {
|
|
21
32
|
if (!/^[A-Za-z0-9_.-]{1,64}$/.test(definition.name)) throw new TypeError(`Invalid tool name "${definition.name}": use 1-64 letters, digits, "_", "-" or "."`);
|
|
@@ -67,15 +78,15 @@ const ANNOTATIONS = {
|
|
|
67
78
|
openWorldHint: false
|
|
68
79
|
}
|
|
69
80
|
};
|
|
70
|
-
function pageSchema(max) {
|
|
81
|
+
function pageSchema(max, cursor) {
|
|
71
82
|
return {
|
|
72
83
|
type: "object",
|
|
73
84
|
properties: {
|
|
74
|
-
page: {
|
|
85
|
+
...cursor ? { after: { type: "string" } } : { page: {
|
|
75
86
|
type: "integer",
|
|
76
87
|
minimum: 1,
|
|
77
88
|
default: 1
|
|
78
|
-
},
|
|
89
|
+
} },
|
|
79
90
|
size: {
|
|
80
91
|
type: "integer",
|
|
81
92
|
minimum: 1,
|
|
@@ -99,7 +110,23 @@ function tableTools(resource, defs) {
|
|
|
99
110
|
required: [keyParam, ...Object.keys(more)],
|
|
100
111
|
additionalProperties: false
|
|
101
112
|
});
|
|
102
|
-
const
|
|
113
|
+
const cursor = resource.pagination === "cursor";
|
|
114
|
+
const page = cursor ? {
|
|
115
|
+
type: "object",
|
|
116
|
+
properties: {
|
|
117
|
+
items: {
|
|
118
|
+
type: "array",
|
|
119
|
+
items: row
|
|
120
|
+
},
|
|
121
|
+
nextCursor: { type: ["string", "null"] },
|
|
122
|
+
hasMore: { type: "boolean" }
|
|
123
|
+
},
|
|
124
|
+
required: [
|
|
125
|
+
"items",
|
|
126
|
+
"nextCursor",
|
|
127
|
+
"hasMore"
|
|
128
|
+
]
|
|
129
|
+
} : {
|
|
103
130
|
type: "object",
|
|
104
131
|
properties: {
|
|
105
132
|
items: {
|
|
@@ -126,7 +153,7 @@ function tableTools(resource, defs) {
|
|
|
126
153
|
};
|
|
127
154
|
for (const operation of resource.operations) switch (operation) {
|
|
128
155
|
case "list": {
|
|
129
|
-
const { $schema: _, ...listSchema } = resource.list?.jsonSchema ?? pageSchema(resource.maxPageSize);
|
|
156
|
+
const { $schema: _, ...listSchema } = resource.list?.jsonSchema ?? pageSchema(resource.maxPageSize, cursor);
|
|
130
157
|
add("list", `List ${table} rows visible to the caller, one page at a time.`, listSchema, page);
|
|
131
158
|
break;
|
|
132
159
|
}
|
|
@@ -174,7 +201,7 @@ function headerName(value) {
|
|
|
174
201
|
const encoded = /^=\?base64\?(.*)\?=$/.exec(value)?.[1];
|
|
175
202
|
if (encoded === void 0) return value;
|
|
176
203
|
try {
|
|
177
|
-
return
|
|
204
|
+
return base64ToText(encoded);
|
|
178
205
|
} catch {
|
|
179
206
|
return value;
|
|
180
207
|
}
|
|
@@ -352,6 +379,7 @@ function createMcp(sb, options) {
|
|
|
352
379
|
const handler = async (request) => {
|
|
353
380
|
const origin = request.headers.get("origin");
|
|
354
381
|
if (origin && options.allowedOrigins && !options.allowedOrigins.includes(origin)) return rpcError(null, INVALID_REQUEST, "Origin not allowed", 403);
|
|
382
|
+
if (options.allowedHosts && !hostAllowed(request, options.allowedHosts)) return rpcError(null, INVALID_REQUEST, "Host not allowed", 403);
|
|
355
383
|
if (request.method !== "POST") return new Response(null, {
|
|
356
384
|
status: 405,
|
|
357
385
|
headers: { allow: "POST" }
|
|
@@ -432,7 +460,8 @@ function createMcp(sb, options) {
|
|
|
432
460
|
resource: resourceOf(request),
|
|
433
461
|
authorization_servers: [...authorizationServers()],
|
|
434
462
|
bearer_methods_supported: ["header"],
|
|
435
|
-
...scopes.length > 0 ? { scopes_supported: scopes } : {}
|
|
463
|
+
...scopes.length > 0 ? { scopes_supported: scopes } : {},
|
|
464
|
+
...options.resourceDocumentation === void 0 ? {} : { resource_documentation: options.resourceDocumentation }
|
|
436
465
|
}, { headers: { "access-control-allow-origin": "*" } });
|
|
437
466
|
const mcp = extendServer(server, {
|
|
438
467
|
get tools() {
|