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.
Files changed (179) hide show
  1. package/README.md +2 -0
  2. package/dist/channel-FQW-CnGR.d.ts +70 -0
  3. package/dist/cli/bin.js +1 -1
  4. package/dist/cli/codemod-DqV7612m.js +388 -0
  5. package/dist/cli/{config-D9R0o1LH.js → config-CPVpXP5I.js} +78 -8
  6. package/dist/cli/{config-SSKSTUl2.js → config-dtoekM0v.js} +1 -1
  7. package/dist/cli/{read-sets-CMa29t9a.js → diff-SjkO5rNr.js} +1 -220
  8. package/dist/cli/{dist-CFgGknmZ.js → dist-DBPdlPYK.js} +4 -4
  9. package/dist/cli/{dist-B1MvLv-M.js → dist-DreZ24Sz.js} +1 -1
  10. package/dist/cli/{dist-Du7VnEUG.js → dist-xn4wlboc.js} +112 -1
  11. package/dist/cli/{doctor-BOym3mbL.js → doctor-L7Z8mYTx.js} +531 -50
  12. package/dist/cli/{env-BZ0uy8XK.js → env-DmKv_G4A.js} +1 -1
  13. package/dist/cli/gen-BJc9vdyu.js +2 -0
  14. package/dist/cli/{gen-Ccn_p90L.js → gen-C2osRcr5.js} +283 -87
  15. package/dist/cli/index.d.ts +1 -1
  16. package/dist/cli/index.js +4 -4
  17. package/dist/cli/{init-ClnxRYBF.js → init-Saf5-R9T.js} +2 -2
  18. package/dist/cli/{introspect-DxqfkVIv.js → introspect--H7oGmcd.js} +2 -2
  19. package/dist/cli/{openapi-8WHjxiTJ.js → openapi-Cp5Fyta-.js} +1 -1
  20. package/dist/cli/read-sets-BfMQcKy8.js +289 -0
  21. package/dist/cli/{run-CP20LKk_.js → run-BKR2W_79.js} +16 -12
  22. package/dist/cli/{seed-5J6eu_Xg.js → seed-CUlqWh-8.js} +1 -1
  23. package/dist/cli/sql-DzPbfngx.js +348 -0
  24. package/dist/cli/{version-5mW_8sY1.js → version-BDJuO0UR.js} +1 -1
  25. package/dist/client/index.d.ts +1 -1
  26. package/dist/client/index.js +3 -10
  27. package/dist/clone-BfgmLU90.js +21 -0
  28. package/dist/{compiler-DB3aSFOA.d.ts → compiler-BXH_aZ7N.d.ts} +2 -2
  29. package/dist/config/index.d.ts +4 -4
  30. package/dist/config/index.js +4 -4
  31. package/dist/{config-CbvFmGD8.js → config-DreERlKo.js} +1 -1
  32. package/dist/{config-C1CYcm6o.d.ts → config-T_ejwyd3.d.ts} +176 -11
  33. package/dist/{define-CRS2MRV3.d.ts → define-Bp_BZa5h.d.ts} +11 -7
  34. package/dist/{define-Bh0AqTik.js → define-cNKzqwL_.js} +202 -16
  35. package/dist/edge/index.d.ts +16 -5
  36. package/dist/edge/index.js +10 -7
  37. package/dist/{entitlements-Dm-FHpNH.d.ts → entitlements-DUxU4oHe.d.ts} +1 -1
  38. package/dist/env/index.d.ts +2 -2
  39. package/dist/env/index.js +34 -1
  40. package/dist/{errors-L0xK41y_.js → errors-DtwXa6b8.js} +1 -0
  41. package/dist/events/index.d.ts +3 -2
  42. package/dist/events/index.js +65 -12
  43. package/dist/events-aIO7vsIk.d.ts +230 -0
  44. package/dist/{result-CqEsbgYs.d.ts → executor-BunPOO-k.d.ts} +200 -1
  45. package/dist/{executor-CZ7ImRiD.d.ts → executor-Cghdstwu.d.ts} +1 -2
  46. package/dist/{executor-CVt6dD0B.js → executor-XycESQPH.js} +3 -3
  47. package/dist/hono/index.d.ts +17 -6
  48. package/dist/hono/index.js +40 -5
  49. package/dist/impersonation-VfaGc4Q_.js +41 -0
  50. package/dist/{index-BBdvo-5d.d.ts → index-BGdgBPdf.d.ts} +49 -6
  51. package/dist/{index-BhHEC04r.d.ts → index-C7f8cOBM.d.ts} +2 -2
  52. package/dist/{index-Dy6kuF0P.d.ts → index-CLg87OBW.d.ts} +6 -1
  53. package/dist/{index-D5IfUIDh.d.ts → index-DOESKG27.d.ts} +33 -8
  54. package/dist/{index-DFmOpBGC.d.ts → index-DgILJRXj.d.ts} +35 -9
  55. package/dist/{index-RlLXd8dL.d.ts → index-DmXSxbLi.d.ts} +4 -4
  56. package/dist/{index-Cf-D2jk1.d.ts → index-Dxn2a5cw.d.ts} +2 -2
  57. package/dist/{index-528Ugmyp.d.ts → index-SaTsEfGP.d.ts} +76 -8
  58. package/dist/{index-BAy4tsAP.d.ts → index-fag8VJZ2.d.ts} +3 -4
  59. package/dist/index.d.ts +30 -13
  60. package/dist/index.js +10 -9
  61. package/dist/jobs/index.d.ts +147 -101
  62. package/dist/jobs/index.js +744 -237
  63. package/dist/{json-schema-DP6V7cbr.js → json-schema-m1fiwKgN.js} +22 -1
  64. package/dist/kit-events-CTFDfSwb.js +60 -0
  65. package/dist/kit-transport-B82k1djG.d.ts +33 -0
  66. package/dist/kit-transport-DJlHQUBU.js +55 -0
  67. package/dist/{config-BpxDfnbg.js → kits-CGLLrNPd.js} +37 -9
  68. package/dist/list/index.d.ts +1 -1
  69. package/dist/{live-BHakSWWo.d.ts → live-D9W2M5aO.d.ts} +6 -5
  70. package/dist/mcp/index.d.ts +25 -8
  71. package/dist/mcp/index.js +23 -14
  72. package/dist/{mfa-C7MNQ1Ob.d.ts → mfa-CusDgPaD.d.ts} +5 -1
  73. package/dist/next/index.d.ts +31 -10
  74. package/dist/next/index.js +136 -27
  75. package/dist/notifications/index.d.ts +162 -0
  76. package/dist/notifications/index.js +264 -0
  77. package/dist/openapi/index.d.ts +1 -1
  78. package/dist/openapi/index.js +2 -2
  79. package/dist/orgs/index.d.ts +132 -0
  80. package/dist/orgs/index.js +245 -0
  81. package/dist/orpc/index.d.ts +28 -8
  82. package/dist/orpc/index.js +16 -3
  83. package/dist/otel/index.d.ts +21 -2
  84. package/dist/otel/index.js +50 -9
  85. package/dist/permdock-sql-C2Inc6Xc.d.ts +29 -0
  86. package/dist/permdock-sql-DgOfeixi.js +51 -0
  87. package/dist/plugin-DwARMuQp.js +59 -0
  88. package/dist/{plugin-bNhvJwRh.d.ts → plugin-o144f322.d.ts} +17 -5
  89. package/dist/plugins/actor/index.d.ts +5 -2
  90. package/dist/plugins/actor/index.js +22 -8
  91. package/dist/plugins/rules/index.d.ts +2 -2
  92. package/dist/plugins/rules/index.js +15 -10
  93. package/dist/plugins/soft-delete/index.d.ts +3 -3
  94. package/dist/plugins/soft-delete/index.js +14 -5
  95. package/dist/plugins/tenant/index.d.ts +2 -2
  96. package/dist/plugins/tenant/index.js +38 -20
  97. package/dist/plugins/timestamps/index.d.ts +1 -1
  98. package/dist/plugins/timestamps/index.js +4 -3
  99. package/dist/plugins/validation/index.d.ts +2 -2
  100. package/dist/plugins/validation/index.js +26 -6
  101. package/dist/{pool-DFwEQwmN.d.ts → pool-C9voM4Kw.d.ts} +17 -6
  102. package/dist/postgres/index.d.ts +5 -5
  103. package/dist/postgres/index.js +20 -11
  104. package/dist/{postgrest-Bc-0Xw62.d.ts → postgrest-CruYKAvV.d.ts} +1 -1
  105. package/dist/{problem-BihjPR0t.js → problem-BW1KhzSG.js} +2 -1
  106. package/dist/{problem-U8UEKccv.d.ts → problem-Blxe8S31.d.ts} +1 -1
  107. package/dist/query/index.d.ts +2 -2
  108. package/dist/query/index.js +3 -3
  109. package/dist/{query-BMMSWbI7.js → query-CCbo44lG.js} +2 -2
  110. package/dist/queue-BYUCjr59.d.ts +252 -0
  111. package/dist/react/index.d.ts +8 -7
  112. package/dist/react/index.js +4 -3
  113. package/dist/react/server.d.ts +14 -3
  114. package/dist/react/server.js +10 -2
  115. package/dist/react/session.d.ts +7 -1
  116. package/dist/react/session.js +9 -1
  117. package/dist/{react-CQHMaESR.js → react-C_s7kiNz.js} +103 -11
  118. package/dist/{read-set-FWK8qQ58.js → read-set-DQFt1N_Y.js} +22 -1
  119. package/dist/realtime/index.d.ts +3 -3
  120. package/dist/realtime/index.js +5 -4
  121. package/dist/{refresh-BwYkyNOQ.d.ts → refresh-BTrQ_ZTp.d.ts} +1 -1
  122. package/dist/{resolve-BAqQcwMW.d.ts → resolve-C15TJq77.d.ts} +11 -5
  123. package/dist/{resource-My_y-Syh.js → resource-CRxxwkQq.js} +22 -7
  124. package/dist/{resource-BJcaIfxi.d.ts → resource-D8iqWMr-.d.ts} +5 -5
  125. package/dist/respond-D2OQl62-.d.ts +371 -0
  126. package/dist/{result-Bj6yt33D.js → result-BKiliPY7.js} +1 -1
  127. package/dist/server/index.d.ts +14 -12
  128. package/dist/server/index.js +11 -17
  129. package/dist/{server-BA75B0mT.js → server-Csx2sRjg.js} +251 -34
  130. package/dist/{shared-DnoCdMDr.js → shared-D1dRUrAg.js} +16 -2
  131. package/dist/signers-Cc6r98SW.d.ts +162 -0
  132. package/dist/{simplify-Cqvfr9dj.js → simplify-_0oUKqCd.js} +4 -115
  133. package/dist/sql/index.d.ts +247 -10
  134. package/dist/sql/index.js +8590 -779
  135. package/dist/{sql-B34HU2DI.js → sql-Coh6n4cC.js} +9 -2
  136. package/dist/ssr/index.d.ts +4 -4
  137. package/dist/ssr/index.js +1 -2
  138. package/dist/{standard-BRyBGTtp.js → standard-D7r6dMUu.js} +6 -3
  139. package/dist/{standard-BsaCYQJc.d.ts → standard-J4YgMOLm.d.ts} +1 -1
  140. package/dist/{stats-C_YxA2r1.js → stats-Bqob3WqT.js} +4 -4
  141. package/dist/storage/index.d.ts +4 -4
  142. package/dist/storage/index.js +155 -26
  143. package/dist/support-DT-OhSxD.d.ts +88 -0
  144. package/dist/support-Dk6vdmEw.js +291 -0
  145. package/dist/support-view-Da5peKFg.d.ts +17 -0
  146. package/dist/support-view-g4YLBeqX.js +18 -0
  147. package/dist/{permdock-sql-C4KFzLc9.js → template-TxrGI5jR.js} +2 -31
  148. package/dist/{temporal-BwshQk0W.js → temporal-CVlIV2DZ.js} +17 -4
  149. package/dist/{temporal-required-CBNHT6ni.js → temporal-required-CAt4Dc9Z.js} +2 -2
  150. package/dist/testing/index.d.ts +100 -10
  151. package/dist/testing/index.js +336 -8
  152. package/dist/{types-DONv9SHN.d.ts → types-Dnf_wRDz.d.ts} +26 -3
  153. package/dist/user-change-CHFEUro1.js +49 -0
  154. package/dist/{webhooks-Bh_Jf_-l.js → verify-Ce49ETPz.js} +5 -5
  155. package/dist/{version-Bmf7_FzM.js → version-DM_Roffb.js} +1 -1
  156. package/dist/{view-B64o81wp.d.ts → view-BEf0diC5.d.ts} +8 -3
  157. package/dist/{mfa-byfjruR9.js → view-Bq64ki8-.js} +95 -38
  158. package/dist/webhooks/index.d.ts +135 -4
  159. package/dist/webhooks/index.js +588 -2
  160. package/dist/wire-D2dT86vi.js +35 -0
  161. package/package.json +16 -8
  162. package/schemas/config-v1.json +131 -1
  163. package/schemas/doctor-report-v1.json +10 -0
  164. package/skills/better-supabase/SKILL.md +9 -6
  165. package/skills/better-supabase/references/plugins.md +6 -3
  166. package/skills/better-supabase/references/troubleshooting.md +1 -0
  167. package/skills/better-supabase-api/SKILL.md +9 -2
  168. package/skills/better-supabase-api/references/adapters.md +11 -8
  169. package/skills/better-supabase-auth/SKILL.md +3 -0
  170. package/skills/better-supabase-auth/references/permdock.md +56 -11
  171. package/dist/cli/gen-D5cLCULx.js +0 -2
  172. package/dist/cli/sql-D4VbqmXi.js +0 -199
  173. package/dist/events-B21UOhvP.d.ts +0 -84
  174. package/dist/executor-B5IoUxLE.d.ts +0 -195
  175. package/dist/invalidate-CgxmRamI.js +0 -24
  176. package/dist/permdock-sql-DImJULY-.d.ts +0 -17
  177. package/dist/plugin-BqR6wKMB.js +0 -23
  178. package/dist/respond-DgIbdrt8.d.ts +0 -194
  179. package/dist/view-BrJI2LKp.js +0 -45
