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.
Files changed (149) hide show
  1. package/README.md +34 -31
  2. package/dist/base64-CQeB1eSf.js +42 -0
  3. package/dist/claims-CoTobNC-.js +25 -0
  4. package/dist/client/index.d.ts +1 -1
  5. package/dist/client/index.js +4 -14
  6. package/dist/{compiler-DL18ukPe.d.ts → compiler-2zqGzxVZ.d.ts} +2 -2
  7. package/dist/config/index.d.ts +4 -3
  8. package/dist/config/index.js +5 -3
  9. package/dist/config-Be4GwsqI.js +129 -0
  10. package/dist/{index-BD6wKthp.d.ts → config-C1CYcm6o.d.ts} +75 -54
  11. package/dist/{config-D-aR2ZoN.js → config-DOsGYSiT.js} +14 -136
  12. package/dist/{define-QGkq04oe.js → define-DF341GDj.js} +133 -110
  13. package/dist/{define-CTfWw-SS.d.ts → define-DUbydmJa.d.ts} +32 -14
  14. package/dist/edge/index.d.ts +4 -4
  15. package/dist/edge/index.js +4 -4
  16. package/dist/entitlements-CTEBm7IB.js +20 -0
  17. package/dist/entitlements-DvJXoI02.d.ts +42 -0
  18. package/dist/env/index.js +203 -1
  19. package/dist/events/index.d.ts +1 -1
  20. package/dist/events/index.js +139 -1
  21. package/dist/{events-2OJsVG_d.d.ts → events-Do8jXh4D.d.ts} +3 -3
  22. package/dist/{executor-Bje4suQF.d.ts → executor-B26lgbHD.d.ts} +2 -2
  23. package/dist/{executor-Ceq60hPM.js → executor-C57jx2pn.js} +7 -2
  24. package/dist/{executor-Bazhgsgq.d.ts → executor-DynmIWhq.d.ts} +2 -2
  25. package/dist/guards-BnzDvFrz.js +10 -0
  26. package/dist/hono/index.d.ts +5 -5
  27. package/dist/hono/index.js +4 -4
  28. package/dist/{index-Hf8PP9sP.d.ts → index-BNsABq7P.d.ts} +17 -13
  29. package/dist/{index-JXHBuS53.d.ts → index-CADlNHK-.d.ts} +27 -14
  30. package/dist/{index-Nxo3TJcm.d.ts → index-CTTMS6_1.d.ts} +27 -8
  31. package/dist/index-Cf-D2jk1.d.ts +50 -0
  32. package/dist/{index-Dup-nSe9.d.ts → index-CriFFJ_p.d.ts} +4 -4
  33. package/dist/{index-DQVvHzjW.d.ts → index-GV77NpRP.d.ts} +5 -4
  34. package/dist/{index-Sw43J8HG.d.ts → index-UJ-LUxtI.d.ts} +6 -6
  35. package/dist/{index-cEeweTeM.d.ts → index-kpgopLNv.d.ts} +4 -3
  36. package/dist/{index-DRid0qIv.d.ts → index-qriRM9cZ.d.ts} +7 -2
  37. package/dist/index.d.ts +13 -13
  38. package/dist/index.js +18 -8
  39. package/dist/jobs/index.d.ts +25 -24
  40. package/dist/jobs/index.js +19 -12
  41. package/dist/{json-schema-BbwO2mZg.js → json-schema--aOXAGfU.js} +9 -3
  42. package/dist/lint/index.js +1 -1
  43. package/dist/list/index.d.ts +2 -2
  44. package/dist/list/index.js +128 -93
  45. package/dist/{live-6b9i72Ux.d.ts → live-QE3UPbGm.d.ts} +27 -6
  46. package/dist/logger-glM28NVN.js +21 -0
  47. package/dist/mcp/index.d.ts +59 -15
  48. package/dist/mcp/index.js +229 -67
  49. package/dist/{mfa-CpogP66z.js → mfa-BxoR1RkN.js} +97 -22
  50. package/dist/{mfa-TVia554c.d.ts → mfa-x3V3vGh-.d.ts} +1 -1
  51. package/dist/next/index.d.ts +51 -38
  52. package/dist/next/index.js +31 -25
  53. package/dist/openapi/index.d.ts +1 -1
  54. package/dist/openapi/index.js +58 -35
  55. package/dist/orpc/index.d.ts +6 -6
  56. package/dist/orpc/index.js +3 -3
  57. package/dist/otel/index.d.ts +3 -3
  58. package/dist/otel/index.js +5 -3
  59. package/dist/{template-BeOGnynM.js → permdock-sql-Bta_G0v-.js} +32 -3
  60. package/dist/permdock-sql-DImJULY-.d.ts +17 -0
  61. package/dist/{plugin-HvGqCurB.d.ts → plugin-DVRB8qNE.d.ts} +5 -4
  62. package/dist/plugins/actor/index.d.ts +1 -1
  63. package/dist/plugins/rules/index.d.ts +4 -3
  64. package/dist/plugins/rules/index.js +8 -15
  65. package/dist/plugins/soft-delete/index.d.ts +3 -3
  66. package/dist/plugins/soft-delete/index.js +2 -2
  67. package/dist/plugins/tenant/index.d.ts +10 -4
  68. package/dist/plugins/tenant/index.js +15 -13
  69. package/dist/plugins/timestamps/index.d.ts +1 -1
  70. package/dist/plugins/timestamps/index.js +1 -1
  71. package/dist/plugins/validation/index.d.ts +2 -2
  72. package/dist/pool-B9qsLyFR.d.ts +71 -0
  73. package/dist/postgres/index.d.ts +10 -5
  74. package/dist/postgres/index.js +36 -16
  75. package/dist/{postgrest-Dm-vhY6R.d.ts → postgrest-DmBlevh-.d.ts} +1 -1
  76. package/dist/{problem-DPu6Fh-t.js → problem-CGt1ADix.js} +14 -8
  77. package/dist/{problem-DlgB0lYl.d.ts → problem-Z2RTQjZD.d.ts} +6 -4
  78. package/dist/query/index.d.ts +2 -2
  79. package/dist/query/index.js +1 -1
  80. package/dist/{query-ChvDbsDE.js → query-BdKLTPlj.js} +2 -2
  81. package/dist/react/index.d.ts +7 -7
  82. package/dist/react/index.js +3 -3
  83. package/dist/react/server.d.ts +5 -5
  84. package/dist/react/server.js +1 -1
  85. package/dist/react/session.d.ts +21 -2
  86. package/dist/react/session.js +19 -1
  87. package/dist/{react-CrOvrnIP.js → react-DNBYNazu.js} +28 -23
  88. package/dist/{read-set-DhX2d04c.js → read-set-D0qBcboM.js} +3 -3
  89. package/dist/realtime/index.d.ts +4 -2
  90. package/dist/realtime/index.js +30 -14
  91. package/dist/{resolve-DW8h9NPm.d.ts → resolve-BRciX2jK.d.ts} +25 -9
  92. package/dist/{resource-SkPTRJDD.d.ts → resource-CwVgiI5q.d.ts} +16 -7
  93. package/dist/{resource-CYiu6Ygi.js → resource-D30rp0GN.js} +38 -16
  94. package/dist/{respond-4-dhoV7x.js → respond-C44EZAJo.js} +38 -19
  95. package/dist/{respond-PoTpy5DT.d.ts → respond-DAkPIdTH.d.ts} +23 -17
  96. package/dist/{result-CTkCZ6JA.d.ts → result-c2OYgCV2.d.ts} +13 -8
  97. package/dist/server/index.d.ts +35 -10
  98. package/dist/server/index.js +38 -9
  99. package/dist/{simplify-oHiDKRVQ.js → simplify-LakqK1vH.js} +22 -28
  100. package/dist/{spec-pins-t6sPaHvl.js → spec-pins-C3oc-Tdp.js} +1 -1
  101. package/dist/{spec-pins-CJPVghHy.d.ts → spec-pins-CqorWDnO.d.ts} +1 -1
  102. package/dist/sql/index.d.ts +139 -0
  103. package/dist/sql/index.js +1983 -0
  104. package/dist/{sql-M8l3fnS0.js → sql-BIgk12ew.js} +72 -16
  105. package/dist/ssr/index.d.ts +5 -5
  106. package/dist/ssr/index.js +3 -3
  107. package/dist/{standard-CyhRzctb.d.ts → standard-eudBiRk1.d.ts} +1 -1
  108. package/dist/{stats-3iy8u3_P.js → stats-D78UQ8Yu.js} +8 -7
  109. package/dist/storage/index.d.ts +4 -2
  110. package/dist/storage/index.js +530 -1
  111. package/dist/{tables-rZxRxNPW.js → tables-Dhha3BXM.js} +5 -5
  112. package/dist/temporal-BwshQk0W.js +27 -0
  113. package/dist/temporal-required-CMxeSVPo.js +19 -0
  114. package/dist/testing/index.d.ts +49 -16
  115. package/dist/testing/index.js +233 -66
  116. package/dist/{types-C2fslP1z.d.ts → types-DONv9SHN.d.ts} +68 -12
  117. package/dist/{version-Cqq1m3UY.js → version-BfpG2gks.js} +1 -1
  118. package/dist/view-3Po_PVlr.d.ts +79 -0
  119. package/dist/{view-B6F9QbG-.js → view-wZ-9qIUz.js} +12 -2
  120. package/dist/webhooks/index.d.ts +19 -17
  121. package/dist/webhooks/index.js +1 -1
  122. package/dist/{webhooks-DbMTJJX2.js → webhooks-CAIFQSTM.js} +12 -10
  123. package/package.json +30 -30
  124. package/schemas/config-v1.json +80 -5
  125. package/schemas/doctor-report-v1.json +18 -1
  126. package/schemas/snapshot-v2.json +39 -0
  127. package/skills/better-supabase/SKILL.md +23 -11
  128. package/skills/better-supabase/references/plugins.md +28 -16
  129. package/skills/better-supabase/references/troubleshooting.md +15 -15
  130. package/skills/better-supabase-api/SKILL.md +9 -9
  131. package/skills/better-supabase-api/references/adapters.md +30 -25
  132. package/skills/better-supabase-testing/SKILL.md +15 -11
  133. package/bin/better-supabase.js +0 -3
  134. package/dist/casing-da0uRTqt.js +0 -14
  135. package/dist/cli/bin.d.ts +0 -1
  136. package/dist/cli/bin.js +0 -15
  137. package/dist/cli/index.d.ts +0 -226
  138. package/dist/cli/index.js +0 -3
  139. package/dist/cli-CoByWYja.js +0 -6472
  140. package/dist/entitlements-BBg61DQZ.d.ts +0 -20
  141. package/dist/entitlements-BC_khwRy.js +0 -17
  142. package/dist/env-BEToab0I.js +0 -204
  143. package/dist/events-BuzTgn2D.js +0 -135
  144. package/dist/pool-BFeqzwQS.d.ts +0 -37
  145. package/dist/seed-CzecODcJ.js +0 -101
  146. package/dist/session-C3osL21k.d.ts +0 -21
  147. package/dist/session-CD-3orO_.js +0 -19
  148. package/dist/storage-BWXVUXGk.js +0 -461
  149. package/dist/view-B0ltUmTi.d.ts +0 -43
