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,2 +1,2 @@
1
- import { a as BrowserOptions, i as BrowserAuth, n as AuthUser, o as createBrowser, r as BetterBrowser, t as AuthSnapshot } from "../index-UJ-LUxtI.js";
2
- export { AuthSnapshot, AuthUser, BetterBrowser, BrowserAuth, BrowserOptions, createBrowser };
1
+ import { a as ClientOptions, i as ClientAuth, n as AuthUser, o as createClient, r as BetterClient, t as AuthSnapshot } from "../index-RlLXd8dL.js";
2
+ export { AuthSnapshot, AuthUser, BetterClient, ClientAuth, ClientOptions, createClient };
@@ -1,8 +1,8 @@
1
- import { n as decodeJwtPayload } from "../base64-CQeB1eSf.js";
1
+ import { n as decodeJwtPayload } from "../base64-D2gsioCY.js";
2
2
  import { EnvValidationError, parseEnv } from "../env/index.js";
3
- import { t as createQueries } from "../query-BdKLTPlj.js";
3
+ import { t as createQueries } from "../query-BMMSWbI7.js";
4
4
  import { createBrowserClient } from "@supabase/ssr";
5
- import { createClient } from "@supabase/supabase-js";
5
+ import { createClient as createClient$1 } from "@supabase/supabase-js";
6
6
  //#region src/client/index.ts
