better-supabase 0.3.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 (211) hide show
  1. package/README.md +9 -6
  2. package/bin/better-supabase.js +4 -0
  3. package/dist/{base64-CQeB1eSf.js → base64-D2gsioCY.js} +2 -2
  4. package/dist/channel-FQW-CnGR.d.ts +70 -0
  5. package/dist/claims-BAf6c7is.js +48 -0
  6. package/dist/cli/_format-Cax5EsM9.js +101 -0
  7. package/dist/cli/bin.d.ts +1 -0
  8. package/dist/cli/bin.js +48 -0
  9. package/dist/cli/codemod-DqV7612m.js +388 -0
  10. package/dist/cli/command-CK14fP91.js +502 -0
  11. package/dist/cli/compare-BmIWVEui.js +9 -0
  12. package/dist/cli/config-CPVpXP5I.js +1965 -0
  13. package/dist/cli/config-dtoekM0v.js +2 -0
  14. package/dist/cli/db-DOBPt660.js +52 -0
  15. package/dist/cli/diff-SjkO5rNr.js +752 -0
  16. package/dist/cli/dist-DBPdlPYK.js +1112 -0
  17. package/dist/cli/dist-DreZ24Sz.js +129 -0
  18. package/dist/cli/dist-xn4wlboc.js +732 -0
  19. package/dist/cli/doctor-L7Z8mYTx.js +2863 -0
  20. package/dist/cli/env-DmKv_G4A.js +108 -0
  21. package/dist/cli/env-jIFUADLY.js +511 -0
  22. package/dist/cli/errors-4v4DTtda.js +56 -0
  23. package/dist/cli/fastest-levenshtein-CFN-lNDv.js +120 -0
  24. package/dist/cli/gen-BJc9vdyu.js +2 -0
  25. package/dist/cli/gen-C2osRcr5.js +2204 -0
  26. package/dist/cli/index.d.ts +493 -0
  27. package/dist/cli/index.js +184 -0
  28. package/dist/cli/init-Saf5-R9T.js +550 -0
  29. package/dist/cli/introspect--H7oGmcd.js +81 -0
  30. package/dist/cli/io-k41_z-jT.js +24 -0
  31. package/dist/cli/json5-ChRycquj.js +787 -0
  32. package/dist/cli/jsonc-BUniUpOk.js +2 -0
  33. package/dist/cli/jsonc-D5Fu67jw.js +45 -0
  34. package/dist/cli/keys-ByKHnvP9.js +83 -0
  35. package/dist/cli/ohash-D7bG_EOb.js +277 -0
  36. package/dist/cli/openapi-Cp5Fyta-.js +66 -0
  37. package/dist/cli/project-B_GeXyHh.js +70 -0
  38. package/dist/cli/prompts-BNcNfAve.js +1142 -0
  39. package/dist/cli/prompts-BTYQGunc.js +2 -0
  40. package/dist/cli/read-sets-BfMQcKy8.js +289 -0
  41. package/dist/cli/rolldown-runtime-BPOCksWG.js +24 -0
  42. package/dist/cli/run-BKR2W_79.js +351 -0
  43. package/dist/cli/seed-CUlqWh-8.js +98 -0
  44. package/dist/cli/skills-LJJIhLFR.js +137 -0
  45. package/dist/cli/sql-DzPbfngx.js +348 -0
  46. package/dist/cli/style-C0NFdJRL.js +29 -0
  47. package/dist/cli/toml-DB-MNnFs.js +424 -0
  48. package/dist/cli/version-BDJuO0UR.js +4 -0
  49. package/dist/cli/yaml-CYCbJ6et.js +2252 -0
  50. package/dist/client/index.d.ts +2 -2
  51. package/dist/client/index.js +15 -22
  52. package/dist/clone-BfgmLU90.js +21 -0
  53. package/dist/{compiler-2zqGzxVZ.d.ts → compiler-BXH_aZ7N.d.ts} +2 -2
  54. package/dist/config/index.d.ts +4 -4
  55. package/dist/config/index.js +5 -5
  56. package/dist/{config-DOsGYSiT.js → config-DreERlKo.js} +1 -1
  57. package/dist/{config-C1CYcm6o.d.ts → config-T_ejwyd3.d.ts} +176 -11
  58. package/dist/{define-DUbydmJa.d.ts → define-Bp_BZa5h.d.ts} +37 -29
  59. package/dist/{define-DF341GDj.js → define-cNKzqwL_.js} +356 -89
  60. package/dist/edge/index.d.ts +20 -13
  61. package/dist/edge/index.js +55 -26
  62. package/dist/{entitlements-DvJXoI02.d.ts → entitlements-DUxU4oHe.d.ts} +2 -2
  63. package/dist/env/index.d.ts +2 -2
  64. package/dist/env/index.js +34 -1
  65. package/dist/{errors-Bl3rkOzo.js → errors-DtwXa6b8.js} +2 -1
  66. package/dist/events/index.d.ts +3 -2
  67. package/dist/events/index.js +69 -16
  68. package/dist/events-aIO7vsIk.d.ts +230 -0
  69. package/dist/executor-BunPOO-k.d.ts +402 -0
  70. package/dist/{executor-B26lgbHD.d.ts → executor-Cghdstwu.d.ts} +1 -2
  71. package/dist/{executor-C57jx2pn.js → executor-XycESQPH.js} +3 -3
  72. package/dist/hono/index.d.ts +27 -23
  73. package/dist/hono/index.js +46 -11
  74. package/dist/impersonation-VfaGc4Q_.js +41 -0
  75. package/dist/{index-BNsABq7P.d.ts → index-BGdgBPdf.d.ts} +66 -25
  76. package/dist/{index-qriRM9cZ.d.ts → index-C7f8cOBM.d.ts} +4 -4
  77. package/dist/{index-Dy6kuF0P.d.ts → index-CLg87OBW.d.ts} +6 -1
  78. package/dist/{index-GV77NpRP.d.ts → index-DOESKG27.d.ts} +34 -9
  79. package/dist/{index-CriFFJ_p.d.ts → index-DgILJRXj.d.ts} +43 -17
  80. package/dist/{index-UJ-LUxtI.d.ts → index-DmXSxbLi.d.ts} +14 -14
  81. package/dist/{index-Cf-D2jk1.d.ts → index-Dxn2a5cw.d.ts} +2 -2
  82. package/dist/{index-CTTMS6_1.d.ts → index-SaTsEfGP.d.ts} +86 -9
  83. package/dist/{index-CADlNHK-.d.ts → index-fag8VJZ2.d.ts} +5 -6
  84. package/dist/index.d.ts +34 -17
  85. package/dist/index.js +15 -14
  86. package/dist/jobs/index.d.ts +147 -95
  87. package/dist/jobs/index.js +773 -250
  88. package/dist/{json-schema--aOXAGfU.js → json-schema-m1fiwKgN.js} +23 -2
  89. package/dist/kit-events-CTFDfSwb.js +60 -0
  90. package/dist/kit-transport-B82k1djG.d.ts +33 -0
  91. package/dist/kit-transport-DJlHQUBU.js +55 -0
  92. package/dist/{config-Be4GwsqI.js → kits-CGLLrNPd.js} +38 -10
  93. package/dist/list/index.d.ts +1 -1
  94. package/dist/list/index.js +3 -3
  95. package/dist/{live-D8rV1Ldw.js → live-CdJBOc4f.js} +13 -9
  96. package/dist/{live-QE3UPbGm.d.ts → live-D9W2M5aO.d.ts} +16 -15
  97. package/dist/lookup-BaP5bI27.js +18 -0
  98. package/dist/mcp/index.d.ts +39 -22
  99. package/dist/mcp/index.js +39 -25
  100. package/dist/{mfa-x3V3vGh-.d.ts → mfa-CusDgPaD.d.ts} +5 -1
  101. package/dist/next/image/index.js +6 -3
  102. package/dist/next/index.d.ts +60 -36
  103. package/dist/next/index.js +167 -50
  104. package/dist/notifications/index.d.ts +162 -0
  105. package/dist/notifications/index.js +264 -0
  106. package/dist/openapi/index.d.ts +1 -1
  107. package/dist/openapi/index.js +6 -6
  108. package/dist/orgs/index.d.ts +132 -0
  109. package/dist/orgs/index.js +245 -0
  110. package/dist/orpc/index.d.ts +37 -20
  111. package/dist/orpc/index.js +24 -11
  112. package/dist/otel/index.d.ts +24 -5
  113. package/dist/otel/index.js +124 -64
  114. package/dist/permdock-sql-C2Inc6Xc.d.ts +29 -0
  115. package/dist/permdock-sql-DgOfeixi.js +51 -0
  116. package/dist/plugin-DwARMuQp.js +59 -0
  117. package/dist/{plugin-DVRB8qNE.d.ts → plugin-o144f322.d.ts} +17 -5
  118. package/dist/plugins/actor/index.d.ts +5 -2
  119. package/dist/plugins/actor/index.js +22 -8
  120. package/dist/plugins/rules/index.d.ts +2 -2
  121. package/dist/plugins/rules/index.js +52 -17
  122. package/dist/plugins/soft-delete/index.d.ts +3 -3
  123. package/dist/plugins/soft-delete/index.js +14 -5
  124. package/dist/plugins/tenant/index.d.ts +3 -3
  125. package/dist/plugins/tenant/index.js +45 -21
  126. package/dist/plugins/timestamps/index.d.ts +1 -1
  127. package/dist/plugins/timestamps/index.js +4 -3
  128. package/dist/plugins/validation/index.d.ts +2 -2
  129. package/dist/plugins/validation/index.js +26 -6
  130. package/dist/{pool-B9qsLyFR.d.ts → pool-C9voM4Kw.d.ts} +24 -6
  131. package/dist/postgres/index.d.ts +5 -5
  132. package/dist/postgres/index.js +20 -10
  133. package/dist/{postgrest-DmBlevh-.d.ts → postgrest-CruYKAvV.d.ts} +1 -1
  134. package/dist/{problem-CGt1ADix.js → problem-BW1KhzSG.js} +2 -1
  135. package/dist/{problem-Z2RTQjZD.d.ts → problem-Blxe8S31.d.ts} +1 -1
  136. package/dist/query/index.d.ts +2 -2
  137. package/dist/query/index.js +3 -3
  138. package/dist/{query-BdKLTPlj.js → query-CCbo44lG.js} +18 -18
  139. package/dist/queue-BYUCjr59.d.ts +252 -0
  140. package/dist/react/index.d.ts +8 -7
  141. package/dist/react/index.js +4 -3
  142. package/dist/react/server.d.ts +16 -5
  143. package/dist/react/server.js +12 -4
  144. package/dist/react/session.d.ts +10 -4
  145. package/dist/react/session.js +11 -3
  146. package/dist/{react-DNBYNazu.js → react-C_s7kiNz.js} +138 -46
  147. package/dist/{read-set-D0qBcboM.js → read-set-DQFt1N_Y.js} +71 -15
  148. package/dist/realtime/index.d.ts +3 -3
  149. package/dist/realtime/index.js +14 -12
  150. package/dist/{index-kpgopLNv.d.ts → refresh-BTrQ_ZTp.d.ts} +6 -3
  151. package/dist/{resolve-BRciX2jK.d.ts → resolve-C15TJq77.d.ts} +22 -11
  152. package/dist/{resource-D30rp0GN.js → resource-CRxxwkQq.js} +24 -9
  153. package/dist/{resource-CwVgiI5q.d.ts → resource-D8iqWMr-.d.ts} +6 -6
  154. package/dist/respond-D2OQl62-.d.ts +371 -0
  155. package/dist/{result-DWXatkd6.js → result-BKiliPY7.js} +2 -2
  156. package/dist/server/index.d.ts +17 -15
  157. package/dist/server/index.js +16 -22
  158. package/dist/server-Csx2sRjg.js +655 -0
  159. package/dist/{shared-_6-uPL7E.js → shared-D1dRUrAg.js} +19 -4
  160. package/dist/signers-Cc6r98SW.d.ts +162 -0
  161. package/dist/{simplify-LakqK1vH.js → simplify-_0oUKqCd.js} +27 -95
  162. package/dist/sql/index.d.ts +247 -10
  163. package/dist/sql/index.js +8591 -780
  164. package/dist/{sql-BIgk12ew.js → sql-Coh6n4cC.js} +11 -3
  165. package/dist/ssr/index.d.ts +4 -4
  166. package/dist/ssr/index.js +1 -2
  167. package/dist/{standard-FUi3Z3-7.js → standard-D7r6dMUu.js} +6 -3
  168. package/dist/{standard-eudBiRk1.d.ts → standard-J4YgMOLm.d.ts} +1 -1
  169. package/dist/{stats-D78UQ8Yu.js → stats-Bqob3WqT.js} +34 -9
  170. package/dist/storage/index.d.ts +4 -4
  171. package/dist/storage/index.js +174 -27
  172. package/dist/support-DT-OhSxD.d.ts +88 -0
  173. package/dist/support-Dk6vdmEw.js +291 -0
  174. package/dist/support-view-Da5peKFg.d.ts +17 -0
  175. package/dist/support-view-g4YLBeqX.js +18 -0
  176. package/dist/{tables-Dhha3BXM.js → tables-Cb-3_VWc.js} +14 -0
  177. package/dist/{permdock-sql-Bta_G0v-.js → template-TxrGI5jR.js} +2 -31
  178. package/dist/{temporal-BwshQk0W.js → temporal-CVlIV2DZ.js} +17 -4
  179. package/dist/{temporal-required-CMxeSVPo.js → temporal-required-CAt4Dc9Z.js} +2 -2
  180. package/dist/testing/index.d.ts +113 -24
  181. package/dist/testing/index.js +375 -47
  182. package/dist/{types-DONv9SHN.d.ts → types-Dnf_wRDz.d.ts} +26 -3
  183. package/dist/user-change-CHFEUro1.js +49 -0
  184. package/dist/{webhooks-CAIFQSTM.js → verify-Ce49ETPz.js} +38 -19
  185. package/dist/{version-BfpG2gks.js → version-DM_Roffb.js} +1 -1
  186. package/dist/{view-3Po_PVlr.d.ts → view-BEf0diC5.d.ts} +10 -5
  187. package/dist/{mfa-BxoR1RkN.js → view-Bq64ki8-.js} +210 -64
  188. package/dist/webhooks/index.d.ts +136 -5
  189. package/dist/webhooks/index.js +588 -2
  190. package/dist/wire-D2dT86vi.js +35 -0
  191. package/package.json +50 -10
  192. package/schemas/config-v1.json +131 -1
  193. package/schemas/doctor-report-v1.json +10 -0
  194. package/skills/better-supabase/SKILL.md +60 -10
  195. package/skills/better-supabase/references/plugins.md +13 -14
  196. package/skills/better-supabase/references/troubleshooting.md +25 -4
  197. package/skills/better-supabase-api/SKILL.md +74 -19
  198. package/skills/better-supabase-api/references/adapters.md +48 -27
  199. package/skills/better-supabase-auth/SKILL.md +149 -0
  200. package/skills/better-supabase-auth/references/permdock.md +100 -0
  201. package/skills/better-supabase-testing/SKILL.md +58 -4
  202. package/dist/claims-CoTobNC-.js +0 -25
  203. package/dist/events-Do8jXh4D.d.ts +0 -84
  204. package/dist/executor-DynmIWhq.d.ts +0 -195
  205. package/dist/invalidate-DbxuL2jL.js +0 -18
  206. package/dist/permdock-sql-DImJULY-.d.ts +0 -17
  207. package/dist/plugin-BqR6wKMB.js +0 -23
  208. package/dist/respond-C44EZAJo.js +0 -399
  209. package/dist/respond-DAkPIdTH.d.ts +0 -175
  210. package/dist/result-c2OYgCV2.d.ts +0 -203
  211. package/dist/view-wZ-9qIUz.js +0 -45
