better-supabase 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -33
- package/dist/base64-D2gsioCY.js +42 -0
- package/dist/claims-BAf6c7is.js +48 -0
- package/dist/cli/_format-Cax5EsM9.js +101 -0
- package/dist/cli/bin.js +39 -6
- package/dist/cli/command-CK14fP91.js +502 -0
- package/dist/cli/compare-BmIWVEui.js +9 -0
- package/dist/cli/config-D9R0o1LH.js +1895 -0
- package/dist/cli/config-SSKSTUl2.js +2 -0
- package/dist/cli/db-DOBPt660.js +52 -0
- package/dist/cli/dist-B1MvLv-M.js +129 -0
- package/dist/cli/dist-CFgGknmZ.js +1112 -0
- package/dist/cli/dist-Du7VnEUG.js +621 -0
- package/dist/cli/doctor-BOym3mbL.js +2382 -0
- package/dist/cli/env-BZ0uy8XK.js +108 -0
- package/dist/cli/env-jIFUADLY.js +511 -0
- package/dist/cli/errors-4v4DTtda.js +56 -0
- package/dist/cli/fastest-levenshtein-CFN-lNDv.js +120 -0
- package/dist/cli/gen-Ccn_p90L.js +2008 -0
- package/dist/cli/gen-D5cLCULx.js +2 -0
- package/dist/cli/index.d.ts +295 -28
- package/dist/cli/index.js +184 -3
- package/dist/cli/init-ClnxRYBF.js +550 -0
- package/dist/cli/introspect-DxqfkVIv.js +81 -0
- package/dist/cli/io-k41_z-jT.js +24 -0
- package/dist/cli/json5-ChRycquj.js +787 -0
- package/dist/cli/jsonc-BUniUpOk.js +2 -0
- package/dist/cli/jsonc-D5Fu67jw.js +45 -0
- package/dist/cli/keys-ByKHnvP9.js +83 -0
- package/dist/cli/ohash-D7bG_EOb.js +277 -0
- package/dist/cli/openapi-8WHjxiTJ.js +66 -0
- package/dist/cli/project-B_GeXyHh.js +70 -0
- package/dist/cli/prompts-BNcNfAve.js +1142 -0
- package/dist/cli/prompts-BTYQGunc.js +2 -0
- package/dist/cli/read-sets-CMa29t9a.js +971 -0
- package/dist/cli/rolldown-runtime-BPOCksWG.js +24 -0
- package/dist/cli/run-CP20LKk_.js +347 -0
- package/dist/cli/seed-5J6eu_Xg.js +98 -0
- package/dist/cli/skills-LJJIhLFR.js +137 -0
- package/dist/cli/sql-D4VbqmXi.js +199 -0
- package/dist/cli/style-C0NFdJRL.js +29 -0
- package/dist/cli/toml-DB-MNnFs.js +424 -0
- package/dist/{version-Cqq1m3UY.js → cli/version-5mW_8sY1.js} +1 -1
- package/dist/cli/yaml-CYCbJ6et.js +2252 -0
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.js +15 -25
- package/dist/{compiler-CZ9nIuqy.d.ts → compiler-DB3aSFOA.d.ts} +2 -2
- package/dist/config/index.d.ts +4 -3
- package/dist/config/index.js +5 -3
- package/dist/config-BpxDfnbg.js +129 -0
- package/dist/{index-DOsjFvMf.d.ts → config-C1CYcm6o.d.ts} +49 -49
- package/dist/{config-ClDXmVtY.js → config-CbvFmGD8.js} +14 -137
- package/dist/{define-BXMYD9AY.js → define-Bh0AqTik.js} +221 -120
- package/dist/{define-e3zI4_4g.d.ts → define-CRS2MRV3.d.ts} +36 -31
- package/dist/edge/index.d.ts +9 -13
- package/dist/edge/index.js +51 -25
- package/dist/{entitlements-Dng-ctuT.d.ts → entitlements-Dm-FHpNH.d.ts} +2 -2
- package/dist/env/index.js +203 -1
- package/dist/{errors-Bl3rkOzo.js → errors-L0xK41y_.js} +1 -1
- package/dist/events/index.d.ts +1 -1
- package/dist/events/index.js +139 -1
- package/dist/{events-DOt3nSO-.d.ts → events-B21UOhvP.d.ts} +4 -4
- package/dist/{executor-CGdNQ27g.d.ts → executor-B5IoUxLE.d.ts} +2 -2
- package/dist/{executor-DeppvirK.js → executor-CVt6dD0B.js} +9 -4
- package/dist/{executor-BYXQ9AGn.d.ts → executor-CZ7ImRiD.d.ts} +2 -2
- package/dist/hono/index.d.ts +17 -24
- package/dist/hono/index.js +11 -11
- package/dist/{index-DuP14VHo.d.ts → index-528Ugmyp.d.ts} +30 -10
- package/dist/{index-Bw1bwtC0.d.ts → index-BAy4tsAP.d.ts} +28 -15
- package/dist/{index-CBDufH1M.d.ts → index-BBdvo-5d.d.ts} +24 -26
- package/dist/{index-DbxFr7XG.d.ts → index-BhHEC04r.d.ts} +9 -4
- package/dist/index-Cf-D2jk1.d.ts +50 -0
- package/dist/{index-Ciu5Xlml.d.ts → index-D5IfUIDh.d.ts} +6 -5
- package/dist/{index-K-3WM2Hp.d.ts → index-DFmOpBGC.d.ts} +13 -13
- package/dist/{index-D4v7sncu.d.ts → index-RlLXd8dL.d.ts} +13 -13
- package/dist/index.d.ts +17 -17
- package/dist/index.js +23 -14
- package/dist/{invalidate-DbxuL2jL.js → invalidate-CgxmRamI.js} +7 -1
- package/dist/jobs/index.d.ts +33 -26
- package/dist/jobs/index.js +44 -22
- package/dist/{json-schema-BpYqkCKf.js → json-schema-DP6V7cbr.js} +8 -3
- package/dist/lint/index.js +1 -1
- package/dist/list/index.d.ts +2 -2
- package/dist/list/index.js +131 -96
- package/dist/{live-DJyZMD6w.d.ts → live-BHakSWWo.d.ts} +26 -16
- package/dist/{live-D8rV1Ldw.js → live-CdJBOc4f.js} +13 -9
- package/dist/lookup-BaP5bI27.js +18 -0
- package/dist/mcp/index.d.ts +34 -27
- package/dist/mcp/index.js +59 -25
- package/dist/{mfa-TVia554c.d.ts → mfa-C7MNQ1Ob.d.ts} +1 -1
- package/dist/{mfa-CpLfJSde.js → mfa-byfjruR9.js} +190 -46
- package/dist/next/image/index.js +6 -3
- package/dist/next/index.d.ts +64 -48
- package/dist/next/index.js +63 -50
- package/dist/openapi/index.d.ts +1 -1
- package/dist/openapi/index.js +61 -38
- package/dist/orpc/index.d.ts +18 -21
- package/dist/orpc/index.js +12 -12
- package/dist/otel/index.d.ts +5 -5
- package/dist/otel/index.js +84 -63
- package/dist/{permdock-sql-CghlHPuH.js → permdock-sql-C4KFzLc9.js} +12 -7
- package/dist/permdock-sql-DImJULY-.d.ts +17 -0
- package/dist/{plugin-C4TR9J0Z.d.ts → plugin-bNhvJwRh.d.ts} +5 -4
- package/dist/plugins/actor/index.d.ts +1 -1
- package/dist/plugins/actor/index.js +1 -1
- package/dist/plugins/rules/index.d.ts +2 -2
- package/dist/plugins/rules/index.js +46 -16
- package/dist/plugins/soft-delete/index.d.ts +3 -3
- package/dist/plugins/soft-delete/index.js +4 -4
- package/dist/plugins/tenant/index.d.ts +3 -3
- package/dist/plugins/tenant/index.js +10 -4
- package/dist/plugins/timestamps/index.d.ts +1 -1
- package/dist/plugins/timestamps/index.js +2 -2
- package/dist/plugins/validation/index.d.ts +2 -2
- package/dist/plugins/validation/index.js +3 -3
- package/dist/pool-DFwEQwmN.d.ts +78 -0
- package/dist/postgres/index.d.ts +10 -5
- package/dist/postgres/index.js +37 -16
- package/dist/{postgrest-cAEQ_IQN.d.ts → postgrest-Bc-0Xw62.d.ts} +1 -1
- package/dist/{problem-BTkTtRV9.js → problem-BihjPR0t.js} +11 -5
- package/dist/{problem-DlgB0lYl.d.ts → problem-U8UEKccv.d.ts} +6 -4
- package/dist/query/index.d.ts +2 -2
- package/dist/query/index.js +2 -2
- package/dist/{query-ChvDbsDE.js → query-BMMSWbI7.js} +19 -19
- package/dist/react/index.d.ts +7 -7
- package/dist/react/index.js +2 -2
- package/dist/react/server.d.ts +6 -6
- package/dist/react/server.js +3 -3
- package/dist/react/session.d.ts +21 -2
- package/dist/react/session.js +19 -1
- package/dist/{react-C83BdBGt.js → react-CQHMaESR.js} +59 -55
- package/dist/{read-set-4lI3W_gZ.js → read-set-FWK8qQ58.js} +51 -16
- package/dist/realtime/index.d.ts +4 -3
- package/dist/realtime/index.js +19 -17
- package/dist/{index-CG69s15v.d.ts → refresh-BwYkyNOQ.d.ts} +7 -3
- package/dist/{resolve-CjHILk2w.d.ts → resolve-BAqQcwMW.d.ts} +17 -11
- package/dist/{resource-CgZa3YG_.d.ts → resource-BJcaIfxi.d.ts} +17 -8
- package/dist/{resource-BHEWmQJK.js → resource-My_y-Syh.js} +43 -21
- package/dist/{respond-Dne1ARSa.d.ts → respond-DgIbdrt8.d.ts} +39 -14
- package/dist/{result-DWXatkd6.js → result-Bj6yt33D.js} +2 -2
- package/dist/{result-CTkCZ6JA.d.ts → result-CqEsbgYs.d.ts} +16 -11
- package/dist/server/index.d.ts +38 -13
- package/dist/server/index.js +43 -14
- package/dist/{respond-DMcbQwfN.js → server-BA75B0mT.js} +151 -96
- package/dist/{shared-_6-uPL7E.js → shared-DnoCdMDr.js} +4 -3
- package/dist/{simplify-bwJpLnaS.js → simplify-Cqvfr9dj.js} +76 -39
- package/dist/sql/index.d.ts +139 -0
- package/dist/sql/index.js +1983 -0
- package/dist/{sql-DTjA1LPj.js → sql-B34HU2DI.js} +74 -17
- package/dist/ssr/index.d.ts +4 -4
- package/dist/ssr/index.js +2 -2
- package/dist/{standard-FUi3Z3-7.js → standard-BRyBGTtp.js} +2 -2
- package/dist/{standard-CyhRzctb.d.ts → standard-BsaCYQJc.d.ts} +1 -1
- package/dist/{stats-C5W-rBfR.js → stats-C_YxA2r1.js} +40 -14
- package/dist/storage/index.d.ts +4 -3
- package/dist/storage/index.js +548 -1
- package/dist/{tables-rZxRxNPW.js → tables-Cb-3_VWc.js} +19 -5
- package/dist/temporal-BwshQk0W.js +27 -0
- package/dist/temporal-required-CBNHT6ni.js +19 -0
- package/dist/testing/index.d.ts +25 -24
- package/dist/testing/index.js +165 -66
- package/dist/{types-C83v5SQz.d.ts → types-DONv9SHN.d.ts} +14 -11
- package/dist/version-Bmf7_FzM.js +4 -0
- package/dist/{view-D1s0wa0T.d.ts → view-B64o81wp.d.ts} +36 -6
- package/dist/{view-aOYizTeE.js → view-BrJI2LKp.js} +11 -2
- package/dist/webhooks/index.d.ts +19 -17
- package/dist/webhooks/index.js +1 -1
- package/dist/{webhooks-DMipjhSP.js → webhooks-Bh_Jf_-l.js} +45 -24
- package/package.json +50 -22
- package/schemas/config-v1.json +31 -1
- package/schemas/doctor-report-v1.json +17 -1
- package/schemas/snapshot-v2.json +35 -0
- package/skills/better-supabase/SKILL.md +77 -18
- package/skills/better-supabase/references/plugins.md +21 -22
- package/skills/better-supabase/references/troubleshooting.md +38 -18
- package/skills/better-supabase-api/SKILL.md +66 -18
- package/skills/better-supabase-api/references/adapters.md +64 -41
- package/skills/better-supabase-auth/SKILL.md +146 -0
- package/skills/better-supabase-auth/references/permdock.md +55 -0
- package/skills/better-supabase-testing/SKILL.md +68 -11
- package/dist/casing-da0uRTqt.js +0 -14
- package/dist/claims-CoTobNC-.js +0 -25
- package/dist/cli-DrSgDOQC.js +0 -6646
- package/dist/env-BEToab0I.js +0 -204
- package/dist/events-BuzTgn2D.js +0 -135
- package/dist/pool-R2m_DkXi.d.ts +0 -37
- package/dist/seed-CzecODcJ.js +0 -101
- package/dist/session-C3LxnQ0e.d.ts +0 -22
- package/dist/session-CqEwzVup.js +0 -20
- package/dist/storage-DuGn38fr.js +0 -527
package/dist/next/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { n as dbError } from "../errors-
|
|
2
|
-
import { n as StatsRecorder, t as EMPTY_STATS } from "../stats-
|
|
3
|
-
import { o as isReadSet } from "../read-set-
|
|
4
|
-
import { t as validate } from "../standard-
|
|
5
|
-
import { a as problemResponse } from "../problem-
|
|
1
|
+
import { n as dbError } from "../errors-L0xK41y_.js";
|
|
2
|
+
import { n as StatsRecorder, t as EMPTY_STATS } from "../stats-C_YxA2r1.js";
|
|
3
|
+
import { o as isReadSet } from "../read-set-FWK8qQ58.js";
|
|
4
|
+
import { t as validate } from "../standard-BRyBGTtp.js";
|
|
5
|
+
import { a as problemResponse } from "../problem-BihjPR0t.js";
|
|
6
6
|
import { t as isList } from "../guards-BnzDvFrz.js";
|
|
7
7
|
import { t as hasEntitlement } from "../entitlements-CTEBm7IB.js";
|
|
8
|
-
import { a as
|
|
9
|
-
import { r as checkAal } from "../mfa-
|
|
10
|
-
import { t as toSession } from "../view-
|
|
8
|
+
import { a as guard, c as settle, d as primaryCookie, i as defaultExpose, n as extendServer, r as withExtra, s as respond, t as createServer, u as PRIMARY_COOKIE } from "../server-BA75B0mT.js";
|
|
9
|
+
import { r as checkAal } from "../mfa-byfjruR9.js";
|
|
10
|
+
import { t as toSession } from "../view-BrJI2LKp.js";
|
|
11
11
|
import { cache } from "react";
|
|
12
12
|
import { cacheLife, cacheTag, revalidateTag, updateTag } from "next/cache.js";
|
|
13
13
|
import { cookies, headers } from "next/headers.js";
|
|
@@ -43,7 +43,9 @@ var StatsCollector = class {
|
|
|
43
43
|
this.#prune();
|
|
44
44
|
const entry = {
|
|
45
45
|
created: Date.now(),
|
|
46
|
-
recorder: new CollectedRecorder(() =>
|
|
46
|
+
recorder: new CollectedRecorder(() => {
|
|
47
|
+
this.#schedule(id);
|
|
48
|
+
})
|
|
47
49
|
};
|
|
48
50
|
this.#entries.set(id, entry);
|
|
49
51
|
return entry.recorder;
|
|
@@ -140,8 +142,8 @@ function serverTimingValue(metrics) {
|
|
|
140
142
|
return Object.entries(metrics).map(([name, ms]) => `${name};dur=${ms.toFixed(1)}`).join(", ");
|
|
141
143
|
}
|
|
142
144
|
//#endregion
|
|
143
|
-
//#region src/next/
|
|
144
|
-
/** Response header with the URL of the request's totals on `
|
|
145
|
+
//#region src/next/create.ts
|
|
146
|
+
/** Response header with the URL of the request's totals on `bs.debugRoute()`. */
|
|
145
147
|
const STATS_URL_HEADER = "x-bs-stats";
|
|
146
148
|
/**
|
|
147
149
|
* `cacheLife` `stale` seconds for a view of `session`: `max`, but never past
|
|
@@ -154,7 +156,7 @@ function sessionStale(session, options = {}, now = Date.now()) {
|
|
|
154
156
|
const remaining = session.expiresAt - Math.floor(now / 1e3);
|
|
155
157
|
return Math.min(max, Math.max(min, remaining));
|
|
156
158
|
}
|
|
157
|
-
/** The tag `
|
|
159
|
+
/** The tag `bs.cached()` puts on a user's entries. */
|
|
158
160
|
function sessionTag(userId) {
|
|
159
161
|
return `bs:session:${userId}`;
|
|
160
162
|
}
|
|
@@ -200,7 +202,7 @@ function formDataObject(form) {
|
|
|
200
202
|
* combine it with your sign-in redirect.
|
|
201
203
|
*
|
|
202
204
|
* ```ts
|
|
203
|
-
*
|
|
205
|
+
* bs.proxy(request, {
|
|
204
206
|
* protect: requireAal('aal2', { redirect: '/mfa', match: (path) => path.startsWith('/settings') }),
|
|
205
207
|
* });
|
|
206
208
|
* ```
|
|
@@ -208,10 +210,10 @@ function formDataObject(form) {
|
|
|
208
210
|
function requireAal(level, options = {}) {
|
|
209
211
|
return (auth, request) => {
|
|
210
212
|
const { pathname, search } = request.nextUrl;
|
|
211
|
-
if (options.redirect !== void 0 && pathname === options.redirect) return
|
|
212
|
-
if (options.match && !options.match(pathname)) return
|
|
213
|
+
if (options.redirect !== void 0 && pathname === options.redirect) return;
|
|
214
|
+
if (options.match && !options.match(pathname)) return;
|
|
213
215
|
const denied = checkAal(auth, level);
|
|
214
|
-
if (!denied) return
|
|
216
|
+
if (!denied) return;
|
|
215
217
|
if (options.redirect === void 0) return problemResponse(denied);
|
|
216
218
|
const target = new URL(options.redirect, request.url);
|
|
217
219
|
target.searchParams.set("next", `${pathname}${search}`);
|
|
@@ -233,20 +235,20 @@ function shouldRefresh(request) {
|
|
|
233
235
|
* actions and cache tags, on top of `createServer`.
|
|
234
236
|
*
|
|
235
237
|
* ```ts
|
|
236
|
-
* export const
|
|
238
|
+
* export const bs = createNext(betterSupabase);
|
|
237
239
|
* ```
|
|
238
240
|
*/
|
|
239
|
-
function createNext(
|
|
240
|
-
const base = createServer(
|
|
241
|
+
function createNext(betterSupabase, options = {}) {
|
|
242
|
+
const base = createServer(betterSupabase, options);
|
|
241
243
|
const pinMs = options.replicas?.pinMs ?? 5e3;
|
|
242
244
|
const expose = options.exposeErrors ?? defaultExpose();
|
|
243
|
-
if (options.cacheTags !== false)
|
|
245
|
+
if (options.cacheTags !== false) betterSupabase.cache(nextCache());
|
|
244
246
|
const debug = options.debug;
|
|
245
247
|
const statsHeader = debug?.header ?? "x-bs-db-calls";
|
|
246
248
|
const statsRoute = debug?.route ?? "/api/bs-stats";
|
|
247
|
-
const development =
|
|
248
|
-
const collector = debug
|
|
249
|
-
logger:
|
|
249
|
+
const development = process.env["NODE_ENV"] === "development";
|
|
250
|
+
const collector = debug?.enabled ? sharedCollector({
|
|
251
|
+
logger: betterSupabase.events.logger,
|
|
250
252
|
warn: development,
|
|
251
253
|
...debug.budget ? { budget: debug.budget } : {}
|
|
252
254
|
}) : void 0;
|
|
@@ -255,48 +257,59 @@ function createNext(sb, options = {}) {
|
|
|
255
257
|
return id ? { stats: collector.recorderFor(id) } : {};
|
|
256
258
|
};
|
|
257
259
|
const incomingRequest = async () => new Request("http://next.local/", { headers: new Headers(await headers()) });
|
|
258
|
-
|
|
260
|
+
/** One token verification per render scope backs `context()`, `session()` and `cached()`. */
|
|
261
|
+
const incoming = cache(async () => {
|
|
259
262
|
const request = await incomingRequest();
|
|
260
|
-
return
|
|
263
|
+
return {
|
|
264
|
+
request,
|
|
265
|
+
resolution: await base.resolve(request, { refresh: false })
|
|
266
|
+
};
|
|
261
267
|
});
|
|
262
|
-
const
|
|
263
|
-
const resolution = await
|
|
264
|
-
return
|
|
268
|
+
const current = cache(async () => {
|
|
269
|
+
const { request, resolution } = await incoming();
|
|
270
|
+
return base.contextFromResolution(resolution, request, statsFor(request));
|
|
265
271
|
});
|
|
266
|
-
const
|
|
272
|
+
const context = (request, contextOptions) => request ? base.context(request, contextOptions) : current();
|
|
273
|
+
const session = cache(async () => toSession((await incoming()).resolution.auth));
|
|
274
|
+
const contextForSession = async (view, { token }) => {
|
|
267
275
|
const request = new Request("http://next.local/", { headers: token ? { authorization: `Bearer ${token}` } : {} });
|
|
268
276
|
const ctx = await base.context(request);
|
|
269
277
|
if (ctx.auth.kind === "user" ? view.kind === "user" && view.user.id === ctx.auth.user.id : view.kind !== "user") return ctx;
|
|
270
278
|
return base.contextFor({
|
|
271
279
|
kind: "invalid",
|
|
272
280
|
reason: "token",
|
|
273
|
-
error: dbError("unauthorized", "The token does not belong to the session passed to
|
|
281
|
+
error: dbError("unauthorized", "The token does not belong to the session passed to contextForSession()")
|
|
274
282
|
});
|
|
275
283
|
};
|
|
276
|
-
const withAccounts = withExtra(base, {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
284
|
+
const withAccounts = withExtra(base, {
|
|
285
|
+
context,
|
|
286
|
+
deleteAccount: ((userId, deleteOptions) => base.deleteAccount(userId, deleteOptions).map((result) => {
|
|
287
|
+
invalidate(sessionTag(userId));
|
|
288
|
+
return result;
|
|
289
|
+
}))
|
|
290
|
+
});
|
|
280
291
|
return extendServer(withAccounts, {
|
|
281
|
-
server,
|
|
282
292
|
session,
|
|
283
|
-
|
|
293
|
+
contextForSession,
|
|
284
294
|
async cached(cachedOptions = {}) {
|
|
285
|
-
const [view, ctx] = await Promise.all([session(),
|
|
295
|
+
const [view, ctx] = await Promise.all([session(), current()]);
|
|
286
296
|
const life = cachedOptions.life;
|
|
297
|
+
const stale = sessionStale(view, life);
|
|
287
298
|
cacheLife({
|
|
288
|
-
stale:
|
|
299
|
+
stale: life?.stale === void 0 ? stale : Math.min(stale, life.stale),
|
|
289
300
|
...life?.revalidate === void 0 ? {} : { revalidate: life.revalidate },
|
|
290
301
|
...life?.expire === void 0 ? {} : { expire: life.expire }
|
|
291
302
|
});
|
|
292
|
-
|
|
303
|
+
const tags = [...view.kind === "user" ? [sessionTag(view.user.id)] : [], ...cachedOptions.tags ?? []];
|
|
304
|
+
if (tags.length > 0) cacheTag(...tags);
|
|
293
305
|
return withExtra(ctx, { session: view });
|
|
294
306
|
},
|
|
295
|
-
invalidateSession(userId) {
|
|
307
|
+
invalidateSession(userId, invalidateOptions = {}) {
|
|
296
308
|
invalidate(sessionTag(userId));
|
|
309
|
+
for (const tag of invalidateOptions.tags ?? []) invalidate(tag);
|
|
297
310
|
},
|
|
298
311
|
async liveCount(spec, db) {
|
|
299
|
-
const result = await (db ?? (await
|
|
312
|
+
const result = await (db ?? (await current()).db).$run(spec);
|
|
300
313
|
return {
|
|
301
314
|
spec,
|
|
302
315
|
count: result.ok && typeof result.data === "number" ? result.data : null
|
|
@@ -337,7 +350,7 @@ function createNext(sb, options = {}) {
|
|
|
337
350
|
route(handler, guardOptions = {}) {
|
|
338
351
|
return async (request, segment) => {
|
|
339
352
|
const ctx = await base.context(request, statsFor(request));
|
|
340
|
-
const denied = guard(ctx.auth, guardOptions.allow, guardOptions.aal);
|
|
353
|
+
const denied = guard(ctx.auth, guardOptions.allow, guardOptions.aal, guardOptions.scopes);
|
|
341
354
|
const instance = request.nextUrl.pathname;
|
|
342
355
|
if (denied) return problemResponse(denied, {
|
|
343
356
|
instance,
|
|
@@ -358,23 +371,23 @@ function createNext(sb, options = {}) {
|
|
|
358
371
|
};
|
|
359
372
|
},
|
|
360
373
|
debugRoute() {
|
|
361
|
-
return
|
|
374
|
+
return (request) => {
|
|
362
375
|
const id = new URL(request.url).searchParams.get("id");
|
|
363
|
-
if (!collector || !id) return Response.json({ error: collector ? "missing ?id=" : "debug is off" }, {
|
|
376
|
+
if (!collector || !id) return Promise.resolve(Response.json({ error: collector ? "missing ?id=" : "debug is off" }, {
|
|
364
377
|
status: 404,
|
|
365
378
|
headers: { "cache-control": "no-store" }
|
|
366
|
-
});
|
|
379
|
+
}));
|
|
367
380
|
const stats = collector.get(id) ?? EMPTY_STATS;
|
|
368
|
-
return Response.json(stats, { headers: {
|
|
381
|
+
return Promise.resolve(Response.json(stats, { headers: {
|
|
369
382
|
[statsHeader]: formatStats(stats),
|
|
370
383
|
"cache-control": "no-store"
|
|
371
|
-
} });
|
|
384
|
+
} }));
|
|
372
385
|
};
|
|
373
386
|
},
|
|
374
387
|
action(actionOptions, fn) {
|
|
375
388
|
return async (input) => {
|
|
376
|
-
const ctx = await
|
|
377
|
-
const denied = guard(ctx.auth, actionOptions.allow, actionOptions.aal);
|
|
389
|
+
const ctx = await current();
|
|
390
|
+
const denied = guard(ctx.auth, actionOptions.allow, actionOptions.aal, actionOptions.scopes);
|
|
378
391
|
if (denied) return {
|
|
379
392
|
ok: false,
|
|
380
393
|
data: null,
|
|
@@ -415,7 +428,7 @@ function createNext(sb, options = {}) {
|
|
|
415
428
|
const specs = isReadSet(target) ? Object.values(target.specs) : isList(target) ? target : [target];
|
|
416
429
|
const tags = /* @__PURE__ */ new Set();
|
|
417
430
|
for (const spec of specs) {
|
|
418
|
-
for (const table of
|
|
431
|
+
for (const table of betterSupabase.tablesOf(spec)) tags.add(tagFor(table));
|
|
419
432
|
const [id] = spec.args;
|
|
420
433
|
if (spec.method === "findById" && (typeof id === "string" || typeof id === "number")) tags.add(tagFor(spec.table, id));
|
|
421
434
|
}
|
package/dist/openapi/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as ResourceOptions, i as ResourceOperation, n as OpenApiOptions, o as createOpenApi, r as PROBLEM_SCHEMA, t as OpenApiDocument } from "../index-
|
|
1
|
+
import { a as ResourceOptions, i as ResourceOperation, n as OpenApiOptions, o as createOpenApi, r as PROBLEM_SCHEMA, t as OpenApiDocument } from "../index-BhHEC04r.js";
|
|
2
2
|
export { OpenApiDocument, OpenApiOptions, PROBLEM_SCHEMA, ResourceOperation, ResourceOptions, createOpenApi };
|
package/dist/openapi/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { n as PROBLEM_TYPE_BASE, t as PROBLEM_CONTENT_TYPE } from "../problem-
|
|
1
|
+
import { n as PROBLEM_TYPE_BASE, t as PROBLEM_CONTENT_TYPE } from "../problem-BihjPR0t.js";
|
|
2
2
|
import { t as SPEC_PINS } from "../spec-pins-C3oc-Tdp.js";
|
|
3
|
-
import { t as buildJsonSchema } from "../json-schema-
|
|
3
|
+
import { t as buildJsonSchema } from "../json-schema-DP6V7cbr.js";
|
|
4
4
|
//#region src/openapi/index.ts
|
|
5
5
|
/** JSON Schema for RFC 9457 Problem Details as better-supabase sends them. */
|
|
6
6
|
const PROBLEM_SCHEMA = {
|
|
@@ -81,7 +81,7 @@ const ERRORS = [
|
|
|
81
81
|
"Validation or a check constraint failed."
|
|
82
82
|
]
|
|
83
83
|
];
|
|
84
|
-
const pascal = (value) => value.
|
|
84
|
+
const pascal = (value) => value.replaceAll(/(^|[_-])(\w)/g, (_, _sep, char) => char.toUpperCase());
|
|
85
85
|
const ref = (name) => ({ $ref: `#/components/schemas/${name}` });
|
|
86
86
|
const response = (name) => ({ $ref: `#/components/responses/${name}` });
|
|
87
87
|
function errorResponses(codes) {
|
|
@@ -95,8 +95,14 @@ function jsonBody(schema, description) {
|
|
|
95
95
|
content: { "application/json": { schema } }
|
|
96
96
|
};
|
|
97
97
|
}
|
|
98
|
-
function pageParameters() {
|
|
99
|
-
return [{
|
|
98
|
+
function pageParameters(cursor, maxPageSize) {
|
|
99
|
+
return [cursor ? {
|
|
100
|
+
name: "after",
|
|
101
|
+
in: "query",
|
|
102
|
+
required: false,
|
|
103
|
+
description: "`nextCursor` of the previous page. Leave it out for the first page.",
|
|
104
|
+
schema: { type: "string" }
|
|
105
|
+
} : {
|
|
100
106
|
name: "page",
|
|
101
107
|
in: "query",
|
|
102
108
|
required: false,
|
|
@@ -114,26 +120,68 @@ function pageParameters() {
|
|
|
114
120
|
schema: {
|
|
115
121
|
type: "integer",
|
|
116
122
|
minimum: 1,
|
|
117
|
-
maximum:
|
|
123
|
+
maximum: maxPageSize,
|
|
118
124
|
default: 50
|
|
119
125
|
}
|
|
120
126
|
}];
|
|
121
127
|
}
|
|
128
|
+
function pageSchema(row, cursor) {
|
|
129
|
+
const items = {
|
|
130
|
+
type: "array",
|
|
131
|
+
items: row
|
|
132
|
+
};
|
|
133
|
+
return cursor ? {
|
|
134
|
+
type: "object",
|
|
135
|
+
required: [
|
|
136
|
+
"items",
|
|
137
|
+
"nextCursor",
|
|
138
|
+
"hasMore"
|
|
139
|
+
],
|
|
140
|
+
properties: {
|
|
141
|
+
items,
|
|
142
|
+
nextCursor: { type: ["string", "null"] },
|
|
143
|
+
hasMore: { type: "boolean" }
|
|
144
|
+
}
|
|
145
|
+
} : {
|
|
146
|
+
type: "object",
|
|
147
|
+
required: ["items", "page"],
|
|
148
|
+
properties: {
|
|
149
|
+
items,
|
|
150
|
+
page: {
|
|
151
|
+
type: "object",
|
|
152
|
+
required: [
|
|
153
|
+
"number",
|
|
154
|
+
"size",
|
|
155
|
+
"total",
|
|
156
|
+
"pages",
|
|
157
|
+
"hasMore"
|
|
158
|
+
],
|
|
159
|
+
properties: {
|
|
160
|
+
number: { type: "integer" },
|
|
161
|
+
size: { type: "integer" },
|
|
162
|
+
total: { type: ["integer", "null"] },
|
|
163
|
+
pages: { type: ["integer", "null"] },
|
|
164
|
+
hasMore: { type: "boolean" }
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
}
|
|
122
170
|
/**
|
|
123
171
|
* An OpenAPI 3.1 document for REST resources over your tables, with schemas
|
|
124
172
|
* from the generated metadata, list-query parameters, Supabase security
|
|
125
173
|
* schemes and Problem Details errors.
|
|
126
174
|
*
|
|
127
175
|
* ```ts
|
|
128
|
-
* const doc = createOpenApi(
|
|
176
|
+
* const doc = createOpenApi(betterSupabase, {
|
|
129
177
|
* info: { title: 'CRM API', version: '1.0.0' },
|
|
130
178
|
* resources: { customers: { list: customerList }, notes: { operations: ['list', 'get'] } },
|
|
131
179
|
* });
|
|
132
180
|
* ```
|
|
133
181
|
*/
|
|
134
|
-
function createOpenApi(
|
|
182
|
+
function createOpenApi(betterSupabase, options) {
|
|
135
183
|
const defs = buildJsonSchema({
|
|
136
|
-
meta:
|
|
184
|
+
meta: betterSupabase.meta,
|
|
137
185
|
config: { json: options.json ?? {} }
|
|
138
186
|
})["$defs"];
|
|
139
187
|
const schemas = { Problem: PROBLEM_SCHEMA };
|
|
@@ -142,7 +190,7 @@ function createOpenApi(sb, options) {
|
|
|
142
190
|
const base = (options.basePath ?? "").replace(/\/$/, "");
|
|
143
191
|
const security = options.security ?? ["bearer"];
|
|
144
192
|
for (const [key, raw] of Object.entries(options.resources)) {
|
|
145
|
-
const table =
|
|
193
|
+
const table = betterSupabase.meta.tables[key];
|
|
146
194
|
if (!table) throw new TypeError(`createOpenApi: unknown table "${key}"`);
|
|
147
195
|
const resource = raw === true ? {} : raw;
|
|
148
196
|
const operations = resource.operations ?? (table.kind === "view" ? ["list", "get"] : [
|
|
@@ -167,33 +215,8 @@ function createOpenApi(sb, options) {
|
|
|
167
215
|
const schema = defs[`${key}${variant}`];
|
|
168
216
|
if (schema) schemas[`${name}${variant}`] = schema;
|
|
169
217
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
required: ["items", "page"],
|
|
173
|
-
properties: {
|
|
174
|
-
items: {
|
|
175
|
-
type: "array",
|
|
176
|
-
items: ref(`${name}Row`)
|
|
177
|
-
},
|
|
178
|
-
page: {
|
|
179
|
-
type: "object",
|
|
180
|
-
required: [
|
|
181
|
-
"number",
|
|
182
|
-
"size",
|
|
183
|
-
"total",
|
|
184
|
-
"pages",
|
|
185
|
-
"hasMore"
|
|
186
|
-
],
|
|
187
|
-
properties: {
|
|
188
|
-
number: { type: "integer" },
|
|
189
|
-
size: { type: "integer" },
|
|
190
|
-
total: { type: ["integer", "null"] },
|
|
191
|
-
pages: { type: ["integer", "null"] },
|
|
192
|
-
hasMore: { type: "boolean" }
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
};
|
|
218
|
+
const cursor = (resource.list ? resource.list.pagination : resource.pagination) === "cursor";
|
|
219
|
+
schemas[`${name}Page`] = pageSchema(ref(`${name}Row`), cursor);
|
|
197
220
|
const collection = `${base}${resource.path ?? `/${key}`}`;
|
|
198
221
|
const keyColumn = table.primaryKey.length === 1 ? table.primaryKey[0] : void 0;
|
|
199
222
|
const idSchema = keyColumn ? (defs[`${key}Row`]?.["properties"])?.[keyColumn] ?? { type: "string" } : void 0;
|
|
@@ -215,7 +238,7 @@ function createOpenApi(sb, options) {
|
|
|
215
238
|
for (const operation of operations) switch (operation) {
|
|
216
239
|
case "list":
|
|
217
240
|
(paths[collection] ??= {})["get"] = op("list", `List ${key}`, {
|
|
218
|
-
parameters: resource.list?.openapi ?? pageParameters(),
|
|
241
|
+
parameters: resource.list?.openapi ?? pageParameters(cursor, resource.maxPageSize ?? 200),
|
|
219
242
|
responses: {
|
|
220
243
|
"200": jsonBody(ref(`${name}Page`), `A page of ${key}.`),
|
|
221
244
|
...errorResponses([
|
package/dist/orpc/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { n as AnyModels, t as AnyFunctions } from "../types-
|
|
2
|
-
import { o as Result, p as DbError } from "../result-
|
|
3
|
-
import { t as BetterSupabase } from "../define-
|
|
4
|
-
import { r as AuthState } from "../resolve-
|
|
5
|
-
import {
|
|
6
|
-
import { r as ProblemDetails } from "../problem-
|
|
1
|
+
import { n as AnyModels, t as AnyFunctions } from "../types-DONv9SHN.js";
|
|
2
|
+
import { o as Result, p as DbError } from "../result-CqEsbgYs.js";
|
|
3
|
+
import { t as BetterSupabase } from "../define-CRS2MRV3.js";
|
|
4
|
+
import { r as AuthState } from "../resolve-BAqQcwMW.js";
|
|
5
|
+
import { f as ServerContext, n as GuardOptions, p as ServerOptions, r as MiddlewareOptions, u as BetterServer } from "../respond-DgIbdrt8.js";
|
|
6
|
+
import { r as ProblemDetails } from "../problem-U8UEKccv.js";
|
|
7
7
|
import { DecoratedMiddleware, ORPCError } from "@orpc/server";
|
|
8
8
|
//#region src/orpc/index.d.ts
|
|
9
9
|
/** Initial context: pass `{ context: { request } }` to the oRPC handler. */
|
|
@@ -11,36 +11,33 @@ export interface OrpcRequestContext {
|
|
|
11
11
|
readonly request: Request;
|
|
12
12
|
}
|
|
13
13
|
/** What `middleware()` adds to `context`. */
|
|
14
|
-
export interface OrpcContext<M extends AnyModels, F extends AnyFunctions, E> {
|
|
15
|
-
readonly bs: ServerContext<M, F, E>;
|
|
16
|
-
readonly db: ServerContext<M, F, E>["db"];
|
|
17
|
-
readonly auth: AuthState
|
|
14
|
+
export interface OrpcContext<M extends AnyModels, F extends AnyFunctions, E, C = unknown, P = unknown> {
|
|
15
|
+
readonly bs: ServerContext<M, F, E, C, P>;
|
|
16
|
+
readonly db: ServerContext<M, F, E, C, P>["db"];
|
|
17
|
+
readonly auth: AuthState<C, P>;
|
|
18
18
|
}
|
|
19
19
|
export interface OrpcOptions extends ServerOptions {
|
|
20
20
|
/** Include internal error messages. Defaults to `NODE_ENV === 'development'`. */
|
|
21
21
|
readonly exposeErrors?: boolean;
|
|
22
22
|
}
|
|
23
|
-
export
|
|
24
|
-
|
|
25
|
-
readonly refresh?: boolean;
|
|
26
|
-
}
|
|
27
|
-
export type OrpcMiddleware<M extends AnyModels, F extends AnyFunctions, E> = DecoratedMiddleware<OrpcRequestContext, OrpcContext<M, F, E>, unknown, unknown, Record<never, never>>;
|
|
28
|
-
export interface BetterOrpc<M extends AnyModels, F extends AnyFunctions, E> extends BetterServer<M, F, E> {
|
|
23
|
+
export type OrpcMiddleware<M extends AnyModels, F extends AnyFunctions, E, C = unknown, P = unknown> = DecoratedMiddleware<OrpcRequestContext, OrpcContext<M, F, E, C, P>, unknown, unknown, Record<never, never>>;
|
|
24
|
+
export interface BetterOrpc<M extends AnyModels, F extends AnyFunctions, E, C = unknown, P = unknown> extends BetterServer<M, F, E, C, P> {
|
|
29
25
|
/**
|
|
30
26
|
* Resolves the caller, enforces `allow` and adds `context.db`, `context.auth`
|
|
31
27
|
* and `context.bs`. Thrown `DbException`s, and errors caused by a
|
|
32
|
-
* `DbError`, become `ORPCError`s.
|
|
28
|
+
* `DbError`, become `ORPCError`s. With `refresh`, pair it with
|
|
29
|
+
* `ctx.bs.resolution.apply` in the handler adapter.
|
|
33
30
|
*/
|
|
34
|
-
middleware(options?: MiddlewareOptions): OrpcMiddleware<M, F, E>;
|
|
31
|
+
middleware(options?: MiddlewareOptions): OrpcMiddleware<M, F, E, C, P>;
|
|
35
32
|
/** A `Result` (or `AsyncResult`) as data, throwing an `ORPCError` on failure. */
|
|
36
33
|
unwrap<T>(value: Result<T> | PromiseLike<Result<T>>): Promise<T>;
|
|
37
34
|
/** Plain values pass through; thrown `DbException`s become `ORPCError`s. */
|
|
38
35
|
unwrap<T>(value: T | PromiseLike<T>): Promise<T>;
|
|
39
|
-
|
|
36
|
+
toOrpcError(error: DbError): ORPCError<string, ProblemDetails>;
|
|
40
37
|
}
|
|
41
38
|
/** oRPC code for a `DbError`, from its HTTP status. */
|
|
42
39
|
export declare function orpcCode(error: DbError): string;
|
|
43
40
|
/** oRPC integration: middleware adding the caller's repositories to `context`. */
|
|
44
|
-
export declare function createOrpc<M extends AnyModels, D, F extends AnyFunctions, E>(
|
|
41
|
+
export declare function createOrpc<M extends AnyModels, D, F extends AnyFunctions, E, C = unknown, P = unknown>(betterSupabase: BetterSupabase<M, D, F, E, C, P>, options?: OrpcOptions): BetterOrpc<M, F, E, C, P>;
|
|
45
42
|
//#endregion
|
|
46
|
-
export type { GuardOptions };
|
|
43
|
+
export type { GuardOptions, MiddlewareOptions };
|
package/dist/orpc/index.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { r as dbErrorOf } from "../errors-
|
|
2
|
-
import { o as toProblem } from "../problem-
|
|
3
|
-
import { a as
|
|
1
|
+
import { r as dbErrorOf } from "../errors-L0xK41y_.js";
|
|
2
|
+
import { o as toProblem } from "../problem-BihjPR0t.js";
|
|
3
|
+
import { a as guard, c as settle, i as defaultExpose, n as extendServer, t as createServer } from "../server-BA75B0mT.js";
|
|
4
4
|
import { COMMON_ERROR_STATUS_MAP, ORPCError, os } from "@orpc/server";
|
|
5
5
|
//#region src/orpc/index.ts
|
|
6
|
-
const CODES = new Map(Object.entries(COMMON_ERROR_STATUS_MAP).map(([code, status]) => [status, code]));
|
|
6
|
+
const CODES = /* @__PURE__ */ new Map(Object.entries(COMMON_ERROR_STATUS_MAP).map(([code, status]) => [status, code]));
|
|
7
7
|
/** oRPC code for a `DbError`, from its HTTP status. */
|
|
8
8
|
function orpcCode(error) {
|
|
9
9
|
return CODES.get(error.status) ?? (error.status >= 500 ? "INTERNAL_SERVER_ERROR" : "BAD_REQUEST");
|
|
10
10
|
}
|
|
11
11
|
/** oRPC integration: middleware adding the caller's repositories to `context`. */
|
|
12
|
-
function createOrpc(
|
|
13
|
-
const server = createServer(
|
|
12
|
+
function createOrpc(betterSupabase, options = {}) {
|
|
13
|
+
const server = createServer(betterSupabase, options);
|
|
14
14
|
const expose = options.exposeErrors ?? defaultExpose();
|
|
15
|
-
const
|
|
15
|
+
const toOrpcError = (error) => {
|
|
16
16
|
const problem = toProblem(error, { expose });
|
|
17
17
|
return new ORPCError(orpcCode(error), {
|
|
18
18
|
message: problem.detail ?? problem.title,
|
|
@@ -20,12 +20,12 @@ function createOrpc(sb, options = {}) {
|
|
|
20
20
|
});
|
|
21
21
|
};
|
|
22
22
|
return extendServer(server, {
|
|
23
|
-
|
|
23
|
+
toOrpcError,
|
|
24
24
|
middleware(middlewareOptions = {}) {
|
|
25
25
|
return os.$context().middleware(async ({ context, next }) => {
|
|
26
26
|
const ctx = await server.context(context.request, { refresh: middlewareOptions.refresh ?? false });
|
|
27
|
-
const denied = guard(ctx.auth, middlewareOptions.allow, middlewareOptions.aal);
|
|
28
|
-
if (denied) throw
|
|
27
|
+
const denied = guard(ctx.auth, middlewareOptions.allow, middlewareOptions.aal, middlewareOptions.scopes);
|
|
28
|
+
if (denied) throw toOrpcError(denied);
|
|
29
29
|
try {
|
|
30
30
|
return await next({ context: {
|
|
31
31
|
bs: ctx,
|
|
@@ -34,14 +34,14 @@ function createOrpc(sb, options = {}) {
|
|
|
34
34
|
} });
|
|
35
35
|
} catch (cause) {
|
|
36
36
|
const thrown = dbErrorOf(cause);
|
|
37
|
-
if (thrown) throw
|
|
37
|
+
if (thrown) throw toOrpcError(thrown);
|
|
38
38
|
throw cause;
|
|
39
39
|
}
|
|
40
40
|
});
|
|
41
41
|
},
|
|
42
42
|
async unwrap(value) {
|
|
43
43
|
const settled = await settle(() => value);
|
|
44
|
-
if (!settled.ok) throw
|
|
44
|
+
if (!settled.ok) throw toOrpcError(settled.error);
|
|
45
45
|
return settled.data;
|
|
46
46
|
}
|
|
47
47
|
});
|
package/dist/otel/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as EventHub } from "../events-
|
|
2
|
-
import { f as Plugin } from "../plugin-
|
|
1
|
+
import { a as EventHub } from "../events-B21UOhvP.js";
|
|
2
|
+
import { f as Plugin } from "../plugin-bNhvJwRh.js";
|
|
3
3
|
import { t as SPEC_PINS } from "../spec-pins-CqorWDnO.js";
|
|
4
4
|
import { Attributes, Meter, Tracer } from "@opentelemetry/api";
|
|
5
5
|
//#region src/otel/index.d.ts
|
|
@@ -20,15 +20,15 @@ export interface OtelOptions {
|
|
|
20
20
|
* never recorded; `db.query.summary` is `SELECT customers`.
|
|
21
21
|
*
|
|
22
22
|
* ```ts
|
|
23
|
-
* const
|
|
23
|
+
* const betterSupabase = defineSupabase(schema).use(otel());
|
|
24
24
|
* ```
|
|
25
25
|
*/
|
|
26
26
|
export declare function otel(options?: OtelOptions): Plugin<"otel">;
|
|
27
27
|
/**
|
|
28
|
-
* Spans for auth resolution and session refreshes, from `
|
|
28
|
+
* Spans for auth resolution and session refreshes, from `betterSupabase.events`.
|
|
29
29
|
* Returns a function that stops listening.
|
|
30
30
|
*/
|
|
31
|
-
export declare function traceAuth(
|
|
31
|
+
export declare function traceAuth(betterSupabase: {
|
|
32
32
|
readonly events: EventHub;
|
|
33
33
|
}, options?: Pick<OtelOptions, "tracer" | "attributes">): () => void;
|
|
34
34
|
/**
|