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.
- package/README.md +9 -6
- package/bin/better-supabase.js +4 -0
- package/dist/{base64-CQeB1eSf.js → base64-D2gsioCY.js} +2 -2
- package/dist/channel-FQW-CnGR.d.ts +70 -0
- package/dist/claims-BAf6c7is.js +48 -0
- package/dist/cli/_format-Cax5EsM9.js +101 -0
- package/dist/cli/bin.d.ts +1 -0
- package/dist/cli/bin.js +48 -0
- package/dist/cli/codemod-DqV7612m.js +388 -0
- package/dist/cli/command-CK14fP91.js +502 -0
- package/dist/cli/compare-BmIWVEui.js +9 -0
- package/dist/cli/config-CPVpXP5I.js +1965 -0
- package/dist/cli/config-dtoekM0v.js +2 -0
- package/dist/cli/db-DOBPt660.js +52 -0
- package/dist/cli/diff-SjkO5rNr.js +752 -0
- package/dist/cli/dist-DBPdlPYK.js +1112 -0
- package/dist/cli/dist-DreZ24Sz.js +129 -0
- package/dist/cli/dist-xn4wlboc.js +732 -0
- package/dist/cli/doctor-L7Z8mYTx.js +2863 -0
- package/dist/cli/env-DmKv_G4A.js +108 -0
- package/dist/cli/env-jIFUADLY.js +511 -0
- package/dist/cli/errors-4v4DTtda.js +56 -0
- package/dist/cli/fastest-levenshtein-CFN-lNDv.js +120 -0
- package/dist/cli/gen-BJc9vdyu.js +2 -0
- package/dist/cli/gen-C2osRcr5.js +2204 -0
- package/dist/cli/index.d.ts +493 -0
- package/dist/cli/index.js +184 -0
- package/dist/cli/init-Saf5-R9T.js +550 -0
- package/dist/cli/introspect--H7oGmcd.js +81 -0
- package/dist/cli/io-k41_z-jT.js +24 -0
- package/dist/cli/json5-ChRycquj.js +787 -0
- package/dist/cli/jsonc-BUniUpOk.js +2 -0
- package/dist/cli/jsonc-D5Fu67jw.js +45 -0
- package/dist/cli/keys-ByKHnvP9.js +83 -0
- package/dist/cli/ohash-D7bG_EOb.js +277 -0
- package/dist/cli/openapi-Cp5Fyta-.js +66 -0
- package/dist/cli/project-B_GeXyHh.js +70 -0
- package/dist/cli/prompts-BNcNfAve.js +1142 -0
- package/dist/cli/prompts-BTYQGunc.js +2 -0
- package/dist/cli/read-sets-BfMQcKy8.js +289 -0
- package/dist/cli/rolldown-runtime-BPOCksWG.js +24 -0
- package/dist/cli/run-BKR2W_79.js +351 -0
- package/dist/cli/seed-CUlqWh-8.js +98 -0
- package/dist/cli/skills-LJJIhLFR.js +137 -0
- package/dist/cli/sql-DzPbfngx.js +348 -0
- package/dist/cli/style-C0NFdJRL.js +29 -0
- package/dist/cli/toml-DB-MNnFs.js +424 -0
- package/dist/cli/version-BDJuO0UR.js +4 -0
- package/dist/cli/yaml-CYCbJ6et.js +2252 -0
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.js +15 -22
- package/dist/clone-BfgmLU90.js +21 -0
- package/dist/{compiler-2zqGzxVZ.d.ts → compiler-BXH_aZ7N.d.ts} +2 -2
- package/dist/config/index.d.ts +4 -4
- package/dist/config/index.js +5 -5
- package/dist/{config-DOsGYSiT.js → config-DreERlKo.js} +1 -1
- package/dist/{config-C1CYcm6o.d.ts → config-T_ejwyd3.d.ts} +176 -11
- package/dist/{define-DUbydmJa.d.ts → define-Bp_BZa5h.d.ts} +37 -29
- package/dist/{define-DF341GDj.js → define-cNKzqwL_.js} +356 -89
- package/dist/edge/index.d.ts +20 -13
- package/dist/edge/index.js +55 -26
- package/dist/{entitlements-DvJXoI02.d.ts → entitlements-DUxU4oHe.d.ts} +2 -2
- package/dist/env/index.d.ts +2 -2
- package/dist/env/index.js +34 -1
- package/dist/{errors-Bl3rkOzo.js → errors-DtwXa6b8.js} +2 -1
- package/dist/events/index.d.ts +3 -2
- package/dist/events/index.js +69 -16
- package/dist/events-aIO7vsIk.d.ts +230 -0
- package/dist/executor-BunPOO-k.d.ts +402 -0
- package/dist/{executor-B26lgbHD.d.ts → executor-Cghdstwu.d.ts} +1 -2
- package/dist/{executor-C57jx2pn.js → executor-XycESQPH.js} +3 -3
- package/dist/hono/index.d.ts +27 -23
- package/dist/hono/index.js +46 -11
- package/dist/impersonation-VfaGc4Q_.js +41 -0
- package/dist/{index-BNsABq7P.d.ts → index-BGdgBPdf.d.ts} +66 -25
- package/dist/{index-qriRM9cZ.d.ts → index-C7f8cOBM.d.ts} +4 -4
- package/dist/{index-Dy6kuF0P.d.ts → index-CLg87OBW.d.ts} +6 -1
- package/dist/{index-GV77NpRP.d.ts → index-DOESKG27.d.ts} +34 -9
- package/dist/{index-CriFFJ_p.d.ts → index-DgILJRXj.d.ts} +43 -17
- package/dist/{index-UJ-LUxtI.d.ts → index-DmXSxbLi.d.ts} +14 -14
- package/dist/{index-Cf-D2jk1.d.ts → index-Dxn2a5cw.d.ts} +2 -2
- package/dist/{index-CTTMS6_1.d.ts → index-SaTsEfGP.d.ts} +86 -9
- package/dist/{index-CADlNHK-.d.ts → index-fag8VJZ2.d.ts} +5 -6
- package/dist/index.d.ts +34 -17
- package/dist/index.js +15 -14
- package/dist/jobs/index.d.ts +147 -95
- package/dist/jobs/index.js +773 -250
- package/dist/{json-schema--aOXAGfU.js → json-schema-m1fiwKgN.js} +23 -2
- package/dist/kit-events-CTFDfSwb.js +60 -0
- package/dist/kit-transport-B82k1djG.d.ts +33 -0
- package/dist/kit-transport-DJlHQUBU.js +55 -0
- package/dist/{config-Be4GwsqI.js → kits-CGLLrNPd.js} +38 -10
- package/dist/list/index.d.ts +1 -1
- package/dist/list/index.js +3 -3
- package/dist/{live-D8rV1Ldw.js → live-CdJBOc4f.js} +13 -9
- package/dist/{live-QE3UPbGm.d.ts → live-D9W2M5aO.d.ts} +16 -15
- package/dist/lookup-BaP5bI27.js +18 -0
- package/dist/mcp/index.d.ts +39 -22
- package/dist/mcp/index.js +39 -25
- package/dist/{mfa-x3V3vGh-.d.ts → mfa-CusDgPaD.d.ts} +5 -1
- package/dist/next/image/index.js +6 -3
- package/dist/next/index.d.ts +60 -36
- package/dist/next/index.js +167 -50
- package/dist/notifications/index.d.ts +162 -0
- package/dist/notifications/index.js +264 -0
- package/dist/openapi/index.d.ts +1 -1
- package/dist/openapi/index.js +6 -6
- package/dist/orgs/index.d.ts +132 -0
- package/dist/orgs/index.js +245 -0
- package/dist/orpc/index.d.ts +37 -20
- package/dist/orpc/index.js +24 -11
- package/dist/otel/index.d.ts +24 -5
- package/dist/otel/index.js +124 -64
- package/dist/permdock-sql-C2Inc6Xc.d.ts +29 -0
- package/dist/permdock-sql-DgOfeixi.js +51 -0
- package/dist/plugin-DwARMuQp.js +59 -0
- package/dist/{plugin-DVRB8qNE.d.ts → plugin-o144f322.d.ts} +17 -5
- package/dist/plugins/actor/index.d.ts +5 -2
- package/dist/plugins/actor/index.js +22 -8
- package/dist/plugins/rules/index.d.ts +2 -2
- package/dist/plugins/rules/index.js +52 -17
- package/dist/plugins/soft-delete/index.d.ts +3 -3
- package/dist/plugins/soft-delete/index.js +14 -5
- package/dist/plugins/tenant/index.d.ts +3 -3
- package/dist/plugins/tenant/index.js +45 -21
- package/dist/plugins/timestamps/index.d.ts +1 -1
- package/dist/plugins/timestamps/index.js +4 -3
- package/dist/plugins/validation/index.d.ts +2 -2
- package/dist/plugins/validation/index.js +26 -6
- package/dist/{pool-B9qsLyFR.d.ts → pool-C9voM4Kw.d.ts} +24 -6
- package/dist/postgres/index.d.ts +5 -5
- package/dist/postgres/index.js +20 -10
- package/dist/{postgrest-DmBlevh-.d.ts → postgrest-CruYKAvV.d.ts} +1 -1
- package/dist/{problem-CGt1ADix.js → problem-BW1KhzSG.js} +2 -1
- package/dist/{problem-Z2RTQjZD.d.ts → problem-Blxe8S31.d.ts} +1 -1
- package/dist/query/index.d.ts +2 -2
- package/dist/query/index.js +3 -3
- package/dist/{query-BdKLTPlj.js → query-CCbo44lG.js} +18 -18
- package/dist/queue-BYUCjr59.d.ts +252 -0
- package/dist/react/index.d.ts +8 -7
- package/dist/react/index.js +4 -3
- package/dist/react/server.d.ts +16 -5
- package/dist/react/server.js +12 -4
- package/dist/react/session.d.ts +10 -4
- package/dist/react/session.js +11 -3
- package/dist/{react-DNBYNazu.js → react-C_s7kiNz.js} +138 -46
- package/dist/{read-set-D0qBcboM.js → read-set-DQFt1N_Y.js} +71 -15
- package/dist/realtime/index.d.ts +3 -3
- package/dist/realtime/index.js +14 -12
- package/dist/{index-kpgopLNv.d.ts → refresh-BTrQ_ZTp.d.ts} +6 -3
- package/dist/{resolve-BRciX2jK.d.ts → resolve-C15TJq77.d.ts} +22 -11
- package/dist/{resource-D30rp0GN.js → resource-CRxxwkQq.js} +24 -9
- package/dist/{resource-CwVgiI5q.d.ts → resource-D8iqWMr-.d.ts} +6 -6
- package/dist/respond-D2OQl62-.d.ts +371 -0
- package/dist/{result-DWXatkd6.js → result-BKiliPY7.js} +2 -2
- package/dist/server/index.d.ts +17 -15
- package/dist/server/index.js +16 -22
- package/dist/server-Csx2sRjg.js +655 -0
- package/dist/{shared-_6-uPL7E.js → shared-D1dRUrAg.js} +19 -4
- package/dist/signers-Cc6r98SW.d.ts +162 -0
- package/dist/{simplify-LakqK1vH.js → simplify-_0oUKqCd.js} +27 -95
- package/dist/sql/index.d.ts +247 -10
- package/dist/sql/index.js +8591 -780
- package/dist/{sql-BIgk12ew.js → sql-Coh6n4cC.js} +11 -3
- package/dist/ssr/index.d.ts +4 -4
- package/dist/ssr/index.js +1 -2
- package/dist/{standard-FUi3Z3-7.js → standard-D7r6dMUu.js} +6 -3
- package/dist/{standard-eudBiRk1.d.ts → standard-J4YgMOLm.d.ts} +1 -1
- package/dist/{stats-D78UQ8Yu.js → stats-Bqob3WqT.js} +34 -9
- package/dist/storage/index.d.ts +4 -4
- package/dist/storage/index.js +174 -27
- package/dist/support-DT-OhSxD.d.ts +88 -0
- package/dist/support-Dk6vdmEw.js +291 -0
- package/dist/support-view-Da5peKFg.d.ts +17 -0
- package/dist/support-view-g4YLBeqX.js +18 -0
- package/dist/{tables-Dhha3BXM.js → tables-Cb-3_VWc.js} +14 -0
- package/dist/{permdock-sql-Bta_G0v-.js → template-TxrGI5jR.js} +2 -31
- package/dist/{temporal-BwshQk0W.js → temporal-CVlIV2DZ.js} +17 -4
- package/dist/{temporal-required-CMxeSVPo.js → temporal-required-CAt4Dc9Z.js} +2 -2
- package/dist/testing/index.d.ts +113 -24
- package/dist/testing/index.js +375 -47
- package/dist/{types-DONv9SHN.d.ts → types-Dnf_wRDz.d.ts} +26 -3
- package/dist/user-change-CHFEUro1.js +49 -0
- package/dist/{webhooks-CAIFQSTM.js → verify-Ce49ETPz.js} +38 -19
- package/dist/{version-BfpG2gks.js → version-DM_Roffb.js} +1 -1
- package/dist/{view-3Po_PVlr.d.ts → view-BEf0diC5.d.ts} +10 -5
- package/dist/{mfa-BxoR1RkN.js → view-Bq64ki8-.js} +210 -64
- package/dist/webhooks/index.d.ts +136 -5
- package/dist/webhooks/index.js +588 -2
- package/dist/wire-D2dT86vi.js +35 -0
- package/package.json +50 -10
- package/schemas/config-v1.json +131 -1
- package/schemas/doctor-report-v1.json +10 -0
- package/skills/better-supabase/SKILL.md +60 -10
- package/skills/better-supabase/references/plugins.md +13 -14
- package/skills/better-supabase/references/troubleshooting.md +25 -4
- package/skills/better-supabase-api/SKILL.md +74 -19
- package/skills/better-supabase-api/references/adapters.md +48 -27
- package/skills/better-supabase-auth/SKILL.md +149 -0
- package/skills/better-supabase-auth/references/permdock.md +100 -0
- package/skills/better-supabase-testing/SKILL.md +58 -4
- package/dist/claims-CoTobNC-.js +0 -25
- package/dist/events-Do8jXh4D.d.ts +0 -84
- package/dist/executor-DynmIWhq.d.ts +0 -195
- package/dist/invalidate-DbxuL2jL.js +0 -18
- package/dist/permdock-sql-DImJULY-.d.ts +0 -17
- package/dist/plugin-BqR6wKMB.js +0 -23
- package/dist/respond-C44EZAJo.js +0 -399
- package/dist/respond-DAkPIdTH.d.ts +0 -175
- package/dist/result-c2OYgCV2.d.ts +0 -203
- package/dist/view-wZ-9qIUz.js +0 -45
|
@@ -1,15 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
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 {
|
|
6
|
-
import { t as
|
|
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 {
|
|
9
|
-
import { a as postgrestExecutor, n as StatsRecorder, r as recordStats } from "./stats-
|
|
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-
|
|
11
|
-
import { a as toBase64Url, t as base64ToText } from "./base64-
|
|
12
|
-
import { t as validate } from "./standard-
|
|
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
|
|
64
|
-
const
|
|
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
|
-
|
|
176
|
-
for (const
|
|
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
|
|
385
|
+
const hooks = this.#hooks;
|
|
205
386
|
let current = op;
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
|
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 (
|
|
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
|
|
260
|
-
|
|
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
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
}
|
|
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
|
-
|
|
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
|
-
$
|
|
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:
|
|
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
|
-
|
|
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
|
|
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 (
|
|
607
|
-
const alias =
|
|
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 = {
|
|
629
|
-
for (const 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
|
-
|
|
648
|
-
|
|
649
|
-
|
|
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
|
|
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
|
|
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
|
|
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: `
|
|
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
|
-
*
|
|
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 =
|
|
1087
|
+
const errorMappers = this.#errorMappersFor(plugins);
|
|
846
1088
|
const runner = new OperationRunner({
|
|
847
1089
|
meta: this.meta,
|
|
848
|
-
builder:
|
|
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
|
|
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
|
|
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
|
|
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 {
|
|
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 };
|