@@ -0,0 +1,42 @@
1
+ import { t as AuthSession } from "./view-3Po_PVlr.js";
2
+ //#region src/auth/entitlements.d.ts
3
+ /**
4
+ * One entry of the `memberships` claim, in PermDock's shape. PermDock strips
5
+ * `null`s, so optional fields are absent rather than `null`.
6
+ */
7
+ interface MembershipClaim {
8
+ /** The scope the membership is in, e.g. `tenant` or `organization`. */
9
+ readonly scope: string;
10
+ readonly id: string;
11
+ /** Parent scopes, e.g. `{ organization: '...' }` for a project membership. */
12
+ readonly within?: Readonly<Record<string, string>>;
13
+ readonly roles: readonly string[];
14
+ readonly via?: string;
15
+ /** Unix seconds. */
16
+ readonly expiresAt?: number;
17
+ readonly grantedBy?: string;
18
+ readonly reason?: string;
19
+ readonly member?: {
20
+ readonly group: string;
21
+ };
22
+ readonly managedBy?: "idp";
23
+ /** Seats, not plan features: those are in the `features` claim. */
24
+ readonly entitlements?: readonly string[];
25
+ }
26
+ /**
27
+ * Entitlement keys of the `features` claim: the literal union when
28
+ * `sb.claims(schema)` declares one (`z.enum([...])`), otherwise `string`.
29
+ */
30
+ type EntitlementKey<C> = C extends {
31
+ readonly features?: infer F;
32
+ } ? NonNullable<F> extends Readonly<Record<string, readonly (infer K)[]>> ? K extends string ? K : string : string : string;
33
+ /**
34
+ * Whether the session's plan-features claim grants `key` in `tenantId`.
35
+ * `claim` is `config.claims.features` (default `features`), the claim
36
+ * `better_supabase.feature_claims()` fills. The claim is as fresh as the
37
+ * token: enforce it in RLS with `better_supabase.has_entitlement()`, and
38
+ * refresh the session after checkout.
39
+ */
40
+ declare function hasEntitlement<C>(session: AuthSession<C>, tenantId: string, key: EntitlementKey<C>, claim?: string): boolean;
41
+ //#endregion
42
+ export { MembershipClaim as n, hasEntitlement as r, EntitlementKey as t };
package/dist/env/index.js CHANGED
@@ -1,2 +1,204 @@
1
- import { a as parseEnv, i as loadEnv, n as EnvValidationError, o as publicEnv, r as envSchema, s as toServerEnv, t as ENV_VARIABLES } from "../env-BEToab0I.js";
1
+ //#region src/env/index.ts
2
+ const PREFIXES = [
3
+ "",
4
+ "NEXT_PUBLIC_",
5
+ "VITE_",
6
+ "PUBLIC_",
7
+ "EXPO_PUBLIC_",
8
+ "NUXT_PUBLIC_"
9
+ ];
10
+ const ENV_VARIABLES = {
11
+ url: PREFIXES.map((prefix) => `${prefix}SUPABASE_URL`),
12
+ publishableKey: PREFIXES.flatMap((prefix) => [`${prefix}SUPABASE_PUBLISHABLE_KEY`, `${prefix}SUPABASE_PUBLISHABLE_DEFAULT_KEY`]),
13
+ secretKey: ["SUPABASE_SECRET_KEY"],
14
+ dbUrl: ["SUPABASE_DB_URL", "DATABASE_URL"],
15
+ jwksUrl: ["SUPABASE_JWKS_URL"],
16
+ readUrl: ["SUPABASE_READ_URL"]
17
+ };
18
+ const LOOPBACK = /^(localhost|127(?:\.\d{1,3}){3}|\[::1\])$/;
19
+ const HOSTED = /^([a-z0-9]{20})\.supabase\.co$/;
20
+ var EnvValidationError = class extends Error {
21
+ name = "EnvValidationError";
22
+ issues;
23
+ constructor(issues) {
24
+ super(`Invalid Supabase environment:\n${issues.map((issue) => ` - ${issue.variables[0] ?? "env"}: ${issue.message}`).join("\n")}`);
25
+ this.issues = issues;
26
+ }
27
+ };
28
+ function first(source, names) {
29
+ for (const name of names) {
30
+ const value = source[name]?.trim();
31
+ if (value) return value;
32
+ }
33
+ }
34
+ /** `SUPABASE_*_KEYS` JSON objects, as `@supabase/server` reads them. */
35
+ function keySet(source, name, kind, issues) {
36
+ const raw = source[name];
37
+ if (!raw) return void 0;
38
+ let parsed;
39
+ try {
40
+ parsed = JSON.parse(raw);
41
+ } catch {
42
+ parsed = void 0;
43
+ }
44
+ const entries = typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? Object.entries(parsed) : [];
45
+ if (entries.length === 0 || entries.some(([, value]) => typeof value !== "string")) {
46
+ issues.push({
47
+ variables: [name],
48
+ message: "must be a JSON object of key names to keys"
49
+ });
50
+ return;
51
+ }
52
+ const keys = {};
53
+ for (const [key, value] of entries) if (checkKey(value, kind, [name], issues)) keys[key] = value;
54
+ return keys;
55
+ }
56
+ function preferred(keys) {
57
+ if (!keys) return void 0;
58
+ return keys["default"] ?? Object.values(keys)[0];
59
+ }
60
+ function checkUrl(value, variables, issues) {
61
+ let url;
62
+ try {
63
+ url = new URL(value);
64
+ } catch {
65
+ issues.push({
66
+ variables,
67
+ message: "is not a valid URL"
68
+ });
69
+ return;
70
+ }
71
+ if (url.protocol === "https:") return url;
72
+ if (url.protocol === "http:" && LOOPBACK.test(url.hostname)) return url;
73
+ issues.push({
74
+ variables,
75
+ message: "must use https (http is only allowed for localhost / 127.0.0.1 / [::1])"
76
+ });
77
+ }
78
+ function checkKey(value, kind, variables, issues) {
79
+ if (value.startsWith(`sb_${kind}_`)) return true;
80
+ if (value.startsWith("eyJ")) issues.push({
81
+ variables,
82
+ message: `is a legacy JWT key; use the new sb_${kind}_ key (Project Settings > API Keys)`
83
+ });
84
+ else if (value.startsWith(kind === "publishable" ? "sb_secret_" : "sb_publishable_")) issues.push({
85
+ variables,
86
+ message: kind === "publishable" ? "holds a secret key; never expose sb_secret_ keys as publishable" : "holds a publishable key, not a secret key"
87
+ });
88
+ else issues.push({
89
+ variables,
90
+ message: `must start with sb_${kind}_`
91
+ });
92
+ return false;
93
+ }
94
+ /**
95
+ * Reads and validates Supabase settings. Accepts the `SUPABASE_`,
96
+ * `NEXT_PUBLIC_`, `VITE_`, `PUBLIC_`, `EXPO_PUBLIC_` and `NUXT_PUBLIC_`
97
+ * spellings. Issues name variables, never values.
98
+ */
99
+ function parseEnv(source, options = {}) {
100
+ const issues = [];
101
+ const required = new Set(options.require ?? []);
102
+ const rawUrl = first(source, ENV_VARIABLES.url);
103
+ const url = rawUrl ? checkUrl(rawUrl, ENV_VARIABLES.url, issues) : void 0;
104
+ if (!rawUrl) issues.push({
105
+ variables: ENV_VARIABLES.url,
106
+ message: "is not set"
107
+ });
108
+ const publishableKeys = keySet(source, "SUPABASE_PUBLISHABLE_KEYS", "publishable", issues);
109
+ const singlePublishable = first(source, ENV_VARIABLES.publishableKey);
110
+ const publishableKey = singlePublishable ?? preferred(publishableKeys);
111
+ if (!publishableKey) issues.push({
112
+ variables: ENV_VARIABLES.publishableKey,
113
+ message: "is not set"
114
+ });
115
+ else if (singlePublishable) checkKey(publishableKey, "publishable", ENV_VARIABLES.publishableKey, issues);
116
+ const namedSecrets = keySet(source, "SUPABASE_SECRET_KEYS", "secret", issues);
117
+ const singleSecret = first(source, ENV_VARIABLES.secretKey);
118
+ const secretKey = singleSecret ?? preferred(namedSecrets);
119
+ if (singleSecret) checkKey(singleSecret, "secret", ENV_VARIABLES.secretKey, issues);
120
+ else if (!secretKey && required.has("secretKey")) issues.push({
121
+ variables: ENV_VARIABLES.secretKey,
122
+ message: "is not set"
123
+ });
124
+ const dbUrl = first(source, ENV_VARIABLES.dbUrl);
125
+ if (dbUrl && !/^postgres(ql)?:\/\//.test(dbUrl)) issues.push({
126
+ variables: ENV_VARIABLES.dbUrl,
127
+ message: "must be a postgres:// connection string"
128
+ });
129
+ else if (!dbUrl && required.has("dbUrl")) issues.push({
130
+ variables: ENV_VARIABLES.dbUrl,
131
+ message: "is not set"
132
+ });
133
+ const rawJwks = first(source, ENV_VARIABLES.jwksUrl);
134
+ const jwksOverride = rawJwks ? checkUrl(rawJwks, ENV_VARIABLES.jwksUrl, issues) : void 0;
135
+ const rawRead = first(source, ENV_VARIABLES.readUrl);
136
+ const readUrl = rawRead ? checkUrl(rawRead, ENV_VARIABLES.readUrl, issues) : void 0;
137
+ if (issues.length > 0 || !url || !publishableKey) return {
138
+ ok: false,
139
+ issues
140
+ };
141
+ const base = url.href.replace(/\/+$/, "");
142
+ const projectRef = HOSTED.exec(url.hostname)?.[1];
143
+ return {
144
+ ok: true,
145
+ env: {
146
+ url: base,
147
+ publishableKey,
148
+ jwksUrl: jwksOverride ?? new URL(`${base}/auth/v1/.well-known/jwks.json`),
149
+ ...secretKey ? {
150
+ secretKey,
151
+ secretKeys: {
152
+ ...namedSecrets,
153
+ default: secretKey
154
+ }
155
+ } : {},
156
+ ...dbUrl ? { dbUrl } : {},
157
+ ...projectRef ? { projectRef } : {},
158
+ ...readUrl ? { readUrl: readUrl.href.replace(/\/+$/, "") } : {}
159
+ }
160
+ };
161
+ }
162
+ function processEnv() {
163
+ return globalThis.process?.env ?? {};
164
+ }
165
+ /** Like `parseEnv`, but throws `EnvValidationError`. Reads `process.env` by default. */
166
+ function loadEnv(source = processEnv(), options = {}) {
167
+ const result = parseEnv(source, options);
168
+ if (!result.ok) throw new EnvValidationError(result.issues);
169
+ return result.env;
170
+ }
171
+ /** The env validator as a Standard Schema, for t3-env, framework config or any validator slot. */
172
+ function envSchema(options = {}) {
173
+ return { "~standard": {
174
+ version: 1,
175
+ vendor: "better-supabase",
176
+ validate(value) {
177
+ if (typeof value !== "object" || value === null) return { issues: [{ message: "Expected an environment object" }] };
178
+ const result = parseEnv(value, options);
179
+ if (result.ok) return { value: result.env };
180
+ return { issues: result.issues.map((issue) => ({
181
+ message: `${issue.variables[0] ?? "env"} ${issue.message}`,
182
+ path: issue.variables.slice(0, 1)
183
+ })) };
184
+ }
185
+ } };
186
+ }
187
+ /** The parts that are safe in client bundles. */
188
+ function publicEnv(env) {
189
+ return {
190
+ url: env.url,
191
+ publishableKey: env.publishableKey
192
+ };
193
+ }
194
+ /** Converts to `@supabase/server`'s `SupabaseEnv` (the `env` option of `withSupabase`). */
195
+ function toServerEnv(env) {
196
+ return {
197
+ url: env.url,
198
+ publishableKeys: { default: env.publishableKey },
199
+ secretKeys: env.secretKeys ?? (env.secretKey ? { default: env.secretKey } : {}),
200
+ jwks: env.jwksUrl
201
+ };
202
+ }
203
+ //#endregion
2
204
  export { ENV_VARIABLES, EnvValidationError, envSchema, loadEnv, parseEnv, publicEnv, toServerEnv };
