better-supabase 0.4.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 +2 -0
- package/dist/channel-FQW-CnGR.d.ts +70 -0
- package/dist/cli/bin.js +1 -1
- package/dist/cli/codemod-DqV7612m.js +388 -0
- package/dist/cli/{config-D9R0o1LH.js → config-CPVpXP5I.js} +78 -8
- package/dist/cli/{config-SSKSTUl2.js → config-dtoekM0v.js} +1 -1
- package/dist/cli/{read-sets-CMa29t9a.js → diff-SjkO5rNr.js} +1 -220
- package/dist/cli/{dist-CFgGknmZ.js → dist-DBPdlPYK.js} +4 -4
- package/dist/cli/{dist-B1MvLv-M.js → dist-DreZ24Sz.js} +1 -1
- package/dist/cli/{dist-Du7VnEUG.js → dist-xn4wlboc.js} +112 -1
- package/dist/cli/{doctor-BOym3mbL.js → doctor-L7Z8mYTx.js} +531 -50
- package/dist/cli/{env-BZ0uy8XK.js → env-DmKv_G4A.js} +1 -1
- package/dist/cli/gen-BJc9vdyu.js +2 -0
- package/dist/cli/{gen-Ccn_p90L.js → gen-C2osRcr5.js} +283 -87
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +4 -4
- package/dist/cli/{init-ClnxRYBF.js → init-Saf5-R9T.js} +2 -2
- package/dist/cli/{introspect-DxqfkVIv.js → introspect--H7oGmcd.js} +2 -2
- package/dist/cli/{openapi-8WHjxiTJ.js → openapi-Cp5Fyta-.js} +1 -1
- package/dist/cli/read-sets-BfMQcKy8.js +289 -0
- package/dist/cli/{run-CP20LKk_.js → run-BKR2W_79.js} +16 -12
- package/dist/cli/{seed-5J6eu_Xg.js → seed-CUlqWh-8.js} +1 -1
- package/dist/cli/sql-DzPbfngx.js +348 -0
- package/dist/cli/{version-5mW_8sY1.js → version-BDJuO0UR.js} +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +3 -10
- package/dist/clone-BfgmLU90.js +21 -0
- package/dist/{compiler-DB3aSFOA.d.ts → compiler-BXH_aZ7N.d.ts} +2 -2
- package/dist/config/index.d.ts +4 -4
- package/dist/config/index.js +4 -4
- package/dist/{config-CbvFmGD8.js → config-DreERlKo.js} +1 -1
- package/dist/{config-C1CYcm6o.d.ts → config-T_ejwyd3.d.ts} +176 -11
- package/dist/{define-CRS2MRV3.d.ts → define-Bp_BZa5h.d.ts} +11 -7
- package/dist/{define-Bh0AqTik.js → define-cNKzqwL_.js} +202 -16
- package/dist/edge/index.d.ts +16 -5
- package/dist/edge/index.js +10 -7
- package/dist/{entitlements-Dm-FHpNH.d.ts → entitlements-DUxU4oHe.d.ts} +1 -1
- package/dist/env/index.d.ts +2 -2
- package/dist/env/index.js +34 -1
- package/dist/{errors-L0xK41y_.js → errors-DtwXa6b8.js} +1 -0
- package/dist/events/index.d.ts +3 -2
- package/dist/events/index.js +65 -12
- package/dist/events-aIO7vsIk.d.ts +230 -0
- package/dist/{result-CqEsbgYs.d.ts → executor-BunPOO-k.d.ts} +200 -1
- package/dist/{executor-CZ7ImRiD.d.ts → executor-Cghdstwu.d.ts} +1 -2
- package/dist/{executor-CVt6dD0B.js → executor-XycESQPH.js} +3 -3
- package/dist/hono/index.d.ts +17 -6
- package/dist/hono/index.js +40 -5
- package/dist/impersonation-VfaGc4Q_.js +41 -0
- package/dist/{index-BBdvo-5d.d.ts → index-BGdgBPdf.d.ts} +49 -6
- package/dist/{index-BhHEC04r.d.ts → index-C7f8cOBM.d.ts} +2 -2
- package/dist/{index-Dy6kuF0P.d.ts → index-CLg87OBW.d.ts} +6 -1
- package/dist/{index-D5IfUIDh.d.ts → index-DOESKG27.d.ts} +33 -8
- package/dist/{index-DFmOpBGC.d.ts → index-DgILJRXj.d.ts} +35 -9
- package/dist/{index-RlLXd8dL.d.ts → index-DmXSxbLi.d.ts} +4 -4
- package/dist/{index-Cf-D2jk1.d.ts → index-Dxn2a5cw.d.ts} +2 -2
- package/dist/{index-528Ugmyp.d.ts → index-SaTsEfGP.d.ts} +76 -8
- package/dist/{index-BAy4tsAP.d.ts → index-fag8VJZ2.d.ts} +3 -4
- package/dist/index.d.ts +30 -13
- package/dist/index.js +10 -9
- package/dist/jobs/index.d.ts +147 -101
- package/dist/jobs/index.js +744 -237
- package/dist/{json-schema-DP6V7cbr.js → json-schema-m1fiwKgN.js} +22 -1
- 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-BpxDfnbg.js → kits-CGLLrNPd.js} +37 -9
- package/dist/list/index.d.ts +1 -1
- package/dist/{live-BHakSWWo.d.ts → live-D9W2M5aO.d.ts} +6 -5
- package/dist/mcp/index.d.ts +25 -8
- package/dist/mcp/index.js +23 -14
- package/dist/{mfa-C7MNQ1Ob.d.ts → mfa-CusDgPaD.d.ts} +5 -1
- package/dist/next/index.d.ts +31 -10
- package/dist/next/index.js +136 -27
- 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 +2 -2
- package/dist/orgs/index.d.ts +132 -0
- package/dist/orgs/index.js +245 -0
- package/dist/orpc/index.d.ts +28 -8
- package/dist/orpc/index.js +16 -3
- package/dist/otel/index.d.ts +21 -2
- package/dist/otel/index.js +50 -9
- 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-bNhvJwRh.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 +15 -10
- 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 +2 -2
- package/dist/plugins/tenant/index.js +38 -20
- 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-DFwEQwmN.d.ts → pool-C9voM4Kw.d.ts} +17 -6
- package/dist/postgres/index.d.ts +5 -5
- package/dist/postgres/index.js +20 -11
- package/dist/{postgrest-Bc-0Xw62.d.ts → postgrest-CruYKAvV.d.ts} +1 -1
- package/dist/{problem-BihjPR0t.js → problem-BW1KhzSG.js} +2 -1
- package/dist/{problem-U8UEKccv.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-BMMSWbI7.js → query-CCbo44lG.js} +2 -2
- 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 +14 -3
- package/dist/react/server.js +10 -2
- package/dist/react/session.d.ts +7 -1
- package/dist/react/session.js +9 -1
- package/dist/{react-CQHMaESR.js → react-C_s7kiNz.js} +103 -11
- package/dist/{read-set-FWK8qQ58.js → read-set-DQFt1N_Y.js} +22 -1
- package/dist/realtime/index.d.ts +3 -3
- package/dist/realtime/index.js +5 -4
- package/dist/{refresh-BwYkyNOQ.d.ts → refresh-BTrQ_ZTp.d.ts} +1 -1
- package/dist/{resolve-BAqQcwMW.d.ts → resolve-C15TJq77.d.ts} +11 -5
- package/dist/{resource-My_y-Syh.js → resource-CRxxwkQq.js} +22 -7
- package/dist/{resource-BJcaIfxi.d.ts → resource-D8iqWMr-.d.ts} +5 -5
- package/dist/respond-D2OQl62-.d.ts +371 -0
- package/dist/{result-Bj6yt33D.js → result-BKiliPY7.js} +1 -1
- package/dist/server/index.d.ts +14 -12
- package/dist/server/index.js +11 -17
- package/dist/{server-BA75B0mT.js → server-Csx2sRjg.js} +251 -34
- package/dist/{shared-DnoCdMDr.js → shared-D1dRUrAg.js} +16 -2
- package/dist/signers-Cc6r98SW.d.ts +162 -0
- package/dist/{simplify-Cqvfr9dj.js → simplify-_0oUKqCd.js} +4 -115
- package/dist/sql/index.d.ts +247 -10
- package/dist/sql/index.js +8590 -779
- package/dist/{sql-B34HU2DI.js → sql-Coh6n4cC.js} +9 -2
- package/dist/ssr/index.d.ts +4 -4
- package/dist/ssr/index.js +1 -2
- package/dist/{standard-BRyBGTtp.js → standard-D7r6dMUu.js} +6 -3
- package/dist/{standard-BsaCYQJc.d.ts → standard-J4YgMOLm.d.ts} +1 -1
- package/dist/{stats-C_YxA2r1.js → stats-Bqob3WqT.js} +4 -4
- package/dist/storage/index.d.ts +4 -4
- package/dist/storage/index.js +155 -26
- 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/{permdock-sql-C4KFzLc9.js → template-TxrGI5jR.js} +2 -31
- package/dist/{temporal-BwshQk0W.js → temporal-CVlIV2DZ.js} +17 -4
- package/dist/{temporal-required-CBNHT6ni.js → temporal-required-CAt4Dc9Z.js} +2 -2
- package/dist/testing/index.d.ts +100 -10
- package/dist/testing/index.js +336 -8
- package/dist/{types-DONv9SHN.d.ts → types-Dnf_wRDz.d.ts} +26 -3
- package/dist/user-change-CHFEUro1.js +49 -0
- package/dist/{webhooks-Bh_Jf_-l.js → verify-Ce49ETPz.js} +5 -5
- package/dist/{version-Bmf7_FzM.js → version-DM_Roffb.js} +1 -1
- package/dist/{view-B64o81wp.d.ts → view-BEf0diC5.d.ts} +8 -3
- package/dist/{mfa-byfjruR9.js → view-Bq64ki8-.js} +95 -38
- package/dist/webhooks/index.d.ts +135 -4
- package/dist/webhooks/index.js +588 -2
- package/dist/wire-D2dT86vi.js +35 -0
- package/package.json +16 -8
- package/schemas/config-v1.json +131 -1
- package/schemas/doctor-report-v1.json +10 -0
- package/skills/better-supabase/SKILL.md +9 -6
- package/skills/better-supabase/references/plugins.md +6 -3
- package/skills/better-supabase/references/troubleshooting.md +1 -0
- package/skills/better-supabase-api/SKILL.md +9 -2
- package/skills/better-supabase-api/references/adapters.md +11 -8
- package/skills/better-supabase-auth/SKILL.md +3 -0
- package/skills/better-supabase-auth/references/permdock.md +56 -11
- package/dist/cli/gen-D5cLCULx.js +0 -2
- package/dist/cli/sql-D4VbqmXi.js +0 -199
- package/dist/events-B21UOhvP.d.ts +0 -84
- package/dist/executor-B5IoUxLE.d.ts +0 -195
- package/dist/invalidate-CgxmRamI.js +0 -24
- package/dist/permdock-sql-DImJULY-.d.ts +0 -17
- package/dist/plugin-BqR6wKMB.js +0 -23
- package/dist/respond-DgIbdrt8.d.ts +0 -194
- package/dist/view-BrJI2LKp.js +0 -45
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/// <reference lib="esnext.temporal" />
|
|
2
|
+
import { S as ErrorMapper, a as AsyncResult } from "../executor-BunPOO-k.js";
|
|
3
|
+
import { a as EventHub } from "../events-aIO7vsIk.js";
|
|
4
|
+
import { g as RequestContext } from "../plugin-o144f322.js";
|
|
5
|
+
import { i as sqlTransport, n as RpcClient, r as rpcTransport, t as KitTransport } from "../kit-transport-B82k1djG.js";
|
|
6
|
+
//#region src/orgs/orgs.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* Organization columns by database name: `name`, `slug` and the columns in
|
|
9
|
+
* `kits.organizations.options.attributes`. Other keys are ignored.
|
|
10
|
+
*/
|
|
11
|
+
interface OrgAttributes {
|
|
12
|
+
readonly name?: string;
|
|
13
|
+
readonly slug?: string;
|
|
14
|
+
readonly [column: string]: unknown;
|
|
15
|
+
}
|
|
16
|
+
interface CreateOrgOptions {
|
|
17
|
+
/** The owner, when the transport runs as the service role. */
|
|
18
|
+
readonly ownerId?: string;
|
|
19
|
+
}
|
|
20
|
+
type SlugProblem = "invalid" | "reserved" | "taken";
|
|
21
|
+
interface InviteRequest {
|
|
22
|
+
/** `null` invites to the platform (`kits.access.model: 'catalog'`). */
|
|
23
|
+
readonly organizationId: string | null;
|
|
24
|
+
readonly email: string;
|
|
25
|
+
/** A role name, or a role id or key under the catalog model. */
|
|
26
|
+
readonly role: string;
|
|
27
|
+
/** A Postgres interval such as `'3 days'`. Defaults to `options.validFor`. */
|
|
28
|
+
readonly validFor?: string;
|
|
29
|
+
/** Profile fields to fill in on sign-up. */
|
|
30
|
+
readonly prefill?: Readonly<Record<string, unknown>>;
|
|
31
|
+
}
|
|
32
|
+
interface Invitation {
|
|
33
|
+
readonly id: string;
|
|
34
|
+
readonly organizationId: string | null;
|
|
35
|
+
readonly email: string;
|
|
36
|
+
readonly role: string;
|
|
37
|
+
readonly expiresAt: Temporal.Instant;
|
|
38
|
+
readonly invitedBy?: string;
|
|
39
|
+
readonly prefill: Readonly<Record<string, unknown>>;
|
|
40
|
+
}
|
|
41
|
+
/** What `onInvite` receives: the place to send the email. */
|
|
42
|
+
interface InvitationSent {
|
|
43
|
+
readonly invitation: Invitation;
|
|
44
|
+
/** Put it in the accept link. It is never stored in plain text by default. */
|
|
45
|
+
readonly token: string;
|
|
46
|
+
readonly resent: boolean;
|
|
47
|
+
}
|
|
48
|
+
type InvitationStatus = "pending" | "accepted" | "declined" | "revoked" | "expired";
|
|
49
|
+
interface InvitationPreview {
|
|
50
|
+
readonly status: InvitationStatus;
|
|
51
|
+
readonly email: string;
|
|
52
|
+
readonly role: string;
|
|
53
|
+
readonly organizationId: string | null;
|
|
54
|
+
readonly expiresAt: Temporal.Instant;
|
|
55
|
+
/** The organization columns in `options.previewColumns`. */
|
|
56
|
+
readonly organization: Readonly<Record<string, unknown>> | null;
|
|
57
|
+
readonly prefill: Readonly<Record<string, unknown>>;
|
|
58
|
+
}
|
|
59
|
+
interface SwitchResult {
|
|
60
|
+
readonly organizationId: string;
|
|
61
|
+
/** The active tenant is a claim: refresh the session to get it. */
|
|
62
|
+
readonly refresh: boolean;
|
|
63
|
+
}
|
|
64
|
+
interface OrgsOptions {
|
|
65
|
+
/** `sqlTransport(postgres.asUser(claims))` or `rpcTransport(supabase)`. */
|
|
66
|
+
readonly transport: KitTransport;
|
|
67
|
+
/**
|
|
68
|
+
* The schema holding the functions: `kits.<module>.schema` with
|
|
69
|
+
* `sqlTransport`, the exposed API schema of wrappers with `rpcTransport`.
|
|
70
|
+
* One schema for both modules, or one per module. Defaults to
|
|
71
|
+
* `better_supabase`.
|
|
72
|
+
*/
|
|
73
|
+
readonly schema?: string | {
|
|
74
|
+
readonly organizations?: string;
|
|
75
|
+
readonly invitations?: string;
|
|
76
|
+
};
|
|
77
|
+
/** `betterSupabase.events`, to emit `org.*` and `invitation.*` kit events. */
|
|
78
|
+
readonly events?: EventHub;
|
|
79
|
+
/** The user acting, for event metadata. */
|
|
80
|
+
readonly actorId?: string;
|
|
81
|
+
readonly context?: RequestContext;
|
|
82
|
+
/**
|
|
83
|
+
* Runs before an invitation is created, after the database checks the
|
|
84
|
+
* caller may invite. Return `false` to refuse (seat limits, plans).
|
|
85
|
+
*/
|
|
86
|
+
readonly canInvite?: (request: InviteRequest) => boolean | Promise<boolean>;
|
|
87
|
+
/**
|
|
88
|
+
* Runs after an invitation is created or resent, to send the email. When
|
|
89
|
+
* it throws, the call returns an error and the invitation stays open:
|
|
90
|
+
* call `resendInvitation` to retry.
|
|
91
|
+
*/
|
|
92
|
+
readonly onInvite?: (sent: InvitationSent) => void | Promise<void>;
|
|
93
|
+
/** Run before the built-in mapping, as in `betterSupabase.mapError()`. */
|
|
94
|
+
readonly errorMappers?: readonly ErrorMapper[];
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The `organizations` and `invitations` SQL kit modules as typed calls. Each
|
|
98
|
+
* method returns an `AsyncResult`; database errors carry the kit's error
|
|
99
|
+
* code (`ORG_FORBIDDEN`, `INVITATION_INVALID`) as `hint`.
|
|
100
|
+
*/
|
|
101
|
+
interface Orgs {
|
|
102
|
+
create(attributes: OrgAttributes, options?: CreateOrgOptions): AsyncResult<{
|
|
103
|
+
readonly id: string;
|
|
104
|
+
}>;
|
|
105
|
+
update(organizationId: string, attributes: OrgAttributes): AsyncResult<true>;
|
|
106
|
+
/** `false` when it did not exist (or was already soft-deleted). */
|
|
107
|
+
delete(organizationId: string): AsyncResult<boolean>;
|
|
108
|
+
/** Why a slug can't be used, or `undefined` when it can. */
|
|
109
|
+
slugProblem(slug: string, exceptOrganizationId?: string): AsyncResult<SlugProblem | undefined>;
|
|
110
|
+
updateMemberRole(organizationId: string, userId: string, role: string): AsyncResult<true>;
|
|
111
|
+
removeMember(organizationId: string, userId: string): AsyncResult<true>;
|
|
112
|
+
leave(organizationId: string): AsyncResult<true>;
|
|
113
|
+
/** `formerRole` defaults to `options.formerOwnerRole`. */
|
|
114
|
+
transferOwnership(organizationId: string, newOwnerId: string, formerRole?: string): AsyncResult<true>;
|
|
115
|
+
markUsed(organizationId: string): AsyncResult<boolean>;
|
|
116
|
+
switch(organizationId: string): AsyncResult<SwitchResult>;
|
|
117
|
+
invite(request: InviteRequest): AsyncResult<InvitationSent>;
|
|
118
|
+
resendInvitation(invitationId: string, validFor?: string): AsyncResult<InvitationSent>;
|
|
119
|
+
/** `false` when it was not open. */
|
|
120
|
+
revokeInvitation(invitationId: string): AsyncResult<boolean>;
|
|
121
|
+
declineInvitation(token: string): AsyncResult<boolean>;
|
|
122
|
+
/** `undefined` for an unknown token. Callable without a session. */
|
|
123
|
+
previewInvitation(token: string): AsyncResult<InvitationPreview | undefined>;
|
|
124
|
+
/** Returns the organization joined, `null` for a platform invitation. */
|
|
125
|
+
acceptInvitation(token: string): AsyncResult<{
|
|
126
|
+
readonly organizationId: string | null;
|
|
127
|
+
}>;
|
|
128
|
+
}
|
|
129
|
+
/** Calls the `organizations` and `invitations` kit functions. */
|
|
130
|
+
export declare function createOrgs(options: OrgsOptions): Orgs;
|
|
131
|
+
//#endregion
|
|
132
|
+
export { type CreateOrgOptions, type Invitation, type InvitationPreview, type InvitationSent, type InvitationStatus, type InviteRequest, type KitTransport, type OrgAttributes, type Orgs, type OrgsOptions, type RpcClient, type SlugProblem, type SwitchResult, rpcTransport, sqlTransport };
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { c as mapDbError, n as dbError } from "../errors-DtwXa6b8.js";
|
|
2
|
+
import { t as temporal } from "../temporal-required-CAt4Dc9Z.js";
|
|
3
|
+
import { i as ok, n as err, o as toDbError, t as AsyncResult } from "../result-BKiliPY7.js";
|
|
4
|
+
import { n as emitKitEvent } from "../kit-events-CTFDfSwb.js";
|
|
5
|
+
import { n as rpcTransport, r as sqlTransport, t as rawError } from "../kit-transport-DJlHQUBU.js";
|
|
6
|
+
//#region src/orgs/orgs.ts
|
|
7
|
+
const DEFAULT_SCHEMA = "better_supabase";
|
|
8
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9
|
+
const text = (value) => typeof value === "string" ? value : String(value);
|
|
10
|
+
const optionalText = (value) => value === null || value === void 0 ? null : text(value);
|
|
11
|
+
function invitationFrom(row) {
|
|
12
|
+
const invitedBy = optionalText(row["invited_by"]);
|
|
13
|
+
return {
|
|
14
|
+
id: text(row["id"]),
|
|
15
|
+
organizationId: optionalText(row["tenant"]),
|
|
16
|
+
email: text(row["email"]),
|
|
17
|
+
role: text(row["role"]),
|
|
18
|
+
expiresAt: temporal().Instant.from(text(row["expires_at"])),
|
|
19
|
+
...invitedBy === null ? {} : { invitedBy },
|
|
20
|
+
prefill: isRecord(row["prefill"]) ? row["prefill"] : {}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
const STATUSES = /* @__PURE__ */ new Set([
|
|
24
|
+
"pending",
|
|
25
|
+
"accepted",
|
|
26
|
+
"declined",
|
|
27
|
+
"revoked",
|
|
28
|
+
"expired"
|
|
29
|
+
]);
|
|
30
|
+
function isStatus(value) {
|
|
31
|
+
return typeof value === "string" && STATUSES.has(value);
|
|
32
|
+
}
|
|
33
|
+
function previewFrom(row) {
|
|
34
|
+
const status = row["status"];
|
|
35
|
+
if (!isStatus(status)) throw new TypeError(`Unknown invitation status ${String(status)}`);
|
|
36
|
+
return {
|
|
37
|
+
status,
|
|
38
|
+
email: text(row["email"]),
|
|
39
|
+
role: text(row["role"]),
|
|
40
|
+
organizationId: optionalText(row["tenant"]),
|
|
41
|
+
expiresAt: temporal().Instant.from(text(row["expires_at"])),
|
|
42
|
+
organization: isRecord(row["organization"]) ? row["organization"] : null,
|
|
43
|
+
prefill: isRecord(row["prefill"]) ? row["prefill"] : {}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function recordOf(value, fn) {
|
|
47
|
+
if (!isRecord(value)) throw new TypeError(`${fn} returned ${JSON.stringify(value)}`);
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
50
|
+
/** Calls the `organizations` and `invitations` kit functions. */
|
|
51
|
+
function createOrgs(options) {
|
|
52
|
+
const { transport } = options;
|
|
53
|
+
const schemaOf = (module) => typeof options.schema === "string" ? options.schema : options.schema?.[module] ?? DEFAULT_SCHEMA;
|
|
54
|
+
const mappers = options.errorMappers ?? [];
|
|
55
|
+
function run(module, fn, args, then) {
|
|
56
|
+
return AsyncResult.from(async () => {
|
|
57
|
+
let value;
|
|
58
|
+
try {
|
|
59
|
+
value = await transport.call(schemaOf(module), fn, args);
|
|
60
|
+
} catch (cause) {
|
|
61
|
+
const raw = rawError(cause);
|
|
62
|
+
return err(raw ? mapDbError(raw, mappers) : toDbError(cause));
|
|
63
|
+
}
|
|
64
|
+
return ok(await then(value));
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function emit(type, data, subject, tenant) {
|
|
68
|
+
if (!options.events) return;
|
|
69
|
+
emitKitEvent(options.events, type, data, {
|
|
70
|
+
subject,
|
|
71
|
+
...tenant ? { tenant } : {},
|
|
72
|
+
...options.actorId ? { actorId: options.actorId } : {},
|
|
73
|
+
...options.context ? { context: options.context } : {}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
const orgEvent = (type, data) => {
|
|
77
|
+
emit(type, data, `organizations/${data.organizationId}`, data.organizationId);
|
|
78
|
+
};
|
|
79
|
+
const invitationEvent = (type, data) => {
|
|
80
|
+
emit(type, data, `invitations/${data.invitationId}`, data.organizationId ?? null);
|
|
81
|
+
};
|
|
82
|
+
const sent = async (value, fn, resent) => {
|
|
83
|
+
const row = recordOf(value, fn);
|
|
84
|
+
const invitation = invitationFrom(row);
|
|
85
|
+
const result = {
|
|
86
|
+
invitation,
|
|
87
|
+
token: text(row["token"]),
|
|
88
|
+
resent
|
|
89
|
+
};
|
|
90
|
+
invitationEvent(resent ? "invitation.resent" : "invitation.created", {
|
|
91
|
+
invitationId: invitation.id,
|
|
92
|
+
...invitation.organizationId === null ? {} : { organizationId: invitation.organizationId },
|
|
93
|
+
email: invitation.email,
|
|
94
|
+
role: invitation.role
|
|
95
|
+
});
|
|
96
|
+
await options.onInvite?.(result);
|
|
97
|
+
return result;
|
|
98
|
+
};
|
|
99
|
+
return {
|
|
100
|
+
create(attributes, createOptions) {
|
|
101
|
+
return run("organizations", "create_organization", { attrs: {
|
|
102
|
+
...attributes,
|
|
103
|
+
...createOptions?.ownerId ? { owner_id: createOptions.ownerId } : {}
|
|
104
|
+
} }, (value) => {
|
|
105
|
+
const id = text(value);
|
|
106
|
+
orgEvent("org.created", {
|
|
107
|
+
organizationId: id,
|
|
108
|
+
...createOptions?.ownerId ?? options.actorId ? { userId: createOptions?.ownerId ?? options.actorId } : {}
|
|
109
|
+
});
|
|
110
|
+
return { id };
|
|
111
|
+
});
|
|
112
|
+
},
|
|
113
|
+
update(organizationId, attributes) {
|
|
114
|
+
return run("organizations", "update_organization", {
|
|
115
|
+
org: organizationId,
|
|
116
|
+
attrs: attributes
|
|
117
|
+
}, () => {
|
|
118
|
+
orgEvent("org.updated", { organizationId });
|
|
119
|
+
return true;
|
|
120
|
+
});
|
|
121
|
+
},
|
|
122
|
+
delete(organizationId) {
|
|
123
|
+
return run("organizations", "delete_organization", { org: organizationId }, (value) => {
|
|
124
|
+
if (value === true) orgEvent("org.deleted", { organizationId });
|
|
125
|
+
return value === true;
|
|
126
|
+
});
|
|
127
|
+
},
|
|
128
|
+
slugProblem(slug, exceptOrganizationId) {
|
|
129
|
+
return run("organizations", "organization_slug_problem", {
|
|
130
|
+
value: slug,
|
|
131
|
+
except_org: exceptOrganizationId
|
|
132
|
+
}, (value) => {
|
|
133
|
+
if (value === null) return;
|
|
134
|
+
if (value === "invalid" || value === "reserved" || value === "taken") return value;
|
|
135
|
+
throw new TypeError(`Unknown slug problem ${String(value)}`);
|
|
136
|
+
});
|
|
137
|
+
},
|
|
138
|
+
updateMemberRole(organizationId, userId, role) {
|
|
139
|
+
return run("organizations", "update_member_role", {
|
|
140
|
+
org: organizationId,
|
|
141
|
+
member: userId,
|
|
142
|
+
role
|
|
143
|
+
}, () => {
|
|
144
|
+
orgEvent("org.role_changed", {
|
|
145
|
+
organizationId,
|
|
146
|
+
userId,
|
|
147
|
+
role
|
|
148
|
+
});
|
|
149
|
+
return true;
|
|
150
|
+
});
|
|
151
|
+
},
|
|
152
|
+
removeMember(organizationId, userId) {
|
|
153
|
+
return run("organizations", "remove_member", {
|
|
154
|
+
org: organizationId,
|
|
155
|
+
member: userId
|
|
156
|
+
}, () => {
|
|
157
|
+
orgEvent("org.member_removed", {
|
|
158
|
+
organizationId,
|
|
159
|
+
userId
|
|
160
|
+
});
|
|
161
|
+
return true;
|
|
162
|
+
});
|
|
163
|
+
},
|
|
164
|
+
leave(organizationId) {
|
|
165
|
+
return run("organizations", "leave_organization", { org: organizationId }, () => {
|
|
166
|
+
orgEvent("org.member_left", {
|
|
167
|
+
organizationId,
|
|
168
|
+
...options.actorId ? { userId: options.actorId } : {}
|
|
169
|
+
});
|
|
170
|
+
return true;
|
|
171
|
+
});
|
|
172
|
+
},
|
|
173
|
+
transferOwnership(organizationId, newOwnerId, formerRole) {
|
|
174
|
+
return run("organizations", "transfer_ownership", {
|
|
175
|
+
org: organizationId,
|
|
176
|
+
new_owner: newOwnerId,
|
|
177
|
+
former_role: formerRole
|
|
178
|
+
}, () => {
|
|
179
|
+
orgEvent("org.ownership_transferred", {
|
|
180
|
+
organizationId,
|
|
181
|
+
userId: newOwnerId
|
|
182
|
+
});
|
|
183
|
+
return true;
|
|
184
|
+
});
|
|
185
|
+
},
|
|
186
|
+
markUsed(organizationId) {
|
|
187
|
+
return run("organizations", "mark_used", { org: organizationId }, (value) => value === true);
|
|
188
|
+
},
|
|
189
|
+
switch(organizationId) {
|
|
190
|
+
return run("organizations", "switch_organization", { org: organizationId }, (value) => {
|
|
191
|
+
const row = recordOf(value, "switch_organization");
|
|
192
|
+
orgEvent("org.switched", {
|
|
193
|
+
organizationId,
|
|
194
|
+
...options.actorId ? { userId: options.actorId } : {}
|
|
195
|
+
});
|
|
196
|
+
return {
|
|
197
|
+
organizationId: text(row["organization_id"]),
|
|
198
|
+
refresh: row["refresh"] === true
|
|
199
|
+
};
|
|
200
|
+
});
|
|
201
|
+
},
|
|
202
|
+
invite(request) {
|
|
203
|
+
return AsyncResult.from(async () => {
|
|
204
|
+
if (options.canInvite && !await options.canInvite(request)) return err(dbError("forbidden", "The invitation was refused", { hint: "INVITATION_FORBIDDEN" }));
|
|
205
|
+
return run("invitations", "invite_member", {
|
|
206
|
+
tenant: request.organizationId,
|
|
207
|
+
invitee_email: request.email,
|
|
208
|
+
invitee_role: request.role,
|
|
209
|
+
valid_for: request.validFor,
|
|
210
|
+
prefill: request.prefill
|
|
211
|
+
}, (value) => sent(value, "invite_member", false));
|
|
212
|
+
});
|
|
213
|
+
},
|
|
214
|
+
resendInvitation(invitationId, validFor) {
|
|
215
|
+
return run("invitations", "resend_invitation", {
|
|
216
|
+
invitation_id: invitationId,
|
|
217
|
+
valid_for: validFor
|
|
218
|
+
}, (value) => sent(value, "resend_invitation", true));
|
|
219
|
+
},
|
|
220
|
+
revokeInvitation(invitationId) {
|
|
221
|
+
return run("invitations", "revoke_invitation", { invitation_id: invitationId }, (value) => {
|
|
222
|
+
if (value === true) invitationEvent("invitation.revoked", { invitationId });
|
|
223
|
+
return value === true;
|
|
224
|
+
});
|
|
225
|
+
},
|
|
226
|
+
declineInvitation(token) {
|
|
227
|
+
return run("invitations", "decline_invitation", { token }, (value) => value === true);
|
|
228
|
+
},
|
|
229
|
+
previewInvitation(token) {
|
|
230
|
+
return run("invitations", "invitation_preview", { token }, (value) => value === null ? void 0 : previewFrom(recordOf(value, "invitation_preview")));
|
|
231
|
+
},
|
|
232
|
+
acceptInvitation(token) {
|
|
233
|
+
return run("invitations", "accept_invitation", { token }, (value) => {
|
|
234
|
+
const organizationId = optionalText(value);
|
|
235
|
+
if (organizationId !== null) orgEvent("org.member_added", {
|
|
236
|
+
organizationId,
|
|
237
|
+
...options.actorId ? { userId: options.actorId } : {}
|
|
238
|
+
});
|
|
239
|
+
return { organizationId };
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
//#endregion
|
|
245
|
+
export { createOrgs, rpcTransport, sqlTransport };
|
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 { f as ServerContext, n as GuardOptions, p as ServerOptions, r as MiddlewareOptions, u as BetterServer } from "../respond-
|
|
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. */
|
|
@@ -20,15 +20,35 @@ export interface OrpcOptions extends ServerOptions {
|
|
|
20
20
|
/** Include internal error messages. Defaults to `NODE_ENV === 'development'`. */
|
|
21
21
|
readonly exposeErrors?: boolean;
|
|
22
22
|
}
|
|
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
|
+
}>;
|
|
32
|
+
}
|
|
33
|
+
export interface OrpcFetchOptions {
|
|
34
|
+
/** Path prefix the router is mounted under, e.g. `/rpc`. */
|
|
35
|
+
readonly prefix?: `/${string}`;
|
|
36
|
+
}
|
|
23
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>>;
|
|
24
38
|
export interface BetterOrpc<M extends AnyModels, F extends AnyFunctions, E, C = unknown, P = unknown> extends BetterServer<M, F, E, C, P> {
|
|
25
39
|
/**
|
|
26
40
|
* Resolves the caller, enforces `allow` and adds `context.db`, `context.auth`
|
|
27
41
|
* and `context.bs`. Thrown `DbException`s, and errors caused by a
|
|
28
|
-
* `DbError`, become `ORPCError`s.
|
|
29
|
-
* `
|
|
42
|
+
* `DbError`, become `ORPCError`s. Serve the router with `fetchHandler` so
|
|
43
|
+
* refreshed session cookies and `bs-primary-until` reach the response.
|
|
30
44
|
*/
|
|
31
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`).
|
|
50
|
+
*/
|
|
51
|
+
fetchHandler(handler: OrpcFetchHandler, options?: OrpcFetchOptions): (request: Request) => Promise<Response>;
|
|
32
52
|
/** A `Result` (or `AsyncResult`) as data, throwing an `ORPCError` on failure. */
|
|
33
53
|
unwrap<T>(value: Result<T> | PromiseLike<Result<T>>): Promise<T>;
|
|
34
54
|
/** Plain values pass through; thrown `DbException`s become `ORPCError`s. */
|
package/dist/orpc/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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
6
|
const CODES = /* @__PURE__ */ new Map(Object.entries(COMMON_ERROR_STATUS_MAP).map(([code, status]) => [status, code]));
|
|
@@ -12,6 +12,7 @@ function orpcCode(error) {
|
|
|
12
12
|
function createOrpc(betterSupabase, options = {}) {
|
|
13
13
|
const server = createServer(betterSupabase, options);
|
|
14
14
|
const expose = options.exposeErrors ?? defaultExpose();
|
|
15
|
+
const contexts = /* @__PURE__ */ new WeakMap();
|
|
15
16
|
const toOrpcError = (error) => {
|
|
16
17
|
const problem = toProblem(error, { expose });
|
|
17
18
|
return new ORPCError(orpcCode(error), {
|
|
@@ -26,6 +27,7 @@ function createOrpc(betterSupabase, options = {}) {
|
|
|
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
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,
|
|
@@ -39,6 +41,17 @@ function createOrpc(betterSupabase, options = {}) {
|
|
|
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
57
|
if (!settled.ok) throw toOrpcError(settled.error);
|
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
|
|
@@ -31,6 +41,15 @@ export declare function otel(options?: OtelOptions): Plugin<"otel">;
|
|
|
31
41
|
export declare function traceAuth(betterSupabase: {
|
|
32
42
|
readonly events: EventHub;
|
|
33
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: {
|
|
51
|
+
readonly events: EventHub;
|
|
52
|
+
}, options?: Pick<OtelOptions, "tracer" | "attributes">): () => void;
|
|
34
53
|
/**
|
|
35
54
|
* A `fetch` that adds W3C `traceparent`/`tracestate` from the active
|
|
36
55
|
* context. Pass it to `createClient(url, key, { global: { fetch } })` so
|
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",
|
|
@@ -30,9 +40,9 @@ function systemOf(executor) {
|
|
|
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: [
|
|
@@ -52,6 +62,8 @@ function otel(options = {}) {
|
|
|
52
62
|
enforce: "post",
|
|
53
63
|
wrapExecutor: (executor) => {
|
|
54
64
|
const system = systemOf(executor);
|
|
65
|
+
const database = options.database ?? (system === "postgresql" ? "postgres" : void 0);
|
|
66
|
+
const server = serverAttributes(options.server);
|
|
55
67
|
/** Span and metric attributes per table and operation, built once. */
|
|
56
68
|
const described = /* @__PURE__ */ new Map();
|
|
57
69
|
const describe = (op) => {
|
|
@@ -65,13 +77,15 @@ function otel(options = {}) {
|
|
|
65
77
|
attributes: {
|
|
66
78
|
...options.attributes,
|
|
67
79
|
"db.system.name": system,
|
|
68
|
-
"db.namespace": op.table.schema,
|
|
80
|
+
"db.namespace": database ? `${database}|${op.table.schema}` : op.table.schema,
|
|
69
81
|
"db.collection.name": op.table.name,
|
|
70
82
|
"db.operation.name": operation,
|
|
71
83
|
"db.query.summary": summary,
|
|
72
|
-
"better_supabase.executor": executor.name
|
|
84
|
+
"better_supabase.executor": executor.name,
|
|
85
|
+
...server
|
|
73
86
|
},
|
|
74
87
|
metric: {
|
|
88
|
+
...server,
|
|
75
89
|
"db.system.name": system,
|
|
76
90
|
"db.collection.name": op.table.name,
|
|
77
91
|
"db.operation.name": operation
|
|
@@ -106,7 +120,7 @@ function otel(options = {}) {
|
|
|
106
120
|
} else {
|
|
107
121
|
span.setAttributes({
|
|
108
122
|
"error.type": result.error.kind,
|
|
109
|
-
...result.error.code ? { "db.response.status_code": result.error.code } : {}
|
|
123
|
+
...result.error.code && SQLSTATE.test(result.error.code) ? { "db.response.status_code": result.error.code } : {}
|
|
110
124
|
});
|
|
111
125
|
span.setStatus({
|
|
112
126
|
code: SpanStatusCode.ERROR,
|
|
@@ -137,7 +151,7 @@ function otel(options = {}) {
|
|
|
137
151
|
* Returns a function that stops listening.
|
|
138
152
|
*/
|
|
139
153
|
function traceAuth(betterSupabase, options = {}) {
|
|
140
|
-
const tracer = options.tracer ?? trace.getTracer("better-supabase", "0.
|
|
154
|
+
const tracer = options.tracer ?? trace.getTracer("better-supabase", "0.5.0");
|
|
141
155
|
const offAuth = betterSupabase.events.on("auth", (event) => {
|
|
142
156
|
const span = tracer.startSpan("auth.resolve", {
|
|
143
157
|
kind: SpanKind.INTERNAL,
|
|
@@ -169,6 +183,33 @@ function traceAuth(betterSupabase, options = {}) {
|
|
|
169
183
|
};
|
|
170
184
|
}
|
|
171
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
|
+
/**
|
|
172
213
|
* A `fetch` that adds W3C `traceparent`/`tracestate` from the active
|
|
173
214
|
* context. Pass it to `createClient(url, key, { global: { fetch } })` so
|
|
174
215
|
* PostgREST, Auth and Storage calls join the trace.
|
|
@@ -186,4 +227,4 @@ function tracedFetch(base = fetch) {
|
|
|
186
227
|
};
|
|
187
228
|
}
|
|
188
229
|
//#endregion
|
|
189
|
-
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 };
|