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/next/index.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { n as AnyModels, t as AnyFunctions } from "../types-
|
|
2
|
-
import { p as DbError } from "../result-
|
|
3
|
-
import { B as ReadSet, P as DbStats, Z as QuerySpec, l as CacheAdapter, t as BetterSupabase } from "../define-
|
|
4
|
-
import { n as AmrEntry, s as Impersonator, t as Aal } from "../mfa-
|
|
5
|
-
import { r as AuthState } from "../resolve-
|
|
6
|
-
import { d as ServerContext, f as ServerOptions, l as BetterServer, n as GuardOptions, t as AuthKind } from "../respond-
|
|
7
|
-
import { n as toSession, t as AuthSession } from "../view-
|
|
8
|
-
import { r as LiveCountSeed, t as CountRunner } from "../live-
|
|
9
|
-
import { n as hasEntitlement, t as EntitlementKey } from "../entitlements-
|
|
1
|
+
import { n as AnyModels, t as AnyFunctions } from "../types-DONv9SHN.js";
|
|
2
|
+
import { p as DbError } from "../result-c2OYgCV2.js";
|
|
3
|
+
import { B as ReadSet, P as DbStats, Z as QuerySpec, l as CacheAdapter, t as BetterSupabase } from "../define-DUbydmJa.js";
|
|
4
|
+
import { n as AmrEntry, s as Impersonator, t as Aal } from "../mfa-x3V3vGh-.js";
|
|
5
|
+
import { r as AuthState } from "../resolve-BRciX2jK.js";
|
|
6
|
+
import { d as ServerContext, f as ServerOptions, l as BetterServer, n as GuardOptions, t as AuthKind } from "../respond-DAkPIdTH.js";
|
|
7
|
+
import { n as toSession, t as AuthSession } from "../view-3Po_PVlr.js";
|
|
8
|
+
import { r as LiveCountSeed, t as CountRunner } from "../live-QE3UPbGm.js";
|
|
9
|
+
import { n as MembershipClaim, r as hasEntitlement, t as EntitlementKey } from "../entitlements-DvJXoI02.js";
|
|
10
10
|
import { NextRequest } from "next/server.js";
|
|
11
11
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
12
12
|
//#region src/next/collector.d.ts
|
|
@@ -17,8 +17,8 @@ interface DbBudget {
|
|
|
17
17
|
readonly waves?: number;
|
|
18
18
|
}
|
|
19
19
|
//#endregion
|
|
20
|
-
//#region src/next/
|
|
21
|
-
|
|
20
|
+
//#region src/next/create.d.ts
|
|
21
|
+
interface NextOptions extends ServerOptions {
|
|
22
22
|
/** Invalidate `tagFor(table)` cache tags after mutations. Defaults to true. */
|
|
23
23
|
readonly cacheTags?: boolean;
|
|
24
24
|
/** Include internal error messages in Problem Details. Defaults to development only. */
|
|
@@ -26,7 +26,7 @@ export interface NextOptions extends ServerOptions {
|
|
|
26
26
|
/** Per-request database call counts. See the Budget docs. */
|
|
27
27
|
readonly debug?: NextDebugOptions;
|
|
28
28
|
}
|
|
29
|
-
|
|
29
|
+
interface NextDebugOptions {
|
|
30
30
|
/** Collect outside development too, e.g. for e2e runs against `next start`. */
|
|
31
31
|
readonly enabled?: boolean;
|
|
32
32
|
/** Header with `calls;waves;ms` on route handler responses. Defaults to `x-bs-db-calls`. */
|
|
@@ -38,7 +38,7 @@ export interface NextDebugOptions {
|
|
|
38
38
|
}
|
|
39
39
|
/** Response header with the URL of the request's totals on `next.debugRoute()`. */
|
|
40
40
|
export declare const STATS_URL_HEADER = "x-bs-stats";
|
|
41
|
-
|
|
41
|
+
interface ProxyOptions<C = unknown, U = unknown> {
|
|
42
42
|
/**
|
|
43
43
|
* Another middleware to compose with, such as next-intl's. It runs on the
|
|
44
44
|
* original request, alongside auth; its rewrite, redirect or request
|
|
@@ -46,16 +46,16 @@ export interface ProxyOptions<C = unknown> {
|
|
|
46
46
|
*/
|
|
47
47
|
readonly before?: (request: NextRequest) => Response | undefined | Promise<Response | undefined>;
|
|
48
48
|
/** Answer before rendering, e.g. redirect signed-out users. Refreshed cookies are kept. */
|
|
49
|
-
readonly protect?: (auth: AuthState<C>, request: NextRequest) => Response | undefined | Promise<Response | undefined>;
|
|
49
|
+
readonly protect?: (auth: AuthState<C, U>, request: NextRequest) => Response | undefined | Promise<Response | undefined>;
|
|
50
50
|
/** Post-processes the final response; return a new one to replace it. */
|
|
51
|
-
readonly after?: (response: Response, auth: AuthState<C>) => Response | undefined | void | Promise<Response | undefined | void>;
|
|
51
|
+
readonly after?: (response: Response, auth: AuthState<C, U>) => Response | undefined | void | Promise<Response | undefined | void>;
|
|
52
52
|
/**
|
|
53
53
|
* Adds `Server-Timing: bs-proxy;dur=..., bs-verify;dur=...` (ms): the whole
|
|
54
54
|
* proxy, and resolving the session (a local verify, or a refresh).
|
|
55
55
|
*/
|
|
56
56
|
readonly serverTiming?: boolean;
|
|
57
57
|
}
|
|
58
|
-
|
|
58
|
+
type ActionResult<T> = {
|
|
59
59
|
readonly ok: true;
|
|
60
60
|
readonly data: T;
|
|
61
61
|
readonly error: null;
|
|
@@ -64,7 +64,7 @@ export type ActionResult<T> = {
|
|
|
64
64
|
readonly data: null;
|
|
65
65
|
readonly error: DbError;
|
|
66
66
|
};
|
|
67
|
-
|
|
67
|
+
interface ActionOptions<S extends StandardSchemaV1 | undefined> extends GuardOptions {
|
|
68
68
|
/** Validates the input (plain object or `FormData`) with any Standard Schema. */
|
|
69
69
|
readonly input?: S;
|
|
70
70
|
}
|
|
@@ -76,29 +76,30 @@ type Unwrapped<T> = T extends {
|
|
|
76
76
|
} | {
|
|
77
77
|
readonly ok: false;
|
|
78
78
|
} ? D : T;
|
|
79
|
-
|
|
79
|
+
interface BetterNext<M extends AnyModels, F extends AnyFunctions, E, C = unknown, U = unknown> extends BetterServer<M, F, E, C, U> {
|
|
80
80
|
/** `proxy.ts`: refreshes sessions for page loads and server actions. */
|
|
81
|
-
proxy(request: NextRequest, options?: ProxyOptions<C>): Promise<Response>;
|
|
81
|
+
proxy(request: NextRequest, options?: ProxyOptions<C, U>): Promise<Response>;
|
|
82
82
|
/** Server Components and actions: the caller's context, memoized per request. */
|
|
83
|
-
server(): Promise<ServerContext<M, F, E, C>>;
|
|
83
|
+
server(): Promise<ServerContext<M, F, E, C, U>>;
|
|
84
84
|
/**
|
|
85
85
|
* The verified caller as serializable data (no token, no clients), memoized
|
|
86
86
|
* per request. Wrap it in a `'use cache: private'` function with Cache
|
|
87
87
|
* Components and pass the promise to `<SessionProvider>`.
|
|
88
88
|
*/
|
|
89
|
-
session(): Promise<AuthSession<C>>;
|
|
89
|
+
session(): Promise<AuthSession<C, U>>;
|
|
90
90
|
/**
|
|
91
91
|
* The context for a session and its token, without reading the request.
|
|
92
92
|
* The token is verified again (memoized, no network call); a token for
|
|
93
93
|
* another user than `session` gives an `invalid` context.
|
|
94
94
|
*/
|
|
95
|
-
serverFor(session: AuthSession<C>, options: {
|
|
95
|
+
serverFor(session: AuthSession<C, U>, options: {
|
|
96
96
|
readonly token: string | null;
|
|
97
|
-
}): Promise<ServerContext<M, F, E, C>>;
|
|
97
|
+
}): Promise<ServerContext<M, F, E, C, U>>;
|
|
98
98
|
/**
|
|
99
99
|
* First statement of an app-authored `'use cache: private'` function: sets
|
|
100
|
-
* `cacheLife` from the session's expiry (`sessionStale
|
|
101
|
-
* `bs:session:<user id>`
|
|
100
|
+
* `cacheLife` from the session's expiry (`sessionStale`, capped by
|
|
101
|
+
* `life.stale`), tags the entry `bs:session:<user id>` plus `tags`, and
|
|
102
|
+
* returns the caller's context plus `session`.
|
|
102
103
|
*
|
|
103
104
|
* ```ts
|
|
104
105
|
* async function getCustomers() {
|
|
@@ -108,9 +109,14 @@ export interface BetterNext<M extends AnyModels, F extends AnyFunctions, E, C =
|
|
|
108
109
|
* }
|
|
109
110
|
* ```
|
|
110
111
|
*/
|
|
111
|
-
cached(options?: CachedOptions): Promise<CachedContext<M, F, E, C>>;
|
|
112
|
-
/**
|
|
113
|
-
|
|
112
|
+
cached(options?: CachedOptions): Promise<CachedContext<M, F, E, C, U>>;
|
|
113
|
+
/**
|
|
114
|
+
* Drops every `next.cached()` entry of a user, e.g. after a role change,
|
|
115
|
+
* and the entries tagged with `tags` (such as PermDock's `snapshotTag(userId)`).
|
|
116
|
+
*/
|
|
117
|
+
invalidateSession(userId: string, options?: {
|
|
118
|
+
readonly tags?: readonly string[];
|
|
119
|
+
}): void;
|
|
114
120
|
/**
|
|
115
121
|
* Counts on the server and returns a serializable seed for
|
|
116
122
|
* `useLiveCount(seed)`, so the badge renders with a number and the client
|
|
@@ -120,13 +126,13 @@ export interface BetterNext<M extends AnyModels, F extends AnyFunctions, E, C =
|
|
|
120
126
|
*/
|
|
121
127
|
liveCount<T extends Extract<keyof M, string>>(spec: QuerySpec<T, "count", number>, db?: CountRunner): Promise<LiveCountSeed<T>>;
|
|
122
128
|
/** Route handler with auth, Result unwrapping and Problem Details errors. */
|
|
123
|
-
route<P = Record<string, string | string[]>>(handler: (request: NextRequest, ctx: ServerContext<M, F, E, C> & {
|
|
129
|
+
route<P = Record<string, string | string[]>>(handler: (request: NextRequest, ctx: ServerContext<M, F, E, C, U> & {
|
|
124
130
|
readonly params: P;
|
|
125
131
|
}) => unknown, options?: GuardOptions): (request: NextRequest, segment: {
|
|
126
132
|
readonly params: Promise<P>;
|
|
127
133
|
}) => Promise<Response>;
|
|
128
134
|
/** Server action returning a serializable `ActionResult`. */
|
|
129
|
-
action<S extends StandardSchemaV1 | undefined, T>(options: ActionOptions<S>, fn: (input: ActionParsed<S>, ctx: ServerContext<M, F, E, C>) => Promise<T> | T): (input: ActionInput<S>) => Promise<ActionResult<Unwrapped<Awaited<T>>>>;
|
|
135
|
+
action<S extends StandardSchemaV1 | undefined, T>(options: ActionOptions<S>, fn: (input: ActionParsed<S>, ctx: ServerContext<M, F, E, C, U>) => Promise<T> | T): (input: ActionInput<S>) => Promise<ActionResult<Unwrapped<Awaited<T>>>>;
|
|
130
136
|
/** Tags the current `"use cache"` scope with a table (and row) tag. */
|
|
131
137
|
cacheTag(table: Extract<keyof M, string>, id?: string | number): void;
|
|
132
138
|
/**
|
|
@@ -141,7 +147,7 @@ export interface BetterNext<M extends AnyModels, F extends AnyFunctions, E, C =
|
|
|
141
147
|
*/
|
|
142
148
|
debugRoute(): (request: Request) => Promise<Response>;
|
|
143
149
|
}
|
|
144
|
-
|
|
150
|
+
interface SessionStaleOptions {
|
|
145
151
|
/** Below this, the result would not be prefetched anyway. Defaults to 30. */
|
|
146
152
|
readonly min?: number;
|
|
147
153
|
/** 300 (5 minutes) joins the route's App Shell. Defaults to 300. */
|
|
@@ -152,15 +158,22 @@ export interface SessionStaleOptions {
|
|
|
152
158
|
* the token's expiry, and at least `min`.
|
|
153
159
|
*/
|
|
154
160
|
export declare function sessionStale(session: AuthSession, options?: SessionStaleOptions, now?: number): number;
|
|
155
|
-
|
|
156
|
-
/**
|
|
161
|
+
interface CachedOptions {
|
|
162
|
+
/**
|
|
163
|
+
* `cacheLife` for the entry. `stale` is `sessionStale`, and never more than
|
|
164
|
+
* `stale` when it is set: pass PermDock's `cacheLifeFor(snapshot)` so the
|
|
165
|
+
* entry goes stale with the snapshot too.
|
|
166
|
+
*/
|
|
157
167
|
readonly life?: SessionStaleOptions & {
|
|
168
|
+
readonly stale?: number;
|
|
158
169
|
readonly revalidate?: number;
|
|
159
170
|
readonly expire?: number;
|
|
160
171
|
};
|
|
172
|
+
/** More `cacheTag`s for the entry, e.g. `snapshotTag(sub)` or `org:<id>`. */
|
|
173
|
+
readonly tags?: readonly string[];
|
|
161
174
|
}
|
|
162
|
-
|
|
163
|
-
readonly session: AuthSession<C>;
|
|
175
|
+
type CachedContext<M extends AnyModels, F extends AnyFunctions, E, C = unknown, U = unknown> = ServerContext<M, F, E, C, U> & {
|
|
176
|
+
readonly session: AuthSession<C, U>;
|
|
164
177
|
};
|
|
165
178
|
/** The tag `next.cached()` puts on a user's entries. */
|
|
166
179
|
export declare function sessionTag(userId: string): string;
|
|
@@ -172,7 +185,7 @@ export declare function tagFor(table: string, id?: string | number): string;
|
|
|
172
185
|
* `cacheTags: false`.
|
|
173
186
|
*/
|
|
174
187
|
export declare function nextCache(): CacheAdapter;
|
|
175
|
-
|
|
188
|
+
interface RequireAalOptions {
|
|
176
189
|
/**
|
|
177
190
|
* Page that enrolls or challenges a second factor. Signed-in users below
|
|
178
191
|
* the level are redirected there with `?next=<path>`; without it they get
|
|
@@ -205,6 +218,6 @@ export declare function shouldRefresh(request: Request): boolean;
|
|
|
205
218
|
* export const next = createNext(sb);
|
|
206
219
|
* ```
|
|
207
220
|
*/
|
|
208
|
-
export declare function createNext<M extends AnyModels, D, F extends AnyFunctions, E, C = unknown>(sb: BetterSupabase<M, D, F, E, C>, options?: NextOptions): BetterNext<M, F, E, C>;
|
|
221
|
+
export declare function createNext<M extends AnyModels, D, F extends AnyFunctions, E, C = unknown, U = unknown>(sb: BetterSupabase<M, D, F, E, C, U>, options?: NextOptions): BetterNext<M, F, E, C, U>;
|
|
209
222
|
//#endregion
|
|
210
|
-
export { type Aal, type AmrEntry, type AuthKind, type AuthSession, type DbBudget, type DbStats, type EntitlementKey, type GuardOptions, type Impersonator, hasEntitlement, toSession };
|
|
223
|
+
export { type Aal, type ActionOptions, type ActionResult, type AmrEntry, type AuthKind, type AuthSession, type BetterNext, type CachedContext, type CachedOptions, type DbBudget, type DbStats, type EntitlementKey, type GuardOptions, type Impersonator, type MembershipClaim, type NextDebugOptions, type NextOptions, type ProxyOptions, type RequireAalOptions, type SessionStaleOptions, hasEntitlement, toSession };
|
package/dist/next/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { n as dbError } from "../errors-Bl3rkOzo.js";
|
|
2
|
-
import { n as StatsRecorder, t as EMPTY_STATS } from "../stats-
|
|
3
|
-
import { o as isReadSet } from "../read-set-
|
|
2
|
+
import { n as StatsRecorder, t as EMPTY_STATS } from "../stats-D78UQ8Yu.js";
|
|
3
|
+
import { o as isReadSet } from "../read-set-D0qBcboM.js";
|
|
4
4
|
import { t as validate } from "../standard-FUi3Z3-7.js";
|
|
5
|
-
import { a as problemResponse } from "../problem-
|
|
6
|
-
import { t as
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
5
|
+
import { a as problemResponse } from "../problem-CGt1ADix.js";
|
|
6
|
+
import { t as isList } from "../guards-BnzDvFrz.js";
|
|
7
|
+
import { t as hasEntitlement } from "../entitlements-CTEBm7IB.js";
|
|
8
|
+
import { a as settle, c as withExtra, d as primaryCookie, i as respond, n as guard, o as createServer, s as extendServer, t as defaultExpose, u as PRIMARY_COOKIE } from "../respond-C44EZAJo.js";
|
|
9
|
+
import { r as checkAal } from "../mfa-BxoR1RkN.js";
|
|
10
|
+
import { t as toSession } from "../view-wZ-9qIUz.js";
|
|
10
11
|
import { cache } from "react";
|
|
11
12
|
import { cacheLife, cacheTag, revalidateTag, updateTag } from "next/cache.js";
|
|
12
13
|
import { cookies, headers } from "next/headers.js";
|
|
@@ -42,7 +43,9 @@ var StatsCollector = class {
|
|
|
42
43
|
this.#prune();
|
|
43
44
|
const entry = {
|
|
44
45
|
created: Date.now(),
|
|
45
|
-
recorder: new CollectedRecorder(() =>
|
|
46
|
+
recorder: new CollectedRecorder(() => {
|
|
47
|
+
this.#schedule(id);
|
|
48
|
+
})
|
|
46
49
|
};
|
|
47
50
|
this.#entries.set(id, entry);
|
|
48
51
|
return entry.recorder;
|
|
@@ -139,7 +142,7 @@ function serverTimingValue(metrics) {
|
|
|
139
142
|
return Object.entries(metrics).map(([name, ms]) => `${name};dur=${ms.toFixed(1)}`).join(", ");
|
|
140
143
|
}
|
|
141
144
|
//#endregion
|
|
142
|
-
//#region src/next/
|
|
145
|
+
//#region src/next/create.ts
|
|
143
146
|
/** Response header with the URL of the request's totals on `next.debugRoute()`. */
|
|
144
147
|
const STATS_URL_HEADER = "x-bs-stats";
|
|
145
148
|
/**
|
|
@@ -207,10 +210,10 @@ function formDataObject(form) {
|
|
|
207
210
|
function requireAal(level, options = {}) {
|
|
208
211
|
return (auth, request) => {
|
|
209
212
|
const { pathname, search } = request.nextUrl;
|
|
210
|
-
if (options.redirect !== void 0 && pathname === options.redirect) return
|
|
211
|
-
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;
|
|
212
215
|
const denied = checkAal(auth, level);
|
|
213
|
-
if (!denied) return
|
|
216
|
+
if (!denied) return;
|
|
214
217
|
if (options.redirect === void 0) return problemResponse(denied);
|
|
215
218
|
const target = new URL(options.redirect, request.url);
|
|
216
219
|
target.searchParams.set("next", `${pathname}${search}`);
|
|
@@ -243,8 +246,8 @@ function createNext(sb, options = {}) {
|
|
|
243
246
|
const debug = options.debug;
|
|
244
247
|
const statsHeader = debug?.header ?? "x-bs-db-calls";
|
|
245
248
|
const statsRoute = debug?.route ?? "/api/bs-stats";
|
|
246
|
-
const development =
|
|
247
|
-
const collector = debug
|
|
249
|
+
const development = process.env["NODE_ENV"] === "development";
|
|
250
|
+
const collector = debug?.enabled ? sharedCollector({
|
|
248
251
|
logger: sb.events.logger,
|
|
249
252
|
warn: development,
|
|
250
253
|
...debug.budget ? { budget: debug.budget } : {}
|
|
@@ -283,16 +286,19 @@ function createNext(sb, options = {}) {
|
|
|
283
286
|
async cached(cachedOptions = {}) {
|
|
284
287
|
const [view, ctx] = await Promise.all([session(), server()]);
|
|
285
288
|
const life = cachedOptions.life;
|
|
289
|
+
const stale = sessionStale(view, life);
|
|
286
290
|
cacheLife({
|
|
287
|
-
stale:
|
|
291
|
+
stale: life?.stale === void 0 ? stale : Math.min(stale, life.stale),
|
|
288
292
|
...life?.revalidate === void 0 ? {} : { revalidate: life.revalidate },
|
|
289
293
|
...life?.expire === void 0 ? {} : { expire: life.expire }
|
|
290
294
|
});
|
|
291
|
-
|
|
295
|
+
const tags = [...view.kind === "user" ? [sessionTag(view.user.id)] : [], ...cachedOptions.tags ?? []];
|
|
296
|
+
if (tags.length > 0) cacheTag(...tags);
|
|
292
297
|
return withExtra(ctx, { session: view });
|
|
293
298
|
},
|
|
294
|
-
invalidateSession(userId) {
|
|
299
|
+
invalidateSession(userId, invalidateOptions = {}) {
|
|
295
300
|
invalidate(sessionTag(userId));
|
|
301
|
+
for (const tag of invalidateOptions.tags ?? []) invalidate(tag);
|
|
296
302
|
},
|
|
297
303
|
async liveCount(spec, db) {
|
|
298
304
|
const result = await (db ?? (await server()).db).$run(spec);
|
|
@@ -336,7 +342,7 @@ function createNext(sb, options = {}) {
|
|
|
336
342
|
route(handler, guardOptions = {}) {
|
|
337
343
|
return async (request, segment) => {
|
|
338
344
|
const ctx = await base.context(request, statsFor(request));
|
|
339
|
-
const denied = guard(ctx.auth, guardOptions.allow, guardOptions.aal);
|
|
345
|
+
const denied = guard(ctx.auth, guardOptions.allow, guardOptions.aal, guardOptions.scopes);
|
|
340
346
|
const instance = request.nextUrl.pathname;
|
|
341
347
|
if (denied) return problemResponse(denied, {
|
|
342
348
|
instance,
|
|
@@ -357,23 +363,23 @@ function createNext(sb, options = {}) {
|
|
|
357
363
|
};
|
|
358
364
|
},
|
|
359
365
|
debugRoute() {
|
|
360
|
-
return
|
|
366
|
+
return (request) => {
|
|
361
367
|
const id = new URL(request.url).searchParams.get("id");
|
|
362
|
-
if (!collector || !id) return Response.json({ error: collector ? "missing ?id=" : "debug is off" }, {
|
|
368
|
+
if (!collector || !id) return Promise.resolve(Response.json({ error: collector ? "missing ?id=" : "debug is off" }, {
|
|
363
369
|
status: 404,
|
|
364
370
|
headers: { "cache-control": "no-store" }
|
|
365
|
-
});
|
|
371
|
+
}));
|
|
366
372
|
const stats = collector.get(id) ?? EMPTY_STATS;
|
|
367
|
-
return Response.json(stats, { headers: {
|
|
373
|
+
return Promise.resolve(Response.json(stats, { headers: {
|
|
368
374
|
[statsHeader]: formatStats(stats),
|
|
369
375
|
"cache-control": "no-store"
|
|
370
|
-
} });
|
|
376
|
+
} }));
|
|
371
377
|
};
|
|
372
378
|
},
|
|
373
379
|
action(actionOptions, fn) {
|
|
374
380
|
return async (input) => {
|
|
375
381
|
const ctx = await server();
|
|
376
|
-
const denied = guard(ctx.auth, actionOptions.allow, actionOptions.aal);
|
|
382
|
+
const denied = guard(ctx.auth, actionOptions.allow, actionOptions.aal, actionOptions.scopes);
|
|
377
383
|
if (denied) return {
|
|
378
384
|
ok: false,
|
|
379
385
|
data: null,
|
|
@@ -411,7 +417,7 @@ function createNext(sb, options = {}) {
|
|
|
411
417
|
cacheTag(tagFor(table), ...id === void 0 ? [] : [tagFor(table, id)]);
|
|
412
418
|
},
|
|
413
419
|
cacheTags(target) {
|
|
414
|
-
const specs = isReadSet(target) ? Object.values(target.specs) :
|
|
420
|
+
const specs = isReadSet(target) ? Object.values(target.specs) : isList(target) ? target : [target];
|
|
415
421
|
const tags = /* @__PURE__ */ new Set();
|
|
416
422
|
for (const spec of specs) {
|
|
417
423
|
for (const table of sb.tablesOf(spec)) tags.add(tagFor(table));
|
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-qriRM9cZ.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-
|
|
2
|
-
import { t as SPEC_PINS } from "../spec-pins-
|
|
3
|
-
import { t as buildJsonSchema } from "../json-schema
|
|
1
|
+
import { n as PROBLEM_TYPE_BASE, t as PROBLEM_CONTENT_TYPE } from "../problem-CGt1ADix.js";
|
|
2
|
+
import { t as SPEC_PINS } from "../spec-pins-C3oc-Tdp.js";
|
|
3
|
+
import { t as buildJsonSchema } from "../json-schema--aOXAGfU.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,11 +120,53 @@ 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
|
|
@@ -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 { d as ServerContext, f as ServerOptions, l as BetterServer, n as GuardOptions } from "../respond-
|
|
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-c2OYgCV2.js";
|
|
3
|
+
import { t as BetterSupabase } from "../define-DUbydmJa.js";
|
|
4
|
+
import { r as AuthState } from "../resolve-BRciX2jK.js";
|
|
5
|
+
import { d as ServerContext, f as ServerOptions, l as BetterServer, n as GuardOptions } from "../respond-DAkPIdTH.js";
|
|
6
|
+
import { r as ProblemDetails } from "../problem-Z2RTQjZD.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. */
|
package/dist/orpc/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { r as dbErrorOf } from "../errors-Bl3rkOzo.js";
|
|
2
|
-
import { o as toProblem } from "../problem-
|
|
3
|
-
import { a as settle, n as guard, o as createServer, s as extendServer, t as defaultExpose } from "../respond-
|
|
2
|
+
import { o as toProblem } from "../problem-CGt1ADix.js";
|
|
3
|
+
import { a as settle, n as guard, o as createServer, s as extendServer, t as defaultExpose } from "../respond-C44EZAJo.js";
|
|
4
4
|
import { COMMON_ERROR_STATUS_MAP, ORPCError, os } from "@orpc/server";
|
|
5
5
|
//#region src/orpc/index.ts
|
|
6
6
|
const CODES = new Map(Object.entries(COMMON_ERROR_STATUS_MAP).map(([code, status]) => [status, code]));
|
|
@@ -24,7 +24,7 @@ function createOrpc(sb, options = {}) {
|
|
|
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);
|
|
27
|
+
const denied = guard(ctx.auth, middlewareOptions.allow, middlewareOptions.aal, middlewareOptions.scopes);
|
|
28
28
|
if (denied) throw toORPCError(denied);
|
|
29
29
|
try {
|
|
30
30
|
return await next({ context: {
|
package/dist/otel/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as EventHub } from "../events-
|
|
2
|
-
import { f as Plugin } from "../plugin-
|
|
3
|
-
import { t as SPEC_PINS } from "../spec-pins-
|
|
1
|
+
import { a as EventHub } from "../events-Do8jXh4D.js";
|
|
2
|
+
import { f as Plugin } from "../plugin-DVRB8qNE.js";
|
|
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
|
|
6
6
|
export declare const INSTRUMENTATION_NAME = "better-supabase";
|
package/dist/otel/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as definePlugin } from "../plugin-BqR6wKMB.js";
|
|
2
|
-
import { t as SPEC_PINS } from "../spec-pins-
|
|
3
|
-
import "../version-
|
|
2
|
+
import { t as SPEC_PINS } from "../spec-pins-C3oc-Tdp.js";
|
|
3
|
+
import "../version-BfpG2gks.js";
|
|
4
4
|
import { SpanKind, SpanStatusCode, context, metrics, propagation, trace } from "@opentelemetry/api";
|
|
5
5
|
//#region src/otel/index.ts
|
|
6
6
|
const INSTRUMENTATION_NAME = "better-supabase";
|
|
@@ -157,7 +157,9 @@ function traceAuth(sb, options = {}) {
|
|
|
157
157
|
function tracedFetch(base = fetch) {
|
|
158
158
|
return (input, init) => {
|
|
159
159
|
const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : void 0));
|
|
160
|
-
propagation.inject(context.active(), headers, { set: (carrier, key, value) =>
|
|
160
|
+
propagation.inject(context.active(), headers, { set: (carrier, key, value) => {
|
|
161
|
+
carrier.set(key, value);
|
|
162
|
+
} });
|
|
161
163
|
return base(input, {
|
|
162
164
|
...init,
|
|
163
165
|
headers
|
|
@@ -21,7 +21,7 @@ function parseSegment(segment) {
|
|
|
21
21
|
});
|
|
22
22
|
return parts;
|
|
23
23
|
}
|
|
24
|
-
const escapeRegex = (text) => text.
|
|
24
|
+
const escapeRegex = (text) => text.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
25
25
|
function parseTemplate(source, separator, validate) {
|
|
26
26
|
const segments = source.split(separator).map(parseSegment);
|
|
27
27
|
const params = [];
|
|
@@ -87,7 +87,36 @@ function sqlIdent(value) {
|
|
|
87
87
|
}
|
|
88
88
|
/** Postgres identifier-safe name part: `customer-logos` → `customer_logos`. */
|
|
89
89
|
function slug(value) {
|
|
90
|
-
return value.
|
|
90
|
+
return value.replaceAll(/[^A-Za-z0-9]+/g, "_").replaceAll(/^_|_$/g, "").toLowerCase();
|
|
91
91
|
}
|
|
92
92
|
//#endregion
|
|
93
|
-
|
|
93
|
+
//#region src/core/permdock-sql.ts
|
|
94
|
+
const SCOPE = /^[a-z_][a-z0-9_]*$/;
|
|
95
|
+
/** PermDock splits a key with row conditions into `key#1`, `key#2`, ...: those need its own policies. */
|
|
96
|
+
const SPLIT_KEY = /#\d+$/;
|
|
97
|
+
/**
|
|
98
|
+
* The SQL condition for one PermDock permission key. `id` is the text
|
|
99
|
+
* expression holding the scope id; it is compared as text, so a malformed
|
|
100
|
+
* path never raises a cast error.
|
|
101
|
+
*
|
|
102
|
+
* The helpers check role and scope, not a permission's row conditions, so
|
|
103
|
+
* the condition is only correct for permissions whose grants have none
|
|
104
|
+
* beyond the scope. `#n` keys are refused, and so are keys the catalog marks
|
|
105
|
+
* with `rowConditions: true` when one is passed.
|
|
106
|
+
*/
|
|
107
|
+
function permdockCheck(where, target, key, id, catalog) {
|
|
108
|
+
if (key === "") throw new TypeError(`${where}: a PermDock permission key is empty`);
|
|
109
|
+
if (SPLIT_KEY.test(key)) throw new TypeError(`${where}: "${key}" is one part of a permission PermDock splits by row condition. Use PermDock's generated policies for it.`);
|
|
110
|
+
if (catalog?.permissions.some((permission) => permission.key === key && permission.rowConditions === true)) throw new TypeError(`${where}: "${key}" has row conditions in PermDock's catalog (rowConditions: true), which the SQL helpers don't check. Use PermDock's generated policies for it.`);
|
|
111
|
+
const schema = sqlIdent(target.schema ?? "public");
|
|
112
|
+
if (target.scope === "global") return `(select ${schema}.permdock_has(${sqlString(key)}))`;
|
|
113
|
+
if (!SCOPE.test(target.scope)) throw new TypeError(`${where}: invalid PermDock scope "${target.scope}"`);
|
|
114
|
+
if (id === void 0) throw new TypeError(`${where}: scope "${target.scope}" needs a segment`);
|
|
115
|
+
return `${id} in (select t.id::text from ${schema}.${sqlIdent(`permitted_${target.scope}_ids`)}(${sqlString(key)}) as t(id))`;
|
|
116
|
+
}
|
|
117
|
+
/** The keys a bucket or topic PermDock policy names. */
|
|
118
|
+
function permdockKeys(policy) {
|
|
119
|
+
return Object.values(policy.permdock).filter((key) => typeof key === "string");
|
|
120
|
+
}
|
|
121
|
+
//#endregion
|
|
122
|
+
export { sqlIdent as a, slug as i, permdockKeys as n, sqlString as o, parseTemplate as r, permdockCheck as t };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/core/permdock-sql.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The part of PermDock's `permissions.catalog.json` the Storage and Realtime
|
|
4
|
+
* builders read: `import catalog from './permissions.catalog.json'`.
|
|
5
|
+
*/
|
|
6
|
+
interface PermdockCatalog {
|
|
7
|
+
readonly permissions: readonly {
|
|
8
|
+
readonly key: string;
|
|
9
|
+
readonly rowConditions?: boolean;
|
|
10
|
+
}[];
|
|
11
|
+
}
|
|
12
|
+
/** The keys a bucket or topic PermDock policy names. */
|
|
13
|
+
declare function permdockKeys(policy: {
|
|
14
|
+
readonly permdock: object;
|
|
15
|
+
}): string[];
|
|
16
|
+
//#endregion
|
|
17
|
+
export { permdockKeys as n, PermdockCatalog as t };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
/// <reference lib="esnext.temporal" />
|
|
2
|
+
import { A as SchemaMeta, P as TableMeta, n as AnyModels } from "./types-DONv9SHN.js";
|
|
3
|
+
import { g as Operation, h as MutationOp, r as Executor } from "./executor-DynmIWhq.js";
|
|
4
|
+
import { v as ErrorMapper } from "./result-c2OYgCV2.js";
|
|
4
5
|
//#region src/core/plugin.d.ts
|
|
5
6
|
/** Who is making the request. */
|
|
6
7
|
interface Actor {
|
|
@@ -28,7 +29,7 @@ interface HookArgs {
|
|
|
28
29
|
readonly schema: SchemaMeta;
|
|
29
30
|
readonly context: RequestContext;
|
|
30
31
|
readonly options: CallOptions;
|
|
31
|
-
readonly now: () =>
|
|
32
|
+
readonly now: () => Temporal.Instant;
|
|
32
33
|
}
|
|
33
34
|
type MutationKind = "insert" | "upsert" | "update" | "delete";
|
|
34
35
|
interface MutationEvent {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { f as Plugin, h as RequestContext } from "../../plugin-
|
|
1
|
+
import { f as Plugin, h as RequestContext } from "../../plugin-DVRB8qNE.js";
|
|
2
2
|
//#region src/plugins/actor/index.d.ts
|
|
3
3
|
export interface ActorOptions {
|
|
4
4
|
/** Custom actor id resolution. Defaults to `context.actor.id` for users and services. */
|