@@ -1,4 +1,4 @@
1
- import { a as invalidRequest, n as simplifyOrFalse } from "./simplify-Cqvfr9dj.js";
1
+ import { a as invalidRequest, n as simplifyOrFalse } from "./simplify-_0oUKqCd.js";
2
2
  import { t as lookupOf } from "./lookup-BaP5bI27.js";
3
3
  //#region src/compile/sql.ts
4
4
  const MAX_PARAMS = 65535;
@@ -291,10 +291,17 @@ function insertPlan(compiler, from, alias, op, columns) {
291
291
  if (op.onConflict) {
292
292
  const target = `(${op.onConflict.columns.map(quoteIdent).join(", ")})`;
293
293
  const updates = columns.map((column) => `${quoteIdent(column)} = excluded.${quoteIdent(column)}`);
294
- conflict = op.onConflict.action === "ignore" || updates.length === 0 ? ` on conflict ${target} do nothing` : ` on conflict ${target} do update set ${updates.join(", ")}`;
294
+ conflict = op.onConflict.action === "ignore" || updates.length === 0 ? ` on conflict ${target} do nothing` : ` on conflict ${target} do update set ${updates.join(", ")}${tenantGuard(op, alias)}`;
295
295
  }
296
296
  return mutation(`insert into ${from} ${body}${conflict}${returningRows(compiler, op, alias)}`, op.returning !== void 0, compiler.params);
297
297
  }