@@ -1,15 +1,18 @@
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";
3
- import { n as nowInstant } from "./temporal-BwshQk0W.js";
1
+ import { t as cloneValue } from "./clone-BfgmLU90.js";
2
+ import { n as dbError, t as DbException } from "./errors-DtwXa6b8.js";
3
+ import { a as invalidRequest, r as builderFor } from "./simplify-_0oUKqCd.js";
4
+ import { t as lookupOf } from "./lookup-BaP5bI27.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 { n as nowInstant } from "./temporal-CVlIV2DZ.js";
7
+ import { n as decodeValue, r as encodeValue, t as decodeScalar } from "./wire-D2dT86vi.js";
8
+ import { i as ok, n as err, o as toDbError, s as withErrorMapper, t as AsyncResult } from "./result-BKiliPY7.js";
9
+ import { t as invalidationTargets } from "./tables-Cb-3_VWc.js";
7
10
  import { t as consoleLogger } from "./logger-glM28NVN.js";
8
- 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";
11
+ import { i as orderPlugins, s as tenantOf } from "./plugin-DwARMuQp.js";
12
+ import { a as postgrestExecutor, n as StatsRecorder, r as recordStats } from "./stats-Bqob3WqT.js";
13
+ 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-DQFt1N_Y.js";
14
+ import { a as toBase64Url, t as base64ToText } from "./base64-D2gsioCY.js";
15
+ import { t as validate } from "./standard-D7r6dMUu.js";
13
16
  //#region src/core/batch.ts
