better-supabase 0.3.0 → 0.4.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 (172) hide show
  1. package/README.md +7 -6
  2. package/bin/better-supabase.js +4 -0
  3. package/dist/{base64-CQeB1eSf.js → base64-D2gsioCY.js} +2 -2
  4. package/dist/claims-BAf6c7is.js +48 -0
  5. package/dist/cli/_format-Cax5EsM9.js +101 -0
  6. package/dist/cli/bin.d.ts +1 -0
  7. package/dist/cli/bin.js +48 -0
  8. package/dist/cli/command-CK14fP91.js +502 -0
  9. package/dist/cli/compare-BmIWVEui.js +9 -0
  10. package/dist/cli/config-D9R0o1LH.js +1895 -0
  11. package/dist/cli/config-SSKSTUl2.js +2 -0
  12. package/dist/cli/db-DOBPt660.js +52 -0
  13. package/dist/cli/dist-B1MvLv-M.js +129 -0
  14. package/dist/cli/dist-CFgGknmZ.js +1112 -0
  15. package/dist/cli/dist-Du7VnEUG.js +621 -0
  16. package/dist/cli/doctor-BOym3mbL.js +2382 -0
  17. package/dist/cli/env-BZ0uy8XK.js +108 -0
  18. package/dist/cli/env-jIFUADLY.js +511 -0
  19. package/dist/cli/errors-4v4DTtda.js +56 -0
  20. package/dist/cli/fastest-levenshtein-CFN-lNDv.js +120 -0
  21. package/dist/cli/gen-Ccn_p90L.js +2008 -0
  22. package/dist/cli/gen-D5cLCULx.js +2 -0
  23. package/dist/cli/index.d.ts +493 -0
  24. package/dist/cli/index.js +184 -0
  25. package/dist/cli/init-ClnxRYBF.js +550 -0
  26. package/dist/cli/introspect-DxqfkVIv.js +81 -0
  27. package/dist/cli/io-k41_z-jT.js +24 -0
  28. package/dist/cli/json5-ChRycquj.js +787 -0
  29. package/dist/cli/jsonc-BUniUpOk.js +2 -0
  30. package/dist/cli/jsonc-D5Fu67jw.js +45 -0
  31. package/dist/cli/keys-ByKHnvP9.js +83 -0
  32. package/dist/cli/ohash-D7bG_EOb.js +277 -0
  33. package/dist/cli/openapi-8WHjxiTJ.js +66 -0
  34. package/dist/cli/project-B_GeXyHh.js +70 -0
  35. package/dist/cli/prompts-BNcNfAve.js +1142 -0
  36. package/dist/cli/prompts-BTYQGunc.js +2 -0
  37. package/dist/cli/read-sets-CMa29t9a.js +971 -0
  38. package/dist/cli/rolldown-runtime-BPOCksWG.js +24 -0
  39. package/dist/cli/run-CP20LKk_.js +347 -0
  40. package/dist/cli/seed-5J6eu_Xg.js +98 -0
  41. package/dist/cli/skills-LJJIhLFR.js +137 -0
  42. package/dist/cli/sql-D4VbqmXi.js +199 -0
  43. package/dist/cli/style-C0NFdJRL.js +29 -0
  44. package/dist/cli/toml-DB-MNnFs.js +424 -0
  45. package/dist/cli/version-5mW_8sY1.js +4 -0
  46. package/dist/cli/yaml-CYCbJ6et.js +2252 -0
  47. package/dist/client/index.d.ts +2 -2
  48. package/dist/client/index.js +13 -13
  49. package/dist/{compiler-2zqGzxVZ.d.ts → compiler-DB3aSFOA.d.ts} +2 -2
  50. package/dist/config/index.js +4 -4
  51. package/dist/{config-Be4GwsqI.js → config-BpxDfnbg.js} +1 -1
  52. package/dist/{config-DOsGYSiT.js → config-CbvFmGD8.js} +1 -1
  53. package/dist/{define-DF341GDj.js → define-Bh0AqTik.js} +160 -79
  54. package/dist/{define-DUbydmJa.d.ts → define-CRS2MRV3.d.ts} +32 -28
  55. package/dist/edge/index.d.ts +8 -12
  56. package/dist/edge/index.js +50 -24
  57. package/dist/{entitlements-DvJXoI02.d.ts → entitlements-Dm-FHpNH.d.ts} +2 -2
  58. package/dist/{errors-Bl3rkOzo.js → errors-L0xK41y_.js} +1 -1
  59. package/dist/events/index.d.ts +1 -1
  60. package/dist/events/index.js +4 -4
  61. package/dist/{events-Do8jXh4D.d.ts → events-B21UOhvP.d.ts} +4 -4
  62. package/dist/{executor-DynmIWhq.d.ts → executor-B5IoUxLE.d.ts} +1 -1
  63. package/dist/{executor-C57jx2pn.js → executor-CVt6dD0B.js} +3 -3
  64. package/dist/{executor-B26lgbHD.d.ts → executor-CZ7ImRiD.d.ts} +2 -2
  65. package/dist/hono/index.d.ts +16 -23
  66. package/dist/hono/index.js +10 -10
  67. package/dist/{index-CTTMS6_1.d.ts → index-528Ugmyp.d.ts} +12 -3
  68. package/dist/{index-CADlNHK-.d.ts → index-BAy4tsAP.d.ts} +5 -5
  69. package/dist/{index-BNsABq7P.d.ts → index-BBdvo-5d.d.ts} +23 -25
  70. package/dist/{index-qriRM9cZ.d.ts → index-BhHEC04r.d.ts} +3 -3
  71. package/dist/{index-GV77NpRP.d.ts → index-D5IfUIDh.d.ts} +3 -3
  72. package/dist/{index-CriFFJ_p.d.ts → index-DFmOpBGC.d.ts} +11 -11
  73. package/dist/{index-UJ-LUxtI.d.ts → index-RlLXd8dL.d.ts} +12 -12
  74. package/dist/index.d.ts +15 -15
  75. package/dist/index.js +13 -13
  76. package/dist/{invalidate-DbxuL2jL.js → invalidate-CgxmRamI.js} +7 -1
  77. package/dist/jobs/index.d.ts +10 -4
  78. package/dist/jobs/index.js +32 -16
  79. package/dist/{json-schema--aOXAGfU.js → json-schema-DP6V7cbr.js} +1 -1
  80. package/dist/list/index.d.ts +1 -1
  81. package/dist/list/index.js +3 -3
  82. package/dist/{live-QE3UPbGm.d.ts → live-BHakSWWo.d.ts} +12 -12
  83. package/dist/{live-D8rV1Ldw.js → live-CdJBOc4f.js} +13 -9
  84. package/dist/lookup-BaP5bI27.js +18 -0
  85. package/dist/mcp/index.d.ts +18 -18
  86. package/dist/mcp/index.js +23 -18
  87. package/dist/{mfa-x3V3vGh-.d.ts → mfa-C7MNQ1Ob.d.ts} +1 -1
  88. package/dist/{mfa-BxoR1RkN.js → mfa-byfjruR9.js} +118 -29
  89. package/dist/next/image/index.js +6 -3
  90. package/dist/next/index.d.ts +38 -35
  91. package/dist/next/index.js +41 -33
  92. package/dist/openapi/index.d.ts +1 -1
  93. package/dist/openapi/index.js +6 -6
  94. package/dist/orpc/index.d.ts +17 -20
  95. package/dist/orpc/index.js +11 -11
  96. package/dist/otel/index.d.ts +5 -5
  97. package/dist/otel/index.js +81 -62
  98. package/dist/{permdock-sql-Bta_G0v-.js → permdock-sql-C4KFzLc9.js} +1 -1
  99. package/dist/{plugin-DVRB8qNE.d.ts → plugin-bNhvJwRh.d.ts} +2 -2
  100. package/dist/plugins/actor/index.d.ts +1 -1
  101. package/dist/plugins/actor/index.js +1 -1
  102. package/dist/plugins/rules/index.d.ts +2 -2
  103. package/dist/plugins/rules/index.js +42 -12
  104. package/dist/plugins/soft-delete/index.d.ts +2 -2
  105. package/dist/plugins/soft-delete/index.js +2 -2
  106. package/dist/plugins/tenant/index.d.ts +2 -2
  107. package/dist/plugins/tenant/index.js +10 -4
  108. package/dist/plugins/timestamps/index.d.ts +1 -1
  109. package/dist/plugins/timestamps/index.js +1 -1
  110. package/dist/plugins/validation/index.d.ts +2 -2
  111. package/dist/plugins/validation/index.js +3 -3
  112. package/dist/{pool-B9qsLyFR.d.ts → pool-DFwEQwmN.d.ts} +11 -4
  113. package/dist/postgres/index.d.ts +5 -5
  114. package/dist/postgres/index.js +3 -2
  115. package/dist/{postgrest-DmBlevh-.d.ts → postgrest-Bc-0Xw62.d.ts} +1 -1
  116. package/dist/{problem-CGt1ADix.js → problem-BihjPR0t.js} +1 -1
  117. package/dist/{problem-Z2RTQjZD.d.ts → problem-U8UEKccv.d.ts} +1 -1
  118. package/dist/query/index.d.ts +2 -2
  119. package/dist/query/index.js +2 -2
  120. package/dist/{query-BdKLTPlj.js → query-BMMSWbI7.js} +18 -18
  121. package/dist/react/index.d.ts +6 -6
  122. package/dist/react/index.js +1 -1
  123. package/dist/react/server.d.ts +5 -5
  124. package/dist/react/server.js +3 -3
  125. package/dist/react/session.d.ts +4 -4
  126. package/dist/react/session.js +2 -2
  127. package/dist/{react-DNBYNazu.js → react-CQHMaESR.js} +39 -39
  128. package/dist/{read-set-D0qBcboM.js → read-set-FWK8qQ58.js} +50 -15
  129. package/dist/realtime/index.d.ts +1 -1
  130. package/dist/realtime/index.js +13 -12
  131. package/dist/{index-kpgopLNv.d.ts → refresh-BwYkyNOQ.d.ts} +6 -3
  132. package/dist/{resolve-BRciX2jK.d.ts → resolve-BAqQcwMW.d.ts} +15 -10
  133. package/dist/{resource-CwVgiI5q.d.ts → resource-BJcaIfxi.d.ts} +5 -5
  134. package/dist/{resource-D30rp0GN.js → resource-My_y-Syh.js} +6 -6
  135. package/dist/{respond-DAkPIdTH.d.ts → respond-DgIbdrt8.d.ts} +31 -12
  136. package/dist/{result-DWXatkd6.js → result-Bj6yt33D.js} +2 -2
  137. package/dist/{result-c2OYgCV2.d.ts → result-CqEsbgYs.d.ts} +3 -3
  138. package/dist/server/index.d.ts +14 -14
  139. package/dist/server/index.js +12 -12
  140. package/dist/{respond-C44EZAJo.js → server-BA75B0mT.js} +139 -100
  141. package/dist/{shared-_6-uPL7E.js → shared-DnoCdMDr.js} +4 -3
  142. package/dist/{simplify-LakqK1vH.js → simplify-Cqvfr9dj.js} +67 -24
  143. package/dist/sql/index.d.ts +1 -1
  144. package/dist/sql/index.js +7 -7
  145. package/dist/{sql-BIgk12ew.js → sql-B34HU2DI.js} +3 -2
  146. package/dist/ssr/index.d.ts +4 -4
  147. package/dist/ssr/index.js +2 -2
  148. package/dist/{standard-FUi3Z3-7.js → standard-BRyBGTtp.js} +2 -2
  149. package/dist/{standard-eudBiRk1.d.ts → standard-BsaCYQJc.d.ts} +1 -1
  150. package/dist/{stats-D78UQ8Yu.js → stats-C_YxA2r1.js} +33 -8
  151. package/dist/storage/index.d.ts +2 -2
  152. package/dist/storage/index.js +26 -8
  153. package/dist/{tables-Dhha3BXM.js → tables-Cb-3_VWc.js} +14 -0
  154. package/dist/{temporal-required-CMxeSVPo.js → temporal-required-CBNHT6ni.js} +1 -1
  155. package/dist/testing/index.d.ts +20 -21
  156. package/dist/testing/index.js +45 -45
  157. package/dist/{version-BfpG2gks.js → version-Bmf7_FzM.js} +1 -1
  158. package/dist/{view-3Po_PVlr.d.ts → view-B64o81wp.d.ts} +5 -5
  159. package/dist/{view-wZ-9qIUz.js → view-BrJI2LKp.js} +1 -1
  160. package/dist/webhooks/index.d.ts +3 -3
  161. package/dist/webhooks/index.js +1 -1
  162. package/dist/{webhooks-CAIFQSTM.js → webhooks-Bh_Jf_-l.js} +37 -18
  163. package/package.json +36 -4
  164. package/skills/better-supabase/SKILL.md +57 -10
  165. package/skills/better-supabase/references/plugins.md +7 -11
  166. package/skills/better-supabase/references/troubleshooting.md +24 -4
  167. package/skills/better-supabase-api/SKILL.md +66 -18
  168. package/skills/better-supabase-api/references/adapters.md +44 -26
  169. package/skills/better-supabase-auth/SKILL.md +146 -0
  170. package/skills/better-supabase-auth/references/permdock.md +55 -0
  171. package/skills/better-supabase-testing/SKILL.md +58 -4
  172. package/dist/claims-CoTobNC-.js +0 -25
