better-supabase 0.3.0 → 0.5.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 +9 -6
- package/bin/better-supabase.js +4 -0
- package/dist/{base64-CQeB1eSf.js → base64-D2gsioCY.js} +2 -2
- package/dist/channel-FQW-CnGR.d.ts +70 -0
- package/dist/claims-BAf6c7is.js +48 -0
- package/dist/cli/_format-Cax5EsM9.js +101 -0
- package/dist/cli/bin.d.ts +1 -0
- package/dist/cli/bin.js +48 -0
- package/dist/cli/codemod-DqV7612m.js +388 -0
- package/dist/cli/command-CK14fP91.js +502 -0
- package/dist/cli/compare-BmIWVEui.js +9 -0
- package/dist/cli/config-CPVpXP5I.js +1965 -0
- package/dist/cli/config-dtoekM0v.js +2 -0
- package/dist/cli/db-DOBPt660.js +52 -0
- package/dist/cli/diff-SjkO5rNr.js +752 -0
- package/dist/cli/dist-DBPdlPYK.js +1112 -0
- package/dist/cli/dist-DreZ24Sz.js +129 -0
- package/dist/cli/dist-xn4wlboc.js +732 -0
- package/dist/cli/doctor-L7Z8mYTx.js +2863 -0
- package/dist/cli/env-DmKv_G4A.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-BJc9vdyu.js +2 -0
- package/dist/cli/gen-C2osRcr5.js +2204 -0
- package/dist/cli/index.d.ts +493 -0
- package/dist/cli/index.js +184 -0
- package/dist/cli/init-Saf5-R9T.js +550 -0
- package/dist/cli/introspect--H7oGmcd.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-Cp5Fyta-.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-BfMQcKy8.js +289 -0
- package/dist/cli/rolldown-runtime-BPOCksWG.js +24 -0
- package/dist/cli/run-BKR2W_79.js +351 -0
- package/dist/cli/seed-CUlqWh-8.js +98 -0
- package/dist/cli/skills-LJJIhLFR.js +137 -0
- package/dist/cli/sql-DzPbfngx.js +348 -0
- package/dist/cli/style-C0NFdJRL.js +29 -0
- package/dist/cli/toml-DB-MNnFs.js +424 -0
- package/dist/cli/version-BDJuO0UR.js +4 -0
- package/dist/cli/yaml-CYCbJ6et.js +2252 -0
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.js +15 -22
- package/dist/clone-BfgmLU90.js +21 -0
- package/dist/{compiler-2zqGzxVZ.d.ts → compiler-BXH_aZ7N.d.ts} +2 -2
- package/dist/config/index.d.ts +4 -4
- package/dist/config/index.js +5 -5
- package/dist/{config-DOsGYSiT.js → config-DreERlKo.js} +1 -1
- package/dist/{config-C1CYcm6o.d.ts → config-T_ejwyd3.d.ts} +176 -11
- package/dist/{define-DUbydmJa.d.ts → define-Bp_BZa5h.d.ts} +37 -29
- package/dist/{define-DF341GDj.js → define-cNKzqwL_.js} +356 -89
- package/dist/edge/index.d.ts +20 -13
- package/dist/edge/index.js +55 -26
- package/dist/{entitlements-DvJXoI02.d.ts → entitlements-DUxU4oHe.d.ts} +2 -2
- package/dist/env/index.d.ts +2 -2
- package/dist/env/index.js +34 -1
- package/dist/{errors-Bl3rkOzo.js → errors-DtwXa6b8.js} +2 -1
- package/dist/events/index.d.ts +3 -2
- package/dist/events/index.js +69 -16
- package/dist/events-aIO7vsIk.d.ts +230 -0
- package/dist/executor-BunPOO-k.d.ts +402 -0
- package/dist/{executor-B26lgbHD.d.ts → executor-Cghdstwu.d.ts} +1 -2
- package/dist/{executor-C57jx2pn.js → executor-XycESQPH.js} +3 -3
- package/dist/hono/index.d.ts +27 -23
- package/dist/hono/index.js +46 -11
- package/dist/impersonation-VfaGc4Q_.js +41 -0
- package/dist/{index-BNsABq7P.d.ts → index-BGdgBPdf.d.ts} +66 -25
- package/dist/{index-qriRM9cZ.d.ts → index-C7f8cOBM.d.ts} +4 -4
- package/dist/{index-Dy6kuF0P.d.ts → index-CLg87OBW.d.ts} +6 -1
- package/dist/{index-GV77NpRP.d.ts → index-DOESKG27.d.ts} +34 -9
- package/dist/{index-CriFFJ_p.d.ts → index-DgILJRXj.d.ts} +43 -17
- package/dist/{index-UJ-LUxtI.d.ts → index-DmXSxbLi.d.ts} +14 -14
- package/dist/{index-Cf-D2jk1.d.ts → index-Dxn2a5cw.d.ts} +2 -2
- package/dist/{index-CTTMS6_1.d.ts → index-SaTsEfGP.d.ts} +86 -9
- package/dist/{index-CADlNHK-.d.ts → index-fag8VJZ2.d.ts} +5 -6
- package/dist/index.d.ts +34 -17
- package/dist/index.js +15 -14
- package/dist/jobs/index.d.ts +147 -95
- package/dist/jobs/index.js +773 -250
- package/dist/{json-schema--aOXAGfU.js → json-schema-m1fiwKgN.js} +23 -2
- package/dist/kit-events-CTFDfSwb.js +60 -0
- package/dist/kit-transport-B82k1djG.d.ts +33 -0
- package/dist/kit-transport-DJlHQUBU.js +55 -0
- package/dist/{config-Be4GwsqI.js → kits-CGLLrNPd.js} +38 -10
- package/dist/list/index.d.ts +1 -1
- package/dist/list/index.js +3 -3
- package/dist/{live-D8rV1Ldw.js → live-CdJBOc4f.js} +13 -9
- package/dist/{live-QE3UPbGm.d.ts → live-D9W2M5aO.d.ts} +16 -15
- package/dist/lookup-BaP5bI27.js +18 -0
- package/dist/mcp/index.d.ts +39 -22
- package/dist/mcp/index.js +39 -25
- package/dist/{mfa-x3V3vGh-.d.ts → mfa-CusDgPaD.d.ts} +5 -1
- package/dist/next/image/index.js +6 -3
- package/dist/next/index.d.ts +60 -36
- package/dist/next/index.js +167 -50
- package/dist/notifications/index.d.ts +162 -0
- package/dist/notifications/index.js +264 -0
- package/dist/openapi/index.d.ts +1 -1
- package/dist/openapi/index.js +6 -6
- package/dist/orgs/index.d.ts +132 -0
- package/dist/orgs/index.js +245 -0
- package/dist/orpc/index.d.ts +37 -20
- package/dist/orpc/index.js +24 -11
- package/dist/otel/index.d.ts +24 -5
- package/dist/otel/index.js +124 -64
- package/dist/permdock-sql-C2Inc6Xc.d.ts +29 -0
- package/dist/permdock-sql-DgOfeixi.js +51 -0
- package/dist/plugin-DwARMuQp.js +59 -0
- package/dist/{plugin-DVRB8qNE.d.ts → plugin-o144f322.d.ts} +17 -5
- package/dist/plugins/actor/index.d.ts +5 -2
- package/dist/plugins/actor/index.js +22 -8
- package/dist/plugins/rules/index.d.ts +2 -2
- package/dist/plugins/rules/index.js +52 -17
- package/dist/plugins/soft-delete/index.d.ts +3 -3
- package/dist/plugins/soft-delete/index.js +14 -5
- package/dist/plugins/tenant/index.d.ts +3 -3
- package/dist/plugins/tenant/index.js +45 -21
- package/dist/plugins/timestamps/index.d.ts +1 -1
- package/dist/plugins/timestamps/index.js +4 -3
- package/dist/plugins/validation/index.d.ts +2 -2
- package/dist/plugins/validation/index.js +26 -6
- package/dist/{pool-B9qsLyFR.d.ts → pool-C9voM4Kw.d.ts} +24 -6
- package/dist/postgres/index.d.ts +5 -5
- package/dist/postgres/index.js +20 -10
- package/dist/{postgrest-DmBlevh-.d.ts → postgrest-CruYKAvV.d.ts} +1 -1
- package/dist/{problem-CGt1ADix.js → problem-BW1KhzSG.js} +2 -1
- package/dist/{problem-Z2RTQjZD.d.ts → problem-Blxe8S31.d.ts} +1 -1
- package/dist/query/index.d.ts +2 -2
- package/dist/query/index.js +3 -3
- package/dist/{query-BdKLTPlj.js → query-CCbo44lG.js} +18 -18
- package/dist/queue-BYUCjr59.d.ts +252 -0
- package/dist/react/index.d.ts +8 -7
- package/dist/react/index.js +4 -3
- package/dist/react/server.d.ts +16 -5
- package/dist/react/server.js +12 -4
- package/dist/react/session.d.ts +10 -4
- package/dist/react/session.js +11 -3
- package/dist/{react-DNBYNazu.js → react-C_s7kiNz.js} +138 -46
- package/dist/{read-set-D0qBcboM.js → read-set-DQFt1N_Y.js} +71 -15
- package/dist/realtime/index.d.ts +3 -3
- package/dist/realtime/index.js +14 -12
- package/dist/{index-kpgopLNv.d.ts → refresh-BTrQ_ZTp.d.ts} +6 -3
- package/dist/{resolve-BRciX2jK.d.ts → resolve-C15TJq77.d.ts} +22 -11
- package/dist/{resource-D30rp0GN.js → resource-CRxxwkQq.js} +24 -9
- package/dist/{resource-CwVgiI5q.d.ts → resource-D8iqWMr-.d.ts} +6 -6
- package/dist/respond-D2OQl62-.d.ts +371 -0
- package/dist/{result-DWXatkd6.js → result-BKiliPY7.js} +2 -2
- package/dist/server/index.d.ts +17 -15
- package/dist/server/index.js +16 -22
- package/dist/server-Csx2sRjg.js +655 -0
- package/dist/{shared-_6-uPL7E.js → shared-D1dRUrAg.js} +19 -4
- package/dist/signers-Cc6r98SW.d.ts +162 -0
- package/dist/{simplify-LakqK1vH.js → simplify-_0oUKqCd.js} +27 -95
- package/dist/sql/index.d.ts +247 -10
- package/dist/sql/index.js +8591 -780
- package/dist/{sql-BIgk12ew.js → sql-Coh6n4cC.js} +11 -3
- package/dist/ssr/index.d.ts +4 -4
- package/dist/ssr/index.js +1 -2
- package/dist/{standard-FUi3Z3-7.js → standard-D7r6dMUu.js} +6 -3
- package/dist/{standard-eudBiRk1.d.ts → standard-J4YgMOLm.d.ts} +1 -1
- package/dist/{stats-D78UQ8Yu.js → stats-Bqob3WqT.js} +34 -9
- package/dist/storage/index.d.ts +4 -4
- package/dist/storage/index.js +174 -27
- package/dist/support-DT-OhSxD.d.ts +88 -0
- package/dist/support-Dk6vdmEw.js +291 -0
- package/dist/support-view-Da5peKFg.d.ts +17 -0
- package/dist/support-view-g4YLBeqX.js +18 -0
- package/dist/{tables-Dhha3BXM.js → tables-Cb-3_VWc.js} +14 -0
- package/dist/{permdock-sql-Bta_G0v-.js → template-TxrGI5jR.js} +2 -31
- package/dist/{temporal-BwshQk0W.js → temporal-CVlIV2DZ.js} +17 -4
- package/dist/{temporal-required-CMxeSVPo.js → temporal-required-CAt4Dc9Z.js} +2 -2
- package/dist/testing/index.d.ts +113 -24
- package/dist/testing/index.js +375 -47
- package/dist/{types-DONv9SHN.d.ts → types-Dnf_wRDz.d.ts} +26 -3
- package/dist/user-change-CHFEUro1.js +49 -0
- package/dist/{webhooks-CAIFQSTM.js → verify-Ce49ETPz.js} +38 -19
- package/dist/{version-BfpG2gks.js → version-DM_Roffb.js} +1 -1
- package/dist/{view-3Po_PVlr.d.ts → view-BEf0diC5.d.ts} +10 -5
- package/dist/{mfa-BxoR1RkN.js → view-Bq64ki8-.js} +210 -64
- package/dist/webhooks/index.d.ts +136 -5
- package/dist/webhooks/index.js +588 -2
- package/dist/wire-D2dT86vi.js +35 -0
- package/package.json +50 -10
- package/schemas/config-v1.json +131 -1
- package/schemas/doctor-report-v1.json +10 -0
- package/skills/better-supabase/SKILL.md +60 -10
- package/skills/better-supabase/references/plugins.md +13 -14
- package/skills/better-supabase/references/troubleshooting.md +25 -4
- package/skills/better-supabase-api/SKILL.md +74 -19
- package/skills/better-supabase-api/references/adapters.md +48 -27
- package/skills/better-supabase-auth/SKILL.md +149 -0
- package/skills/better-supabase-auth/references/permdock.md +100 -0
- package/skills/better-supabase-testing/SKILL.md +58 -4
- package/dist/claims-CoTobNC-.js +0 -25
- package/dist/events-Do8jXh4D.d.ts +0 -84
- package/dist/executor-DynmIWhq.d.ts +0 -195
- package/dist/invalidate-DbxuL2jL.js +0 -18
- package/dist/permdock-sql-DImJULY-.d.ts +0 -17
- package/dist/plugin-BqR6wKMB.js +0 -23
- package/dist/respond-C44EZAJo.js +0 -399
- package/dist/respond-DAkPIdTH.d.ts +0 -175
- package/dist/result-c2OYgCV2.d.ts +0 -203
- package/dist/view-wZ-9qIUz.js +0 -45
package/dist/orpc/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { n as
|
|
2
|
-
import {
|
|
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 AnyFunctions, r as AnyModels } from "../types-Dnf_wRDz.js";
|
|
2
|
+
import { _ as DbError, u as Result } from "../executor-BunPOO-k.js";
|
|
3
|
+
import { t as BetterSupabase } from "../define-Bp_BZa5h.js";
|
|
4
|
+
import { r as AuthState } from "../resolve-C15TJq77.js";
|
|
5
|
+
import { f as ServerContext, n as GuardOptions, p as ServerOptions, r as MiddlewareOptions, u as BetterServer } from "../respond-D2OQl62-.js";
|
|
6
|
+
import { r as ProblemDetails } from "../problem-Blxe8S31.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,53 @@ 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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
/** The part of an oRPC fetch handler (`RPCHandler`, `OpenAPIHandler`) `fetchHandler` calls. */
|
|
24
|
+
export interface OrpcFetchHandler {
|
|
25
|
+
handle(request: Request, options: {
|
|
26
|
+
readonly prefix?: `/${string}`;
|
|
27
|
+
readonly context: OrpcRequestContext;
|
|
28
|
+
}): Promise<{
|
|
29
|
+
readonly matched: boolean;
|
|
30
|
+
readonly response?: Response | undefined;
|
|
31
|
+
}>;
|
|
26
32
|
}
|
|
27
|
-
export
|
|
28
|
-
|
|
33
|
+
export interface OrpcFetchOptions {
|
|
34
|
+
/** Path prefix the router is mounted under, e.g. `/rpc`. */
|
|
35
|
+
readonly prefix?: `/${string}`;
|
|
36
|
+
}
|
|
37
|
+
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>>;
|
|
38
|
+
export interface BetterOrpc<M extends AnyModels, F extends AnyFunctions, E, C = unknown, P = unknown> extends BetterServer<M, F, E, C, P> {
|
|
29
39
|
/**
|
|
30
40
|
* Resolves the caller, enforces `allow` and adds `context.db`, `context.auth`
|
|
31
41
|
* and `context.bs`. Thrown `DbException`s, and errors caused by a
|
|
32
|
-
* `DbError`, become `ORPCError`s.
|
|
42
|
+
* `DbError`, become `ORPCError`s. Serve the router with `fetchHandler` so
|
|
43
|
+
* refreshed session cookies and `bs-primary-until` reach the response.
|
|
44
|
+
*/
|
|
45
|
+
middleware(options?: MiddlewareOptions): OrpcMiddleware<M, F, E, C, P>;
|
|
46
|
+
/**
|
|
47
|
+
* A fetch handler for an oRPC handler: passes `{ request }` as the initial
|
|
48
|
+
* context, answers 404 when no procedure matched, and applies the cookies
|
|
49
|
+
* of the request's context (`ctx.apply`).
|
|
33
50
|
*/
|
|
34
|
-
|
|
51
|
+
fetchHandler(handler: OrpcFetchHandler, options?: OrpcFetchOptions): (request: Request) => Promise<Response>;
|
|
35
52
|
/** A `Result` (or `AsyncResult`) as data, throwing an `ORPCError` on failure. */
|
|
36
53
|
unwrap<T>(value: Result<T> | PromiseLike<Result<T>>): Promise<T>;
|
|
37
54
|
/** Plain values pass through; thrown `DbException`s become `ORPCError`s. */
|
|
38
55
|
unwrap<T>(value: T | PromiseLike<T>): Promise<T>;
|
|
39
|
-
|
|
56
|
+
toOrpcError(error: DbError): ORPCError<string, ProblemDetails>;
|
|
40
57
|
}
|
|
41
58
|
/** oRPC code for a `DbError`, from its HTTP status. */
|
|
42
59
|
export declare function orpcCode(error: DbError): string;
|
|
43
60
|
/** oRPC integration: middleware adding the caller's repositories to `context`. */
|
|
44
|
-
export declare function createOrpc<M extends AnyModels, D, F extends AnyFunctions, E>(
|
|
61
|
+
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
62
|
//#endregion
|
|
46
|
-
export type { GuardOptions };
|
|
63
|
+
export type { GuardOptions, MiddlewareOptions };
|
package/dist/orpc/index.js
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
import { r as dbErrorOf } from "../errors-
|
|
2
|
-
import { o as toProblem } from "../problem-
|
|
3
|
-
import {
|
|
1
|
+
import { r as dbErrorOf } from "../errors-DtwXa6b8.js";
|
|
2
|
+
import { o as toProblem } from "../problem-BW1KhzSG.js";
|
|
3
|
+
import { d as guard, m as settle, n as createServer, r as extendServer, u as defaultExpose } from "../server-Csx2sRjg.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 contexts = /* @__PURE__ */ new WeakMap();
|
|
16
|
+
const toOrpcError = (error) => {
|
|
16
17
|
const problem = toProblem(error, { expose });
|
|
17
18
|
return new ORPCError(orpcCode(error), {
|
|
18
19
|
message: problem.detail ?? problem.title,
|
|
@@ -20,12 +21,13 @@ function createOrpc(sb, options = {}) {
|
|
|
20
21
|
});
|
|
21
22
|
};
|
|
22
23
|
return extendServer(server, {
|
|
23
|
-
|
|
24
|
+
toOrpcError,
|
|
24
25
|
middleware(middlewareOptions = {}) {
|
|
25
26
|
return os.$context().middleware(async ({ context, next }) => {
|
|
26
27
|
const ctx = await server.context(context.request, { refresh: middlewareOptions.refresh ?? false });
|
|
27
28
|
const denied = guard(ctx.auth, middlewareOptions.allow, middlewareOptions.aal, middlewareOptions.scopes);
|
|
28
|
-
if (denied) throw
|
|
29
|
+
if (denied) throw toOrpcError(denied);
|
|
30
|
+
contexts.set(context.request, ctx);
|
|
29
31
|
try {
|
|
30
32
|
return await next({ context: {
|
|
31
33
|
bs: ctx,
|
|
@@ -34,14 +36,25 @@ function createOrpc(sb, options = {}) {
|
|
|
34
36
|
} });
|
|
35
37
|
} catch (cause) {
|
|
36
38
|
const thrown = dbErrorOf(cause);
|
|
37
|
-
if (thrown) throw
|
|
39
|
+
if (thrown) throw toOrpcError(thrown);
|
|
38
40
|
throw cause;
|
|
39
41
|
}
|
|
40
42
|
});
|
|
41
43
|
},
|
|
44
|
+
fetchHandler(handler, fetchOptions = {}) {
|
|
45
|
+
return async (request) => {
|
|
46
|
+
const { response } = await handler.handle(request, {
|
|
47
|
+
...fetchOptions.prefix ? { prefix: fetchOptions.prefix } : {},
|
|
48
|
+
context: { request }
|
|
49
|
+
});
|
|
50
|
+
const ctx = contexts.get(request);
|
|
51
|
+
const answer = response ?? new Response("Not found", { status: 404 });
|
|
52
|
+
return ctx ? ctx.apply(answer) : answer;
|
|
53
|
+
};
|
|
54
|
+
},
|
|
42
55
|
async unwrap(value) {
|
|
43
56
|
const settled = await settle(() => value);
|
|
44
|
-
if (!settled.ok) throw
|
|
57
|
+
if (!settled.ok) throw toOrpcError(settled.error);
|
|
45
58
|
return settled.data;
|
|
46
59
|
}
|
|
47
60
|
});
|
package/dist/otel/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as EventHub } from "../events-
|
|
2
|
-
import {
|
|
1
|
+
import { a as EventHub } from "../events-aIO7vsIk.js";
|
|
2
|
+
import { p as Plugin } from "../plugin-o144f322.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
|
|
@@ -13,6 +13,16 @@ export interface OtelOptions {
|
|
|
13
13
|
readonly attributes?: Attributes;
|
|
14
14
|
/** Set `false` to skip the duration histogram. */
|
|
15
15
|
readonly metrics?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* The database name in `db.namespace` (`{database}|{schema}`). Supabase
|
|
18
|
+
* names it `postgres`, the default for Postgres executors.
|
|
19
|
+
*/
|
|
20
|
+
readonly database?: string;
|
|
21
|
+
/** `server.address` and `server.port` on spans and metrics, e.g. the project host. */
|
|
22
|
+
readonly server?: {
|
|
23
|
+
readonly address: string;
|
|
24
|
+
readonly port?: number;
|
|
25
|
+
};
|
|
16
26
|
}
|
|
17
27
|
/**
|
|
18
28
|
* Database client spans and metrics for every repository operation, using
|
|
@@ -20,15 +30,24 @@ export interface OtelOptions {
|
|
|
20
30
|
* never recorded; `db.query.summary` is `SELECT customers`.
|
|
21
31
|
*
|
|
22
32
|
* ```ts
|
|
23
|
-
* const
|
|
33
|
+
* const betterSupabase = defineSupabase(schema).use(otel());
|
|
24
34
|
* ```
|
|
25
35
|
*/
|
|
26
36
|
export declare function otel(options?: OtelOptions): Plugin<"otel">;
|
|
27
37
|
/**
|
|
28
|
-
* Spans for auth resolution and session refreshes, from `
|
|
38
|
+
* Spans for auth resolution and session refreshes, from `betterSupabase.events`.
|
|
29
39
|
* Returns a function that stops listening.
|
|
30
40
|
*/
|
|
31
|
-
export declare function traceAuth(
|
|
41
|
+
export declare function traceAuth(betterSupabase: {
|
|
42
|
+
readonly events: EventHub;
|
|
43
|
+
}, options?: Pick<OtelOptions, "tracer" | "attributes">): () => void;
|
|
44
|
+
/**
|
|
45
|
+
* Records kit events (`support.started`, `webhook.failed`, ...) with the
|
|
46
|
+
* `KIT_ATTRIBUTES` names: as an event on the active span, or as a short
|
|
47
|
+
* `kit <type>` span when there is none. Failures (`*.failed`, `*.denied`)
|
|
48
|
+
* set an error status. Returns a function that stops listening.
|
|
49
|
+
*/
|
|
50
|
+
export declare function traceKitEvents(betterSupabase: {
|
|
32
51
|
readonly events: EventHub;
|
|
33
52
|
}, options?: Pick<OtelOptions, "tracer" | "attributes">): () => void;
|
|
34
53
|
/**
|
package/dist/otel/index.js
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { r as definePlugin } from "../plugin-DwARMuQp.js";
|
|
2
2
|
import { t as SPEC_PINS } from "../spec-pins-C3oc-Tdp.js";
|
|
3
|
-
import "../
|
|
3
|
+
import { r as kitEventAttributes } from "../kit-events-CTFDfSwb.js";
|
|
4
|
+
import "../version-DM_Roffb.js";
|
|
4
5
|
import { SpanKind, SpanStatusCode, context, metrics, propagation, trace } from "@opentelemetry/api";
|
|
5
6
|
//#region src/otel/index.ts
|
|
6
7
|
const INSTRUMENTATION_NAME = "better-supabase";
|
|
7
8
|
/** OpenTelemetry semantic conventions version the attributes follow. */
|
|
8
9
|
const SEMCONV_VERSION = SPEC_PINS.otelSemconv;
|
|
10
|
+
/** A five-character SQLSTATE; PostgREST's own codes (`PGRST116`) are not one. */
|
|
11
|
+
const SQLSTATE = /^[0-9A-Z]{5}$/;
|
|
12
|
+
function serverAttributes(server) {
|
|
13
|
+
if (!server) return {};
|
|
14
|
+
return server.port === void 0 ? { "server.address": server.address } : {
|
|
15
|
+
"server.address": server.address,
|
|
16
|
+
"server.port": server.port
|
|
17
|
+
};
|
|
18
|
+
}
|
|
9
19
|
const OPERATION_NAME = {
|
|
10
20
|
select: "SELECT",
|
|
11
21
|
insert: "INSERT",
|
|
@@ -26,13 +36,13 @@ function systemOf(executor) {
|
|
|
26
36
|
* never recorded; `db.query.summary` is `SELECT customers`.
|
|
27
37
|
*
|
|
28
38
|
* ```ts
|
|
29
|
-
* const
|
|
39
|
+
* const betterSupabase = defineSupabase(schema).use(otel());
|
|
30
40
|
* ```
|
|
31
41
|
*/
|
|
32
42
|
function otel(options = {}) {
|
|
33
|
-
const tracer = options.tracer ?? trace.getTracer("better-supabase", "0.
|
|
43
|
+
const tracer = options.tracer ?? trace.getTracer("better-supabase", "0.5.0");
|
|
34
44
|
let histogram;
|
|
35
|
-
if (options.metrics !== false) histogram = (options.meter ?? metrics.getMeter("better-supabase", "0.
|
|
45
|
+
if (options.metrics !== false) histogram = (options.meter ?? metrics.getMeter("better-supabase", "0.5.0")).createHistogram("db.client.operation.duration", {
|
|
36
46
|
unit: "s",
|
|
37
47
|
description: "Duration of database client operations.",
|
|
38
48
|
advice: { explicitBucketBoundaries: [
|
|
@@ -50,76 +60,99 @@ function otel(options = {}) {
|
|
|
50
60
|
return definePlugin({
|
|
51
61
|
name: "otel",
|
|
52
62
|
enforce: "post",
|
|
53
|
-
wrapExecutor: (executor) =>
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
63
|
+
wrapExecutor: (executor) => {
|
|
64
|
+
const system = systemOf(executor);
|
|
65
|
+
const database = options.database ?? (system === "postgresql" ? "postgres" : void 0);
|
|
66
|
+
const server = serverAttributes(options.server);
|
|
67
|
+
/** Span and metric attributes per table and operation, built once. */
|
|
68
|
+
const described = /* @__PURE__ */ new Map();
|
|
69
|
+
const describe = (op) => {
|
|
57
70
|
const operation = operationName(op);
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const started = performance.now();
|
|
69
|
-
return tracer.startActiveSpan(summary, {
|
|
70
|
-
kind: SpanKind.CLIENT,
|
|
71
|
-
attributes
|
|
72
|
-
}, async (span) => {
|
|
73
|
-
const finish = (errorType) => {
|
|
74
|
-
const metricAttributes = {
|
|
75
|
-
"db.system.name": attributes["db.system.name"],
|
|
71
|
+
const key = `${op.table.key}:${operation}`;
|
|
72
|
+
let entry = described.get(key);
|
|
73
|
+
if (!entry) {
|
|
74
|
+
const summary = `${operation} ${op.table.name}`;
|
|
75
|
+
entry = {
|
|
76
|
+
summary,
|
|
77
|
+
attributes: {
|
|
78
|
+
...options.attributes,
|
|
79
|
+
"db.system.name": system,
|
|
80
|
+
"db.namespace": database ? `${database}|${op.table.schema}` : op.table.schema,
|
|
76
81
|
"db.collection.name": op.table.name,
|
|
77
82
|
"db.operation.name": operation,
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
"db.query.summary": summary,
|
|
84
|
+
"better_supabase.executor": executor.name,
|
|
85
|
+
...server
|
|
86
|
+
},
|
|
87
|
+
metric: {
|
|
88
|
+
...server,
|
|
89
|
+
"db.system.name": system,
|
|
90
|
+
"db.collection.name": op.table.name,
|
|
91
|
+
"db.operation.name": operation
|
|
92
|
+
}
|
|
82
93
|
};
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
94
|
+
described.set(key, entry);
|
|
95
|
+
}
|
|
96
|
+
return entry;
|
|
97
|
+
};
|
|
98
|
+
return {
|
|
99
|
+
...executor,
|
|
100
|
+
name: executor.name,
|
|
101
|
+
execute(op, executeContext) {
|
|
102
|
+
const { summary, attributes, metric } = describe(op);
|
|
103
|
+
const started = performance.now();
|
|
104
|
+
return tracer.startActiveSpan(summary, {
|
|
105
|
+
kind: SpanKind.CLIENT,
|
|
106
|
+
attributes
|
|
107
|
+
}, async (span) => {
|
|
108
|
+
const finish = (errorType) => {
|
|
109
|
+
histogram?.record((performance.now() - started) / 1e3, errorType ? {
|
|
110
|
+
...metric,
|
|
111
|
+
"error.type": errorType
|
|
112
|
+
} : metric);
|
|
113
|
+
span.end();
|
|
114
|
+
};
|
|
115
|
+
try {
|
|
116
|
+
const result = await executor.execute(op, executeContext);
|
|
117
|
+
if (result.ok) {
|
|
118
|
+
if (span.isRecording()) span.setAttribute("db.response.returned_rows", result.data.rows.length);
|
|
119
|
+
finish();
|
|
120
|
+
} else {
|
|
121
|
+
span.setAttributes({
|
|
122
|
+
"error.type": result.error.kind,
|
|
123
|
+
...result.error.code && SQLSTATE.test(result.error.code) ? { "db.response.status_code": result.error.code } : {}
|
|
124
|
+
});
|
|
125
|
+
span.setStatus({
|
|
126
|
+
code: SpanStatusCode.ERROR,
|
|
127
|
+
message: result.error.message
|
|
128
|
+
});
|
|
129
|
+
finish(result.error.kind);
|
|
130
|
+
}
|
|
131
|
+
return result;
|
|
132
|
+
} catch (cause) {
|
|
133
|
+
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
134
|
+
span.recordException(error);
|
|
135
|
+
span.setAttribute("error.type", error.name);
|
|
93
136
|
span.setStatus({
|
|
94
137
|
code: SpanStatusCode.ERROR,
|
|
95
|
-
message:
|
|
138
|
+
message: error.message
|
|
96
139
|
});
|
|
97
|
-
finish(
|
|
140
|
+
finish(error.name);
|
|
141
|
+
throw cause;
|
|
98
142
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
span.setAttribute("error.type", error.name);
|
|
104
|
-
span.setStatus({
|
|
105
|
-
code: SpanStatusCode.ERROR,
|
|
106
|
-
message: error.message
|
|
107
|
-
});
|
|
108
|
-
finish(error.name);
|
|
109
|
-
throw cause;
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
})
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
114
147
|
});
|
|
115
148
|
}
|
|
116
149
|
/**
|
|
117
|
-
* Spans for auth resolution and session refreshes, from `
|
|
150
|
+
* Spans for auth resolution and session refreshes, from `betterSupabase.events`.
|
|
118
151
|
* Returns a function that stops listening.
|
|
119
152
|
*/
|
|
120
|
-
function traceAuth(
|
|
121
|
-
const tracer = options.tracer ?? trace.getTracer("better-supabase", "0.
|
|
122
|
-
const offAuth =
|
|
153
|
+
function traceAuth(betterSupabase, options = {}) {
|
|
154
|
+
const tracer = options.tracer ?? trace.getTracer("better-supabase", "0.5.0");
|
|
155
|
+
const offAuth = betterSupabase.events.on("auth", (event) => {
|
|
123
156
|
const span = tracer.startSpan("auth.resolve", {
|
|
124
157
|
kind: SpanKind.INTERNAL,
|
|
125
158
|
attributes: {
|
|
@@ -131,7 +164,7 @@ function traceAuth(sb, options = {}) {
|
|
|
131
164
|
if (!event.ok) span.setStatus({ code: SpanStatusCode.ERROR });
|
|
132
165
|
span.end();
|
|
133
166
|
});
|
|
134
|
-
const offRefresh =
|
|
167
|
+
const offRefresh = betterSupabase.events.on("refresh", (event) => {
|
|
135
168
|
const end = Date.now();
|
|
136
169
|
const span = tracer.startSpan("auth.refresh", {
|
|
137
170
|
kind: SpanKind.CLIENT,
|
|
@@ -150,6 +183,33 @@ function traceAuth(sb, options = {}) {
|
|
|
150
183
|
};
|
|
151
184
|
}
|
|
152
185
|
/**
|
|
186
|
+
* Records kit events (`support.started`, `webhook.failed`, ...) with the
|
|
187
|
+
* `KIT_ATTRIBUTES` names: as an event on the active span, or as a short
|
|
188
|
+
* `kit <type>` span when there is none. Failures (`*.failed`, `*.denied`)
|
|
189
|
+
* set an error status. Returns a function that stops listening.
|
|
190
|
+
*/
|
|
191
|
+
function traceKitEvents(betterSupabase, options = {}) {
|
|
192
|
+
const tracer = options.tracer ?? trace.getTracer("better-supabase", "0.5.0");
|
|
193
|
+
return betterSupabase.events.on("kit", (event) => {
|
|
194
|
+
const attributes = {
|
|
195
|
+
...options.attributes,
|
|
196
|
+
...kitEventAttributes(event)
|
|
197
|
+
};
|
|
198
|
+
const failed = event.type.endsWith(".failed") || event.type.endsWith(".denied");
|
|
199
|
+
const active = trace.getActiveSpan();
|
|
200
|
+
if (active) {
|
|
201
|
+
active.addEvent(event.type, attributes);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const span = tracer.startSpan(`kit ${event.type}`, {
|
|
205
|
+
kind: SpanKind.INTERNAL,
|
|
206
|
+
attributes
|
|
207
|
+
});
|
|
208
|
+
if (failed) span.setStatus({ code: SpanStatusCode.ERROR });
|
|
209
|
+
span.end();
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
153
213
|
* A `fetch` that adds W3C `traceparent`/`tracestate` from the active
|
|
154
214
|
* context. Pass it to `createClient(url, key, { global: { fetch } })` so
|
|
155
215
|
* PostgREST, Auth and Storage calls join the trace.
|
|
@@ -167,4 +227,4 @@ function tracedFetch(base = fetch) {
|
|
|
167
227
|
};
|
|
168
228
|
}
|
|
169
229
|
//#endregion
|
|
170
|
-
export { INSTRUMENTATION_NAME, SEMCONV_VERSION, otel, traceAuth, tracedFetch };
|
|
230
|
+
export { INSTRUMENTATION_NAME, SEMCONV_VERSION, otel, traceAuth, traceKitEvents, tracedFetch };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//#region src/core/permdock-sql.d.ts
|
|
2
|
+
/** PermDock's default `rls.schema`, a schema the Data API does not expose. */
|
|
3
|
+
declare const PERMDOCK_SCHEMA = "permdock";
|
|
4
|
+
/**
|
|
5
|
+
* The part of PermDock's `permissions.catalog.json` the Storage and Realtime
|
|
6
|
+
* builders read: `import catalog from './permissions.catalog.json'`.
|
|
7
|
+
*/
|
|
8
|
+
interface PermdockCatalog {
|
|
9
|
+
readonly permissions: readonly {
|
|
10
|
+
readonly key: string;
|
|
11
|
+
readonly rowConditions?: boolean;
|
|
12
|
+
/** The scope PermDock declares the permission at. */
|
|
13
|
+
readonly scope?: string;
|
|
14
|
+
}[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* What PermDock's catalog says about a key: `scope-only` (`rowConditions:
|
|
18
|
+
* false`), `row-conditions` (`true`), `no-flag` (an entry without a boolean
|
|
19
|
+
* flag, from an older `permdock catalog`) or `missing`. Only `scope-only` is
|
|
20
|
+
* safe for the SQL helpers.
|
|
21
|
+
*/
|
|
22
|
+
type PermdockKeyStatus = "scope-only" | "row-conditions" | "no-flag" | "missing";
|
|
23
|
+
declare function permdockKeyStatus(catalog: PermdockCatalog, key: string): PermdockKeyStatus;
|
|
24
|
+
/** The keys a bucket or topic PermDock policy names. */
|
|
25
|
+
declare function permdockKeys(policy: {
|
|
26
|
+
readonly permdock: object;
|
|
27
|
+
}): string[];
|
|
28
|
+
//#endregion
|
|
29
|
+
export { permdockKeys as a, permdockKeyStatus as i, PermdockCatalog as n, PermdockKeyStatus as r, PERMDOCK_SCHEMA as t };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { i as sqlString, r as sqlIdent } from "./template-TxrGI5jR.js";
|
|
2
|
+
//#region src/core/permdock-sql.ts
|
|
3
|
+
const SCOPE = /^[a-z][a-z0-9_]*$/;
|
|
4
|
+
/** PermDock's default `rls.schema`, a schema the Data API does not expose. */
|
|
5
|
+
const PERMDOCK_SCHEMA = "permdock";
|
|
6
|
+
/** PermDock splits a key with row conditions into `key#1`, `key#2`, ...: those need its own policies. */
|
|
7
|
+
const SPLIT_KEY = /#\d+$/;
|
|
8
|
+
function permdockKeyStatus(catalog, key) {
|
|
9
|
+
const entry = catalog.permissions.find((permission) => permission.key === key);
|
|
10
|
+
if (!entry) return "missing";
|
|
11
|
+
if (entry.rowConditions === true) return "row-conditions";
|
|
12
|
+
if (entry.rowConditions === false) return "scope-only";
|
|
13
|
+
return "no-flag";
|
|
14
|
+
}
|
|
15
|
+
const REGENERATE = "Regenerate permissions.catalog.json with a current `permdock catalog`, which writes rowConditions for every permission.";
|
|
16
|
+
/**
|
|
17
|
+
* The SQL condition for one PermDock permission key. `id` is the text
|
|
18
|
+
* expression holding the scope id; it is compared as text, so a malformed
|
|
19
|
+
* path never raises a cast error.
|
|
20
|
+
*
|
|
21
|
+
* The helpers check role and scope, not a permission's row conditions, so
|
|
22
|
+
* the condition is only correct for permissions whose grants have none
|
|
23
|
+
* beyond the scope. `#n` keys are refused. With a catalog, only keys it marks
|
|
24
|
+
* `rowConditions: false` are accepted: `true`, a missing flag and a key the
|
|
25
|
+
* catalog doesn't list are refused.
|
|
26
|
+
*/
|
|
27
|
+
function permdockCheck(where, target, key, id, catalog) {
|
|
28
|
+
if (key === "") throw new TypeError(`${where}: a PermDock permission key is empty`);
|
|
29
|
+
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.`);
|
|
30
|
+
if (catalog) {
|
|
31
|
+
const status = permdockKeyStatus(catalog, key);
|
|
32
|
+
switch (status) {
|
|
33
|
+
case "scope-only": break;
|
|
34
|
+
case "row-conditions": 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.`);
|
|
35
|
+
case "no-flag": throw new TypeError(`${where}: "${key}" has no rowConditions flag in PermDock's catalog, so whether the SQL helpers check it fully is unknown. ${REGENERATE}`);
|
|
36
|
+
case "missing": throw new TypeError(`${where}: "${key}" is not in PermDock's catalog. ${REGENERATE}`);
|
|
37
|
+
default: return status;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const schema = sqlIdent(target.schema ?? "permdock");
|
|
41
|
+
if (target.scope === "global") return `(select ${schema}.permdock_has(${sqlString(key)}))`;
|
|
42
|
+
if (!SCOPE.test(target.scope)) throw new TypeError(`${where}: invalid PermDock scope "${target.scope}"`);
|
|
43
|
+
if (id === void 0) throw new TypeError(`${where}: scope "${target.scope}" needs a segment`);
|
|
44
|
+
return `${id} in (select t.id::text from ${schema}.${sqlIdent(`permitted_${target.scope}_ids`)}(${sqlString(key)}) as t(id))`;
|
|
45
|
+
}
|
|
46
|
+
/** The keys a bucket or topic PermDock policy names. */
|
|
47
|
+
function permdockKeys(policy) {
|
|
48
|
+
return Object.values(policy.permdock).filter((key) => typeof key === "string");
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
export { permdockKeys as i, permdockCheck as n, permdockKeyStatus as r, PERMDOCK_SCHEMA as t };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
//#region src/core/plugin.ts
|
|
2
|
+
const PLUGIN_API_VERSION = 1;
|
|
3
|
+
const RANK = {
|
|
4
|
+
pre: 0,
|
|
5
|
+
normal: 1,
|
|
6
|
+
post: 2
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* `rules()` checks the query as the caller wrote it, so it runs before every
|
|
10
|
+
* other `pre` plugin (soft delete turns deletes into updates, tenant adds
|
|
11
|
+
* filters).
|
|
12
|
+
*/
|
|
13
|
+
function rankOf(plugin) {
|
|
14
|
+
return plugin.name === "rules" ? -1 : RANK[plugin.enforce ?? "normal"];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Plugins in hook order: `rules`, then `pre`, then unmarked in `use()` order,
|
|
18
|
+
* then `post`.
|
|
19
|
+
*/
|
|
20
|
+
function orderPlugins(plugins) {
|
|
21
|
+
return plugins.map((plugin, index) => ({
|
|
22
|
+
plugin,
|
|
23
|
+
index
|
|
24
|
+
})).sort((a, b) => rankOf(a.plugin) - rankOf(b.plugin) || a.index - b.index).map(({ plugin }) => plugin);
|
|
25
|
+
}
|
|
26
|
+
/** Identity helper that fixes `apiVersion` and infers the plugin types. */
|
|
27
|
+
function definePlugin(plugin) {
|
|
28
|
+
return {
|
|
29
|
+
...plugin,
|
|
30
|
+
apiVersion: 1
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/** Tenants that `tenant()` resolved from claims, per connection context. */
|
|
34
|
+
const resolvedTenants = /* @__PURE__ */ new WeakMap();
|
|
35
|
+
/** Records the tenant a plugin resolved for `context`, so mutation notices carry it. */
|
|
36
|
+
function recordTenant(context, tenant) {
|
|
37
|
+
resolvedTenants.set(context, tenant);
|
|
38
|
+
}
|
|
39
|
+
/** `context.tenant`, else the tenant a plugin resolved for this connection. */
|
|
40
|
+
function tenantOf(context) {
|
|
41
|
+
return context.tenant ?? resolvedTenants.get(context);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A symbol key, so JSON, claims and spread request data can't set it, while
|
|
45
|
+
* `$with` copies it to the derived context.
|
|
46
|
+
*/
|
|
47
|
+
const ALL_TENANTS = Symbol("better-supabase.allTenants");
|
|
48
|
+
/** `context`, marked so `tenant()` and tenant buckets skip their tenant scope. */
|
|
49
|
+
function allTenantsContext(context) {
|
|
50
|
+
return {
|
|
51
|
+
...context,
|
|
52
|
+
[ALL_TENANTS]: true
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function spansAllTenants(context) {
|
|
56
|
+
return Object.hasOwn(context, ALL_TENANTS);
|
|
57
|
+
}
|
|
58
|
+
//#endregion
|
|
59
|
+
export { recordTenant as a, orderPlugins as i, allTenantsContext as n, spansAllTenants as o, definePlugin as r, tenantOf as s, PLUGIN_API_VERSION as t };
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { v as ErrorMapper } from "./result-c2OYgCV2.js";
|
|
2
|
+
import { F as TableMeta, j as SchemaMeta, r as AnyModels } from "./types-Dnf_wRDz.js";
|
|
3
|
+
import { H as MutationOp, S as ErrorMapper, U as Operation, r as Executor } from "./executor-BunPOO-k.js";
|
|
5
4
|
//#region src/core/plugin.d.ts
|
|
6
5
|
/** Who is making the request. */
|
|
7
6
|
interface Actor {
|
|
@@ -32,11 +31,24 @@ interface HookArgs {
|
|
|
32
31
|
readonly now: () => Temporal.Instant;
|
|
33
32
|
}
|
|
34
33
|
type MutationKind = "insert" | "upsert" | "update" | "delete";
|
|
34
|
+
/** What the caller asked for: `softDelete` is a `delete` that a plugin turned into an update. */
|
|
35
|
+
type MutationIntent = MutationKind | "softDelete";
|
|
35
36
|
interface MutationEvent {
|
|
36
37
|
readonly table: TableMeta;
|
|
38
|
+
/** The statement that ran. */
|
|
37
39
|
readonly kind: MutationKind;
|
|
38
|
-
/**
|
|
40
|
+
/** What the caller asked for; differs from `kind` when a plugin rewrote the mutation. */
|
|
41
|
+
readonly intent?: MutationIntent;
|
|
42
|
+
/** App-cased rows returned by the mutation. A copy: changing it never changes the result. */
|
|
39
43
|
readonly rows: readonly Readonly<Record<string, unknown>>[];
|
|
44
|
+
/**
|
|
45
|
+
* App-cased primary keys of the changed rows, when they are known: from
|
|
46
|
+
* the returned rows, or from a `where` on the primary key when nothing is
|
|
47
|
+
* returned (soft deletes, `returning: false`).
|
|
48
|
+
*/
|
|
49
|
+
readonly keys?: readonly Readonly<Record<string, unknown>>[];
|
|
50
|
+
/** The tenant of the write: `context.tenant`, or the one `tenant()` resolved. */
|
|
51
|
+
readonly tenant?: string;
|
|
40
52
|
readonly context: RequestContext;
|
|
41
53
|
}
|
|
42
54
|
/** Runtime handle passed to `Plugin.repository`. */
|
|
@@ -116,4 +128,4 @@ declare function definePlugin<const Name extends string, Ext extends RepositoryE
|
|
|
116
128
|
readonly apiVersion?: 1;
|
|
117
129
|
}): Plugin<Name, Ext>;
|
|
118
130
|
//#endregion
|
|
119
|
-
export {
|
|
131
|
+
export { WithExtension as _, ExtensionOf as a, MutationEvent as c, NoExtension as d, PLUGIN_API_VERSION as f, RequestContext as g, RepositoryExtension as h, CallOptions as i, MutationIntent as l, RepositoryApi as m, AnyPlugin as n, HasFlag as o, Plugin as p, ApplyExtension as r, HookArgs as s, Actor as t, MutationKind as u, definePlugin as v };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { g as RequestContext, p as Plugin } from "../../plugin-o144f322.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. */
|
|
@@ -7,7 +7,10 @@ export interface ActorOptions {
|
|
|
7
7
|
/**
|
|
8
8
|
* Stamps the columns generated as `Flags.actor`: `createdBy` on insert and
|
|
9
9
|
* `updatedBy` on insert and update. Soft deletes are updates, so they record
|
|
10
|
-
* who deleted the row.
|
|
10
|
+
* who deleted the row. `impersonatedBy` gets `context.actor.impersonator` on
|
|
11
|
+
* an impersonated insert or update and is left alone otherwise, so a user's
|
|
12
|
+
* own update keeps the stamp, like the SQL kit's `track_actor`. Without an
|
|
13
|
+
* actor (anonymous requests) nothing is set.
|
|
11
14
|
*/
|
|
12
15
|
export declare function actor(options?: ActorOptions): Plugin<"actor">;
|
|
13
16
|
//#endregion
|