14
17
  /**
15
18
  * Holds operations until each of `readers` has either submitted one or
@@ -60,8 +63,9 @@ function rowKey(table, row) {
60
63
  }
61
64
  function cacheTargetOf(meta, notice) {
62
65
  const table = meta.tables[notice.table];
63
- const ids = table ? [...new Set(notice.rows.flatMap((row) => rowKey(table, row) ?? []))] : [];
64
- const tenant = notice.context.tenant;
66
+ const rows = notice.rows.length > 0 ? notice.rows : notice.keys ?? [];
67
+ const ids = table ? [...new Set(rows.flatMap((row) => rowKey(table, row) ?? []))] : [];
68
+ const tenant = notice.tenant ?? notice.context.tenant;
65
69
  return {
66
70
  table: notice.table,
67
71
  tables: notice.kind === "delete" ? invalidationTargets(meta, notice.table) : [notice.table],
@@ -95,12 +99,19 @@ function memoryCache() {
95
99
  }
96
100
  //#endregion
97
101
  //#region src/core/events.ts
102
+ /** More handlers than this for one event usually means a missing unsubscribe. */
103
+ const HANDLER_WARNING = 50;
104
+ function isDevelopment() {
105
+ return typeof process !== "undefined" && process.env["NODE_ENV"] !== "production";
106
+ }
98
107
  /**
99
108
  * Observers only: a handler that throws is reported to the logger and never
100
109
  * changes a result.
101
110
  */