@@ -1,2 +1,2 @@
1
- import { a as HttpMode, c as RowEventData, d as httpSink, f as isCloudEvent, i as ForwardOptions, l as forwardMutations, m as toHttp, n as CloudEventOptions, o as HttpSinkOptions, p as toCloudEvents, r as EventSink, s as ROW_EVENT_TYPES, t as CloudEvent, u as fromHttp } from "../index-DQVvHzjW.js";
1
+ import { a as HttpMode, c as RowEventData, d as httpSink, f as isCloudEvent, i as ForwardOptions, l as forwardMutations, m as toHttp, n as CloudEventOptions, o as HttpSinkOptions, p as toCloudEvents, r as EventSink, s as ROW_EVENT_TYPES, t as CloudEvent, u as fromHttp } from "../index-GV77NpRP.js";
2
2
  export { CloudEvent, CloudEventOptions, EventSink, ForwardOptions, HttpMode, HttpSinkOptions, ROW_EVENT_TYPES, RowEventData, forwardMutations, fromHttp, httpSink, isCloudEvent, toCloudEvents, toHttp };
@@ -1,2 +1,140 @@
1
- import { a as isCloudEvent, i as httpSink, n as forwardMutations, o as toCloudEvents, r as fromHttp, s as toHttp, t as ROW_EVENT_TYPES } from "../events-BuzTgn2D.js";
1
+ import { n as nowInstant } from "../temporal-BwshQk0W.js";
2
+ //#region src/events/index.ts
3
+ const ROW_EVENT_TYPES = {
4
+ insert: "dev.better-supabase.row.created",
5
+ upsert: "dev.better-supabase.row.upserted",
6
+ update: "dev.better-supabase.row.updated",
7
+ delete: "dev.better-supabase.row.deleted"
8
+ };
9
+ function subjectOf(meta, table, row) {
10
+ const key = meta?.tables[table]?.primaryKey;
11
+ if (!key || key.length === 0) return void 0;
12
+ const values = key.map((column) => row[column]);
13
+ if (values.some((value) => value === void 0 || value === null)) return void 0;
14
+ return `${table}/${values.map((value) => encodeURIComponent(String(value))).join(",")}`;
15
+ }
16
+ /**
17
+ * One CloudEvent per mutated row: `dev.better-supabase.row.created` with the
18
+ * app-cased row as `data`, the primary key as `subject`, the tenant as
19
+ * `partitionkey` and the actor as `actorid`.
20
+ */
21
+ function toCloudEvents(notice, options) {
22
+ const type = options.typePrefix ? `${options.typePrefix}.row.${ROW_EVENT_TYPES[notice.kind].split(".").at(-1)}` : ROW_EVENT_TYPES[notice.kind];
23
+ const time = (options.now ?? nowInstant)().toString();
24
+ return notice.rows.map((row) => {
25
+ const subject = subjectOf(options.meta, notice.table, row);
26
+ return {
27
+ specversion: "1.0",
28
+ id: options.id?.() ?? crypto.randomUUID(),
29
+ source: options.source,
30
+ type,
31
+ ...subject ? { subject } : {},
32
+ time,
33
+ datacontenttype: "application/json",
34
+ data: {
35
+ table: notice.table,
36
+ row
37
+ },
38
+ ...notice.context.tenant ? { partitionkey: notice.context.tenant } : {},
39
+ ...notice.context.actor?.id ? { actorid: notice.context.actor.id } : {}
40
+ };
41
+ });
42
+ }
43
+ /**
44
+ * Sends a CloudEvent for every mutation to `sink`. Returns a function that
45
+ * stops forwarding. Use an outbox (SQL kit) when events must not be lost.
46
+ */
47
+ function forwardMutations(sb, sink, options) {
48
+ return sb.events.on("mutation", (notice) => {
49
+ if (options.filter && !options.filter(notice)) return;
50
+ const events = toCloudEvents(notice, {
51
+ ...options,
52
+ meta: sb.meta
53
+ });
54
+ if (events.length === 0) return;
55
+ const report = (error) => {
56
+ (options.onError ?? ((cause) => {
57
+ sb.events.logger.error("event sink failed", { cause });
58
+ }))(error, events);
59
+ };
60
+ try {
61
+ Promise.resolve(sink.send(events)).catch(report);
62
+ } catch (error) {
63
+ report(error);
64
+ }
65
+ });
66
+ }
67
+ const STRUCTURED = "application/cloudevents+json";
68
+ const BATCH = "application/cloudevents-batch+json";
69
+ /** The HTTP protocol binding: headers and body for one event (or a batch). */
70
+ function toHttp(events, mode = "structured") {
71
+ const list = Array.isArray(events) ? events : [events];
72
+ switch (mode) {
73
+ case "batch": return [{
74
+ headers: { "content-type": BATCH },
75
+ body: JSON.stringify(list)
76
+ }];
77
+ case "structured": return list.map((event) => ({
78
+ headers: { "content-type": STRUCTURED },
79
+ body: JSON.stringify(event)
80
+ }));
81
+ case "binary": return list.map((event) => {
82
+ const headers = {};
83
+ for (const [key, value] of Object.entries(event)) {
84
+ if (key === "data" || key === "datacontenttype" || value === void 0) continue;
85
+ headers[`ce-${key}`] = encodeURIComponent(String(value)).replaceAll("%20", " ");
86
+ }
87
+ headers["content-type"] = event.datacontenttype ?? "application/json";
88
+ return {
89
+ headers,
90
+ body: event.data === void 0 ? "" : JSON.stringify(event.data)
91
+ };
92
+ });
93
+ default: throw new TypeError(`Unknown CloudEvents mode ${String(mode)}`);
94
+ }
95
+ }
96
+ function isCloudEvent(value) {
97
+ if (typeof value !== "object" || value === null) return false;
98
+ const event = value;
99
+ return event["specversion"] === "1.0" && typeof event["id"] === "string" && typeof event["source"] === "string" && typeof event["type"] === "string";
100
+ }
101
+ /** Reads CloudEvents from a request in any of the three HTTP modes. */
102
+ async function fromHttp(request) {
103
+ const contentType = request.headers.get("content-type") ?? "";
104
+ const text = await request.text();
105
+ if (contentType.startsWith(BATCH)) {
106
+ const parsed = JSON.parse(text);
107
+ if (!Array.isArray(parsed) || !parsed.every(isCloudEvent)) throw new TypeError("Invalid CloudEvents batch");
108
+ return parsed;
109
+ }
110
+ if (contentType.startsWith(STRUCTURED)) {
111
+ const parsed = JSON.parse(text);
112
+ if (!isCloudEvent(parsed)) throw new TypeError("Invalid CloudEvent");
113
+ return [parsed];
114
+ }
115
+ const event = {};
116
+ for (const [key, value] of request.headers) if (key.startsWith("ce-")) event[key.slice(3)] = decodeURIComponent(value);
117
+ if (contentType) event["datacontenttype"] = contentType;
118
+ if (text) event["data"] = contentType.includes("json") ? JSON.parse(text) : text;
119
+ if (!isCloudEvent(event)) throw new TypeError("Request is not a CloudEvent");
120
+ return [event];
121
+ }
122
+ /** POSTs events to `url`. Non-2xx responses reject. */
123
+ function httpSink(url, options = {}) {
124
+ const send = options.fetch ?? fetch;
125
+ return { async send(events) {
126
+ for (const message of toHttp(events, options.mode ?? "batch")) {
127
+ const response = await send(url, {
128
+ method: "POST",
129
+ headers: {
130
+ ...options.headers,
131
+ ...message.headers
132
+ },
133
+ body: message.body
134
+ });
135
+ if (!response.ok) throw new Error(`Event sink responded ${String(response.status)}`);
136
+ }
137
+ } };
138
+ }
139
+ //#endregion
2
140
  export { ROW_EVENT_TYPES, forwardMutations, fromHttp, httpSink, isCloudEvent, toCloudEvents, toHttp };