298
+ /** An upsert never updates a row that belongs to another tenant. */
299
+ function tenantGuard(op, alias) {
300
+ const app = op.table.flags.tenant;
301
+ const column = app === void 0 ? void 0 : op.table.columns[app]?.db;
302
+ if (column === void 0) return "";
303
+ return ` where ${alias}.${quoteIdent(column)} = excluded.${quoteIdent(column)}`;
304
+ }
298
305
  function mutation(statement, returning, params) {
299
306
  if (returning) return {
300
307
  rows: {
@@ -1,5 +1,5 @@
1
- import { _ as readSession, a as ResolveAuthOptions, b as writeSession, d as CookieOptions, f as CookieRecord, g as parseCookies, h as applyCookieWrites, l as resolveAuth, m as StoredSession, n as AuthResolver, p as CookieWrite, r as AuthState, s as authContext, t as AuthResolution, u as AUTH_CACHE_HEADERS, v as serializeCookie, y as sessionCookieName } from "../resolve-BAqQcwMW.js";
2
- import { n as toSession, t as AuthSession } from "../view-B64o81wp.js";
3
- import { n as MembershipClaim, r as hasEntitlement, t as EntitlementKey } from "../entitlements-Dm-FHpNH.js";
4
- import { n as RefreshOutcome, r as refreshSession, t as RefreshOptions } from "../refresh-BwYkyNOQ.js";
1
+ import { _ as readSession, a as ResolveAuthOptions, b as writeSession, d as CookieOptions, f as CookieRecord, g as parseCookies, h as applyCookieWrites, l as resolveAuth, m as StoredSession, n as AuthResolver, p as CookieWrite, r as AuthState, s as authContext, t as AuthResolution, u as AUTH_CACHE_HEADERS, v as serializeCookie, y as sessionCookieName } from "../resolve-C15TJq77.js";
2
+ import { n as toSession, t as AuthSession } from "../view-BEf0diC5.js";
3
+ import { n as MembershipClaim, r as hasEntitlement, t as EntitlementKey } from "../entitlements-DUxU4oHe.js";
4
+ import { n as RefreshOutcome, r as refreshSession, t as RefreshOptions } from "../refresh-BTrQ_ZTp.js";
5
5
  export { AUTH_CACHE_HEADERS, type AuthResolution, type AuthResolver, type AuthSession, type AuthState, type CookieOptions, type CookieRecord, type CookieWrite, type EntitlementKey, type MembershipClaim, type RefreshOptions, type RefreshOutcome, type ResolveAuthOptions, type StoredSession, applyCookieWrites, authContext, hasEntitlement, parseCookies, readSession, refreshSession, resolveAuth, serializeCookie, sessionCookieName, toSession, writeSession };
package/dist/ssr/index.js CHANGED
@@ -1,4 +1,3 @@
1
1
  import { t as hasEntitlement } from "../entitlements-CTEBm7IB.js";
2
- import { c as AUTH_CACHE_HEADERS, d as readSession, f as serializeCookie, h as refreshSession, i as authContext, l as applyCookieWrites, m as writeSession, p as sessionCookieName, s as resolveAuth, u as parseCookies } from "../mfa-byfjruR9.js";
3
- import { t as toSession } from "../view-BrJI2LKp.js";
2
+ import { _ as writeSession, d as AUTH_CACHE_HEADERS, f as applyCookieWrites, g as sessionCookieName, h as serializeCookie, m as readSession, n as toSession, o as authContext, p as parseCookies, u as resolveAuth, v as refreshSession } from "../view-Bq64ki8-.js";
4
3
  export { AUTH_CACHE_HEADERS, applyCookieWrites, authContext, hasEntitlement, parseCookies, readSession, refreshSession, resolveAuth, serializeCookie, sessionCookieName, toSession, writeSession };
@@ -1,5 +1,5 @@
1
- import { n as dbError } from "./errors-L0xK41y_.js";
2
- import { i as ok, n as err } from "./result-Bj6yt33D.js";
1
+ import { n as dbError } from "./errors-DtwXa6b8.js";
2
+ import { i as ok, n as err } from "./result-BKiliPY7.js";
3
3
  //#region src/core/standard.ts
4
4
  /** Validates with any Standard Schema (zod, valibot, arktype, ...). */
5
5
  async function validate(schema, value, label = "value") {
@@ -10,7 +10,10 @@ async function validate(schema, value, label = "value") {
10
10
  }
11
11
  function toIssues(issues) {
12
12
  return issues.map((issue) => {
13
- const path = issue.path?.map((segment) => typeof segment === "object" ? segment.key : segment);
13
+ const path = issue.path?.map((segment) => {
14
+ const key = typeof segment === "object" ? segment.key : segment;
15
+ return typeof key === "symbol" ? String(key) : key;
16
+ });
14
17
  return path && path.length > 0 ? {
15
18
  message: issue.message,
16
19
  path
@@ -1,4 +1,4 @@
1
- import { o as Result } from "./result-CqEsbgYs.js";
1
+ import { u as Result } from "./executor-BunPOO-k.js";
2
2
  import { StandardSchemaV1 as StandardSchemaV1$1 } from "@standard-schema/spec";
3
3
  //#region src/core/standard.d.ts
4
4
  /** Validates with any Standard Schema (zod, valibot, arktype, ...). */
@@ -1,7 +1,7 @@
1
- import { c as mapDbError, n as dbError } from "./errors-L0xK41y_.js";
2
- import { a as invalidRequest, n as simplifyOrFalse } from "./simplify-Cqvfr9dj.js";
3
- import { i as temporalText } from "./temporal-BwshQk0W.js";
4
- import { i as ok, n as err, o as toDbError } from "./result-Bj6yt33D.js";
1
+ import { c as mapDbError, n as dbError } from "./errors-DtwXa6b8.js";
2
+ import { a as invalidRequest, n as simplifyOrFalse } from "./simplify-_0oUKqCd.js";
3
+ import { i as temporalText } from "./temporal-CVlIV2DZ.js";
4
+ import { i as ok, n as err, o as toDbError } from "./result-BKiliPY7.js";
5
5
  //#region src/compile/postgrest.ts
6
6
  function scalar(value) {
7
7
  if (typeof value === "string") return value;
@@ -1,6 +1,6 @@
1
- import { b as PermdockBucketPolicy } from "../types-DONv9SHN.js";
1
+ import { t as AccessBucketPolicy, x as PermdockBucketPolicy } from "../types-Dnf_wRDz.js";
2
+ import { n as PermdockCatalog } from "../permdock-sql-C2Inc6Xc.js";
2
3
  import { n as StoragePath, t as PathIn } from "../path-Cv__-OTj.js";
3
- import { t as PermdockCatalog } from "../permdock-sql-DImJULY-.js";
4
4
  import { n as TemplateValues, t as TemplateParams } from "../template-mbvlysOo.js";
5
- import { C as fromStorageError, S as defineBucket, _ as TransformOptions, a as BucketConnectOptions, b as UploadOptions, c as ObjectTarget, d as Reservation, f as StorageClient, g as TTL, h as SweepResult, i as BucketConfig, l as ReplaceOptions, m as SweepOptions, n as Bucket, o as BucketDrift, p as StoredObject, r as BucketClient, s as BucketPolicy, t as ActualBucket, u as ReplaceResult, v as TtlPreset, w as parseSize, x as UrlOptions, y as UploadBody } from "../index-528Ugmyp.js";
6
- export { type ActualBucket, type Bucket, type BucketClient, type BucketConfig, type BucketConnectOptions, type BucketDrift, type BucketPolicy, type ObjectTarget, type PathIn, type PermdockBucketPolicy, type PermdockCatalog, type ReplaceOptions, type ReplaceResult, type Reservation, type StorageClient, type StoragePath, type StoredObject, type SweepOptions, type SweepResult, TTL, type TemplateParams, type TemplateValues, type TransformOptions, type TtlPreset, type UploadBody, type UploadOptions, type UrlOptions, defineBucket, fromStorageError, parseSize };
5
+ import { A as parseSize, C as TransformOptions, D as UrlOptions, E as UploadOptions, O as defineBucket, S as TTL, T as UploadBody, _ as Reservation, a as avatarBucket, b as SweepOptions, c as Bucket, d as BucketConnectOptions, f as BucketDrift, g as ReplaceResult, h as ReplaceOptions, i as OrgLogoBucketOptions, k as fromStorageError, l as BucketClient, m as ObjectTarget, n as IMAGE_TYPES, o as orgLogoBucket, p as BucketPolicy, r as ImageBucketOptions, s as ActualBucket, t as AvatarBucketOptions, u as BucketConfig, v as StorageClient, w as TtlPreset, x as SweepResult, y as StoredObject } from "../index-SaTsEfGP.js";
6
+ export { type AccessBucketPolicy, type ActualBucket, type AvatarBucketOptions, type Bucket, type BucketClient, type BucketConfig, type BucketConnectOptions, type BucketDrift, type BucketPolicy, IMAGE_TYPES, type ImageBucketOptions, type ObjectTarget, type OrgLogoBucketOptions, type PathIn, type PermdockBucketPolicy, type PermdockCatalog, type ReplaceOptions, type ReplaceResult, type Reservation, type StorageClient, type StoragePath, type StoredObject, type SweepOptions, type SweepResult, TTL, type TemplateParams, type TemplateValues, type TransformOptions, type TtlPreset, type UploadBody, type UploadOptions, type UrlOptions, avatarBucket, defineBucket, fromStorageError, orgLogoBucket, parseSize };
@@ -1,9 +1,82 @@
1
- import { n as dbError, o as isDbError, t as DbException } from "../errors-L0xK41y_.js";
2
- import { r as optionalTemporal } from "../temporal-BwshQk0W.js";
3
- import { n as temporalMissing } from "../temporal-required-CBNHT6ni.js";
4
- import { i as ok, n as err, o as toDbError, t as AsyncResult } from "../result-Bj6yt33D.js";
5
- import { i as tenantClaimPaths } from "../claims-BAf6c7is.js";
6
- import { a as sqlIdent, i as slug, o as sqlString, r as parseTemplate, t as permdockCheck } from "../permdock-sql-C4KFzLc9.js";
1
+ import { n as dbError, o as isDbError, t as DbException } from "../errors-DtwXa6b8.js";
2
+ import { r as optionalTemporal } from "../temporal-CVlIV2DZ.js";
3
+ import { n as temporalMissing } from "../temporal-required-CAt4Dc9Z.js";
4
+ import { i as ok, n as err, o as toDbError, t as AsyncResult } from "../result-BKiliPY7.js";
5
+ import { o as spansAllTenants, s as tenantOf } from "../plugin-DwARMuQp.js";
6
+ import { i as tenantClaimPaths, n as claimAt } from "../claims-BAf6c7is.js";
7
+ import { i as sqlString, n as slug, r as sqlIdent, t as parseTemplate } from "../template-TxrGI5jR.js";
8
+ import { n as permdockCheck } from "../permdock-sql-DgOfeixi.js";
9
+ //#region src/storage/policy.ts
10
+ /**
11
+ * The checks of a PermDock or access contract policy, or `undefined` for a
12
+ * named policy. `segmentOf` returns the tenant segment, or throws when the
13
+ * path has none.
14
+ */
15
+ function policyChecks(config, policy, segmentOf) {
16
+ if (typeof policy === "string") return void 0;
17
+ let check;
18
+ let keys;
19
+ if ("permdock" in policy) {
20
+ const where = `defineBucket(${config.id})`;
21
+ const id = policy.scope === "global" ? void 0 : `split_part(name, '/', ${String(policy.segment ?? segmentOf("PermDock"))})`;
22
+ check = (key) => permdockCheck(where, policy, key, id, config.catalog);
23
+ keys = policy.permdock;
24
+ } else {
25
+ const segment = policy.segment ?? (policy.scope === "platform" ? void 0 : segmentOf("permission"));
26
+ check = (key) => segment === void 0 ? `(select better_supabase.is_platform(${sqlString(key)}))` : `split_part(name, '/', ${String(segment)}) in (select t::text from better_supabase.tenant_ids_with(${sqlString(key)}) t)`;
27
+ keys = policy.access;
28
+ }
29
+ return {
30
+ read: check(keys.read),
31
+ ...keys.list === void 0 ? {} : { list: check(keys.list) },
32
+ write: check(keys.write),
33
+ delete: check(keys.delete ?? keys.write)
34
+ };
35
+ }
36
+ //#endregion
37
+ //#region src/storage/tenant-scope.ts
38
+ const forbidden = (bucket, message) => new DbException(dbError("forbidden", message, { table: bucket }));
39
+ function resolveTenant(options, claim) {
40
+ const { context } = options;
41
+ if (options.tenant !== void 0) return options.tenant;
42
+ if (!context) return void 0;
43
+ const tenant = tenantOf(context);
44
+ if (tenant !== void 0) return tenant;
45
+ const paths = typeof claim === "string" ? [claim] : claim ?? tenantClaimPaths();
46
+ for (const path of paths) {
47
+ const value = claimAt(context.claims, path);
48
+ if (value !== void 0) return value;
49
+ }
50
+ }
51
+ /**
52
+ * The guard for a client of bucket `bucket` whose template holds the tenant
53
+ * in `param`, or `undefined` when nothing is checked (no tenant config,
54
+ * `allTenants`, or an all-tenants context).
55
+ */
56
+ function tenantGuard(bucket, param, claim, match, options) {
57
+ const { context } = options;
58
+ if (param === void 0 || options.allTenants === true || context && spansAllTenants(context)) return;
59
+ const tenant = resolveTenant(options, claim);
60
+ const tenantOrThrow = () => {
61
+ if (tenant !== void 0) return tenant;
62
+ throw forbidden(bucket, `No tenant for bucket "${bucket}": pass { context } or { tenant } to connect(), or { allTenants: true }`);
63
+ };
64
+ return {
65
+ param,
66
+ path(path) {
67
+ const owner = tenantOrThrow();
68
+ if (String(match(path)?.[param]) !== owner) throw forbidden(bucket, `Path "${path}" belongs to another tenant`);
69
+ return path;
70
+ },
71
+ within(within) {
72
+ const owner = tenantOrThrow();
73
+ const given = within?.[param];
74
+ if (given !== void 0 && String(given) !== owner) throw forbidden(bucket, "Listing belongs to another tenant");
75
+ return owner;
76
+ }
77
+ };
78
+ }
79
+ //#endregion
7
80
  //#region src/storage/bucket.ts
8
81
  /** Storage operations that list objects; every other `select` is a read. */
9
82
  const LIST_OPERATIONS = [
@@ -123,7 +196,10 @@ function defineBucket(config) {
123
196
  if (index === void 0) throw new TypeError(`defineBucket: a ${kind} policy needs {${param}} as a whole path segment in "${config.path}"`);
124
197
  return index;
125
198
  }
126
- const mode = typeof policy === "string" ? policy : "permdock";
199
+ const tenantParam = config.tenant ? config.tenant.param ?? "orgId" : void 0;
200
+ if (tenantParam !== void 0 && !template.params.includes(tenantParam)) throw new TypeError(`defineBucket: tenant.param {${tenantParam}} is not in "${config.path}"`);
201
+ const tenantClaim = config.tenant?.claim;
202
+ const mode = typeof policy === "string" ? policy : "permdock" in policy ? "permdock" : "access";
127
203
  const accessCheck = (() => {
128
204
  switch (mode) {
129
205
  case "tenant": {
@@ -137,23 +213,12 @@ function defineBucket(config) {
137
213
  }
138
214
  case "public":
139
215
  case "none":
140
- case "permdock": return;
216
+ case "permdock":
217
+ case "access": return;
141
218
  default: throw new TypeError(`defineBucket: unknown policy "${String(mode)}"`);
142
219
  }
143
220
  })();
144
- const permdock = (() => {
145
- if (typeof policy === "string") return void 0;
146
- const where = `defineBucket(${config.id})`;
147
- const id = policy.scope === "global" ? void 0 : `split_part(name, '/', ${String(policy.segment ?? segmentFor(config.tenant?.param ?? "orgId", "PermDock"))})`;
148
- const check = (key) => permdockCheck(where, policy, key, id, config.catalog);
149
- const keys = policy.permdock;
150
- return {
151
- read: check(keys.read),
152
- ...keys.list === void 0 ? {} : { list: check(keys.list) },
153
- write: check(keys.write),
154
- delete: check(keys.delete ?? keys.write)
155
- };
156
- })();
221
+ const permdock = policyChecks(config, policy, (kind) => segmentFor(config.tenant?.param ?? "orgId", kind));
157
222
  const resolve = (target) => {
158
223
  if (typeof target !== "string") return template.build(target);
159
224
  if (!template.match(target)) throw new DbException(dbError("invalid_input", `Path "${target}" does not match "${config.path}"`));
@@ -168,6 +233,7 @@ function defineBucket(config) {
168
233
  template: config.path,
169
234
  params: template.params,
170
235
  owner: policy === "owner" ? config.owner?.param ?? "userId" : template.params.includes("userId") ? "userId" : void 0,
236
+ tenant: tenantParam,
171
237
  public: config.public ?? false,
172
238
  policy,
173
239
  fileSizeLimit,
@@ -217,7 +283,8 @@ function defineBucket(config) {
217
283
  using,
218
284
  void 0
219
285
  ]);
220
- } else if (policy === "public") policies.push([
286
+ } else if (policy === "public" && bucket.public) lines.push("-- Public URLs serve objects without a policy; a select policy would also let anyone list them.");
287
+ else if (policy === "public") policies.push([
221
288
  "select",
222
289
  "select",
223
290
  "anon, authenticated",
@@ -309,7 +376,7 @@ function defineBucket(config) {
309
376
  });
310
377
  return issues;
311
378
  },
312
- connect: (client, options) => connectBucket(bucket, client, resolve, options)
379
+ connect: (client, options = {}) => connectBucket(bucket, client, resolve, options, tenantGuard(bucket.id, tenantParam, tenantClaim, (path) => bucket.match(path), options))
313
380
  };
314
381
  return bucket;
315
382
  }
@@ -328,7 +395,15 @@ function ttlSeconds(ttl) {
328
395
  function isErrorResult(value) {
329
396
  return typeof value === "object" && value !== null && value.ok === false && "error" in value;
330
397
  }
331
- function connectBucket(bucket, client, resolve, connectOptions = {}) {
398
+ function connectBucket(bucket, client, resolveTarget, connectOptions, guard) {
399
+ const resolve = (target) => {
400
+ const path = resolveTarget(target);
401
+ return guard ? guard.path(path) : path;
402
+ };
403
+ const scopedWithin = (within) => guard ? {
404
+ ...within,
405
+ [guard.param]: guard.within(within)
406
+ } : within ?? {};
332
407
  const api = () => client.storage.from(bucket.id);
333
408
  const signed = connectOptions.cacheSignedUrls ? /* @__PURE__ */ new Map() : void 0;
334
409
  const run = (fn) => AsyncResult.from(async () => {
@@ -391,7 +466,7 @@ function connectBucket(bucket, client, resolve, connectOptions = {}) {
391
466
  };
392
467
  const list = (within, options) => AsyncResult.from(async () => {
393
468
  const out = [];
394
- await walk(bucket.prefix(within ?? {}), options?.signal, out);
469
+ await walk(bucket.prefix(scopedWithin(within)), options?.signal, out);
395
470
  return ok(out);
396
471
  }).mapError((error) => ({
397
472
  ...error,
@@ -423,6 +498,13 @@ function connectBucket(bucket, client, resolve, connectOptions = {}) {
423
498
  });
424
499
  return {
425
500
  bucket,
501
+ path: (target) => {
502
+ try {
503
+ return ok(resolve(target));
504
+ } catch (cause) {
505
+ return err(toDbError(cause));
506
+ }
507
+ },
426
508
  upload,
427
509
  download: (target, options) => AsyncResult.from(async () => {
428
510
  const path = resolve(target);
@@ -546,4 +628,51 @@ function connectBucket(bucket, client, resolve, connectOptions = {}) {
546
628
  };
547
629
  }
548
630
  //#endregion
549
- export { TTL, defineBucket, fromStorageError, parseSize };
631
+ //#region src/storage/presets.ts
632
+ /** The image types browsers display everywhere. */
633
+ const IMAGE_TYPES = [
634
+ "image/png",
635
+ "image/jpeg",
636
+ "image/webp",
637
+ "image/gif",
638
+ "image/avif"
639
+ ];
640
+ const AVATAR_PATH = "{userId}/avatar-{version}.{ext}";
641
+ const LOGO_PATH = "{orgId}/logo-{version}.{ext}";
642
+ /**
643
+ * A bucket for profile pictures: `{userId}/avatar-{version}.{ext}`, public,
644
+ * 2 MiB of images, written only by their owner (the `owner` policy).
645
+ */
646
+ function avatarBucket(options = {}) {
647
+ return defineBucket({
648
+ id: options.id ?? "avatars",
649
+ path: options.path ?? AVATAR_PATH,
650
+ public: options.public ?? true,
651
+ fileSizeLimit: options.fileSizeLimit ?? "2MiB",
652
+ allowedMimeTypes: options.allowedMimeTypes ?? IMAGE_TYPES,
653
+ policy: options.policy ?? "owner"
654
+ });
655
+ }
656
+ /**
657
+ * A bucket for organization logos: `{orgId}/logo-{version}.{ext}`, public,
658
+ * 2 MiB of images, written by members with the update permission through
659
+ * the access contract (`tenant_ids_with`), so it follows the app's roles.
660
+ */
661
+ function orgLogoBucket(options = {}) {
662
+ const permission = options.permission ?? "organization.update";
663
+ const tenant = options.tenant === false ? void 0 : options.tenant ?? { param: "orgId" };
664
+ return defineBucket({
665
+ id: options.id ?? "organization-logos",
666
+ path: options.path ?? LOGO_PATH,
667
+ public: options.public ?? true,
668
+ fileSizeLimit: options.fileSizeLimit ?? "2MiB",
669
+ allowedMimeTypes: options.allowedMimeTypes ?? IMAGE_TYPES,
670
+ policy: options.policy ?? { access: {
671
+ read: permission,
672
+ write: permission
673
+ } },
674
+ ...tenant ? { tenant } : {}
675
+ });
676
+ }
677
+ //#endregion
678
+ export { IMAGE_TYPES, TTL, avatarBucket, defineBucket, fromStorageError, orgLogoBucket, parseSize };
@@ -0,0 +1,88 @@
1
+ /// <reference lib="esnext.temporal" />
2
+ import { t as BetterPostgres } from "./pool-C9voM4Kw.js";
3
+ //#region src/auth/support.d.ts
4
+ type SupportEndedBy = "admin" | "expired" | "revoked";
5
+ /** A support session: a platform admin viewing the app as `targetUserId`. */
6
+ interface SupportSession {
7
+ readonly id: string;
8
+ readonly adminId: string;
9
+ readonly targetUserId: string;
10
+ readonly reason: string;
11
+ /** Writes fail while it is true (`begin read only`). */
12
+ readonly readOnly: boolean;
13
+ readonly tenant?: string;
14
+ readonly startedAt: Temporal.Instant;
15
+ readonly expiresAt: Temporal.Instant;
16
+ readonly endedAt?: Temporal.Instant;
17
+ readonly endedBy?: SupportEndedBy;
18
+ /** App data, such as a ticket id. */
19
+ readonly metadata: Readonly<Record<string, unknown>>;
20
+ }
21
+ interface SupportStartInput {
22
+ readonly adminId: string;
23
+ /**
24
+ * The admin's verified claims. The SQL store runs `start_support_session`
25
+ * with them, so `is_platform()` checks the admin in the database too.
26
+ */
27
+ readonly adminClaims: Readonly<Record<string, unknown>>;
28
+ readonly targetUserId: string;
29
+ readonly reason: string;
30
+ readonly ttlSeconds: number;
31
+ readonly readOnly: boolean;
32
+ readonly tenant?: string;
33
+ readonly metadata: Readonly<Record<string, unknown>>;
34
+ }
35
+ /** A caller's verified claims, which the store runs a call with. */
36
+ type SupportCallerClaims = Readonly<Record<string, unknown>>;
37
+ interface SupportListFilter {
38
+ readonly adminId?: string;
39
+ readonly targetUserId?: string;
40
+ /** `true`: running sessions only; `false`: ended or expired ones only. */
41
+ readonly active?: boolean;
42
+ /** Defaults to 50. */
43
+ readonly limit?: number;
44
+ }
45
+ /**
46
+ * Where support sessions live. `sqlSupportStore` uses the `support-sessions`
47
+ * SQL kit module; implement this for another store and check it with
48
+ * `testSupportSessionStore` from `better-supabase/testing`.
49
+ */
50
+ interface SupportSessionStore {
51
+ readonly apiVersion: 1;
52
+ readonly name: string;
53
+ /** Starts a session and ends the admin's previous one. Rejects when the store refuses. */
54
+ start(input: SupportStartInput): Promise<SupportSession>;
55
+ /**
56
+ * The admin's session while it is running; `undefined` once ended or
57
+ * expired. With the admin's `claims`, the store also checks that the admin
58
+ * may still start sessions.
59
+ */
60
+ get(sessionId: string, adminId: string, claims?: SupportCallerClaims): Promise<SupportSession | undefined>;
61
+ /**
62
+ * `false` when it was not running. With `claims`, it runs as that caller:
63
+ * the session's admin, or staff with the revoke permission.
64
+ */
65
+ end(sessionId: string, endedBy: SupportEndedBy, claims?: SupportCallerClaims): Promise<boolean>;
66
+ /** With `claims`, only what that caller may view. */
67
+ list(filter?: SupportListFilter, claims?: SupportCallerClaims): Promise<readonly SupportSession[]>;
68
+ /** The claims `targetUserId` would get, when the store can build them. */
69
+ claims?(targetUserId: string): Promise<Readonly<Record<string, unknown>>>;
70
+ }
71
+ interface SqlSupportStoreOptions {
72
+ /** `kits.support-sessions.schema`. Defaults to `better_supabase`. */
73
+ readonly schema?: string;
74
+ }
75
+ /**
76
+ * Support sessions in the `support-sessions` SQL kit module. `start` runs
77
+ * as the admin, so the module's `is_platform()` gate applies; the reads run
78
+ * on the admin connection.
79
+ */
80
+ declare function sqlSupportStore(postgres: Pick<BetterPostgres, "admin" | "asUser">, options?: SqlSupportStoreOptions): SupportSessionStore;
81
+ /**
82
+ * The claims a support session runs with: the target's, plus `act` naming
83
+ * the admin, the reason and the session (the audit module records all three).
84
+ * `role` falls back to `authenticated`; the admin's claims never leak in.
85
+ */
86
+ declare function supportClaims(session: Pick<SupportSession, "id" | "adminId" | "targetUserId" | "reason" | "readOnly">, target: Readonly<Record<string, unknown>>): Record<string, unknown>;
87
+ //#endregion
88
+ export { SupportSession as a, sqlSupportStore as c, SupportListFilter as i, supportClaims as l, SupportCallerClaims as n, SupportSessionStore as o, SupportEndedBy as r, SupportStartInput as s, SqlSupportStoreOptions as t };