better-supabase 0.1.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/claims-CoTobNC-.js +25 -0
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +4 -14
- package/dist/{compiler-DL18ukPe.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-BD6wKthp.d.ts → config-C1CYcm6o.d.ts} +75 -54
- package/dist/{config-D-aR2ZoN.js → config-DOsGYSiT.js} +14 -136
- package/dist/{define-QGkq04oe.js → define-DF341GDj.js} +133 -110
- package/dist/{define-CTfWw-SS.d.ts → define-DUbydmJa.d.ts} +32 -14
- package/dist/edge/index.d.ts +4 -4
- package/dist/edge/index.js +4 -4
- package/dist/entitlements-CTEBm7IB.js +20 -0
- package/dist/entitlements-DvJXoI02.d.ts +42 -0
- 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-2OJsVG_d.d.ts → events-Do8jXh4D.d.ts} +3 -3
- package/dist/{executor-Bje4suQF.d.ts → executor-B26lgbHD.d.ts} +2 -2
- package/dist/{executor-Ceq60hPM.js → executor-C57jx2pn.js} +7 -2
- package/dist/{executor-Bazhgsgq.d.ts → executor-DynmIWhq.d.ts} +2 -2
- package/dist/guards-BnzDvFrz.js +10 -0
- package/dist/hono/index.d.ts +5 -5
- package/dist/hono/index.js +4 -4
- package/dist/{index-Hf8PP9sP.d.ts → index-BNsABq7P.d.ts} +17 -13
- package/dist/{index-JXHBuS53.d.ts → index-CADlNHK-.d.ts} +27 -14
- package/dist/{index-Nxo3TJcm.d.ts → index-CTTMS6_1.d.ts} +27 -8
- package/dist/index-Cf-D2jk1.d.ts +50 -0
- package/dist/{index-Dup-nSe9.d.ts → index-CriFFJ_p.d.ts} +4 -4
- package/dist/{index-DQVvHzjW.d.ts → index-GV77NpRP.d.ts} +5 -4
- package/dist/{index-Sw43J8HG.d.ts → index-UJ-LUxtI.d.ts} +6 -6
- package/dist/{index-cEeweTeM.d.ts → index-kpgopLNv.d.ts} +4 -3
- package/dist/{index-DRid0qIv.d.ts → index-qriRM9cZ.d.ts} +7 -2
- package/dist/index.d.ts +13 -13
- package/dist/index.js +18 -8
- package/dist/jobs/index.d.ts +25 -24
- package/dist/jobs/index.js +19 -12
- package/dist/{json-schema-BbwO2mZg.js → json-schema--aOXAGfU.js} +9 -3
- 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-6b9i72Ux.d.ts → live-QE3UPbGm.d.ts} +27 -6
- package/dist/logger-glM28NVN.js +21 -0
- package/dist/mcp/index.d.ts +59 -15
- package/dist/mcp/index.js +229 -67
- package/dist/{mfa-CpogP66z.js → mfa-BxoR1RkN.js} +97 -22
- package/dist/{mfa-TVia554c.d.ts → mfa-x3V3vGh-.d.ts} +1 -1
- package/dist/next/index.d.ts +51 -38
- package/dist/next/index.js +31 -25
- package/dist/openapi/index.d.ts +1 -1
- package/dist/openapi/index.js +58 -35
- package/dist/orpc/index.d.ts +6 -6
- package/dist/orpc/index.js +3 -3
- package/dist/otel/index.d.ts +3 -3
- package/dist/otel/index.js +5 -3
- package/dist/{template-BeOGnynM.js → permdock-sql-Bta_G0v-.js} +32 -3
- package/dist/permdock-sql-DImJULY-.d.ts +17 -0
- package/dist/{plugin-HvGqCurB.d.ts → plugin-DVRB8qNE.d.ts} +5 -4
- package/dist/plugins/actor/index.d.ts +1 -1
- package/dist/plugins/rules/index.d.ts +4 -3
- package/dist/plugins/rules/index.js +8 -15
- 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 +10 -4
- package/dist/plugins/tenant/index.js +15 -13
- 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-Dm-vhY6R.d.ts → postgrest-DmBlevh-.d.ts} +1 -1
- package/dist/{problem-DPu6Fh-t.js → problem-CGt1ADix.js} +14 -8
- 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 +3 -3
- package/dist/react/server.d.ts +5 -5
- 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-CrOvrnIP.js → react-DNBYNazu.js} +28 -23
- package/dist/{read-set-DhX2d04c.js → read-set-D0qBcboM.js} +3 -3
- package/dist/realtime/index.d.ts +4 -2
- package/dist/realtime/index.js +30 -14
- package/dist/{resolve-DW8h9NPm.d.ts → resolve-BRciX2jK.d.ts} +25 -9
- package/dist/{resource-SkPTRJDD.d.ts → resource-CwVgiI5q.d.ts} +16 -7
- package/dist/{resource-CYiu6Ygi.js → resource-D30rp0GN.js} +38 -16
- package/dist/{respond-4-dhoV7x.js → respond-C44EZAJo.js} +38 -19
- package/dist/{respond-PoTpy5DT.d.ts → respond-DAkPIdTH.d.ts} +23 -17
- 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-oHiDKRVQ.js → simplify-LakqK1vH.js} +22 -28
- package/dist/{spec-pins-t6sPaHvl.js → spec-pins-C3oc-Tdp.js} +1 -1
- package/dist/{spec-pins-CJPVghHy.d.ts → spec-pins-CqorWDnO.d.ts} +1 -1
- package/dist/sql/index.d.ts +139 -0
- package/dist/sql/index.js +1983 -0
- package/dist/{sql-M8l3fnS0.js → sql-BIgk12ew.js} +72 -16
- package/dist/ssr/index.d.ts +5 -5
- package/dist/ssr/index.js +3 -3
- package/dist/{standard-CyhRzctb.d.ts → standard-eudBiRk1.d.ts} +1 -1
- package/dist/{stats-3iy8u3_P.js → stats-D78UQ8Yu.js} +8 -7
- package/dist/storage/index.d.ts +4 -2
- 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 +49 -16
- package/dist/testing/index.js +233 -66
- package/dist/{types-C2fslP1z.d.ts → types-DONv9SHN.d.ts} +68 -12
- package/dist/{version-Cqq1m3UY.js → version-BfpG2gks.js} +1 -1
- package/dist/view-3Po_PVlr.d.ts +79 -0
- package/dist/{view-B6F9QbG-.js → view-wZ-9qIUz.js} +12 -2
- package/dist/webhooks/index.d.ts +19 -17
- package/dist/webhooks/index.js +1 -1
- package/dist/{webhooks-DbMTJJX2.js → webhooks-CAIFQSTM.js} +12 -10
- package/package.json +30 -30
- package/schemas/config-v1.json +80 -5
- package/schemas/doctor-report-v1.json +18 -1
- package/schemas/snapshot-v2.json +39 -0
- package/skills/better-supabase/SKILL.md +23 -11
- package/skills/better-supabase/references/plugins.md +28 -16
- package/skills/better-supabase/references/troubleshooting.md +15 -15
- package/skills/better-supabase-api/SKILL.md +9 -9
- package/skills/better-supabase-api/references/adapters.md +30 -25
- package/skills/better-supabase-testing/SKILL.md +15 -11
- package/bin/better-supabase.js +0 -3
- 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-CoByWYja.js +0 -6472
- package/dist/entitlements-BBg61DQZ.d.ts +0 -20
- package/dist/entitlements-BC_khwRy.js +0 -17
- package/dist/env-BEToab0I.js +0 -204
- package/dist/events-BuzTgn2D.js +0 -135
- package/dist/pool-BFeqzwQS.d.ts +0 -37
- package/dist/seed-CzecODcJ.js +0 -101
- package/dist/session-C3osL21k.d.ts +0 -21
- package/dist/session-CD-3orO_.js +0 -19
- package/dist/storage-BWXVUXGk.js +0 -461
- package/dist/view-B0ltUmTi.d.ts +0 -43
package/dist/react/session.js
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
2
|
+
import { createContext, createElement, use, useContext } from "react";
|
|
3
|
+
//#region src/react/session.ts
|
|
4
|
+
const SessionContext = createContext(null);
|
|
5
|
+
/** Shares the server-verified session with Client Components. */
|
|
6
|
+
function SessionProvider(props) {
|
|
7
|
+
return createElement(SessionContext.Provider, { value: props.sessionPromise }, props.children);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The session from the nearest `<SessionProvider>`. Suspends until the
|
|
11
|
+
* promise resolves, so render it inside `<Suspense>`. `C` types the claims
|
|
12
|
+
* (the output of `sb.claims(schema)`) and `P` the profile (the output of
|
|
13
|
+
* `sb.userMetadata(schema)`); `createHooks().useSession` infers both.
|
|
14
|
+
*/
|
|
15
|
+
function useSession() {
|
|
16
|
+
const promise = useContext(SessionContext);
|
|
17
|
+
if (!promise) throw new Error("better-supabase: useSession() needs <SessionProvider sessionPromise={...}>");
|
|
18
|
+
return use(promise);
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
3
21
|
export { SessionProvider, useSession };
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { i as tenantClaimPaths, n as claimAt, r as claimsOf } from "./claims-CoTobNC-.js";
|
|
1
2
|
import { t as invalidateTables } from "./invalidate-DbxuL2jL.js";
|
|
2
3
|
import { n as liveQuery, t as liveCount } from "./live-D8rV1Ldw.js";
|
|
3
|
-
import {
|
|
4
|
+
import { useSession } from "./react/session.js";
|
|
4
5
|
import { createContext, createElement, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
5
|
-
//#region src/react/
|
|
6
|
+
//#region src/react/hooks.ts
|
|
6
7
|
const BrowserContext = createContext(null);
|
|
7
8
|
const LOADING = {
|
|
8
9
|
status: "loading",
|
|
@@ -90,9 +91,9 @@ function useBroadcast(topic, values, handlers, options) {
|
|
|
90
91
|
const userId = auth.user?.id ?? null;
|
|
91
92
|
if (options?.invalidate && !queryClient) throw new Error("better-supabase: useBroadcast({ invalidate }) needs <BetterSupabaseProvider queryClient={...}>");
|
|
92
93
|
useEffect(() => {
|
|
93
|
-
if (!name || auth.status === "loading") return
|
|
94
|
+
if (!name || auth.status === "loading") return;
|
|
94
95
|
const matched = topic.match(name);
|
|
95
|
-
if (!matched) return
|
|
96
|
+
if (!matched) return;
|
|
96
97
|
const forward = (payload, message) => {
|
|
97
98
|
const current = latest.current;
|
|
98
99
|
const table = current.handlers;
|
|
@@ -124,12 +125,12 @@ function useBroadcast(topic, values, handlers, options) {
|
|
|
124
125
|
]);
|
|
125
126
|
return status;
|
|
126
127
|
}
|
|
127
|
-
function claimedTenant(auth) {
|
|
128
|
+
function claimedTenant(auth, meta) {
|
|
128
129
|
if (auth.status !== "signed-in") return void 0;
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
130
|
+
for (const path of tenantClaimPaths(claimsOf(meta).tenant)) {
|
|
131
|
+
const value = claimAt(auth.claims, path);
|
|
132
|
+
if (value !== void 0) return value;
|
|
133
|
+
}
|
|
133
134
|
}
|
|
134
135
|
/**
|
|
135
136
|
* Keeps a query fresh: invalidates every cached query that read a table the
|
|
@@ -148,11 +149,11 @@ function useLiveQuery(spec, options = {}) {
|
|
|
148
149
|
const [status, setStatus] = useState("closed");
|
|
149
150
|
if (spec && !queryClient) throw new Error("better-supabase: useLiveQuery needs <BetterSupabaseProvider queryClient={...}>");
|
|
150
151
|
const key = spec ? JSON.stringify(spec) : null;
|
|
151
|
-
const tenant = options.tenant ?? claimedTenant(auth);
|
|
152
|
+
const tenant = options.tenant ?? claimedTenant(auth, browser.sb.meta);
|
|
152
153
|
const userId = auth.user?.id ?? null;
|
|
153
154
|
const debounceMs = options.debounceMs;
|
|
154
155
|
useEffect(() => {
|
|
155
|
-
if (!key || !queryClient || auth.status === "loading") return
|
|
156
|
+
if (!key || !queryClient || auth.status === "loading") return;
|
|
156
157
|
const live = liveQuery(browser.sb, browser.supabase, JSON.parse(key), {
|
|
157
158
|
onChange: (tables) => void invalidateTables(queryClient, tables),
|
|
158
159
|
onStatus: setStatus,
|
|
@@ -196,24 +197,28 @@ function useLiveCount(source, options = {}) {
|
|
|
196
197
|
});
|
|
197
198
|
const [status, setStatus] = useState("closed");
|
|
198
199
|
const key = spec ? JSON.stringify(spec) : null;
|
|
199
|
-
const tenant = options.tenant ?? claimedTenant(auth);
|
|
200
|
+
const tenant = options.tenant ?? claimedTenant(auth, browser.sb.meta);
|
|
200
201
|
const userId = auth.user?.id ?? null;
|
|
201
202
|
const debounceMs = options.debounceMs;
|
|
202
203
|
const hasInitial = initial !== void 0;
|
|
203
204
|
useEffect(() => {
|
|
204
|
-
if (!key || auth.status === "loading") return
|
|
205
|
+
if (!key || auth.status === "loading") return;
|
|
205
206
|
const live = liveCount(browser.sb, browser.supabase, browser.db, JSON.parse(key), {
|
|
206
207
|
immediate: !hasInitial,
|
|
207
|
-
onCount: (count) =>
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
208
|
+
onCount: (count) => {
|
|
209
|
+
setState({
|
|
210
|
+
key,
|
|
211
|
+
count,
|
|
212
|
+
error: void 0
|
|
213
|
+
});
|
|
214
|
+
},
|
|
215
|
+
onError: (error) => {
|
|
216
|
+
setState((previous) => ({
|
|
217
|
+
key,
|
|
218
|
+
count: previous.key === key ? previous.count : void 0,
|
|
219
|
+
error
|
|
220
|
+
}));
|
|
221
|
+
},
|
|
217
222
|
onStatus: setStatus,
|
|
218
223
|
...tenant === void 0 ? {} : { tenant },
|
|
219
224
|
...debounceMs === void 0 ? {} : { debounceMs }
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { r as IrBuilder, u as tableMeta } from "./simplify-
|
|
2
|
-
import { n as touchedTables } from "./tables-
|
|
1
|
+
import { r as IrBuilder, u as tableMeta } from "./simplify-LakqK1vH.js";
|
|
2
|
+
import { n as touchedTables } from "./tables-Dhha3BXM.js";
|
|
3
3
|
//#region src/core/spec.ts
|
|
4
4
|
const READ_METHODS = [
|
|
5
5
|
"findMany",
|
|
@@ -87,7 +87,7 @@ const TYPE = /^(?:[a-z_][a-z0-9_]*\.)?[a-z_][a-z0-9_]*(?:\[\])?$/;
|
|
|
87
87
|
/** `jsonb_build_object` takes at most 100 arguments. */
|
|
88
88
|
const MAX_ENTRIES = 50;
|
|
89
89
|
/** Placeholders are `NUL bs:<name> NUL`: no real parameter value holds a NUL. */
|
|
90
|
-
const NUL =
|
|
90
|
+
const NUL = "\0";
|
|
91
91
|
const SENTINEL = new RegExp(`${NUL}bs:([a-z_][a-z0-9_]*)${NUL}`, "gi");
|
|
92
92
|
const EXACT = new RegExp(`^${NUL}bs:([a-z_][a-z0-9_]*)${NUL}$`, "i");
|
|
93
93
|
function sentinel(name) {
|
package/dist/realtime/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { x as PermdockTopicPolicy } from "../types-DONv9SHN.js";
|
|
2
|
+
import { t as PermdockCatalog } from "../permdock-sql-DImJULY-.js";
|
|
1
3
|
import { n as TemplateValues, t as TemplateParams } from "../template-mbvlysOo.js";
|
|
2
|
-
import { C as defineTopic, S as TriggerOptions, _ as TopicEvent, a as LiveSource, b as TopicOptions, c as liveQuery, d as RealtimeClient, f as RowChange, g as Topic, h as SubscriptionStatus, i as LiveQueryOptions, l as liveTopic, m as Subscription, n as LiveCountOptions, o as LiveSubscription, p as SubscribeOptions, r as LiveCountSeed, s as liveCount, t as CountRunner, u as EventSchemas, v as TopicHandlers, w as rowChange, x as TopicPayload, y as TopicMessage } from "../live-
|
|
3
|
-
export { type CountRunner, EventSchemas, type LiveCountOptions, type LiveCountSeed, type LiveQueryOptions, type LiveSource, type LiveSubscription, RealtimeClient, RowChange, SubscribeOptions, Subscription, SubscriptionStatus, type TemplateParams, type TemplateValues, Topic, TopicEvent, TopicHandlers, TopicMessage, TopicOptions, TopicPayload, TriggerOptions, defineTopic, liveCount, liveQuery, liveTopic, rowChange };
|
|
4
|
+
import { C as defineTopic, S as TriggerOptions, _ as TopicEvent, a as LiveSource, b as TopicOptions, c as liveQuery, d as RealtimeClient, f as RowChange, g as Topic, h as SubscriptionStatus, i as LiveQueryOptions, l as liveTopic, m as Subscription, n as LiveCountOptions, o as LiveSubscription, p as SubscribeOptions, r as LiveCountSeed, s as liveCount, t as CountRunner, u as EventSchemas, v as TopicHandlers, w as rowChange, x as TopicPayload, y as TopicMessage } from "../live-QE3UPbGm.js";
|
|
5
|
+
export { type CountRunner, type EventSchemas, type LiveCountOptions, type LiveCountSeed, type LiveQueryOptions, type LiveSource, type LiveSubscription, type PermdockCatalog, type PermdockTopicPolicy, type RealtimeClient, type RowChange, type SubscribeOptions, type Subscription, type SubscriptionStatus, type TemplateParams, type TemplateValues, type Topic, type TopicEvent, type TopicHandlers, type TopicMessage, type TopicOptions, type TopicPayload, type TriggerOptions, defineTopic, liveCount, liveQuery, liveTopic, rowChange };
|
package/dist/realtime/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { n as dbError } from "../errors-Bl3rkOzo.js";
|
|
2
2
|
import { i as ok, n as err, t as AsyncResult } from "../result-DWXatkd6.js";
|
|
3
|
+
import { i as tenantClaimPaths } from "../claims-CoTobNC-.js";
|
|
3
4
|
import { n as liveQuery, r as liveTopic, t as liveCount } from "../live-D8rV1Ldw.js";
|
|
4
5
|
import { s as toApp } from "../shared-_6-uPL7E.js";
|
|
5
|
-
import {
|
|
6
|
+
import { a as sqlIdent, i as slug, o as sqlString, r as parseTemplate, t as permdockCheck } from "../permdock-sql-Bta_G0v-.js";
|
|
6
7
|
import { REALTIME_SUBSCRIBE_STATES } from "@supabase/supabase-js";
|
|
7
|
-
//#region src/realtime/
|
|
8
|
+
//#region src/realtime/topic.ts
|
|
8
9
|
const VALUE = /^[\w.@+=-]+$/;
|
|
9
10
|
function validateValue(_name, value) {
|
|
10
11
|
return VALUE.test(value) ? void 0 : "contains characters topics do not allow";
|
|
@@ -54,7 +55,7 @@ function sendError(status, message) {
|
|
|
54
55
|
*/
|
|
55
56
|
function defineTopic(template, options = {}) {
|
|
56
57
|
const parsed = parseTemplate(template, ":", validateValue);
|
|
57
|
-
const literal = template.
|
|
58
|
+
const literal = template.replaceAll(/\{[^}]+\}/g, " ");
|
|
58
59
|
const name = slug(options.name ?? literal) || "topic";
|
|
59
60
|
const isPrivate = options.private ?? true;
|
|
60
61
|
const schemas = options.events ?? {};
|
|
@@ -64,10 +65,21 @@ function defineTopic(template, options = {}) {
|
|
|
64
65
|
return index;
|
|
65
66
|
};
|
|
66
67
|
const checks = [];
|
|
68
|
+
const permdock = options.permdock;
|
|
67
69
|
const tenantParam = options.tenant === false ? void 0 : options.tenant?.param ?? "orgId";
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const
|
|
70
|
+
const permdockChecks = (() => {
|
|
71
|
+
if (!permdock) return void 0;
|
|
72
|
+
const where = `defineTopic(${template})`;
|
|
73
|
+
const id = permdock.scope === "global" ? void 0 : `split_part((select realtime.topic()), ':', ${String(permdock.segment ?? segment(tenantParam ?? "orgId", "PermDock"))})`;
|
|
74
|
+
const receive = permdockCheck(where, permdock, permdock.receive, id, options.catalog);
|
|
75
|
+
return {
|
|
76
|
+
receive,
|
|
77
|
+
send: permdock.send === void 0 ? receive : permdockCheck(where, permdock, permdock.send, id, options.catalog)
|
|
78
|
+
};
|
|
79
|
+
})();
|
|
80
|
+
if (!permdock && tenantParam && (options.tenant || parsed.params.includes(tenantParam))) {
|
|
81
|
+
const tenant = options.tenant === false ? {} : options.tenant ?? {};
|
|
82
|
+
const expression = tenant.sql ?? claimSql(tenant.claim ?? tenantClaimPaths());
|
|
71
83
|
checks.push(`split_part((select realtime.topic()), ':', ${String(segment(tenantParam, "tenant"))}) = (${expression})`);
|
|
72
84
|
}
|
|
73
85
|
const ownerParam = options.owner === false ? void 0 : options.owner?.param ?? "userId";
|
|
@@ -82,25 +94,28 @@ function defineTopic(template, options = {}) {
|
|
|
82
94
|
match: (topic) => parsed.match(topic),
|
|
83
95
|
sql() {
|
|
84
96
|
const extensions = options.presence ? "('broadcast', 'presence')" : "('broadcast')";
|
|
85
|
-
const condition = [
|
|
97
|
+
const condition = (extra) => [
|
|
86
98
|
`(select realtime.topic()) ~ ${sqlString(parsed.sqlPattern)}`,
|
|
87
99
|
`realtime.messages.extension in ${extensions}`,
|
|
88
|
-
...checks
|
|
100
|
+
...checks,
|
|
101
|
+
...extra === void 0 ? [] : [extra]
|
|
89
102
|
].join("\n and ");
|
|
90
103
|
const lines = [`-- better-supabase: topic ${template}`];
|
|
91
104
|
const policies = [[
|
|
92
105
|
"receive",
|
|
93
106
|
"select",
|
|
94
|
-
"using"
|
|
107
|
+
"using",
|
|
108
|
+
condition(permdockChecks?.receive)
|
|
95
109
|
]];
|
|
96
|
-
if (options.send || options.presence) policies.push([
|
|
110
|
+
if (options.send || options.presence || permdock?.send !== void 0) policies.push([
|
|
97
111
|
"send",
|
|
98
112
|
"insert",
|
|
99
|
-
"with check"
|
|
113
|
+
"with check",
|
|
114
|
+
condition(permdockChecks?.send)
|
|
100
115
|
]);
|
|
101
|
-
for (const [suffix, command, clause] of policies) {
|
|
116
|
+
for (const [suffix, command, clause, check] of policies) {
|
|
102
117
|
const policy = sqlIdent(`bs_topic_${name}_${suffix}`);
|
|
103
|
-
lines.push(`drop policy if exists ${policy} on realtime.messages;`, `create policy ${policy} on realtime.messages for ${command} to authenticated\n ${clause} (\n ${
|
|
118
|
+
lines.push(`drop policy if exists ${policy} on realtime.messages;`, `create policy ${policy} on realtime.messages for ${command} to authenticated\n ${clause} (\n ${check}\n );`);
|
|
104
119
|
}
|
|
105
120
|
return `${lines.join("\n")}\n`;
|
|
106
121
|
},
|
|
@@ -116,7 +131,7 @@ function defineTopic(template, options = {}) {
|
|
|
116
131
|
if (!db) throw new TypeError(`defineTopic: no column for {${param}} on "${table}"`);
|
|
117
132
|
return `rec.${sqlIdent(db)}::text`;
|
|
118
133
|
});
|
|
119
|
-
const schema = trigger.functionSchema ?? "
|
|
134
|
+
const schema = trigger.functionSchema ?? "better_supabase";
|
|
120
135
|
const fn = `${sqlIdent(schema)}.${sqlIdent(`bs_broadcast_${name}_${slug(meta.name)}`)}`;
|
|
121
136
|
const triggerName = sqlIdent(`bs_broadcast_${name}`);
|
|
122
137
|
const target = `${sqlIdent(meta.schema)}.${sqlIdent(meta.name)}`;
|
|
@@ -127,6 +142,7 @@ function defineTopic(template, options = {}) {
|
|
|
127
142
|
]).join(" or ");
|
|
128
143
|
return [
|
|
129
144
|
`-- better-supabase: broadcast ${meta.name} changes to ${template}`,
|
|
145
|
+
`create schema if not exists ${sqlIdent(schema)};`,
|
|
130
146
|
`create or replace function ${fn}()`,
|
|
131
147
|
"returns trigger",
|
|
132
148
|
"language plpgsql",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { p as DbError } from "./result-
|
|
2
|
-
import { l as RefreshEvent } from "./events-
|
|
3
|
-
import { h as RequestContext } from "./plugin-
|
|
4
|
-
import { t as StandardSchemaV1 } from "./standard-
|
|
1
|
+
import { p as DbError } from "./result-c2OYgCV2.js";
|
|
2
|
+
import { f as Logger, l as RefreshEvent } from "./events-Do8jXh4D.js";
|
|
3
|
+
import { h as RequestContext } from "./plugin-DVRB8qNE.js";
|
|
4
|
+
import { t as StandardSchemaV1 } from "./standard-eudBiRk1.js";
|
|
5
5
|
import { t as BetterSupabaseEnv } from "./index-Dy6kuF0P.js";
|
|
6
6
|
import { CookieOptions } from "@supabase/ssr";
|
|
7
7
|
import { JWTClaims, SupabaseEnv, UserClaims } from "@supabase/server";
|
|
@@ -47,9 +47,10 @@ declare function applyCookieWrites(cookies: readonly CookieRecord[], writes: rea
|
|
|
47
47
|
//#region src/auth/resolve.d.ts
|
|
48
48
|
/**
|
|
49
49
|
* Who is calling. `C` is the claims type from `sb.claims(schema)`; the
|
|
50
|
-
* verified payload keeps every JWT claim and adds the schema's output.
|
|
50
|
+
* verified payload keeps every JWT claim and adds the schema's output. `P`
|
|
51
|
+
* is the `user_metadata` type from `sb.userMetadata(schema)`.
|
|
51
52
|
*/
|
|
52
|
-
type AuthState<C = unknown> = {
|
|
53
|
+
type AuthState<C = unknown, P = unknown> = {
|
|
53
54
|
readonly kind: "user";
|
|
54
55
|
readonly token: string;
|
|
55
56
|
readonly claims: JWTClaims & C;
|
|
@@ -57,6 +58,12 @@ type AuthState<C = unknown> = {
|
|
|
57
58
|
readonly source: "bearer" | "cookie" | (string & {});
|
|
58
59
|
/** Seconds since epoch, from the token's `exp`. */
|
|
59
60
|
readonly expiresAt: number | null;
|
|
61
|
+
/**
|
|
62
|
+
* `user_metadata` parsed by `sb.userMetadata(schema)`; `undefined`
|
|
63
|
+
* without a schema or when the metadata fails it. Users can write this
|
|
64
|
+
* data themselves: use it for display, never for access.
|
|
65
|
+
*/
|
|
66
|
+
readonly profile?: P;
|
|
60
67
|
} | {
|
|
61
68
|
readonly kind: "service";
|
|
62
69
|
readonly keyName: string;
|
|
@@ -69,7 +76,7 @@ type AuthState<C = unknown> = {
|
|
|
69
76
|
readonly reason: InvalidReason;
|
|
70
77
|
readonly error: DbError;
|
|
71
78
|
};
|
|
72
|
-
type InvalidReason = "token" | "claims";
|
|
79
|
+
type InvalidReason = "token" | "claims" | "actor";
|
|
73
80
|
/**
|
|
74
81
|
* What an `AuthResolver` returns. `reason` may be left out of an `invalid`
|
|
75
82
|
* state; it then counts as `token`.
|
|
@@ -114,6 +121,14 @@ interface ResolveAuthOptions {
|
|
|
114
121
|
* resolves to `{ kind: 'invalid', reason: 'claims' }`.
|
|
115
122
|
*/
|
|
116
123
|
readonly claims?: StandardSchemaV1;
|
|
124
|
+
/**
|
|
125
|
+
* Parses `user_metadata` into `profile` (`sb.userMetadata(schema)` sets
|
|
126
|
+
* it). A failure leaves `profile` undefined and warns once; the session
|
|
127
|
+
* stays valid.
|
|
128
|
+
*/
|
|
129
|
+
readonly userMetadata?: StandardSchemaV1;
|
|
130
|
+
/** Receives the `userMetadata` warning. Defaults to `console`. */
|
|
131
|
+
readonly logger?: Logger;
|
|
117
132
|
readonly resolvers?: readonly AuthResolver[];
|
|
118
133
|
/**
|
|
119
134
|
* The caller's IP, sent to Auth as `Sb-Forwarded-For` on refreshes so its
|
|
@@ -125,10 +140,11 @@ interface ResolveAuthOptions {
|
|
|
125
140
|
/** Called after every refresh attempt (metrics, logging). */
|
|
126
141
|
readonly onRefresh?: (event: RefreshEvent) => void;
|
|
127
142
|
readonly fetch?: typeof fetch;
|
|
143
|
+
/** Epoch milliseconds, the unit JWT `exp` and `iat` math needs. */
|
|
128
144
|
readonly now?: () => number;
|
|
129
145
|
}
|
|
130
|
-
interface AuthResolution<C = unknown> {
|
|
131
|
-
readonly auth: AuthState<C>;
|
|
146
|
+
interface AuthResolution<C = unknown, P = unknown> {
|
|
147
|
+
readonly auth: AuthState<C, P>;
|
|
132
148
|
/** Cookie writes for the response. Empty unless the session was refreshed or cleared. */
|
|
133
149
|
readonly cookies: readonly CookieWrite[];
|
|
134
150
|
/** No-store headers, set whenever `cookies` is not empty. */
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { b as ValidationIssue, o as Result } from "./result-
|
|
3
|
-
import { t as BetterSupabase, xt as SelectArg } from "./define-
|
|
4
|
-
import { r as RespondOptions } from "./respond-
|
|
5
|
-
import { i as ResourceOperation } from "./index-
|
|
1
|
+
import { N as TableKey, P as TableMeta, R as Update, g as Insert, n as AnyModels, t as AnyFunctions } from "./types-DONv9SHN.js";
|
|
2
|
+
import { b as ValidationIssue, o as Result } from "./result-c2OYgCV2.js";
|
|
3
|
+
import { t as BetterSupabase, xt as SelectArg } from "./define-DUbydmJa.js";
|
|
4
|
+
import { r as RespondOptions } from "./respond-DAkPIdTH.js";
|
|
5
|
+
import { i as ResourceOperation } from "./index-qriRM9cZ.js";
|
|
6
6
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
7
7
|
//#region src/server/resource.d.ts
|
|
8
8
|
/** What a resource needs from `defineListQuery`. */
|
|
9
9
|
interface ResourceList {
|
|
10
10
|
readonly table: string;
|
|
11
|
+
readonly pagination?: ResourcePagination;
|
|
11
12
|
readonly openapi: readonly unknown[];
|
|
12
13
|
readonly jsonSchema: Readonly<Record<string, unknown>>;
|
|
13
14
|
parse(input: never): {
|
|
@@ -31,9 +32,16 @@ interface ResourceRouteOptions<M extends AnyModels, T extends TableKey<M>> {
|
|
|
31
32
|
readonly create?: StandardSchemaV1<unknown, Insert<M, T>>;
|
|
32
33
|
readonly update?: StandardSchemaV1<unknown, Update<M, T>>;
|
|
33
34
|
};
|
|
34
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* Paging of the default list: `offset` (`page` and `size`) or `cursor`
|
|
37
|
+
* (`after` and `size`, keyset). A `list` query brings its own. Defaults
|
|
38
|
+
* to `offset`.
|
|
39
|
+
*/
|
|
40
|
+
readonly pagination?: ResourcePagination;
|
|
41
|
+
/** Largest page for the default list. Defaults to 200. */
|
|
35
42
|
readonly maxPageSize?: number;
|
|
36
43
|
}
|
|
44
|
+
type ResourcePagination = "offset" | "cursor";
|
|
37
45
|
interface ResourceInput {
|
|
38
46
|
/** Primary key value; strings are converted for integer keys. */
|
|
39
47
|
readonly id?: unknown;
|
|
@@ -49,6 +57,7 @@ interface ResourceHandler {
|
|
|
49
57
|
/** Name of the key parameter, or `undefined` for tables without a single-column key. */
|
|
50
58
|
readonly keyParam: string | undefined;
|
|
51
59
|
readonly list: ResourceList | undefined;
|
|
60
|
+
readonly pagination: ResourcePagination;
|
|
52
61
|
readonly maxPageSize: number;
|
|
53
62
|
/** Runs one operation as whoever `db` is bound to. Resolves to a `Result`. */
|
|
54
63
|
execute(db: object, operation: ResourceOperation, input?: ResourceInput): Promise<Result<unknown>>;
|
|
@@ -62,4 +71,4 @@ interface ResourceHandler {
|
|
|
62
71
|
*/
|
|
63
72
|
declare function defineResource<M extends AnyModels, D, F extends AnyFunctions, E, T extends TableKey<M>>(sb: BetterSupabase<M, D, F, E>, table: T, options?: ResourceRouteOptions<M, T>): ResourceHandler;
|
|
64
73
|
//#endregion
|
|
65
|
-
export {
|
|
74
|
+
export { ResourceRouteOptions as a, ResourcePagination as i, ResourceInput as n, defineResource as o, ResourceList as r, ResourceHandler as t };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { n as dbError } from "./errors-Bl3rkOzo.js";
|
|
2
2
|
import { i as ok, n as err } from "./result-DWXatkd6.js";
|
|
3
3
|
import { t as validate } from "./standard-FUi3Z3-7.js";
|
|
4
|
-
import { i as respond } from "./respond-
|
|
4
|
+
import { i as respond } from "./respond-C44EZAJo.js";
|
|
5
5
|
//#region src/server/resource.ts
|
|
6
6
|
const INTEGER = /* @__PURE__ */ new Set([
|
|
7
7
|
"int2",
|
|
@@ -24,24 +24,44 @@ async function readBody(request) {
|
|
|
24
24
|
} catch {}
|
|
25
25
|
return err(dbError("invalid_request", "The request body must be a JSON object"));
|
|
26
26
|
}
|
|
27
|
-
function pageArgs(query, max) {
|
|
27
|
+
function pageArgs(query, max, pagination) {
|
|
28
|
+
const raw = (name) => {
|
|
29
|
+
const value = query instanceof URLSearchParams ? query.get(name) : query?.[name];
|
|
30
|
+
return value === null || value === "" ? void 0 : value;
|
|
31
|
+
};
|
|
28
32
|
const read = (name, fallback) => {
|
|
29
|
-
const
|
|
30
|
-
if (
|
|
31
|
-
const
|
|
32
|
-
return Number.isSafeInteger(
|
|
33
|
+
const value = raw(name);
|
|
34
|
+
if (value === void 0) return fallback;
|
|
35
|
+
const number = Number(value);
|
|
36
|
+
return Number.isSafeInteger(number) && number >= 1 ? number : void 0;
|
|
33
37
|
};
|
|
34
|
-
const
|
|
38
|
+
const issues = [];
|
|
35
39
|
const size = read("size", 50);
|
|
36
|
-
if (
|
|
37
|
-
message: "Must be a positive integer",
|
|
38
|
-
path: ["page"]
|
|
39
|
-
}] : [], ...size === void 0 || size > max ? [{
|
|
40
|
+
if (size === void 0 || size > max) issues.push({
|
|
40
41
|
message: `Must be between 1 and ${max}`,
|
|
41
42
|
path: ["size"]
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
});
|
|
44
|
+
const [used, unused] = pagination === "cursor" ? ["after", "page"] : ["page", "after"];
|
|
45
|
+
if (raw(unused) !== void 0) issues.push({
|
|
46
|
+
message: `This list pages with \`${used}\``,
|
|
47
|
+
path: [unused]
|
|
48
|
+
});
|
|
49
|
+
const page = read("page", 1);
|
|
50
|
+
if (pagination === "offset" && page === void 0) issues.push({
|
|
51
|
+
message: "Must be a positive integer",
|
|
52
|
+
path: ["page"]
|
|
53
|
+
});
|
|
54
|
+
const after = raw("after");
|
|
55
|
+
if (pagination === "cursor" && after !== void 0 && typeof after !== "string") issues.push({
|
|
56
|
+
message: "Must be text",
|
|
57
|
+
path: ["after"]
|
|
58
|
+
});
|
|
59
|
+
if (issues.length > 0 || size === void 0) return err(dbError("validation", "Invalid page parameters", { issues }));
|
|
60
|
+
return pagination === "cursor" ? ok({
|
|
61
|
+
after: typeof after === "string" ? after : null,
|
|
62
|
+
size
|
|
63
|
+
}) : ok({
|
|
64
|
+
page: page ?? 1,
|
|
45
65
|
size
|
|
46
66
|
});
|
|
47
67
|
}
|
|
@@ -78,6 +98,7 @@ function defineResource(sb, table, options = {}) {
|
|
|
78
98
|
const extra = options.select === void 0 ? {} : { select: options.select };
|
|
79
99
|
const maxPageSize = options.maxPageSize ?? 200;
|
|
80
100
|
const list = options.list;
|
|
101
|
+
const pagination = list ? list.pagination ?? "offset" : options.pagination ?? "offset";
|
|
81
102
|
const execute = async (db, operation, input = {}) => {
|
|
82
103
|
const repository = db[table];
|
|
83
104
|
if (!repository) throw new TypeError(`The db has no "${table}" repository`);
|
|
@@ -90,12 +111,12 @@ function defineResource(sb, table, options = {}) {
|
|
|
90
111
|
if (!query.ok) return err(dbError("validation", "Invalid list query", { issues: query.issues }));
|
|
91
112
|
return settled(list.run(db, query.value, extra));
|
|
92
113
|
}
|
|
93
|
-
const page = pageArgs(input.query, maxPageSize);
|
|
114
|
+
const page = pageArgs(input.query, maxPageSize, pagination);
|
|
94
115
|
if (!page.ok) return page;
|
|
95
116
|
return settled(repository.paginate({
|
|
96
117
|
...extra,
|
|
97
118
|
...page.data,
|
|
98
|
-
count: "exact"
|
|
119
|
+
..."page" in page.data ? { count: "exact" } : {}
|
|
99
120
|
}));
|
|
100
121
|
}
|
|
101
122
|
case "get": {
|
|
@@ -160,6 +181,7 @@ function defineResource(sb, table, options = {}) {
|
|
|
160
181
|
operations,
|
|
161
182
|
keyParam,
|
|
162
183
|
list,
|
|
184
|
+
pagination,
|
|
163
185
|
maxPageSize,
|
|
164
186
|
execute,
|
|
165
187
|
handle(request, db, id, respondOptions = {}) {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { n as dbError, o as isDbError, r as dbErrorOf } from "./errors-Bl3rkOzo.js";
|
|
2
2
|
import { i as ok, n as err, o as toDbError, t as AsyncResult } from "./result-DWXatkd6.js";
|
|
3
|
-
import { a as postgrestExecutor, n as StatsRecorder } from "./stats-
|
|
4
|
-
import { a as problemResponse } from "./problem-
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { n as postgresExecutor } from "./executor-
|
|
3
|
+
import { a as postgrestExecutor, n as StatsRecorder } from "./stats-D78UQ8Yu.js";
|
|
4
|
+
import { a as problemResponse } from "./problem-CGt1ADix.js";
|
|
5
|
+
import { loadEnv } from "./env/index.js";
|
|
6
|
+
import { _ as actClaim, g as delegationOf, h as actorOf, i as authContext, o as resolveAuth, r as checkAal } from "./mfa-BxoR1RkN.js";
|
|
7
|
+
import { n as postgresExecutor } from "./executor-C57jx2pn.js";
|
|
8
|
+
import { parseCookieHeader, serializeCookieHeader } from "@supabase/ssr";
|
|
8
9
|
import { createClient } from "@supabase/supabase-js";
|
|
9
10
|
//#region src/server/replicas.ts
|
|
10
11
|
/** Cookie holding the epoch ms until which reads stay on the primary. */
|
|
@@ -29,19 +30,18 @@ function replicaState(pinnedUntil = 0, now = Date.now()) {
|
|
|
29
30
|
/** `bs-primary-until` from a `cookie` header, or 0. */
|
|
30
31
|
function pinnedUntil(cookieHeader) {
|
|
31
32
|
if (!cookieHeader) return 0;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const until = Number(value);
|
|
36
|
-
return Number.isFinite(until) ? until : 0;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return 0;
|
|
33
|
+
const value = parseCookieHeader(cookieHeader).find((cookie) => cookie.name === PRIMARY_COOKIE)?.value;
|
|
34
|
+
const until = Number(value);
|
|
35
|
+
return value !== void 0 && Number.isFinite(until) ? until : 0;
|
|
40
36
|
}
|
|
41
37
|
/** A `Set-Cookie` value keeping the next requests on the primary for `pinMs`. */
|
|
42
38
|
function primaryCookie(pinMs, now = Date.now()) {
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
return serializeCookieHeader(PRIMARY_COOKIE, String(now + pinMs), {
|
|
40
|
+
path: "/",
|
|
41
|
+
maxAge: Math.max(1, Math.ceil(pinMs / 1e3)),
|
|
42
|
+
httpOnly: true,
|
|
43
|
+
sameSite: "lax"
|
|
44
|
+
});
|
|
45
45
|
}
|
|
46
46
|
/**
|
|
47
47
|
* Reads go to `replica` until the context is pinned; writes, and every read
|
|
@@ -248,7 +248,7 @@ function createServer(sb, options = {}) {
|
|
|
248
248
|
return replica;
|
|
249
249
|
},
|
|
250
250
|
get sql() {
|
|
251
|
-
if (!options.postgres || !sqlClaims) return
|
|
251
|
+
if (!options.postgres || !sqlClaims) return;
|
|
252
252
|
sql ??= sqlFor(sqlClaims, context, recorder);
|
|
253
253
|
return sql;
|
|
254
254
|
},
|
|
@@ -257,11 +257,14 @@ function createServer(sb, options = {}) {
|
|
|
257
257
|
};
|
|
258
258
|
const resolve = async (request, resolveOptions = {}) => {
|
|
259
259
|
const claims = options.auth?.claims ?? sb.claimsSchema;
|
|
260
|
+
const userMetadata = sb.userMetadataSchema;
|
|
260
261
|
const resolution = await resolveAuth(request, {
|
|
262
|
+
logger: sb.events.logger,
|
|
261
263
|
...options.auth,
|
|
262
264
|
...claims ? { claims } : {},
|
|
265
|
+
...userMetadata ? { userMetadata } : {},
|
|
263
266
|
env: env(),
|
|
264
|
-
...resolveOptions.refresh
|
|
267
|
+
...resolveOptions.refresh === void 0 ? {} : { refresh: resolveOptions.refresh },
|
|
265
268
|
onRefresh: (event) => {
|
|
266
269
|
options.auth?.onRefresh?.(event);
|
|
267
270
|
sb.events.emit("refresh", event);
|
|
@@ -326,10 +329,26 @@ function createServer(sb, options = {}) {
|
|
|
326
329
|
}
|
|
327
330
|
//#endregion
|
|
328
331
|
//#region src/server/respond.ts
|
|
332
|
+
/** The scopes a delegated user token lacks; empty for the user's own session. */
|
|
333
|
+
function missingScopes(auth, required) {
|
|
334
|
+
if (required.length === 0) return [];
|
|
335
|
+
const outcome = actorOf(auth.claims);
|
|
336
|
+
if (!outcome.ok || !outcome.actor) return [];
|
|
337
|
+
const granted = new Set(delegationOf(auth.claims)?.scopes);
|
|
338
|
+
return required.filter((scope) => !granted.has(scope));
|
|
339
|
+
}
|
|
329
340
|
/** `undefined` when `auth` may pass; otherwise the 401/403 error to send. */
|
|
330
|
-
function guard(auth, allow = ["user"], aal = "aal1") {
|
|
341
|
+
function guard(auth, allow = ["user"], aal = "aal1", scopes = []) {
|
|
331
342
|
if (auth.kind === "invalid") return auth.error;
|
|
332
|
-
if (allow.includes(auth.kind))
|
|
343
|
+
if (allow.includes(auth.kind)) {
|
|
344
|
+
const denied = checkAal(auth, aal);
|
|
345
|
+
if (denied || auth.kind !== "user") return denied;
|
|
346
|
+
const missing = missingScopes(auth, scopes);
|
|
347
|
+
return missing.length > 0 ? dbError("forbidden", `The token lacks the scope ${missing.join(" ")}`, {
|
|
348
|
+
code: "INSUFFICIENT_SCOPE",
|
|
349
|
+
scopes
|
|
350
|
+
}) : void 0;
|
|
351
|
+
}
|
|
333
352
|
return auth.kind === "anon" ? dbError("unauthorized", "Sign in to continue", { code: "MISSING_CREDENTIALS" }) : dbError("forbidden", `A ${auth.kind} caller is not allowed here`);
|
|
334
353
|
}
|
|
335
354
|
function isResult(value) {
|