@@ -1,6 +1,6 @@
1
- import { g as Operation } from "./executor-Bazhgsgq.js";
2
- import { p as DbError } from "./result-CTkCZ6JA.js";
3
- import { h as RequestContext, l as MutationKind } from "./plugin-HvGqCurB.js";
1
+ import { g as Operation } from "./executor-DynmIWhq.js";
2
+ import { p as DbError } from "./result-c2OYgCV2.js";
3
+ import { h as RequestContext, l as MutationKind } from "./plugin-DVRB8qNE.js";
4
4
  //#region src/core/logger.d.ts
5
5
  type LogFields = Readonly<Record<string, unknown>>;
6
6
  /**
@@ -1,5 +1,5 @@
1
- import { r as Executor } from "./executor-Bazhgsgq.js";
2
- import { y as RawDbError } from "./result-CTkCZ6JA.js";
1
+ import { r as Executor } from "./executor-DynmIWhq.js";
2
+ import { y as RawDbError } from "./result-c2OYgCV2.js";
3
3
  //#region src/postgres/executor.d.ts
4
4
  /**
5
5
  * Anything that runs parameterized SQL: `ctx.postgres` from
@@ -1,6 +1,6 @@
1
1
  import { c as mapDbError, n as dbError } from "./errors-Bl3rkOzo.js";
2
2
  import { i as ok, n as err, o as toDbError } from "./result-DWXatkd6.js";
3
- import { t as compileSql } from "./sql-M8l3fnS0.js";
3
+ import { t as compileSql } from "./sql-BIgk12ew.js";
4
4
  //#region src/postgres/executor.ts
5
5
  function isPgError(value) {
6
6
  return typeof value === "object" && value !== null && typeof value.code === "string";
@@ -25,6 +25,11 @@ async function run(client, plan, op) {
25
25
  const [first] = await client.queryRaw(plan.count.text, [...plan.count.params]);
26
26
  count = first?.count ?? 0;
27
27
  } else if (op.kind !== "select") count = rows.length;
28
+ for (const next of plan.chunks ?? []) {
29
+ const more = await run(client, next, op);
30
+ rows.push(...more.rows);
31
+ count = (count ?? 0) + (more.count ?? 0);
32
+ }
28
33
  return {
29
34
  rows,
30
35
  count
@@ -74,7 +79,7 @@ async function executeOn(client, op, context) {
74
79
  if (context.signal?.aborted) return err(dbError("aborted", "The request was aborted"));
75
80
  let result;
76
81
  try {
77
- result = await run(client, plan, op);
82
+ result = plan.chunks && client.transaction ? await client.transaction((scoped) => run(scoped, plan, op)) : await run(client, plan, op);
78
83
  } catch (cause) {
79
84
  const raw = fromPgError(cause);
80
85
  return err(raw ? mapDbError(raw, context.errorMappers) : toDbError(cause));
@@ -1,5 +1,5 @@
1
- import { A as TableMeta, o as Codec, x as RelationMeta } from "./types-C2fslP1z.js";
2
- import { o as Result, v as ErrorMapper } from "./result-CTkCZ6JA.js";
1
+ import { P as TableMeta, T as RelationMeta, c as Codec } from "./types-DONv9SHN.js";
2
+ import { o as Result, v as ErrorMapper } from "./result-c2OYgCV2.js";
3
3
  //#region src/ir/types.d.ts
4
4
  /**
5
5
  * The query IR. Every repository call becomes one of these operations; the
@@ -0,0 +1,10 @@
1
+ //#region src/core/guards.ts
2
+ /**
3
+ * `Array.isArray` narrows `unknown` and readonly arrays to `any[]`; this keeps
4
+ * the element type (or `unknown`).
5
+ */
6
+ function isList(value) {
7
+ return Array.isArray(value);
8
+ }
9
+ //#endregion
10
+ export { isList as t };
@@ -1,8 +1,8 @@
1
- import { k as TableKey, n as AnyModels, t as AnyFunctions } from "../types-C2fslP1z.js";
2
- import { t as BetterSupabase } from "../define-CTfWw-SS.js";
3
- import { r as AuthState } from "../resolve-DW8h9NPm.js";
4
- import { d as ServerContext, f as ServerOptions, l as BetterServer, n as GuardOptions } from "../respond-PoTpy5DT.js";
5
- import { i as ResourceRouteOptions } from "../resource-SkPTRJDD.js";
1
+ import { N as TableKey, n as AnyModels, t as AnyFunctions } from "../types-DONv9SHN.js";
2
+ import { t as BetterSupabase } from "../define-DUbydmJa.js";
3
+ import { r as AuthState } from "../resolve-BRciX2jK.js";
4
+ import { d as ServerContext, f as ServerOptions, l as BetterServer, n as GuardOptions } from "../respond-DAkPIdTH.js";
5
+ import { a as ResourceRouteOptions } from "../resource-CwVgiI5q.js";
6
6
  import { Context, ErrorHandler, Hono, MiddlewareHandler } from "hono";