@@ -1,6 +1,6 @@
1
- import { n as PROBLEM_TYPE_BASE, t as PROBLEM_CONTENT_TYPE } from "../problem-CGt1ADix.js";
1
+ import { n as PROBLEM_TYPE_BASE, t as PROBLEM_CONTENT_TYPE } from "../problem-BihjPR0t.js";
2
2
  import { t as SPEC_PINS } from "../spec-pins-C3oc-Tdp.js";
3
- import { t as buildJsonSchema } from "../json-schema--aOXAGfU.js";
3
+ import { t as buildJsonSchema } from "../json-schema-DP6V7cbr.js";
4
4
  //#region src/openapi/index.ts
5
5
  /** JSON Schema for RFC 9457 Problem Details as better-supabase sends them. */
6
6
  const PROBLEM_SCHEMA = {
@@ -173,15 +173,15 @@ function pageSchema(row, cursor) {
173
173
  * schemes and Problem Details errors.
174
174
  *
175
175
  * ```ts
176
- * const doc = createOpenApi(sb, {
176
+ * const doc = createOpenApi(betterSupabase, {
177
177
  * info: { title: 'CRM API', version: '1.0.0' },
178
178
  * resources: { customers: { list: customerList }, notes: { operations: ['list', 'get'] } },
179
179
  * });
180
180
  * ```
181
181
  */
182
- function createOpenApi(sb, options) {
182
+ function createOpenApi(betterSupabase, options) {
183
183
  const defs = buildJsonSchema({
184
- meta: sb.meta,
184
+ meta: betterSupabase.meta,
185
185
  config: { json: options.json ?? {} }
186
186
  })["$defs"];
187
187
  const schemas = { Problem: PROBLEM_SCHEMA };
@@ -190,7 +190,7 @@ function createOpenApi(sb, options) {
190
190
  const base = (options.basePath ?? "").replace(/\/$/, "");
191
191
  const security = options.security ?? ["bearer"];
192
192
  for (const [key, raw] of Object.entries(options.resources)) {
193
- const table = sb.meta.tables[key];
193
+ const table = betterSupabase.meta.tables[key];
194
194
  if (!table) throw new TypeError(`createOpenApi: unknown table "${key}"`);
195
195
  const resource = raw === true ? {} : raw;
196
196
  const operations = resource.operations ?? (table.kind === "view" ? ["list", "get"] : [
@@ -1,9 +1,9 @@
1
1
  import { n as AnyModels, t as AnyFunctions } from "../types-DONv9SHN.js";
2
- import { o as Result, p as DbError } from "../result-c2OYgCV2.js";
3
- import { t as BetterSupabase } from "../define-DUbydmJa.js";
4
- import { r as AuthState } from "../resolve-BRciX2jK.js";
5
- import { d as ServerContext, f as ServerOptions, l as BetterServer, n as GuardOptions } from "../respond-DAkPIdTH.js";
6
- import { r as ProblemDetails } from "../problem-Z2RTQjZD.js";
2
+ import { o as Result, p as DbError } from "../result-CqEsbgYs.js";
3
+ import { t as BetterSupabase } from "../define-CRS2MRV3.js";
4
+ import { r as AuthState } from "../resolve-BAqQcwMW.js";
5
+ import { f as ServerContext, n as GuardOptions, p as ServerOptions, r as MiddlewareOptions, u as BetterServer } from "../respond-DgIbdrt8.js";
6
+ import { r as ProblemDetails } from "../problem-U8UEKccv.js";
7
7
  import { DecoratedMiddleware, ORPCError } from "@orpc/server";
8
8
  //#region src/orpc/index.d.ts
9
9
  /** Initial context: pass `{ context: { request } }` to the oRPC handler. */
@@ -11,36 +11,33 @@ export interface OrpcRequestContext {
11
11
  readonly request: Request;
12
12
  }
13
13
  /** What `middleware()` adds to `context`. */
14
- export interface OrpcContext<M extends AnyModels, F extends AnyFunctions, E> {
15
- readonly bs: ServerContext<M, F, E>;
16
- readonly db: ServerContext<M, F, E>["db"];
17
- readonly auth: AuthState;
14
+ export interface OrpcContext<M extends AnyModels, F extends AnyFunctions, E, C = unknown, P = unknown> {
15
+ readonly bs: ServerContext<M, F, E, C, P>;
16
+ readonly db: ServerContext<M, F, E, C, P>["db"];
17
+ readonly auth: AuthState<C, P>;
18
18
  }
19
19
  export interface OrpcOptions extends ServerOptions {
20
20
  /** Include internal error messages. Defaults to `NODE_ENV === 'development'`. */
21
21
  readonly exposeErrors?: boolean;
22
22
  }
23
- export interface MiddlewareOptions extends GuardOptions {
24
- /** Refresh an expired cookie session. Pair with `ctx.bs.resolution.apply` in the handler adapter. */
25
- readonly refresh?: boolean;
26
- }
27
- export type OrpcMiddleware<M extends AnyModels, F extends AnyFunctions, E> = DecoratedMiddleware<OrpcRequestContext, OrpcContext<M, F, E>, unknown, unknown, Record<never, never>>;
28
- export interface BetterOrpc<M extends AnyModels, F extends AnyFunctions, E> extends BetterServer<M, F, E> {
23
+ 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
+ export interface BetterOrpc<M extends AnyModels, F extends AnyFunctions, E, C = unknown, P = unknown> extends BetterServer<M, F, E, C, P> {
29
25
  /**
30
26
  * Resolves the caller, enforces `allow` and adds `context.db`, `context.auth`
31
27
  * and `context.bs`. Thrown `DbException`s, and errors caused by a
32
- * `DbError`, become `ORPCError`s.
28
+ * `DbError`, become `ORPCError`s. With `refresh`, pair it with
29
+ * `ctx.bs.resolution.apply` in the handler adapter.
33
30
  */
34
- middleware(options?: MiddlewareOptions): OrpcMiddleware<M, F, E>;
31
+ middleware(options?: MiddlewareOptions): OrpcMiddleware<M, F, E, C, P>;
35
32
  /** A `Result` (or `AsyncResult`) as data, throwing an `ORPCError` on failure. */
36
33
  unwrap<T>(value: Result<T> | PromiseLike<Result<T>>): Promise<T>;
37
34
  /** Plain values pass through; thrown `DbException`s become `ORPCError`s. */
38
35
  unwrap<T>(value: T | PromiseLike<T>): Promise<T>;
39
- toORPCError(error: DbError): ORPCError<string, ProblemDetails>;
36
+ toOrpcError(error: DbError): ORPCError<string, ProblemDetails>;
40
37
  }
41
38
  /** oRPC code for a `DbError`, from its HTTP status. */
42
39
  export declare function orpcCode(error: DbError): string;
43
40
  /** oRPC integration: middleware adding the caller's repositories to `context`. */
44
- export declare function createOrpc<M extends AnyModels, D, F extends AnyFunctions, E>(sb: BetterSupabase<M, D, F, E>, options?: OrpcOptions): BetterOrpc<M, F, E>;
41
+ export declare function createOrpc<M extends AnyModels, D, F extends AnyFunctions, E, C = unknown, P = unknown>(betterSupabase: BetterSupabase<M, D, F, E, C, P>, options?: OrpcOptions): BetterOrpc<M, F, E, C, P>;
45
42
  //#endregion
46
- export type { GuardOptions };
43
+ export type { GuardOptions, MiddlewareOptions };
@@ -1,18 +1,18 @@
1
- import { r as dbErrorOf } from "../errors-Bl3rkOzo.js";
2
- import { o as toProblem } from "../problem-CGt1ADix.js";
3
- import { a as settle, n as guard, o as createServer, s as extendServer, t as defaultExpose } from "../respond-C44EZAJo.js";
1
+ import { r as dbErrorOf } from "../errors-L0xK41y_.js";
2
+ import { o as toProblem } from "../problem-BihjPR0t.js";
3
+ import { a as guard, c as settle, i as defaultExpose, n as extendServer, t as createServer } from "../server-BA75B0mT.js";
4
4
  import { COMMON_ERROR_STATUS_MAP, ORPCError, os } from "@orpc/server";
5
5
  //#region src/orpc/index.ts
6
- const CODES = new Map(Object.entries(COMMON_ERROR_STATUS_MAP).map(([code, status]) => [status, code]));
6
+ const CODES = /* @__PURE__ */ new Map(Object.entries(COMMON_ERROR_STATUS_MAP).map(([code, status]) => [status, code]));
7
7
  /** oRPC code for a `DbError`, from its HTTP status. */
8
8
  function orpcCode(error) {
9
9
  return CODES.get(error.status) ?? (error.status >= 500 ? "INTERNAL_SERVER_ERROR" : "BAD_REQUEST");
10
10
  }
11
11
  /** oRPC integration: middleware adding the caller's repositories to `context`. */
12
- function createOrpc(sb, options = {}) {
13
- const server = createServer(sb, options);
12
+ function createOrpc(betterSupabase, options = {}) {
13
+ const server = createServer(betterSupabase, options);
14
14
  const expose = options.exposeErrors ?? defaultExpose();
15
- const toORPCError = (error) => {
15
+ const toOrpcError = (error) => {
16
16
  const problem = toProblem(error, { expose });
17
17
  return new ORPCError(orpcCode(error), {
18
18
  message: problem.detail ?? problem.title,
@@ -20,12 +20,12 @@ function createOrpc(sb, options = {}) {
20
20
  });
21
21
  };
22
22
  return extendServer(server, {
23
- toORPCError,
23
+ toOrpcError,
24
24
  middleware(middlewareOptions = {}) {
25
25
  return os.$context().middleware(async ({ context, next }) => {
26
26
  const ctx = await server.context(context.request, { refresh: middlewareOptions.refresh ?? false });
27
27
  const denied = guard(ctx.auth, middlewareOptions.allow, middlewareOptions.aal, middlewareOptions.scopes);
28
- if (denied) throw toORPCError(denied);
28
+ if (denied) throw toOrpcError(denied);
29
29
  try {
30
30
  return await next({ context: {
31
31
  bs: ctx,
@@ -34,14 +34,14 @@ function createOrpc(sb, options = {}) {
34
34
  } });
35
35
  } catch (cause) {
36
36
  const thrown = dbErrorOf(cause);
37
- if (thrown) throw toORPCError(thrown);
37
+ if (thrown) throw toOrpcError(thrown);
38
38
  throw cause;
39
39
  }
40
40
  });
41
41
  },
42
42
  async unwrap(value) {
43
43
  const settled = await settle(() => value);
44
- if (!settled.ok) throw toORPCError(settled.error);
44
+ if (!settled.ok) throw toOrpcError(settled.error);
45
45
  return settled.data;
46
46
  }
47
47
  });
@@ -1,5 +1,5 @@
1
- import { a as EventHub } from "../events-Do8jXh4D.js";
2
- import { f as Plugin } from "../plugin-DVRB8qNE.js";
1
+ import { a as EventHub } from "../events-B21UOhvP.js";
2
+ import { f as Plugin } from "../plugin-bNhvJwRh.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
@@ -20,15 +20,15 @@ export interface OtelOptions {
20
20
  * never recorded; `db.query.summary` is `SELECT customers`.
21
21
  *
22
22
  * ```ts
23
- * const sb = defineSupabase(schema).use(otel());
23
+ * const betterSupabase = defineSupabase(schema).use(otel());
24
24
  * ```
25
25
  */
26
26
  export declare function otel(options?: OtelOptions): Plugin<"otel">;
27
27
  /**
28
- * Spans for auth resolution and session refreshes, from `sb.events`.
28
+ * Spans for auth resolution and session refreshes, from `betterSupabase.events`.
29
29
  * Returns a function that stops listening.
30
30
  */
31
- export declare function traceAuth(sb: {
31
+ export declare function traceAuth(betterSupabase: {
32
32
  readonly events: EventHub;
33
33
  }, options?: Pick<OtelOptions, "tracer" | "attributes">): () => void;
34
34
  /**
@@ -1,6 +1,6 @@
1
1
  import { n as definePlugin } from "../plugin-BqR6wKMB.js";
2
2
  import { t as SPEC_PINS } from "../spec-pins-C3oc-Tdp.js";
3
- import "../version-BfpG2gks.js";
3
+ import "../version-Bmf7_FzM.js";
4
4
  import { SpanKind, SpanStatusCode, context, metrics, propagation, trace } from "@opentelemetry/api";
5
5
  //#region src/otel/index.ts
6
6
  const INSTRUMENTATION_NAME = "better-supabase";
@@ -26,13 +26,13 @@ function systemOf(executor) {
26
26
  * never recorded; `db.query.summary` is `SELECT customers`.
27
27
  *
28
28
  * ```ts
29
- * const sb = defineSupabase(schema).use(otel());
29
+ * const betterSupabase = defineSupabase(schema).use(otel());
30
30
  * ```
31
31
  */
32
32
  function otel(options = {}) {
33
- const tracer = options.tracer ?? trace.getTracer("better-supabase", "0.0.0");
33
+ const tracer = options.tracer ?? trace.getTracer("better-supabase", "0.4.0");
34
34
  let histogram;
35
- if (options.metrics !== false) histogram = (options.meter ?? metrics.getMeter("better-supabase", "0.0.0")).createHistogram("db.client.operation.duration", {
35
+ if (options.metrics !== false) histogram = (options.meter ?? metrics.getMeter("better-supabase", "0.4.0")).createHistogram("db.client.operation.duration", {
36
36
  unit: "s",
37
37
  description: "Duration of database client operations.",
38
38
  advice: { explicitBucketBoundaries: [
@@ -50,76 +50,95 @@ function otel(options = {}) {
50
50
  return definePlugin({
51
51
  name: "otel",
52
52
  enforce: "post",
53
- wrapExecutor: (executor) => ({
54
- ...executor,
55
- name: executor.name,
56
- execute(op, executeContext) {
53
+ wrapExecutor: (executor) => {
54
+ const system = systemOf(executor);
55
+ /** Span and metric attributes per table and operation, built once. */
56
+ const described = /* @__PURE__ */ new Map();
57
+ const describe = (op) => {
57
58
  const operation = operationName(op);
58
- const summary = `${operation} ${op.table.name}`;
59
- const attributes = {
60
- ...options.attributes,
61
- "db.system.name": systemOf(executor),
62
- "db.namespace": op.table.schema,
63
- "db.collection.name": op.table.name,
64
- "db.operation.name": operation,
65
- "db.query.summary": summary,
66
- "better_supabase.executor": executor.name
67
- };
68
- const started = performance.now();
69
- return tracer.startActiveSpan(summary, {
70
- kind: SpanKind.CLIENT,
71
- attributes
72
- }, async (span) => {
73
- const finish = (errorType) => {
74
- const metricAttributes = {
75
- "db.system.name": attributes["db.system.name"],
59
+ const key = `${op.table.key}:${operation}`;
60
+ let entry = described.get(key);
61
+ if (!entry) {
62
+ const summary = `${operation} ${op.table.name}`;
63
+ entry = {
64
+ summary,
65
+ attributes: {
66
+ ...options.attributes,
67
+ "db.system.name": system,
68
+ "db.namespace": op.table.schema,
76
69
  "db.collection.name": op.table.name,
77
70
  "db.operation.name": operation,
78
- ...errorType ? { "error.type": errorType } : {}
79
- };
80
- histogram?.record((performance.now() - started) / 1e3, metricAttributes);
81
- span.end();
71
+ "db.query.summary": summary,
72
+ "better_supabase.executor": executor.name
73
+ },
74
+ metric: {
75
+ "db.system.name": system,
76
+ "db.collection.name": op.table.name,
77
+ "db.operation.name": operation
78
+ }
82
79
  };
83
- try {
84
- const result = await executor.execute(op, executeContext);
85
- if (result.ok) {
86
- span.setAttribute("db.response.returned_rows", result.data.rows.length);
87
- finish();
88
- } else {
89
- span.setAttributes({
90
- "error.type": result.error.kind,
91
- ...result.error.code ? { "db.response.status_code": result.error.code } : {}
92
- });
80
+ described.set(key, entry);
81
+ }
82
+ return entry;
83
+ };
84
+ return {
85
+ ...executor,
86
+ name: executor.name,
87
+ execute(op, executeContext) {
88
+ const { summary, attributes, metric } = describe(op);
89
+ const started = performance.now();
90
+ return tracer.startActiveSpan(summary, {
91
+ kind: SpanKind.CLIENT,
92
+ attributes
93
+ }, async (span) => {
94
+ const finish = (errorType) => {
95
+ histogram?.record((performance.now() - started) / 1e3, errorType ? {
96
+ ...metric,
97
+ "error.type": errorType
98
+ } : metric);
99
+ span.end();
100
+ };
101
+ try {
102
+ const result = await executor.execute(op, executeContext);
103
+ if (result.ok) {
104
+ if (span.isRecording()) span.setAttribute("db.response.returned_rows", result.data.rows.length);
105
+ finish();
106
+ } else {
107
+ span.setAttributes({
108
+ "error.type": result.error.kind,
109
+ ...result.error.code ? { "db.response.status_code": result.error.code } : {}
110
+ });
111
+ span.setStatus({
112
+ code: SpanStatusCode.ERROR,
113
+ message: result.error.message
114
+ });
115
+ finish(result.error.kind);
116
+ }
117
+ return result;
118
+ } catch (cause) {
119
+ const error = cause instanceof Error ? cause : new Error(String(cause));
120
+ span.recordException(error);
121
+ span.setAttribute("error.type", error.name);
93
122
  span.setStatus({
94
123
  code: SpanStatusCode.ERROR,
95
- message: result.error.message
124
+ message: error.message
96
125
  });
97
- finish(result.error.kind);
126
+ finish(error.name);
127
+ throw cause;
98
128
  }
99
- return result;
100
- } catch (cause) {
101
- const error = cause instanceof Error ? cause : new Error(String(cause));
102
- span.recordException(error);
103
- span.setAttribute("error.type", error.name);
104
- span.setStatus({
105
- code: SpanStatusCode.ERROR,
106
- message: error.message
107
- });
108
- finish(error.name);
109
- throw cause;
110
- }
111
- });
112
- }
113
- })
129
+ });
130
+ }
131
+ };
132
+ }
114
133
  });
115
134
  }
116
135
  /**
117
- * Spans for auth resolution and session refreshes, from `sb.events`.
136
+ * Spans for auth resolution and session refreshes, from `betterSupabase.events`.
118
137
  * Returns a function that stops listening.
119
138
  */
120
- function traceAuth(sb, options = {}) {
121
- const tracer = options.tracer ?? trace.getTracer("better-supabase", "0.0.0");
122
- const offAuth = sb.events.on("auth", (event) => {
139
+ function traceAuth(betterSupabase, options = {}) {
140
+ const tracer = options.tracer ?? trace.getTracer("better-supabase", "0.4.0");
141
+ const offAuth = betterSupabase.events.on("auth", (event) => {
123
142
  const span = tracer.startSpan("auth.resolve", {
124
143
  kind: SpanKind.INTERNAL,
125
144
  attributes: {
@@ -131,7 +150,7 @@ function traceAuth(sb, options = {}) {
131
150
  if (!event.ok) span.setStatus({ code: SpanStatusCode.ERROR });
132
151
  span.end();
133
152
  });
134
- const offRefresh = sb.events.on("refresh", (event) => {
153
+ const offRefresh = betterSupabase.events.on("refresh", (event) => {
135
154
  const end = Date.now();
136
155
  const span = tracer.startSpan("auth.refresh", {
137
156
  kind: SpanKind.CLIENT,
@@ -1,4 +1,4 @@
1
- import { n as dbError, t as DbException } from "./errors-Bl3rkOzo.js";
1
+ import { n as dbError, t as DbException } from "./errors-L0xK41y_.js";
2
2
  //#region src/core/template.ts
3
3
  const PLACEHOLDER = /\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
4
4
  function parseSegment(segment) {
@@ -1,7 +1,7 @@
1
1
  /// <reference lib="esnext.temporal" />
2
2
  import { A as SchemaMeta, P as TableMeta, n as AnyModels } from "./types-DONv9SHN.js";
3
- import { g as Operation, h as MutationOp, r as Executor } from "./executor-DynmIWhq.js";
4
- import { v as ErrorMapper } from "./result-c2OYgCV2.js";
3
+ import { g as Operation, h as MutationOp, r as Executor } from "./executor-B5IoUxLE.js";
4
+ import { v as ErrorMapper } from "./result-CqEsbgYs.js";
5
5
  //#region src/core/plugin.d.ts
6
6
  /** Who is making the request. */
7
7
  interface Actor {
@@ -1,4 +1,4 @@
1
- import { f as Plugin, h as RequestContext } from "../../plugin-DVRB8qNE.js";
1
+ import { f as Plugin, h as RequestContext } from "../../plugin-bNhvJwRh.js";
2
2
  //#region src/plugins/actor/index.d.ts
3
3
  export interface ActorOptions {
4
4
  /** Custom actor id resolution. Defaults to `context.actor.id` for users and services. */
@@ -1,5 +1,5 @@
1
1
  import { n as definePlugin } from "../../plugin-BqR6wKMB.js";
2
- import { i as insertsOnly, l as withDefault, t as dbName } from "../../shared-_6-uPL7E.js";
2
+ import { i as insertsOnly, l as withDefault, t as dbName } from "../../shared-DnoCdMDr.js";
3
3
  //#region src/plugins/actor/index.ts
4
4
  function defaultActor(context) {
5
5
  const actor = context.actor;
@@ -1,5 +1,5 @@
1
- import { g as Operation } from "../../executor-DynmIWhq.js";
2
- import { f as Plugin, m as RepositoryExtension } from "../../plugin-DVRB8qNE.js";
1
+ import { g as Operation } from "../../executor-B5IoUxLE.js";
2
+ import { f as Plugin, m as RepositoryExtension } from "../../plugin-bNhvJwRh.js";
3
3
  //#region src/plugins/rules/index.d.ts
4
4
  export type RuleLevel = "off" | "warn" | "error";
5
5
  /** A level, or a level with the rule's option: `['error', 500]`. */
@@ -1,6 +1,7 @@
1
- import { n as dbError, t as DbException } from "../../errors-Bl3rkOzo.js";
1
+ import { n as dbError, t as DbException } from "../../errors-L0xK41y_.js";
2
+ import { t as lookupOf } from "../../lookup-BaP5bI27.js";
2
3
  import { n as definePlugin } from "../../plugin-BqR6wKMB.js";
3
- import { n as claimAt, r as claimsOf } from "../../claims-CoTobNC-.js";
4
+ import { n as claimAt, r as claimsOf } from "../../claims-BAf6c7is.js";
4
5
  //#region src/plugins/rules/index.ts
5
6
  /** Catches data-exposure and correctness mistakes; everything else off. */
6
7
  function safe() {
@@ -40,17 +41,37 @@ function levelOf(setting) {
40
41
  function optionOf(setting, fallback) {
41
42
  return Array.isArray(setting) ? setting[1] : fallback;
42
43
  }
44
+ const depths = /* @__PURE__ */ new WeakMap();
43
45
  function includeDepth(selection) {
46
+ let depth = depths.get(selection);
47
+ if (depth === void 0) {
48
+ depth = computeIncludeDepth(selection);
49
+ depths.set(selection, depth);
50
+ }
51
+ return depth;
52
+ }
53
+ function computeIncludeDepth(selection) {
44
54
  let depth = 0;
45
55
  for (const include of selection.includes) if (include.count !== void 0 || include.aggregate !== void 0) depth = Math.max(depth, 1);
46
56
  else depth = Math.max(depth, 1 + includeDepth(include.selection));
47
57
  return depth;
48
58
  }
59
+ const sensitiveByTable = /* @__PURE__ */ new WeakMap();
60
+ function sensitiveOf(table) {
61
+ let sensitive = sensitiveByTable.get(table);
62
+ if (!sensitive) {
63
+ sensitive = new Set(lookupOf(table).entries.filter(([, column]) => column.sensitive).map(([, column]) => column.db));
64
+ sensitiveByTable.set(table, sensitive);
65
+ }
66
+ return sensitive;
67
+ }
49
68
  function sensitiveColumns(selection, table) {
50
69
  const found = [];
51
- const sensitive = new Set(Object.values(table.columns).filter((column) => column.sensitive).map((column) => column.db));
52
- const columns = [...selection.columns, ...selection.aggregate?.measures ?? []];
53
- for (const column of columns) if (sensitive.has(column.column)) found.push(`${table.key}.${column.column}`);
70
+ const sensitive = sensitiveOf(table);
71
+ if (sensitive.size > 0) {
72
+ for (const column of selection.columns) if (sensitive.has(column.column)) found.push(`${table.key}.${column.column}`);
73
+ for (const measure of selection.aggregate?.measures ?? []) if (sensitive.has(measure.column)) found.push(`${table.key}.${measure.column}`);
74
+ }
54
75
  for (const include of selection.includes) found.push(...sensitiveColumns(include.selection, include.target));
55
76
  return found;
56
77
  }
@@ -73,11 +94,21 @@ function bucketTemplates(schema) {
73
94
  }
74
95
  return templates;
75
96
  }
97
+ const urlColumnsByTable = /* @__PURE__ */ new WeakMap();
98
+ /** Scalar text columns named `*_url`, the only ones the rule inspects. */
99
+ function urlColumns(table) {
100
+ let columns = urlColumnsByTable.get(table);
101
+ if (!columns) {
102
+ columns = lookupOf(table).entries.map(([, column]) => column).filter((column) => column.db.endsWith("_url") && !column.array && TEXT_TYPES.has(column.type));
103
+ urlColumnsByTable.set(table, columns);
104
+ }
105
+ return columns;
106
+ }
76
107
  function storedObjectColumn(op, schema) {
77
108
  const rows = op.kind === "insert" ? op.rows : op.kind === "update" ? [op.set] : [];
78
109
  if (rows.length === 0) return void 0;
79
- for (const column of Object.values(op.table.columns)) {
80
- if (!rows.some((row) => column.db in row) || !column.db.endsWith("_url") || column.array || !TEXT_TYPES.has(column.type)) continue;
110
+ for (const column of urlColumns(op.table)) {
111
+ if (!rows.some((row) => column.db in row)) continue;
81
112
  const path = `${column.db.slice(0, -4)}_path`;
82
113
  if (column.storage !== void 0) return `"${column.db}" holds paths in bucket "${column.storage}"; name it "${path}"`;
83
114
  if (rows.some((row) => {
@@ -87,15 +118,14 @@ function storedObjectColumn(op, schema) {
87
118
  }
88
119
  }
89
120
  function checks(rules) {
121
+ const maxLimit = optionOf(rules.maxLimit, 1e3);
122
+ const maxDepth = optionOf(rules.maxIncludeDepth, 3);
90
123
  return {
91
124
  noUnboundedFindMany: (op) => op.kind === "select" && op.single === void 0 && !op.head && op.limit === void 0 ? "findMany without limit reads every visible row" : void 0,
92
- maxLimit: (op) => {
93
- const max = optionOf(rules.maxLimit, 1e3);
94
- return op.kind === "select" && op.limit !== void 0 && op.limit > max ? `limit ${op.limit} is above the maximum of ${max}` : void 0;
95
- },
125
+ maxLimit: (op) => op.kind === "select" && op.limit !== void 0 && op.limit > maxLimit ? `limit ${op.limit} is above the maximum of ${maxLimit}` : void 0,
96
126
  requireOrderByForCursor: (op) => op.kind === "select" && !op.head && op.orderBy.length === 0 && (op.offset !== void 0 || op.limit !== void 0 && op.limit > 1) ? "paging without orderBy returns rows in no stable order" : void 0,
97
127
  maxIncludeDepth: (op) => {
98
- const max = optionOf(rules.maxIncludeDepth, 3);
128
+ const max = maxDepth;
99
129
  const selection = op.kind === "select" ? op.selection : op.returning;
100
130
  const depth = selection ? includeDepth(selection) : 0;
101
131
  return depth > max ? `includes nest ${depth} levels deep (maximum ${max})` : void 0;
@@ -1,6 +1,6 @@
1
1
  import { C as PrimaryKeyValue } from "../../types-DONv9SHN.js";
2
- import { t as AsyncResult } from "../../result-c2OYgCV2.js";
3
- import { f as Plugin, m as RepositoryExtension, o as HasFlag } from "../../plugin-DVRB8qNE.js";
2
+ import { t as AsyncResult } from "../../result-CqEsbgYs.js";
3
+ import { f as Plugin, m as RepositoryExtension, o as HasFlag } from "../../plugin-bNhvJwRh.js";
4
4
  //#region src/plugins/soft-delete/index.d.ts
5
5
  export interface SoftDeleteFindArgs {
6
6
  /** Include soft-deleted rows of the queried table. */
@@ -1,8 +1,8 @@
1
1
  import { t as and } from "../../types-6cYK9pbJ.js";
2
- import { i as ok, t as AsyncResult } from "../../result-DWXatkd6.js";
2
+ import { i as ok, t as AsyncResult } from "../../result-Bj6yt33D.js";
3
3
  import { n as definePlugin } from "../../plugin-BqR6wKMB.js";
4
4
  import { n as scopeOperation } from "../../scope-CDHcS7dE.js";
5
- import { a as isNotNull, o as isNull, t as dbName } from "../../shared-_6-uPL7E.js";
5
+ import { a as isNotNull, o as isNull, t as dbName } from "../../shared-DnoCdMDr.js";
6
6
  //#region src/plugins/soft-delete/index.ts
7
7
  function deletedColumn(table) {
8
8
  return dbName(table, table.flags.softDelete);
@@ -1,5 +1,5 @@
1
1
  import { A as SchemaMeta } from "../../types-DONv9SHN.js";
2
- import { f as Plugin, h as RequestContext, m as RepositoryExtension, o as HasFlag } from "../../plugin-DVRB8qNE.js";
2
+ import { f as Plugin, h as RequestContext, m as RepositoryExtension, o as HasFlag } from "../../plugin-bNhvJwRh.js";
3
3
  //#region src/plugins/tenant/index.d.ts
4
4
  /**
5
5
  * Dotted paths to the string claims of `C` (up to three levels), e.g.
@@ -7,7 +7,7 @@ import { f as Plugin, h as RequestContext, m as RepositoryExtension, o as HasFla
7
7
  */
8
8
  export type ClaimPath<C> = unknown extends C ? string : StringPaths<C, [1, 2, 3]>;
9
9
  type StringPaths<T, Depth extends readonly unknown[]> = T extends object ? T extends readonly unknown[] ? never : { [K in keyof T & string]-?: (NonNullable<T[K]> extends string ? K : never) | (Depth extends readonly [unknown, ...infer Rest] ? `${K}.${StringPaths<NonNullable<T[K]>, Rest>}` : never); }[keyof T & string] : never;
10
- /** `C` is the claims type (`sb.claims(schema)`'s output); it types `claim`. */
10
+ /** `C` is the claims type (`betterSupabase.claims(schema)`'s output); it types `claim`. */
11
11
  export interface TenantOptions<C = unknown> {
12
12
  /**
13
13
  * JWT claim path(s) holding the tenant id when `context.tenant` is not set.
@@ -1,8 +1,8 @@
1
- import { n as dbError, t as DbException } from "../../errors-Bl3rkOzo.js";
1
+ import { n as dbError, t as DbException } from "../../errors-L0xK41y_.js";
2
2
  import { n as definePlugin } from "../../plugin-BqR6wKMB.js";
3
3
  import { n as scopeOperation } from "../../scope-CDHcS7dE.js";
4
- import { i as tenantClaimPaths, n as claimAt, r as claimsOf } from "../../claims-CoTobNC-.js";
5
- import { n as equals, r as forbidden, t as dbName } from "../../shared-_6-uPL7E.js";
4
+ import { i as tenantClaimPaths, n as claimAt, r as claimsOf } from "../../claims-BAf6c7is.js";
5
+ import { n as equals, r as forbidden, t as dbName } from "../../shared-DnoCdMDr.js";
6
6
  //#region src/plugins/tenant/index.ts
7
7
  /**
8
8
  * The tenant for a request: `context.tenant`, then the configured claim.
@@ -30,8 +30,14 @@ function tenantColumn(table) {
30
30
  */
31
31
  function tenant(options = {}) {
32
32
  const onMissing = options.onMissing ?? "error";
33
+ /** A context is fixed for a connection, so its tenant is resolved once. */
34
+ const resolved = /* @__PURE__ */ new WeakMap();
33
35
  const current = (table, context, schema) => {
34
- const id = resolveTenant(context, options, schema);
36
+ let id = resolved.get(context);
37
+ if (id === void 0 && !resolved.has(context)) {
38
+ id = resolveTenant(context, options, schema);
39
+ resolved.set(context, id);
40
+ }
35
41
  if (id === void 0 && onMissing === "error" && tenantColumn(table)) throw new DbException(dbError("forbidden", `No tenant in the request context for "${table.key}"`));
36
42
  return id;
37
43
  };
@@ -1,4 +1,4 @@
1
- import { f as Plugin } from "../../plugin-DVRB8qNE.js";
1
+ import { f as Plugin } from "../../plugin-bNhvJwRh.js";
2
2
  //#region src/plugins/timestamps/index.d.ts
3
3
  /**
4
4
  * Stamps the columns generated as `Flags.timestamps`: `createdAt` on insert,
@@ -1,5 +1,5 @@
1
1
  import { n as definePlugin } from "../../plugin-BqR6wKMB.js";
2
- import { i as insertsOnly, l as withDefault, t as dbName } from "../../shared-_6-uPL7E.js";
2
+ import { i as insertsOnly, l as withDefault, t as dbName } from "../../shared-DnoCdMDr.js";
3
3
  //#region src/plugins/timestamps/index.ts
4
4
  /**
5
5
  * Stamps the columns generated as `Flags.timestamps`: `createdAt` on insert,
@@ -1,5 +1,5 @@
1
- import { f as Plugin } from "../../plugin-DVRB8qNE.js";
2
- import { t as StandardSchemaV1 } from "../../standard-eudBiRk1.js";
1
+ import { f as Plugin } from "../../plugin-bNhvJwRh.js";
2
+ import { t as StandardSchemaV1 } from "../../standard-BsaCYQJc.js";
3
3
  //#region src/plugins/validation/index.d.ts
4
4
  export interface TableValidators {
5
5
  /** Validates inserts and upserts, with app-cased keys. */
@@ -1,7 +1,7 @@
1
- import { n as dbError, t as DbException } from "../../errors-Bl3rkOzo.js";
1
+ import { n as dbError, t as DbException } from "../../errors-L0xK41y_.js";
2
2
  import { n as definePlugin } from "../../plugin-BqR6wKMB.js";
3
- import { t as validate } from "../../standard-FUi3Z3-7.js";
4
- import { c as toDb, s as toApp } from "../../shared-_6-uPL7E.js";
3
+ import { t as validate } from "../../standard-BRyBGTtp.js";
4
+ import { c as toDb, s as toApp } from "../../shared-DnoCdMDr.js";
5
5
  //#region src/plugins/validation/index.ts
6
6
  async function check(schema, table, row, label, index) {
7
7
  const result = await validate(schema, toApp(table, row), label);