7
7
  const LOADING = {
8
8
  status: "loading",
@@ -49,13 +49,13 @@ function contextOf(snapshot) {
49
49
  }
50
50
  function clientFor(options) {
51
51
  if (options.client) return options.client;
52
- if (!options.env) throw new TypeError("createBrowser needs `env` ({ url, publishableKey }) or `client`");
52
+ if (!options.env) throw new TypeError("createClient needs `env` ({ url, publishableKey }) or `client`");
53
53
  const checked = parseEnv({
54
54
  SUPABASE_URL: options.env.url,
55
55
  SUPABASE_PUBLISHABLE_KEY: options.env.publishableKey
56
56
  });
57
57
  if (!checked.ok) throw new EnvValidationError(checked.issues);
58
- if (options.storage === "local") return createClient(checked.env.url, checked.env.publishableKey);
58
+ if (options.storage === "local") return createClient$1(checked.env.url, checked.env.publishableKey);
59
59
  return createBrowserClient(checked.env.url, checked.env.publishableKey);
60
60
  }
61
61
  /**
@@ -63,10 +63,10 @@ function clientFor(options) {
63
63
  * session, TanStack Query options and an auth store for UI state.
64
64
  *
65
65
  * ```ts
66
- * export const browser = createBrowser(sb, { env: { url, publishableKey } });
66
+ * export const bs = createClient(betterSupabase, { env: { url, publishableKey } });
67
67
  * ```
68
68
  */
69
- function createBrowser(sb, options = {}) {
69
+ function createClient(betterSupabase, options = {}) {
70
70
  const supabase = clientFor(options);
71
71
  let snapshot = LOADING;
72
72
  let db;
@@ -78,14 +78,14 @@ function createBrowser(sb, options = {}) {
78
78
  db = void 0;
79
79
  for (const listener of listeners) listener();
80
80
  });
81
- const browser = {
82
- sb,
81
+ const client = {
82
+ betterSupabase,
83
83
  supabase,
84
84
  get db() {
85
- db ??= sb.connect(supabase, contextOf(snapshot));
85
+ db ??= betterSupabase.connect(supabase, contextOf(snapshot));
86
86
  return db;
87
87
  },
88
- queries: createQueries(sb, () => browser.db),
88
+ queries: createQueries(betterSupabase, () => client.db),
89
89
  auth: {
90
90
  current: () => snapshot,
91
91
  subscribe: (listener) => {
@@ -96,7 +96,7 @@ function createBrowser(sb, options = {}) {
96
96
  }
97
97
  }
98
98
  };
99
- return browser;
99
+ return client;
100
100
  }
101
101
  //#endregion
102
- export { createBrowser };
102
+ export { createClient };
@@ -1,5 +1,5 @@
1
- import { g as Operation } from "./executor-DynmIWhq.js";
2
- import { i as PostgrestPlan } from "./postgrest-DmBlevh-.js";
1
+ import { g as Operation } from "./executor-B5IoUxLE.js";
2
+ import { i as PostgrestPlan } from "./postgrest-Bc-0Xw62.js";
3
3
  //#region src/core/compiler.d.ts
4
4
  /**
5
5
  * Turns an IR operation into something a backend runs. An `Executor` for a
@@ -1,5 +1,5 @@
1
- import { i as tenantClaimPaths, t as DEFAULT_CLAIMS } from "../claims-CoTobNC-.js";
2
- import { i as resolveJsonSchema, n as defineConfig, r as resolveConfig, t as CONFIG_SCHEMA_URL } from "../config-Be4GwsqI.js";
3
- import { n as jsonSchema, t as buildJsonSchema } from "../json-schema--aOXAGfU.js";
4
- import { n as valibot, t as zod } from "../config-DOsGYSiT.js";
1
+ import { i as tenantClaimPaths, t as DEFAULT_CLAIMS } from "../claims-BAf6c7is.js";
2
+ import { i as resolveJsonSchema, n as defineConfig, r as resolveConfig, t as CONFIG_SCHEMA_URL } from "../config-BpxDfnbg.js";
3
+ import { n as jsonSchema, t as buildJsonSchema } from "../json-schema-DP6V7cbr.js";
4
+ import { n as valibot, t as zod } from "../config-CbvFmGD8.js";
5
5
  export { CONFIG_SCHEMA_URL, DEFAULT_CLAIMS, buildJsonSchema, defineConfig, jsonSchema, resolveConfig, resolveJsonSchema, tenantClaimPaths, valibot, zod };
@@ -1,4 +1,4 @@
1
- import { t as DEFAULT_CLAIMS } from "./claims-CoTobNC-.js";
1
+ import { t as DEFAULT_CLAIMS } from "./claims-BAf6c7is.js";
2
2
  //#region src/config/config.ts
3
3
  /** The JSON Schema document for a `json[...].schema` entry. */
4
4
  function resolveJsonSchema(source) {
@@ -1,4 +1,4 @@
1
- import { a as parseImport, c as schemaName, d as variantsFor, i as fieldsFor, l as siblingPath, o as propertyKey, r as HEADER, s as rowType, u as tableEntries } from "./json-schema--aOXAGfU.js";
1
+ import { a as parseImport, c as schemaName, d as variantsFor, i as fieldsFor, l as siblingPath, o as propertyKey, r as HEADER, s as rowType, u as tableEntries } from "./json-schema-DP6V7cbr.js";
2
2
  //#region src/generators/valibot.ts
3
3
  function scalar$1(kind) {
4
4
  switch (kind.kind) {
@@ -1,15 +1,16 @@
1
- import { n as dbError, t as DbException } from "./errors-Bl3rkOzo.js";
2
- import { a as invalidRequest, c as needsDecoding, o as decodeRows, r as IrBuilder, s as encodeValue } from "./simplify-LakqK1vH.js";
1
+ import { n as dbError, t as DbException } from "./errors-L0xK41y_.js";
2
+ import { a as invalidRequest, c as needsDecoding, o as decodeRows, r as builderFor, s as encodeValue } from "./simplify-Cqvfr9dj.js";
3
+ import { t as lookupOf } from "./lookup-BaP5bI27.js";
3
4
  import { n as nowInstant } from "./temporal-BwshQk0W.js";
4
5
  import { i as or, n as column, r as not, t as and } from "./types-6cYK9pbJ.js";
5
- import { i as ok, n as err, o as toDbError, s as withErrorMapper, t as AsyncResult } from "./result-DWXatkd6.js";
6
- import { t as invalidationTargets } from "./tables-Dhha3BXM.js";
6
+ import { i as ok, n as err, o as toDbError, s as withErrorMapper, t as AsyncResult } from "./result-Bj6yt33D.js";
7
+ import { t as invalidationTargets } from "./tables-Cb-3_VWc.js";
7
8
  import { t as consoleLogger } from "./logger-glM28NVN.js";
8
9
  import { r as orderPlugins } from "./plugin-BqR6wKMB.js";
9
- import { a as postgrestExecutor, n as StatsRecorder, r as recordStats } from "./stats-D78UQ8Yu.js";
10
- import { d as isQuerySpec, f as specTables, n as checkParams, o as isReadSet, r as containsPlaceholder, t as bindParams, u as createSpecs } from "./read-set-D0qBcboM.js";
11
- import { a as toBase64Url, t as base64ToText } from "./base64-CQeB1eSf.js";
12
- import { t as validate } from "./standard-FUi3Z3-7.js";
10
+ import { a as postgrestExecutor, n as StatsRecorder, r as recordStats } from "./stats-C_YxA2r1.js";
11
+ import { d as isQuerySpec, f as specTables, n as checkParams, o as isReadSet, r as containsPlaceholder, t as bindParams, u as createSpecs } from "./read-set-FWK8qQ58.js";
12
+ import { a as toBase64Url, t as base64ToText } from "./base64-D2gsioCY.js";
13
+ import { t as validate } from "./standard-BRyBGTtp.js";
13
14
  //#region src/core/batch.ts
14
15
  /**
15
16
  * Holds operations until each of `readers` has either submitted one or
@@ -95,12 +96,18 @@ function memoryCache() {
95
96
  }
96
97
  //#endregion
97
98
  //#region src/core/events.ts
99
+ /** More handlers than this for one event usually means a missing unsubscribe. */
100
+ const HANDLER_WARNING = 50;
101
+ function isDevelopment() {
102
+ return typeof process !== "undefined" && process.env["NODE_ENV"] !== "production";
103
+ }
98
104
  /**
99
105
  * Observers only: a handler that throws is reported to the logger and never
100
106
  * changes a result.
101
107
  */
102
108
  var EventHub = class {
103
109
  #handlers = /* @__PURE__ */ new Map();
110
+ #warned = /* @__PURE__ */ new Set();
104
111
  logger;
105
112
  constructor(logger = consoleLogger) {
106
113
  this.logger = logger;
@@ -112,6 +119,10 @@ var EventHub = class {
112
119
  this.#handlers.set(name, set);
113
120
  }
114
121
  set.add(handler);
122
+ if (set.size > HANDLER_WARNING && !this.#warned.has(name) && isDevelopment()) {
123
+ this.#warned.add(name);
124
+ this.logger.warn(`${set.size} "${name}" handlers are registered; call the function on() returns to remove one you no longer need`);
125
+ }
115
126
  return () => {
116
127
  set.delete(handler);
117
128
  };
@@ -170,11 +181,28 @@ const KNOWN_KEYS = /* @__PURE__ */ new Set([
170
181
  "_min",
171
182
  "_max"
172
183
  ]);
184
+ const NO_OPTIONS = Object.freeze({});
173
185
  function optionsOf(args) {
174
- if (!args) return {};
175
- const options = {};
176
- for (const [key, value] of Object.entries(args)) if (!KNOWN_KEYS.has(key)) options[key] = value;
177
- return options;
186
+ if (!args) return NO_OPTIONS;
187
+ let options;
188
+ for (const key in args) if (!KNOWN_KEYS.has(key) && Object.hasOwn(args, key)) (options ??= {})[key] = args[key];
189
+ return options ?? NO_OPTIONS;
190
+ }
191
+ const hooksByPlugins = /* @__PURE__ */ new WeakMap();
192
+ function hooksOf(plugins) {
193
+ let hooks = hooksByPlugins.get(plugins);
194
+ if (!hooks) {
195
+ hooks = {
196
+ transformQuery: plugins.filter((plugin) => plugin.transformQuery),
197
+ beforeMutation: plugins.filter((plugin) => plugin.beforeMutation),
198
+ afterMutation: plugins.filter((plugin) => plugin.afterMutation)
199
+ };
200
+ hooksByPlugins.set(plugins, hooks);
201
+ }
202
+ return hooks;
203
+ }
204
+ function isThenable(value) {
205
+ return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
178
206
  }
179
207
  function signalOf(args) {
180
208
  const signal = args?.["signal"];
@@ -187,8 +215,10 @@ function mutationKind(op) {
187
215
  /** Runs operations through plugins, the executor and the event hub. */
188
216
  var OperationRunner = class {
189
217
  runtime;
218
+ #hooks;
190
219
  constructor(runtime) {
191
220
  this.runtime = runtime;
221
+ this.#hooks = hooksOf(runtime.plugins);
192
222
  }
193
223
  hookArgs(table, options) {
194
224
  return {
@@ -201,20 +231,27 @@ var OperationRunner = class {
201
231
  }
202
232
  async run(op, options, signal) {
203
233
  const { runtime } = this;
204
- const hook = this.hookArgs(op.table, options);
234
+ const hooks = this.#hooks;
205
235
  let current = op;
206
- try {
207
- for (const plugin of runtime.plugins) if (plugin.transformQuery) current = plugin.transformQuery(current, hook);
208
- if (current.kind !== "select") {
209
- let mutation = current;
210
- for (const plugin of runtime.plugins) if (plugin.beforeMutation) mutation = await plugin.beforeMutation(mutation, hook);
211
- current = mutation;
236
+ if (hooks.transformQuery.length > 0 || hooks.beforeMutation.length > 0) {
237
+ const hook = this.hookArgs(op.table, options);
238
+ try {
239
+ for (const plugin of hooks.transformQuery) current = plugin.transformQuery(current, hook);
240
+ if (current.kind !== "select") {
241
+ let mutation = current;
242
+ for (const plugin of hooks.beforeMutation) {
243
+ const next = plugin.beforeMutation(mutation, hook);
244
+ mutation = isThenable(next) ? await next : next;
245
+ }
246
+ current = mutation;
247
+ }
248
+ } catch (cause) {
249
+ if (cause instanceof DbException) return this.fail(op.table, cause.error);
250
+ throw cause;
212
251
  }
213
- } catch (cause) {
214
- if (cause instanceof DbException) return this.fail(op.table, cause.error);
215
- throw cause;
216
252
  }
217
- const started = performance.now();
253
+ const timed = runtime.events.has("query");
254
+ const started = timed ? performance.now() : 0;
218
255
  const context = signal ? {
219
256
  signal,
220
257
  errorMappers: runtime.errorMappers
@@ -225,7 +262,7 @@ var OperationRunner = class {
225
262
  if (executed.ok && selection && needsDecoding(selection)) try {
226
263
  result = ok({
227
264
  ...executed.data,
228
- rows: decodeRows(selection, executed.data.rows)
265
+ rows: decodeRows(selection, executed.data.rows, true)
229
266
  });
230
267
  } catch (cause) {
231
268
  if (cause instanceof DbException) return this.fail(op.table, cause.error);
@@ -240,7 +277,7 @@ var OperationRunner = class {
240
277
  maxRows: runtime.maxRows
241
278
  });
242
279
  }
243
- if (runtime.events.has("query")) runtime.events.emit("query", {
280
+ if (timed) runtime.events.emit("query", {
244
281
  table: op.table.key,
245
282
  operation: current.kind,
246
283
  ok: result.ok,
@@ -254,21 +291,22 @@ var OperationRunner = class {
254
291
  }
255
292
  async afterMutation(op, data) {
256
293
  const { runtime } = this;
294
+ const plugins = this.#hooks.afterMutation;
295
+ const listening = runtime.events.has("mutation");
296
+ if (plugins.length === 0 && !listening) return;
257
297
  const event = {
258
298
  table: op.table,
259
299
  kind: mutationKind(op),
260
300
  rows: data.rows,
261
301
  context: runtime.context
262
302
  };
263
- for (const plugin of runtime.plugins) {
264
- if (!plugin.afterMutation) continue;
265
- try {
266
- await plugin.afterMutation(event);
267
- } catch (cause) {
268
- runtime.events.logger.error(`plugin "${plugin.name}" afterMutation threw`, { cause });
269
- }
303
+ for (const plugin of plugins) try {
304
+ const pending = plugin.afterMutation(event);
305
+ if (isThenable(pending)) await pending;
306
+ } catch (cause) {
307
+ runtime.events.logger.error(`plugin "${plugin.name}" afterMutation threw`, { cause });
270
308
  }
271
- runtime.events.emit("mutation", {
309
+ if (listening) runtime.events.emit("mutation", {
272
310
  table: op.table.key,
273
311
  kind: event.kind,
274
312
  rows: event.rows,
@@ -280,7 +318,7 @@ var OperationRunner = class {
280
318
  ...error,
281
319
  table: table.key
282
320
  };
283
- this.runtime.events.emit("error", {
321
+ if (this.runtime.events.has("error")) this.runtime.events.emit("error", {
284
322
  table: table.key,
285
323
  error: withTable
286
324
  });
@@ -306,19 +344,22 @@ function createRepository(runner, table) {
306
344
  });
307
345
  const returning = (args) => args?.["returning"] === false ? void 0 : selection(args);
308
346
  const run = (op, args) => runner.run(op, optionsOf(args), signalOf(args));
347
+ let primaryOrder;
348
+ /** `primaryKey` holds app names; order terms take database names. */
349
+ const defaultOrder = () => primaryOrder ??= table.primaryKey.map((name) => ({
350
+ column: builder.column(table, name),
351
+ direction: "asc"
352
+ }));
309
353
  const notFound = () => runner.fail(table, dbError("not_found", `No ${table.key} row matched`));
310
354
  const base = {
311
- $table: table.key,
355
+ $tableName: table.key,
312
356
  $meta: table,
313
357
  findMany(args) {
314
358
  return AsyncResult.from(async () => {
315
359
  const op = selectOp(args);
316
360
  const ordered = op.orderBy.length > 0 || table.primaryKey.length === 0 ? op : {
317
361
  ...op,
318
- orderBy: table.primaryKey.map((column) => ({
319
- column,
320
- direction: "asc"
321
- }))
362
+ orderBy: defaultOrder()
322
363
  };
323
364
  const result = await run(ordered, args);
324
365
  return result.ok ? ok(result.data.rows) : result;
@@ -567,7 +608,8 @@ function createRepository(runner, table) {
567
608
  if (typeof args["after"] === "string") {
568
609
  const values = decodeCursor(args["after"]);
569
610
  if (!values || values.length !== orderBy.length) return runner.fail(table, dbError("invalid_request", "Invalid cursor"));
570
- after = keysetCondition(orderBy, values, (name) => Object.values(table.columns).find((meta) => meta.db === name)?.nullable ?? true);
611
+ const { byDb } = lookupOf(table);
612
+ after = keysetCondition(orderBy, values, (name) => byDb.get(name)?.[1].nullable ?? true);
571
613
  }
572
614
  const op = selectOp(args, {
573
615
  selection: withSort,
@@ -581,7 +623,8 @@ function createRepository(runner, table) {
581
623
  const rows = result.data.rows.slice(0, size);
582
624
  const hasMore = result.data.rows.length > size;
583
625
  const last = rows.at(-1);
584
- const nextCursor = hasMore && last ? encodeCursor(orderBy.map((term) => encodeValue(last[aliasOf(withSort, term.column)]))) : null;
626
+ const aliases = new Map(withSort.columns.map((entry) => [entry.column, entry.alias]));
627
+ const nextCursor = hasMore && last ? encodeCursor(orderBy.map((term) => encodeValue(last[aliases.get(term.column) ?? term.column]))) : null;
585
628
  return ok({
586
629
  items: added.length > 0 ? rows.map((row) => strip(row, added)) : rows,
587
630
  nextCursor,
@@ -601,14 +644,17 @@ function createRepository(runner, table) {
601
644
  }
602
645
  function ensureColumns(base, orderBy) {
603
646
  const columns = [...base.columns];
647
+ const selected = new Set(columns.map((entry) => entry.column));
648
+ const { byDb } = lookupOf(table);
604
649
  const added = [];
605
650
  for (const term of orderBy) {
606
- if (columns.some((entry) => entry.column === term.column)) continue;
607
- const alias = Object.entries(table.columns).find(([, meta]) => meta.db === term.column)?.[0] ?? term.column;
651
+ if (selected.has(term.column)) continue;
652
+ const alias = byDb.get(term.column)?.[0] ?? term.column;
608
653
  columns.push({
609
654
  alias,
610
655
  column: term.column
611
656
  });
657
+ selected.add(term.column);
612
658
  added.push(alias);
613
659
  }
614
660
  return {
@@ -621,12 +667,9 @@ function createRepository(runner, table) {
621
667
  }
622
668
  return base;
623
669
  }
624
- function aliasOf(selection, column) {
625
- return selection.columns.find((entry) => entry.column === column)?.alias ?? column;
626
- }
627
670
  function strip(row, keys) {
628
- const copy = { ...row };
629
- for (const key of keys) delete copy[key];
671
+ const copy = {};
672
+ for (const key in row) if (!keys.includes(key) && Object.hasOwn(row, key)) copy[key] = row[key];
630
673
  return copy;
631
674
  }
632
675
  /**
@@ -644,13 +687,21 @@ function byKey(rows, columns) {
644
687
  const b = String(right);
645
688
  return a < b ? -1 : a > b ? 1 : 0;
646
689
  };
647
- return [...rows].sort((left, right) => {
648
- for (const column of columns) {
649
- const order = compare(left[column], right[column]);
690
+ const keyed = rows.map((row) => ({
691
+ row,
692
+ key: columns.map((column) => {
693
+ const value = row[column];
694
+ return typeof value === "object" && value !== null ? String(value) : value;
695
+ })
696
+ }));
697
+ keyed.sort((left, right) => {
698
+ for (let index = 0; index < columns.length; index++) {
699
+ const order = compare(left.key[index], right.key[index]);
650
700
  if (order !== 0) return order;
651
701
  }
652
702
  return 0;
653
703
  });
704
+ return keyed.map((entry) => entry.row);
654
705
  }
655
706
  /**
656
707
  * The rows after the cursor `values` in `orderBy` order, as an OR of ANDs:
@@ -716,7 +767,7 @@ var BetterSupabase = class BetterSupabase {
716
767
  * A token that fails it resolves to `{ kind: 'invalid', reason: 'claims' }`.
717
768
  *
718
769
  * ```ts
719
- * const sb = defineSupabase(schema).claims(z.object({ tenant_id: z.uuid() }));
770
+ * const betterSupabase = defineSupabase(schema).claims(z.object({ tenant_id: z.uuid() }));
720
771
  * ```
721
772
  */
722
773
  claims(schema) {
@@ -734,7 +785,7 @@ var BetterSupabase = class BetterSupabase {
734
785
  * the verified claims, never from `profile`.
735
786
  *
736
787
  * ```ts
737
- * const sb = defineSupabase(schema).userMetadata(z.object({ display_name: z.string() }));
788
+ * const betterSupabase = defineSupabase(schema).userMetadata(z.object({ display_name: z.string() }));
738
789
  * ```
739
790
  */
740
791
  userMetadata(schema) {
@@ -749,7 +800,7 @@ var BetterSupabase = class BetterSupabase {
749
800
  * Results themselves keep their `DbError`.
750
801
  *
751
802
  * ```ts
752
- * const sb = defineSupabase(schema).mapError((error) => new AppError(error));
803
+ * const betterSupabase = defineSupabase(schema).mapError((error) => new AppError(error));
753
804
  * ```
754
805
  */
755
806
  mapError(mapper) {
@@ -760,9 +811,14 @@ var BetterSupabase = class BetterSupabase {
760
811
  }
761
812
  #specs;
762
813
  #truncatedTables = /* @__PURE__ */ new Set();
814
+ /** A hub without listeners, for decoding read-set results. */
815
+ #decodeEvents;
816
+ /** One getter per table, shared by every db this instance connects. */
817
+ #tables;
818
+ #errorMappers = /* @__PURE__ */ new WeakMap();
763
819
  /**
764
820
  * Builds serializable `QuerySpec`s with the same arguments and result types
765
- * as the repository: `sb.spec.customers.findMany({ select: ['id'] })`.
821
+ * as the repository: `betterSupabase.spec.customers.findMany({ select: ['id'] })`.
766
822
  */
767
823
  get spec() {
768
824
  this.#specs ??= createSpecs(this.meta);
@@ -783,7 +839,7 @@ var BetterSupabase = class BetterSupabase {
783
839
  * invalidate them after it succeeds.
784
840
  *
785
841
  * ```ts
786
- * sb.defineRpc('archive_customer', { invalidates: ['customers', 'notes'] });
842
+ * betterSupabase.defineRpc('archive_customer', { invalidates: ['customers', 'notes'] });
787
843
  * ```
788
844
  */
789
845
  defineRpc(name, options) {
@@ -842,10 +898,10 @@ var BetterSupabase = class BetterSupabase {
842
898
  let executor = base;
843
899
  for (const plugin of plugins) if (plugin.wrapExecutor) executor = plugin.wrapExecutor(executor);
844
900
  executor = recordStats(executor, recorder);
845
- const errorMappers = [...this.options.errors ?? [], ...plugins.flatMap((plugin) => plugin.mapError ? [plugin.mapError] : [])];
901
+ const errorMappers = this.#errorMappersFor(plugins);
846
902
  const runner = new OperationRunner({
847
903
  meta: this.meta,
848
- builder: new IrBuilder(this.meta),
904
+ builder: builderFor(this.meta),
849
905
  executor,
850
906
  plugins,
851
907
  context,
@@ -855,7 +911,13 @@ var BetterSupabase = class BetterSupabase {
855
911
  maxRows: this.options.maxRows ?? 1e3,
856
912
  truncatedTables: this.#truncatedTables
857
913
  });
858
- const db = {
914
+ const db = Object.create(this.#tablePrototype());
915
+ dbStates.set(db, {
916
+ runner,
917
+ plugins,
918
+ repositories: /* @__PURE__ */ new Map()
919
+ });
920
+ Object.assign(db, {
859
921
  $client: client,
860
922
  $executor: executor,
861
923
  $context: context,
@@ -923,10 +985,10 @@ var BetterSupabase = class BetterSupabase {
923
985
  hint: `Add "${table.schema}.${table.name}" to vectorSearch in better-supabase.config.ts and run \`better-supabase sql sync\`.`
924
986
  }) : result;
925
987
  })
926
- };
988
+ });
927
989
  const many = (specs, signal, usePlugins) => AsyncResult.from(async () => {
928
990
  const invalid = specs.findIndex((spec) => !isQuerySpec(spec));
929
- if (invalid !== -1) return err(dbError("invalid_request", `db.$many() entry ${invalid} is not a QuerySpec from sb.spec`));
991
+ if (invalid !== -1) return err(dbError("invalid_request", `db.$many() entry ${invalid} is not a QuerySpec from betterSupabase.spec`));
930
992
  const active = usePlugins ? plugins : [];
931
993
  const batch = base.batch?.bind(base);
932
994
  const batching = batch && specs.length > 1 ? batchingExecutor({
@@ -974,21 +1036,6 @@ var BetterSupabase = class BetterSupabase {
974
1036
  "$many",
975
1037
  "$search"
976
1038
  ]) db[name] = mapThrows(db[name], throwAs);
977
- for (const [key, table] of Object.entries(this.meta.tables)) {
978
- let repository;
979
- Object.defineProperty(db, key, {
980
- enumerable: true,
981
- get: () => {
982
- if (!repository) {
983
- repository = this.#repository(runner, key, table, plugins);
984
- if (throwAs) {
985
- for (const [name, method] of Object.entries(repository)) if (typeof method === "function" && name !== "extend") repository[name] = mapThrows(method, throwAs);
986
- }
987
- }
988
- return repository;
989
- }
990
- });
991
- }
992
1039
  return db;
993
1040
  }
994
1041
  /**
@@ -1015,12 +1062,45 @@ var BetterSupabase = class BetterSupabase {
1015
1062
  count: entry.count ?? null
1016
1063
  }));
1017
1064
  }
1018
- }, context, [], new StatsRecorder(), new EventHub(this.events.logger)), spec, void 0);
1065
+ }, context, [], new StatsRecorder(), this.#decodeEvents ??= new EventHub(this.events.logger)), spec, void 0);
1019
1066
  if (!result.ok) return result;
1020
1067
  out[key] = result.data;
1021
1068
  }
1022
1069
  return ok(out);
1023
1070
  }
1071
+ #errorMappersFor(plugins) {
1072
+ let mappers = this.#errorMappers.get(plugins);
1073
+ if (!mappers) {
1074
+ mappers = [...this.options.errors ?? [], ...plugins.flatMap((plugin) => plugin.mapError ? [plugin.mapError] : [])];
1075
+ this.#errorMappers.set(plugins, mappers);
1076
+ }
1077
+ return mappers;
1078
+ }
1079
+ #tablePrototype() {
1080
+ if (this.#tables) return this.#tables;
1081
+ const tables = {};
1082
+ const throwAs = this.options.throwAs;
1083
+ const build = (state, key) => {
1084
+ let repository = state.repositories.get(key);
1085
+ if (repository) return repository;
1086
+ const table = this.meta.tables[key];
1087
+ repository = this.#repository(state.runner, key, table, state.plugins);
1088
+ if (throwAs) {
1089
+ for (const [name, method] of Object.entries(repository)) if (typeof method === "function" && name !== "extend") repository[name] = mapThrows(method, throwAs);
1090
+ }
1091
+ state.repositories.set(key, repository);
1092
+ return repository;
1093
+ };
1094
+ for (const key of Object.keys(this.meta.tables)) Object.defineProperty(tables, key, {
1095
+ enumerable: true,
1096
+ get() {
1097
+ const state = dbStates.get(this);
1098
+ return state ? build(state, key) : void 0;
1099
+ }
1100
+ });
1101
+ this.#tables = tables;
1102
+ return tables;
1103
+ }
1024
1104
  #repository(runner, key, table, plugins) {
1025
1105
  const repository = createRepository(runner, table);
1026
1106
  for (const plugin of plugins) {
@@ -1041,6 +1121,7 @@ var BetterSupabase = class BetterSupabase {
1041
1121
  return repository;
1042
1122
  }
1043
1123
  };
1124
+ const dbStates = /* @__PURE__ */ new WeakMap();
1044
1125
  function mapThrows(method, throwAs) {
1045
1126
  return function(...args) {
1046
1127
  const value = method.apply(this, args);
@@ -1048,7 +1129,7 @@ function mapThrows(method, throwAs) {
1048
1129
  };
1049
1130
  }
1050
1131
  function runSpec(db, spec, signal) {
1051
- if (!isQuerySpec(spec)) return AsyncResult.err(dbError("invalid_request", "db.$run() expects a QuerySpec from sb.spec"));
1132
+ if (!isQuerySpec(spec)) return AsyncResult.err(dbError("invalid_request", "db.$run() expects a QuerySpec from betterSupabase.spec"));
1052
1133
  const method = db[spec.table]?.[spec.method];
1053
1134
  if (!method) return AsyncResult.err(dbError("invalid_request", `Unknown table "${spec.table}" in QuerySpec`));
1054
1135
  if (containsPlaceholder(spec.args)) return AsyncResult.err(dbError("invalid_request", "This spec comes from a read set and still holds placeholders; run it with db.$many(readSet, params)"));
@@ -1081,7 +1162,7 @@ function rpc(executor, errorMappers, name, rest) {
1081
1162
  * Creates the data-layer definition from the generated `schema`.
1082
1163
  *
1083
1164
  * ```ts
1084
- * export const sb = defineSupabase(schema).use(timestamps()).use(softDelete());
1165
+ * export const betterSupabase = defineSupabase(schema).use(timestamps()).use(softDelete());
1085
1166
  * ```
1086
1167
  */
1087
1168
  function defineSupabase(schema, options) {