102
111
  var EventHub = class {
103
112
  #handlers = /* @__PURE__ */ new Map();
113
+ #warned = /* @__PURE__ */ new Set();
114
+ #pending = /* @__PURE__ */ new Set();
104
115
  logger;
105
116
  constructor(logger = consoleLogger) {
106
117
  this.logger = logger;
@@ -112,6 +123,10 @@ var EventHub = class {
112
123
  this.#handlers.set(name, set);
113
124
  }
114
125
  set.add(handler);
126
+ if (set.size > HANDLER_WARNING && !this.#warned.has(name) && isDevelopment()) {
127
+ this.#warned.add(name);
128
+ this.logger.warn(`${set.size} "${name}" handlers are registered; call the function on() returns to remove one you no longer need`);
129
+ }
115
130
  return () => {
116
131
  set.delete(handler);
117
132
  };
@@ -119,6 +134,24 @@ var EventHub = class {
119
134
  has(name) {
120
135
  return (this.#handlers.get(name)?.size ?? 0) > 0;
121
136
  }
137
+ /**
138
+ * Keeps async work a handler started (a sink send) until it settles, so
139
+ * adapters can hand it to `after()` or `waitUntil`. Its outcome is ignored:
140
+ * handlers report their own failures.
141
+ */
142
+ track(work) {
143
+ const settled = Promise.resolve(work).then(() => void 0, () => void 0);
144
+ this.#pending.add(settled);
145
+ settled.then(() => this.#pending.delete(settled));
146
+ }
147
+ /** Whether tracked work is still running. */
148
+ get pending() {
149
+ return this.#pending.size > 0;
150
+ }
151
+ /** Resolves once the work tracked so far has settled. Never rejects. */
152
+ async settled() {
153
+ await Promise.all(this.#pending);
154
+ }
122
155
  emit(name, event) {
123
156
  const set = this.#handlers.get(name);
124
157
  if (!set) return;
@@ -130,6 +163,99 @@ var EventHub = class {
130
163
  }
131
164
  };
132
165
  //#endregion
166
+ //#region src/ir/codec.ts
167
+ /** Whether decoding `selection` changes anything, so plain reads skip the walk. */
168
+ function needsDecoding(selection) {
169
+ let needs = decodes.get(selection);
170
+ if (needs === void 0) {
171
+ needs = computeNeedsDecoding(selection);
172
+ decodes.set(selection, needs);
173
+ }
174
+ return needs;
175
+ }
176
+ const decodes = /* @__PURE__ */ new WeakMap();
177
+ function computeNeedsDecoding(selection) {
178
+ return selection.aggregate !== void 0 || selection.columns.some((column) => column.codec !== void 0) || selection.includes.some((include) => include.count !== void 0 || include.aggregate !== void 0 || needsDecoding(include.selection));
179
+ }
180
+ /** PostgREST returns `[{ count }]` for a `(count)` embed; SQL returns the number. */
181
+ function countOf(value) {
182
+ if (Array.isArray(value)) {
183
+ const first = value[0];
184
+ return Number(first?.count ?? 0);
185
+ }
186
+ return Number(value ?? 0);
187
+ }
188
+ function measureOf(measure, value) {
189
+ if (value === void 0 || value === null) return null;
190
+ return measure.codec ? decodeScalar(measure.codec, value) : value;
191
+ }
192
+ /** Adds `value` under `row[key][name]`, next to what is already there. */
193
+ function fold(row, folded, key, name, value) {
194
+ let target = folded.get(key);
195
+ if (!target) {
196
+ target = { ...row[key] };
197
+ folded.set(key, target);
198
+ row[key] = target;
199
+ }
200
+ target[name] = value;
201
+ }
202
+ const droppedKeys = /* @__PURE__ */ new WeakMap();
203
+ /** Wire keys that decoding folds into `_count`, `_sum` and the rest. */
204
+ function droppedOf(selection) {
205
+ let dropped = droppedKeys.get(selection);
206
+ if (!dropped) {
207
+ dropped = /* @__PURE__ */ new Set([...selection.aggregate?.measures.map((measure) => measure.key) ?? [], ...selection.includes.filter((include) => include.count !== void 0 || include.aggregate !== void 0).map((include) => include.alias)]);
208
+ droppedKeys.set(selection, dropped);
209
+ }
210
+ return dropped;
211
+ }
212
+ function decodeRow(selection, row) {
213
+ const dropped = droppedOf(selection);
214
+ const out = {};
215
+ for (const key in row) if (!dropped.has(key) && Object.hasOwn(row, key)) out[key] = row[key];
216
+ const folded = /* @__PURE__ */ new Map();
217
+ for (const column of selection.columns) {
218
+ if (!column.codec || !(column.alias in out)) continue;
219
+ try {
220
+ out[column.alias] = decodeValue(column.codec, out[column.alias]);
221
+ } catch (cause) {
222
+ if (!(cause instanceof DbException) || cause.error.kind !== "invalid_value") throw cause;
223
+ throw new DbException(dbError("invalid_value", `${column.alias}: ${cause.error.message}`, { column: column.alias }));
224
+ }
225
+ }
226
+ if (selection.aggregate) {
227
+ if (selection.aggregate.count) out["_count"] = countOf(out["_count"]);
228
+ for (const measure of selection.aggregate.measures) {
229
+ const value = row[measure.key];
230
+ fold(out, folded, `_${measure.fn}`, measure.alias, measureOf(measure, value));
231
+ }
232
+ }
233
+ for (const include of selection.includes) {
234
+ const value = row[include.alias];
235
+ if (include.count !== void 0) {
236
+ if (!(include.alias in row)) continue;
237
+ fold(out, folded, "_count", include.count, countOf(value));
238
+ continue;
239
+ }
240
+ if (include.aggregate !== void 0) {
241
+ if (!(include.alias in row)) continue;
242
+ const inner = Array.isArray(value) ? value[0] : value;
243
+ const measures = {};
244
+ for (const measure of include.selection.aggregate?.measures ?? []) measures[measure.alias] = measureOf(measure, inner?.[measure.key]);
245
+ fold(out, folded, `_${include.aggregate.fn}`, include.aggregate.name, measures);
246
+ continue;
247
+ }
248
+ if (Array.isArray(value)) out[include.alias] = value.map((item) => decodeRow(include.selection, item));
249
+ else if (value && typeof value === "object") out[include.alias] = decodeRow(include.selection, value);
250
+ }
251
+ return out;
252
+ }
253
+ /** Applies column codecs to returned rows, including embedded relations. */
254
+ function decodeRows(selection, rows, checked = false) {
255
+ if (!selection || !checked && !needsDecoding(selection)) return rows;
256
+ return rows.map((row) => decodeRow(selection, row));
257
+ }
258
+ //#endregion
133
259
  //#region src/core/cursor.ts
134
260
  /** Opaque, URL-safe keyset cursor. */
135
261
  function encodeCursor(values) {
@@ -170,16 +296,69 @@ const KNOWN_KEYS = /* @__PURE__ */ new Set([
170
296
  "_min",
171
297
  "_max"
172
298
  ]);
299
+ const NO_OPTIONS = Object.freeze({});
173
300
  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;
301
+ if (!args) return NO_OPTIONS;
302
+ let options;
303
+ for (const key in args) if (!KNOWN_KEYS.has(key) && Object.hasOwn(args, key)) (options ??= {})[key] = args[key];
304
+ return options ?? NO_OPTIONS;
305
+ }
306
+ const hooksByPlugins = /* @__PURE__ */ new WeakMap();
307
+ function hooksOf(plugins) {
308
+ let hooks = hooksByPlugins.get(plugins);
309
+ if (!hooks) {
310
+ hooks = {
311
+ transformQuery: plugins.filter((plugin) => plugin.transformQuery),
312
+ beforeMutation: plugins.filter((plugin) => plugin.beforeMutation),
313
+ afterMutation: plugins.filter((plugin) => plugin.afterMutation)
314
+ };
315
+ hooksByPlugins.set(plugins, hooks);
316
+ }
317
+ return hooks;
318
+ }
319
+ function isThenable(value) {
320
+ return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
178
321
  }
179
322
  function signalOf(args) {
180
323
  const signal = args?.["signal"];
181
324
  return signal instanceof AbortSignal ? signal : void 0;
182
325
  }
326
+ function cloneRows(rows) {
327
+ return rows.map((row) => cloneValue(row));
328
+ }
329
+ /** App-cased primary keys of `rows`, or of a `where` that pins the primary key. */
330
+ function keysOf(op, rows) {
331
+ const { primaryKey } = op.table;
332
+ if (primaryKey.length === 0) return void 0;
333
+ const fromRows = rows.flatMap((row) => primaryKey.every((name) => row[name] !== void 0 && row[name] !== null) ? [Object.fromEntries(primaryKey.map((name) => [name, row[name]]))] : []);
334
+ if (fromRows.length > 0) return fromRows;
335
+ if (op.kind === "insert") return void 0;
336
+ return keysFromWhere(op.table, op.where);
337
+ }
338
+ function keysFromWhere(table, where) {
339
+ if (!where) return void 0;
340
+ const { byDb } = lookupOf(table);
341
+ const pinned = /* @__PURE__ */ new Map();
342
+ const items = [];
343
+ const flatten = (condition) => {
344
+ if (condition.kind === "and") condition.items.forEach(flatten);
345
+ else items.push(condition);
346
+ };
347
+ flatten(where);
348
+ for (const item of items) {
349
+ if (item.kind !== "column") continue;
350
+ const app = byDb.get(item.column)?.[0];
351
+ if (app === void 0 || !table.primaryKey.includes(app)) continue;
352
+ if (item.op === "eq") pinned.set(app, [item.value]);
353
+ else if (item.op === "in" && Array.isArray(item.value)) pinned.set(app, item.value);
354
+ }
355
+ if (pinned.size !== table.primaryKey.length) return void 0;
356
+ const [first, ...rest] = table.primaryKey;
357
+ if (first === void 0) return void 0;
358
+ if (rest.length === 0) return (pinned.get(first) ?? []).map((value) => ({ [first]: value }));
359
+ if ([...pinned.values()].some((values) => values.length !== 1)) return void 0;
360
+ return [Object.fromEntries(table.primaryKey.map((name) => [name, pinned.get(name)?.[0]]))];
361
+ }
183
362
  function mutationKind(op) {
184
363
  if (op.kind === "insert") return op.onConflict ? "upsert" : "insert";
185
364
  return op.kind;
@@ -187,8 +366,10 @@ function mutationKind(op) {
187
366
  /** Runs operations through plugins, the executor and the event hub. */
188
367
  var OperationRunner = class {
189
368
  runtime;
369
+ #hooks;
190
370
  constructor(runtime) {
191
371
  this.runtime = runtime;
372
+ this.#hooks = hooksOf(runtime.plugins);
192
373
  }
193
374
  hookArgs(table, options) {
194
375
  return {
@@ -201,20 +382,27 @@ var OperationRunner = class {
201
382
  }
202
383
  async run(op, options, signal) {
203
384
  const { runtime } = this;
204
- const hook = this.hookArgs(op.table, options);
385
+ const hooks = this.#hooks;
205
386
  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;
387
+ if (hooks.transformQuery.length > 0 || hooks.beforeMutation.length > 0) {
388
+ const hook = this.hookArgs(op.table, options);
389
+ try {
390
+ for (const plugin of hooks.transformQuery) current = plugin.transformQuery(current, hook);
391
+ if (current.kind !== "select") {
392
+ let mutation = current;
393
+ for (const plugin of hooks.beforeMutation) {
394
+ const next = plugin.beforeMutation(mutation, hook);
395
+ mutation = isThenable(next) ? await next : next;
396
+ }
397
+ current = mutation;
398
+ }
399
+ } catch (cause) {
400
+ if (cause instanceof DbException) return this.fail(op.table, cause.error);
401
+ throw cause;
212
402
  }
213
- } catch (cause) {
214
- if (cause instanceof DbException) return this.fail(op.table, cause.error);
215
- throw cause;
216
403
  }
217
- const started = performance.now();
404
+ const timed = runtime.events.has("query");
405
+ const started = timed ? performance.now() : 0;
218
406
  const context = signal ? {
219
407
  signal,
220
408
  errorMappers: runtime.errorMappers
@@ -225,7 +413,7 @@ var OperationRunner = class {
225
413
  if (executed.ok && selection && needsDecoding(selection)) try {
226
414
  result = ok({
227
415
  ...executed.data,
228
- rows: decodeRows(selection, executed.data.rows)
416
+ rows: decodeRows(selection, executed.data.rows, true)
229
417
  });
230
418
  } catch (cause) {
231
419
  if (cause instanceof DbException) return this.fail(op.table, cause.error);
@@ -240,7 +428,7 @@ var OperationRunner = class {
240
428
  maxRows: runtime.maxRows
241
429
  });
242
430
  }
243
- if (runtime.events.has("query")) runtime.events.emit("query", {
431
+ if (timed) runtime.events.emit("query", {
244
432
  table: op.table.key,
245
433
  operation: current.kind,
246
434
  ok: result.ok,
@@ -249,29 +437,40 @@ var OperationRunner = class {
249
437
  truncated
250
438
  });
251
439
  if (!result.ok) return this.fail(op.table, result.error);
252
- if (current.kind !== "select") await this.afterMutation(current, result.data);
440
+ if (current.kind !== "select") await this.afterMutation(op, current, result.data);
253
441
  return result;
254
442
  }
255
- async afterMutation(op, data) {
443
+ async afterMutation(requested, op, data) {
256
444
  const { runtime } = this;
445
+ const plugins = this.#hooks.afterMutation;
446
+ const listening = runtime.events.has("mutation");
447
+ if (plugins.length === 0 && !listening) return;
448
+ const kind = mutationKind(op);
449
+ const rows = cloneRows(data.rows);
450
+ const keys = keysOf(op, rows);
451
+ const tenant = tenantOf(runtime.context);
257
452
  const event = {
258
453
  table: op.table,
259
- kind: mutationKind(op),
260
- rows: data.rows,
454
+ kind,
455
+ intent: requested.kind === "delete" && op.kind === "update" ? "softDelete" : kind,
456
+ rows,
457
+ ...keys ? { keys } : {},
458
+ ...tenant === void 0 ? {} : { tenant },
261
459
  context: runtime.context
262
460
  };
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
- }
461
+ for (const plugin of plugins) try {
462
+ const pending = plugin.afterMutation(event);
463
+ if (isThenable(pending)) await pending;
464
+ } catch (cause) {
465
+ runtime.events.logger.error(`plugin "${plugin.name}" afterMutation threw`, { cause });
270
466
  }
271
- runtime.events.emit("mutation", {
467
+ if (listening) runtime.events.emit("mutation", {
272
468
  table: op.table.key,
273
469
  kind: event.kind,
470
+ ...event.intent ? { intent: event.intent } : {},
274
471
  rows: event.rows,
472
+ ...event.keys ? { keys: event.keys } : {},
473
+ ...event.tenant === void 0 ? {} : { tenant: event.tenant },
275
474
  context: event.context
276
475
  });
277
476
  }
@@ -280,16 +479,27 @@ var OperationRunner = class {
280
479
  ...error,
281
480
  table: table.key
282
481
  };
283
- this.runtime.events.emit("error", {
482
+ if (this.runtime.events.has("error")) this.runtime.events.emit("error", {
284
483
  table: table.key,
285
484
  error: withTable
286
485
  });
287
486
  return err(withTable);
288
487
  }
289
488
  };
489
+ const sensitiveByTable = /* @__PURE__ */ new WeakMap();
490
+ /** Database names of the table's `config.sensitive` columns. */
491
+ function sensitiveColumnsOf(table) {
492
+ let sensitive = sensitiveByTable.get(table);
493
+ if (!sensitive) {
494
+ sensitive = new Set(lookupOf(table).entries.filter(([, column]) => column.sensitive).map(([, column]) => column.db));
495
+ sensitiveByTable.set(table, sensitive);
496
+ }
497
+ return sensitive;
498
+ }
290
499
  /** Creates the runtime repository object for one table. */
291
500
  function createRepository(runner, table) {
292
501
  const { builder } = runner.runtime;
502
+ const sensitiveDb = sensitiveColumnsOf(table);
293
503
  const selection = (args) => builder.selection(table, args?.["select"], args?.["include"]);
294
504
  const selectOp = (args, patch = {}) => ({
295
505
  kind: "select",
@@ -304,21 +514,34 @@ function createRepository(runner, table) {
304
514
  single: void 0,
305
515
  ...patch
306
516
  });
307
- const returning = (args) => args?.["returning"] === false ? void 0 : selection(args);
517
+ /** Writes return every column but the sensitive ones unless they are asked for. */
518
+ const returning = (args) => {
519
+ if (args?.["returning"] === false) return void 0;
520
+ const all = selection(args);
521
+ if (args?.["select"] !== void 0 || args?.["sensitive"] === true) return all;
522
+ const columns = all.columns.filter((entry) => !sensitiveDb.has(entry.column));
523
+ return columns.length === all.columns.length ? all : {
524
+ ...all,
525
+ columns
526
+ };
527
+ };
308
528
  const run = (op, args) => runner.run(op, optionsOf(args), signalOf(args));
529
+ let primaryOrder;
530
+ /** `primaryKey` holds app names; order terms take database names. */
531
+ const defaultOrder = () => primaryOrder ??= table.primaryKey.map((name) => ({
532
+ column: builder.column(table, name),
533
+ direction: "asc"
534
+ }));
309
535
  const notFound = () => runner.fail(table, dbError("not_found", `No ${table.key} row matched`));
310
536
  const base = {
311
- $table: table.key,
537
+ $tableName: table.key,
312
538
  $meta: table,
313
539
  findMany(args) {
314
540
  return AsyncResult.from(async () => {
315
541
  const op = selectOp(args);
316
542
  const ordered = op.orderBy.length > 0 || table.primaryKey.length === 0 ? op : {
317
543
  ...op,
318
- orderBy: table.primaryKey.map((column) => ({
319
- column,
320
- direction: "asc"
321
- }))
544
+ orderBy: defaultOrder()
322
545
  };
323
546
  const result = await run(ordered, args);
324
547
  return result.ok ? ok(result.data.rows) : result;
@@ -538,8 +761,11 @@ function createRepository(runner, table) {
538
761
  const number = args["page"] === void 0 ? 1 : Number(args["page"]);
539
762
  if (!Number.isInteger(number) || number < 1) return runner.fail(table, dbError("invalid_request", "\"page\" must be a positive integer"));
540
763
  const count = args["count"];
764
+ const orderBy = builder.orderBy(table, args["orderBy"]);
541
765
  const op = selectOp(args, {
766
+ orderBy: orderBy.length > 0 || table.primaryKey.length === 0 ? orderBy : defaultOrder(),
542
767
  limit: size + 1,
768
+ lookAhead: true,
543
769
  offset: (number - 1) * size,
544
770
  count: count === "exact" || count === "planned" || count === "estimated" ? count : void 0
545
771
  });
@@ -567,13 +793,15 @@ function createRepository(runner, table) {
567
793
  if (typeof args["after"] === "string") {
568
794
  const values = decodeCursor(args["after"]);
569
795
  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);
796
+ const { byDb } = lookupOf(table);
797
+ after = keysetCondition(orderBy, values, (name) => byDb.get(name)?.[1].nullable ?? true);
571
798
  }
572
799
  const op = selectOp(args, {
573
800
  selection: withSort,
574
801
  where: and(builder.where(table, args["where"]), after),
575
802
  orderBy,
576
803
  limit: size + 1,
804
+ lookAhead: true,
577
805
  offset: void 0
578
806
  });
579
807
  const result = await run(op, args);
@@ -581,7 +809,8 @@ function createRepository(runner, table) {
581
809
  const rows = result.data.rows.slice(0, size);
582
810
  const hasMore = result.data.rows.length > size;
583
811
  const last = rows.at(-1);
584
- const nextCursor = hasMore && last ? encodeCursor(orderBy.map((term) => encodeValue(last[aliasOf(withSort, term.column)]))) : null;
812
+ const aliases = new Map(withSort.columns.map((entry) => [entry.column, entry.alias]));
813
+ const nextCursor = hasMore && last ? encodeCursor(orderBy.map((term) => encodeValue(last[aliases.get(term.column) ?? term.column]))) : null;
585
814
  return ok({
586
815
  items: added.length > 0 ? rows.map((row) => strip(row, added)) : rows,
587
816
  nextCursor,
@@ -601,14 +830,17 @@ function createRepository(runner, table) {
601
830
  }
602
831
  function ensureColumns(base, orderBy) {
603
832
  const columns = [...base.columns];
833
+ const selected = new Set(columns.map((entry) => entry.column));
834
+ const { byDb } = lookupOf(table);
604
835
  const added = [];
605
836
  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;
837
+ if (selected.has(term.column)) continue;
838
+ const alias = byDb.get(term.column)?.[0] ?? term.column;
608
839
  columns.push({
609
840
  alias,
610
841
  column: term.column
611
842
  });
843
+ selected.add(term.column);
612
844
  added.push(alias);
613
845
  }
614
846
  return {
@@ -621,12 +853,9 @@ function createRepository(runner, table) {
621
853
  }
622
854
  return base;
623
855
  }
624
- function aliasOf(selection, column) {
625
- return selection.columns.find((entry) => entry.column === column)?.alias ?? column;
626
- }
627
856
  function strip(row, keys) {
628
- const copy = { ...row };
629
- for (const key of keys) delete copy[key];
857
+ const copy = {};
858
+ for (const key in row) if (!keys.includes(key) && Object.hasOwn(row, key)) copy[key] = row[key];
630
859
  return copy;
631
860
  }
632
861
  /**
@@ -644,13 +873,21 @@ function byKey(rows, columns) {
644
873
  const b = String(right);
645
874
  return a < b ? -1 : a > b ? 1 : 0;
646
875
  };
647
- return [...rows].sort((left, right) => {
648
- for (const column of columns) {
649
- const order = compare(left[column], right[column]);
876
+ const keyed = rows.map((row) => ({
877
+ row,
878
+ key: columns.map((column) => {
879
+ const value = row[column];
880
+ return typeof value === "object" && value !== null ? String(value) : value;
881
+ })
882
+ }));
883
+ keyed.sort((left, right) => {
884
+ for (let index = 0; index < columns.length; index++) {
885
+ const order = compare(left.key[index], right.key[index]);
650
886
  if (order !== 0) return order;
651
887
  }
652
888
  return 0;
653
889
  });
890
+ return keyed.map((entry) => entry.row);
654
891
  }
655
892
  /**
656
893
  * The rows after the cursor `values` in `orderBy` order, as an OR of ANDs:
@@ -716,7 +953,7 @@ var BetterSupabase = class BetterSupabase {
716
953
  * A token that fails it resolves to `{ kind: 'invalid', reason: 'claims' }`.
717
954
  *
718
955
  * ```ts
719
- * const sb = defineSupabase(schema).claims(z.object({ tenant_id: z.uuid() }));
956
+ * const betterSupabase = defineSupabase(schema).claims(z.object({ tenant_id: z.uuid() }));
720
957
  * ```
721
958
  */
722
959
  claims(schema) {
@@ -734,7 +971,7 @@ var BetterSupabase = class BetterSupabase {
734
971
  * the verified claims, never from `profile`.
735
972
  *
736
973
  * ```ts
737
- * const sb = defineSupabase(schema).userMetadata(z.object({ display_name: z.string() }));
974
+ * const betterSupabase = defineSupabase(schema).userMetadata(z.object({ display_name: z.string() }));
738
975
  * ```
739
976
  */
740
977
  userMetadata(schema) {
@@ -749,7 +986,7 @@ var BetterSupabase = class BetterSupabase {
749
986
  * Results themselves keep their `DbError`.
750
987
  *
751
988
  * ```ts
752
- * const sb = defineSupabase(schema).mapError((error) => new AppError(error));
989
+ * const betterSupabase = defineSupabase(schema).mapError((error) => new AppError(error));
753
990
  * ```
754
991
  */
755
992
  mapError(mapper) {
@@ -760,9 +997,14 @@ var BetterSupabase = class BetterSupabase {
760
997
  }
761
998
  #specs;
762
999
  #truncatedTables = /* @__PURE__ */ new Set();
1000
+ /** A hub without listeners, for decoding read-set results. */
1001
+ #decodeEvents;
1002
+ /** One getter per table, shared by every db this instance connects. */
1003
+ #tables;
1004
+ #errorMappers = /* @__PURE__ */ new WeakMap();
763
1005
  /**
764
1006
  * Builds serializable `QuerySpec`s with the same arguments and result types
765
- * as the repository: `sb.spec.customers.findMany({ select: ['id'] })`.
1007
+ * as the repository: `betterSupabase.spec.customers.findMany({ select: ['id'] })`.
766
1008
  */
767
1009
  get spec() {
768
1010
  this.#specs ??= createSpecs(this.meta);
@@ -783,7 +1025,7 @@ var BetterSupabase = class BetterSupabase {
783
1025
  * invalidate them after it succeeds.
784
1026
  *
785
1027
  * ```ts
786
- * sb.defineRpc('archive_customer', { invalidates: ['customers', 'notes'] });
1028
+ * betterSupabase.defineRpc('archive_customer', { invalidates: ['customers', 'notes'] });
787
1029
  * ```
788
1030
  */
789
1031
  defineRpc(name, options) {
@@ -842,10 +1084,10 @@ var BetterSupabase = class BetterSupabase {
842
1084
  let executor = base;
843
1085
  for (const plugin of plugins) if (plugin.wrapExecutor) executor = plugin.wrapExecutor(executor);
844
1086
  executor = recordStats(executor, recorder);
845
- const errorMappers = [...this.options.errors ?? [], ...plugins.flatMap((plugin) => plugin.mapError ? [plugin.mapError] : [])];
1087
+ const errorMappers = this.#errorMappersFor(plugins);
846
1088
  const runner = new OperationRunner({
847
1089
  meta: this.meta,
848
- builder: new IrBuilder(this.meta),
1090
+ builder: builderFor(this.meta),
849
1091
  executor,
850
1092
  plugins,
851
1093
  context,
@@ -855,7 +1097,13 @@ var BetterSupabase = class BetterSupabase {
855
1097
  maxRows: this.options.maxRows ?? 1e3,
856
1098
  truncatedTables: this.#truncatedTables
857
1099
  });
858
- const db = {
1100
+ const db = Object.create(this.#tablePrototype());
1101
+ dbStates.set(db, {
1102
+ runner,
1103
+ plugins,
1104
+ repositories: /* @__PURE__ */ new Map()
1105
+ });
1106
+ Object.assign(db, {
859
1107
  $client: client,
860
1108
  $executor: executor,
861
1109
  $context: context,
@@ -923,10 +1171,10 @@ var BetterSupabase = class BetterSupabase {
923
1171
  hint: `Add "${table.schema}.${table.name}" to vectorSearch in better-supabase.config.ts and run \`better-supabase sql sync\`.`
924
1172
  }) : result;
925
1173
  })
926
- };
1174
+ });
927
1175
  const many = (specs, signal, usePlugins) => AsyncResult.from(async () => {
928
1176
  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`));
1177
+ if (invalid !== -1) return err(dbError("invalid_request", `db.$many() entry ${invalid} is not a QuerySpec from betterSupabase.spec`));
930
1178
  const active = usePlugins ? plugins : [];
931
1179
  const batch = base.batch?.bind(base);
932
1180
  const batching = batch && specs.length > 1 ? batchingExecutor({
@@ -974,21 +1222,6 @@ var BetterSupabase = class BetterSupabase {
974
1222
  "$many",
975
1223
  "$search"
976
1224
  ]) 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
1225
  return db;
993
1226
  }
994
1227
  /**
@@ -1015,12 +1248,45 @@ var BetterSupabase = class BetterSupabase {
1015
1248
  count: entry.count ?? null
1016
1249
  }));
1017
1250
  }
1018
- }, context, [], new StatsRecorder(), new EventHub(this.events.logger)), spec, void 0);
1251
+ }, context, [], new StatsRecorder(), this.#decodeEvents ??= new EventHub(this.events.logger)), spec, void 0);
1019
1252
  if (!result.ok) return result;
1020
1253
  out[key] = result.data;
1021
1254
  }
1022
1255
  return ok(out);
1023
1256
  }
1257
+ #errorMappersFor(plugins) {
1258
+ let mappers = this.#errorMappers.get(plugins);
1259
+ if (!mappers) {
1260
+ mappers = [...this.options.errors ?? [], ...plugins.flatMap((plugin) => plugin.mapError ? [plugin.mapError] : [])];
1261
+ this.#errorMappers.set(plugins, mappers);
1262
+ }
1263
+ return mappers;
1264
+ }
1265
+ #tablePrototype() {
1266
+ if (this.#tables) return this.#tables;
1267
+ const tables = {};
1268
+ const throwAs = this.options.throwAs;
1269
+ const build = (state, key) => {
1270
+ let repository = state.repositories.get(key);
1271
+ if (repository) return repository;
1272
+ const table = this.meta.tables[key];
1273
+ repository = this.#repository(state.runner, key, table, state.plugins);
1274
+ if (throwAs) {
1275
+ for (const [name, method] of Object.entries(repository)) if (typeof method === "function" && name !== "extend") repository[name] = mapThrows(method, throwAs);
1276
+ }
1277
+ state.repositories.set(key, repository);
1278
+ return repository;
1279
+ };
1280
+ for (const key of Object.keys(this.meta.tables)) Object.defineProperty(tables, key, {
1281
+ enumerable: true,
1282
+ get() {
1283
+ const state = dbStates.get(this);
1284
+ return state ? build(state, key) : void 0;
1285
+ }
1286
+ });
1287
+ this.#tables = tables;
1288
+ return tables;
1289
+ }
1024
1290
  #repository(runner, key, table, plugins) {
1025
1291
  const repository = createRepository(runner, table);
1026
1292
  for (const plugin of plugins) {
@@ -1041,6 +1307,7 @@ var BetterSupabase = class BetterSupabase {
1041
1307
  return repository;
1042
1308
  }
1043
1309
  };
1310
+ const dbStates = /* @__PURE__ */ new WeakMap();
1044
1311
  function mapThrows(method, throwAs) {
1045
1312
  return function(...args) {
1046
1313
  const value = method.apply(this, args);
@@ -1048,7 +1315,7 @@ function mapThrows(method, throwAs) {
1048
1315
  };
1049
1316
  }
1050
1317
  function runSpec(db, spec, signal) {
1051
- if (!isQuerySpec(spec)) return AsyncResult.err(dbError("invalid_request", "db.$run() expects a QuerySpec from sb.spec"));
1318
+ if (!isQuerySpec(spec)) return AsyncResult.err(dbError("invalid_request", "db.$run() expects a QuerySpec from betterSupabase.spec"));
1052
1319
  const method = db[spec.table]?.[spec.method];
1053
1320
  if (!method) return AsyncResult.err(dbError("invalid_request", `Unknown table "${spec.table}" in QuerySpec`));
1054
1321
  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,11 +1348,11 @@ function rpc(executor, errorMappers, name, rest) {
1081
1348
  * Creates the data-layer definition from the generated `schema`.
1082
1349
  *
1083
1350
  * ```ts
1084
- * export const sb = defineSupabase(schema).use(timestamps()).use(softDelete());
1351
+ * export const betterSupabase = defineSupabase(schema).use(timestamps()).use(softDelete());
1085
1352
  * ```
1086
1353
  */
1087
1354
  function defineSupabase(schema, options) {
1088
1355
  return new BetterSupabase(schema, [], options);
1089
1356
  }
1090
1357
  //#endregion
1091
- export { EventHub as a, rowKey as c, encodeCursor as i, rpcCacheTargets as l, defineSupabase as n, cacheTargetOf as o, decodeCursor as r, memoryCache as s, BetterSupabase as t };
1358
+ export { decodeRows as a, memoryCache as c, encodeCursor as i, rowKey as l, defineSupabase as n, EventHub as o, decodeCursor as r, cacheTargetOf as s, BetterSupabase as t, rpcCacheTargets as u };