7
7
  //#region src/hono/index.d.ts
8
8
  /** Hono `Env` with the request's better-supabase context in `c.var`. */
@@ -1,7 +1,7 @@
1
1
  import { n as dbError, r as dbErrorOf } from "../errors-Bl3rkOzo.js";
2
- import { a as problemResponse } from "../problem-DPu6Fh-t.js";
3
- import { i as respond, n as guard, o as createServer, s as extendServer, t as defaultExpose } from "../respond-4-dhoV7x.js";
4
- import { t as defineResource } from "../resource-CYiu6Ygi.js";
2
+ import { a as problemResponse } from "../problem-CGt1ADix.js";
3
+ import { i as respond, n as guard, o as createServer, s as extendServer, t as defaultExpose } from "../respond-C44EZAJo.js";
4
+ import { t as defineResource } from "../resource-D30rp0GN.js";
5
5
  import { Hono } from "hono";
6
6
  //#region src/hono/index.ts
7
7
  function contextOf(c) {
@@ -31,7 +31,7 @@ function createHono(sb, options = {}) {
31
31
  middleware(middlewareOptions = {}) {
32
32
  return async (c, next) => {
33
33
  const ctx = await server.context(c.req.raw, { refresh: middlewareOptions.refresh ?? false });
34
- const denied = guard(ctx.auth, middlewareOptions.allow, middlewareOptions.aal);
34
+ const denied = guard(ctx.auth, middlewareOptions.allow, middlewareOptions.aal, middlewareOptions.scopes);
35
35
  if (denied) return ctx.resolution.apply(problemResponse(denied, {
36
36
  instance: new URL(c.req.url).pathname,
37
37
  expose
@@ -1,16 +1,16 @@
1
- import { p as DbError } from "./result-CTkCZ6JA.js";
2
- import { Z as QuerySpec } from "./define-CTfWw-SS.js";
3
- import { i as BrowserAuth, t as AuthSnapshot } from "./index-Sw43J8HG.js";
1
+ import { p as DbError } from "./result-c2OYgCV2.js";
2
+ import { Z as QuerySpec } from "./define-DUbydmJa.js";
3
+ import { i as BrowserAuth, t as AuthSnapshot } from "./index-UJ-LUxtI.js";
4
4
  import { n as TemplateValues } from "./template-mbvlysOo.js";
5
- import { t as AuthSession } from "./view-B0ltUmTi.js";
6
- import { a as LiveSource, g as Topic, h as SubscriptionStatus, p as SubscribeOptions, r as LiveCountSeed, u as EventSchemas, v as TopicHandlers, y as TopicMessage } from "./live-6b9i72Ux.js";
7
- import "./entitlements-BBg61DQZ.js";
8
- import "./session-C3osL21k.js";
5
+ import { t as AuthSession } from "./view-3Po_PVlr.js";
6
+ import { a as LiveSource, g as Topic, h as SubscriptionStatus, p as SubscribeOptions, r as LiveCountSeed, u as EventSchemas, v as TopicHandlers, y as TopicMessage } from "./live-QE3UPbGm.js";
7
+ import "./entitlements-DvJXoI02.js";
8
+ import "./react/session.js";
9
9
  import { SupabaseClient } from "@supabase/supabase-js";
10
10
  import { ReactNode } from "react";
11
11
  import { StandardSchemaV1 } from "@standard-schema/spec";
12
12
  import { QueryClient } from "@tanstack/query-core";
13
- //#region src/react/index.d.ts
13
+ //#region src/react/hooks.d.ts
14
14
  /** The parts of `createBrowser()` the provider needs. */
15
15
  interface BrowserLike {
16
16
  readonly sb: LiveSource;
@@ -37,13 +37,17 @@ interface BetterHooks<B extends BrowserLike> {
37
37
  readonly useQueries: () => B["queries"];
38
38
  readonly useSupabase: () => SupabaseClient;
39
39
  readonly useAuth: () => AuthSnapshot;
40
- /** `useSession()` with the claims typed by the browser's `sb.claims(schema)`. */
41
- readonly useSession: () => AuthSession<ClaimsOf<B>>;
40
+ /** `useSession()` typed by the browser's `sb.claims(schema)` and `sb.userMetadata(schema)`. */
41
+ readonly useSession: () => AuthSession<ClaimsOf<B>, ProfileOf<B>>;
42
42
  }
43
43
  /** The claims type of a browser's `sb.claims(schema)`, `unknown` without one. */
44
44
  type ClaimsOf<B extends BrowserLike> = B["sb"] extends {
45
45
  readonly claimsSchema: StandardSchemaV1<unknown, infer C> | undefined;
46
46
  } ? C : unknown;
47
+ /** The profile type of a browser's `sb.userMetadata(schema)`, `unknown` without one. */
48
+ type ProfileOf<B extends BrowserLike> = B["sb"] extends {
49
+ readonly userMetadataSchema: StandardSchemaV1<unknown, infer P> | undefined;
50
+ } ? P : unknown;
47
51
  /**
48
52
  * Hooks typed for your schema.
49
53
  *
@@ -71,8 +75,8 @@ interface BroadcastOptions extends Omit<SubscribeOptions, "onStatus"> {
71
75
  declare function useBroadcast<P extends string, E extends EventSchemas>(topic: Topic<P, E>, values: TemplateValues<P> | null | undefined, handlers?: TopicHandlers<E>, options?: BroadcastOptions): SubscriptionStatus;
72
76
  interface LiveQueryHookOptions {
73
77
  /**
74
- * Tenant for tenant-scoped tables. Defaults to the `org_id` claim
75
- * (top-level or in `app_metadata`).
78
+ * Tenant for tenant-scoped tables. Defaults to the `config.claims.tenant`
79
+ * claim (`tenant_id`, top-level or in `app_metadata`).
76
80
  */
77
81
  readonly tenant?: string;
78
82
  /** Defaults to 100 ms. */
@@ -113,4 +117,4 @@ interface LiveCount {
113
117
  */
114
118
  declare function useLiveCount(source: QuerySpec<string, "count", number> | LiveCountSeed | null | undefined, options?: LiveCountHookOptions): LiveCount;
115
119
  //#endregion
116
- export { BrowserLike as a, LiveCountHookOptions as c, useAuth as d, useBroadcast as f, useSupabase as h, BroadcastOptions as i, LiveQueryHookOptions as l, useLiveQuery as m, BetterSupabaseProvider as n, ClaimsOf as o, useLiveCount as p, BetterSupabaseProviderProps as r, LiveCount as s, BetterHooks as t, createHooks as u };
120
+ export { BrowserLike as a, LiveCountHookOptions as c, createHooks as d, useAuth as f, useSupabase as g, useLiveQuery as h, BroadcastOptions as i, LiveQueryHookOptions as l, useLiveCount as m, BetterSupabaseProvider as n, ClaimsOf as o, useBroadcast as p, BetterSupabaseProviderProps as r, LiveCount as s, BetterHooks as t, ProfileOf as u };