better-supabase 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -33
- package/dist/base64-D2gsioCY.js +42 -0
- package/dist/claims-BAf6c7is.js +48 -0
- package/dist/cli/_format-Cax5EsM9.js +101 -0
- package/dist/cli/bin.js +39 -6
- package/dist/cli/command-CK14fP91.js +502 -0
- package/dist/cli/compare-BmIWVEui.js +9 -0
- package/dist/cli/config-D9R0o1LH.js +1895 -0
- package/dist/cli/config-SSKSTUl2.js +2 -0
- package/dist/cli/db-DOBPt660.js +52 -0
- package/dist/cli/dist-B1MvLv-M.js +129 -0
- package/dist/cli/dist-CFgGknmZ.js +1112 -0
- package/dist/cli/dist-Du7VnEUG.js +621 -0
- package/dist/cli/doctor-BOym3mbL.js +2382 -0
- package/dist/cli/env-BZ0uy8XK.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-Ccn_p90L.js +2008 -0
- package/dist/cli/gen-D5cLCULx.js +2 -0
- package/dist/cli/index.d.ts +295 -28
- package/dist/cli/index.js +184 -3
- package/dist/cli/init-ClnxRYBF.js +550 -0
- package/dist/cli/introspect-DxqfkVIv.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-8WHjxiTJ.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-CMa29t9a.js +971 -0
- package/dist/cli/rolldown-runtime-BPOCksWG.js +24 -0
- package/dist/cli/run-CP20LKk_.js +347 -0
- package/dist/cli/seed-5J6eu_Xg.js +98 -0
- package/dist/cli/skills-LJJIhLFR.js +137 -0
- package/dist/cli/sql-D4VbqmXi.js +199 -0
- package/dist/cli/style-C0NFdJRL.js +29 -0
- package/dist/cli/toml-DB-MNnFs.js +424 -0
- package/dist/{version-Cqq1m3UY.js → cli/version-5mW_8sY1.js} +1 -1
- package/dist/cli/yaml-CYCbJ6et.js +2252 -0
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.js +15 -25
- package/dist/{compiler-CZ9nIuqy.d.ts → compiler-DB3aSFOA.d.ts} +2 -2
- package/dist/config/index.d.ts +4 -3
- package/dist/config/index.js +5 -3
- package/dist/config-BpxDfnbg.js +129 -0
- package/dist/{index-DOsjFvMf.d.ts → config-C1CYcm6o.d.ts} +49 -49
- package/dist/{config-ClDXmVtY.js → config-CbvFmGD8.js} +14 -137
- package/dist/{define-BXMYD9AY.js → define-Bh0AqTik.js} +221 -120
- package/dist/{define-e3zI4_4g.d.ts → define-CRS2MRV3.d.ts} +36 -31
- package/dist/edge/index.d.ts +9 -13
- package/dist/edge/index.js +51 -25
- package/dist/{entitlements-Dng-ctuT.d.ts → entitlements-Dm-FHpNH.d.ts} +2 -2
- package/dist/env/index.js +203 -1
- package/dist/{errors-Bl3rkOzo.js → errors-L0xK41y_.js} +1 -1
- package/dist/events/index.d.ts +1 -1
- package/dist/events/index.js +139 -1
- package/dist/{events-DOt3nSO-.d.ts → events-B21UOhvP.d.ts} +4 -4
- package/dist/{executor-CGdNQ27g.d.ts → executor-B5IoUxLE.d.ts} +2 -2
- package/dist/{executor-DeppvirK.js → executor-CVt6dD0B.js} +9 -4
- package/dist/{executor-BYXQ9AGn.d.ts → executor-CZ7ImRiD.d.ts} +2 -2
- package/dist/hono/index.d.ts +17 -24
- package/dist/hono/index.js +11 -11
- package/dist/{index-DuP14VHo.d.ts → index-528Ugmyp.d.ts} +30 -10
- package/dist/{index-Bw1bwtC0.d.ts → index-BAy4tsAP.d.ts} +28 -15
- package/dist/{index-CBDufH1M.d.ts → index-BBdvo-5d.d.ts} +24 -26
- package/dist/{index-DbxFr7XG.d.ts → index-BhHEC04r.d.ts} +9 -4
- package/dist/index-Cf-D2jk1.d.ts +50 -0
- package/dist/{index-Ciu5Xlml.d.ts → index-D5IfUIDh.d.ts} +6 -5
- package/dist/{index-K-3WM2Hp.d.ts → index-DFmOpBGC.d.ts} +13 -13
- package/dist/{index-D4v7sncu.d.ts → index-RlLXd8dL.d.ts} +13 -13
- package/dist/index.d.ts +17 -17
- package/dist/index.js +23 -14
- package/dist/{invalidate-DbxuL2jL.js → invalidate-CgxmRamI.js} +7 -1
- package/dist/jobs/index.d.ts +33 -26
- package/dist/jobs/index.js +44 -22
- package/dist/{json-schema-BpYqkCKf.js → json-schema-DP6V7cbr.js} +8 -3
- package/dist/lint/index.js +1 -1
- package/dist/list/index.d.ts +2 -2
- package/dist/list/index.js +131 -96
- package/dist/{live-DJyZMD6w.d.ts → live-BHakSWWo.d.ts} +26 -16
- package/dist/{live-D8rV1Ldw.js → live-CdJBOc4f.js} +13 -9
- package/dist/lookup-BaP5bI27.js +18 -0
- package/dist/mcp/index.d.ts +34 -27
- package/dist/mcp/index.js +59 -25
- package/dist/{mfa-TVia554c.d.ts → mfa-C7MNQ1Ob.d.ts} +1 -1
- package/dist/{mfa-CpLfJSde.js → mfa-byfjruR9.js} +190 -46
- package/dist/next/image/index.js +6 -3
- package/dist/next/index.d.ts +64 -48
- package/dist/next/index.js +63 -50
- package/dist/openapi/index.d.ts +1 -1
- package/dist/openapi/index.js +61 -38
- package/dist/orpc/index.d.ts +18 -21
- package/dist/orpc/index.js +12 -12
- package/dist/otel/index.d.ts +5 -5
- package/dist/otel/index.js +84 -63
- package/dist/{permdock-sql-CghlHPuH.js → permdock-sql-C4KFzLc9.js} +12 -7
- package/dist/permdock-sql-DImJULY-.d.ts +17 -0
- package/dist/{plugin-C4TR9J0Z.d.ts → plugin-bNhvJwRh.d.ts} +5 -4
- package/dist/plugins/actor/index.d.ts +1 -1
- package/dist/plugins/actor/index.js +1 -1
- package/dist/plugins/rules/index.d.ts +2 -2
- package/dist/plugins/rules/index.js +46 -16
- package/dist/plugins/soft-delete/index.d.ts +3 -3
- package/dist/plugins/soft-delete/index.js +4 -4
- package/dist/plugins/tenant/index.d.ts +3 -3
- package/dist/plugins/tenant/index.js +10 -4
- package/dist/plugins/timestamps/index.d.ts +1 -1
- package/dist/plugins/timestamps/index.js +2 -2
- package/dist/plugins/validation/index.d.ts +2 -2
- package/dist/plugins/validation/index.js +3 -3
- package/dist/pool-DFwEQwmN.d.ts +78 -0
- package/dist/postgres/index.d.ts +10 -5
- package/dist/postgres/index.js +37 -16
- package/dist/{postgrest-cAEQ_IQN.d.ts → postgrest-Bc-0Xw62.d.ts} +1 -1
- package/dist/{problem-BTkTtRV9.js → problem-BihjPR0t.js} +11 -5
- package/dist/{problem-DlgB0lYl.d.ts → problem-U8UEKccv.d.ts} +6 -4
- package/dist/query/index.d.ts +2 -2
- package/dist/query/index.js +2 -2
- package/dist/{query-ChvDbsDE.js → query-BMMSWbI7.js} +19 -19
- package/dist/react/index.d.ts +7 -7
- package/dist/react/index.js +2 -2
- package/dist/react/server.d.ts +6 -6
- package/dist/react/server.js +3 -3
- package/dist/react/session.d.ts +21 -2
- package/dist/react/session.js +19 -1
- package/dist/{react-C83BdBGt.js → react-CQHMaESR.js} +59 -55
- package/dist/{read-set-4lI3W_gZ.js → read-set-FWK8qQ58.js} +51 -16
- package/dist/realtime/index.d.ts +4 -3
- package/dist/realtime/index.js +19 -17
- package/dist/{index-CG69s15v.d.ts → refresh-BwYkyNOQ.d.ts} +7 -3
- package/dist/{resolve-CjHILk2w.d.ts → resolve-BAqQcwMW.d.ts} +17 -11
- package/dist/{resource-CgZa3YG_.d.ts → resource-BJcaIfxi.d.ts} +17 -8
- package/dist/{resource-BHEWmQJK.js → resource-My_y-Syh.js} +43 -21
- package/dist/{respond-Dne1ARSa.d.ts → respond-DgIbdrt8.d.ts} +39 -14
- package/dist/{result-DWXatkd6.js → result-Bj6yt33D.js} +2 -2
- package/dist/{result-CTkCZ6JA.d.ts → result-CqEsbgYs.d.ts} +16 -11
- package/dist/server/index.d.ts +38 -13
- package/dist/server/index.js +43 -14
- package/dist/{respond-DMcbQwfN.js → server-BA75B0mT.js} +151 -96
- package/dist/{shared-_6-uPL7E.js → shared-DnoCdMDr.js} +4 -3
- package/dist/{simplify-bwJpLnaS.js → simplify-Cqvfr9dj.js} +76 -39
- package/dist/sql/index.d.ts +139 -0
- package/dist/sql/index.js +1983 -0
- package/dist/{sql-DTjA1LPj.js → sql-B34HU2DI.js} +74 -17
- package/dist/ssr/index.d.ts +4 -4
- package/dist/ssr/index.js +2 -2
- package/dist/{standard-FUi3Z3-7.js → standard-BRyBGTtp.js} +2 -2
- package/dist/{standard-CyhRzctb.d.ts → standard-BsaCYQJc.d.ts} +1 -1
- package/dist/{stats-C5W-rBfR.js → stats-C_YxA2r1.js} +40 -14
- package/dist/storage/index.d.ts +4 -3
- package/dist/storage/index.js +548 -1
- package/dist/{tables-rZxRxNPW.js → tables-Cb-3_VWc.js} +19 -5
- package/dist/temporal-BwshQk0W.js +27 -0
- package/dist/temporal-required-CBNHT6ni.js +19 -0
- package/dist/testing/index.d.ts +25 -24
- package/dist/testing/index.js +165 -66
- package/dist/{types-C83v5SQz.d.ts → types-DONv9SHN.d.ts} +14 -11
- package/dist/version-Bmf7_FzM.js +4 -0
- package/dist/{view-D1s0wa0T.d.ts → view-B64o81wp.d.ts} +36 -6
- package/dist/{view-aOYizTeE.js → view-BrJI2LKp.js} +11 -2
- package/dist/webhooks/index.d.ts +19 -17
- package/dist/webhooks/index.js +1 -1
- package/dist/{webhooks-DMipjhSP.js → webhooks-Bh_Jf_-l.js} +45 -24
- package/package.json +50 -22
- package/schemas/config-v1.json +31 -1
- package/schemas/doctor-report-v1.json +17 -1
- package/schemas/snapshot-v2.json +35 -0
- package/skills/better-supabase/SKILL.md +77 -18
- package/skills/better-supabase/references/plugins.md +21 -22
- package/skills/better-supabase/references/troubleshooting.md +38 -18
- package/skills/better-supabase-api/SKILL.md +66 -18
- package/skills/better-supabase-api/references/adapters.md +64 -41
- package/skills/better-supabase-auth/SKILL.md +146 -0
- package/skills/better-supabase-auth/references/permdock.md +55 -0
- package/skills/better-supabase-testing/SKILL.md +68 -11
- package/dist/casing-da0uRTqt.js +0 -14
- package/dist/claims-CoTobNC-.js +0 -25
- package/dist/cli-DrSgDOQC.js +0 -6646
- package/dist/env-BEToab0I.js +0 -204
- package/dist/events-BuzTgn2D.js +0 -135
- package/dist/pool-R2m_DkXi.d.ts +0 -37
- package/dist/seed-CzecODcJ.js +0 -101
- package/dist/session-C3LxnQ0e.d.ts +0 -22
- package/dist/session-CqEwzVup.js +0 -20
- package/dist/storage-DuGn38fr.js +0 -527
package/dist/events/index.js
CHANGED
|
@@ -1,2 +1,140 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as nowInstant } from "../temporal-BwshQk0W.js";
|
|
2
|
+
//#region src/events/index.ts
|
|
3
|
+
const ROW_EVENT_TYPES = {
|
|
4
|
+
insert: "dev.better-supabase.row.created",
|
|
5
|
+
upsert: "dev.better-supabase.row.upserted",
|
|
6
|
+
update: "dev.better-supabase.row.updated",
|
|
7
|
+
delete: "dev.better-supabase.row.deleted"
|
|
8
|
+
};
|
|
9
|
+
function subjectOf(meta, table, row) {
|
|
10
|
+
const key = meta?.tables[table]?.primaryKey;
|
|
11
|
+
if (!key || key.length === 0) return void 0;
|
|
12
|
+
const values = key.map((column) => row[column]);
|
|
13
|
+
if (values.some((value) => value === void 0 || value === null)) return void 0;
|
|
14
|
+
return `${table}/${values.map((value) => encodeURIComponent(String(value))).join(",")}`;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* One CloudEvent per mutated row: `dev.better-supabase.row.created` with the
|
|
18
|
+
* app-cased row as `data`, the primary key as `subject`, the tenant as
|
|
19
|
+
* `partitionkey` and the actor as `actorid`.
|
|
20
|
+
*/
|
|
21
|
+
function toCloudEvents(notice, options) {
|
|
22
|
+
const type = options.typePrefix ? `${options.typePrefix}.row.${ROW_EVENT_TYPES[notice.kind].split(".").at(-1)}` : ROW_EVENT_TYPES[notice.kind];
|
|
23
|
+
const time = (options.now ?? nowInstant)().toString();
|
|
24
|
+
return notice.rows.map((row) => {
|
|
25
|
+
const subject = subjectOf(options.meta, notice.table, row);
|
|
26
|
+
return {
|
|
27
|
+
specversion: "1.0",
|
|
28
|
+
id: options.id?.() ?? crypto.randomUUID(),
|
|
29
|
+
source: options.source,
|
|
30
|
+
type,
|
|
31
|
+
...subject ? { subject } : {},
|
|
32
|
+
time,
|
|
33
|
+
datacontenttype: "application/json",
|
|
34
|
+
data: {
|
|
35
|
+
table: notice.table,
|
|
36
|
+
row
|
|
37
|
+
},
|
|
38
|
+
...notice.context.tenant ? { partitionkey: notice.context.tenant } : {},
|
|
39
|
+
...notice.context.actor?.id ? { actorid: notice.context.actor.id } : {}
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Sends a CloudEvent for every mutation to `sink`. Returns a function that
|
|
45
|
+
* stops forwarding. Use an outbox (SQL kit) when events must not be lost.
|
|
46
|
+
*/
|
|
47
|
+
function forwardMutations(betterSupabase, sink, options) {
|
|
48
|
+
return betterSupabase.events.on("mutation", (notice) => {
|
|
49
|
+
if (options.filter && !options.filter(notice)) return;
|
|
50
|
+
const events = toCloudEvents(notice, {
|
|
51
|
+
...options,
|
|
52
|
+
meta: betterSupabase.meta
|
|
53
|
+
});
|
|
54
|
+
if (events.length === 0) return;
|
|
55
|
+
const report = (error) => {
|
|
56
|
+
(options.onError ?? ((cause) => {
|
|
57
|
+
betterSupabase.events.logger.error("event sink failed", { cause });
|
|
58
|
+
}))(error, events);
|
|
59
|
+
};
|
|
60
|
+
try {
|
|
61
|
+
Promise.resolve(sink.send(events)).catch(report);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
report(error);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
const STRUCTURED = "application/cloudevents+json";
|
|
68
|
+
const BATCH = "application/cloudevents-batch+json";
|
|
69
|
+
/** The HTTP protocol binding: headers and body for one event (or a batch). */
|
|
70
|
+
function toHttp(events, mode = "structured") {
|
|
71
|
+
const list = Array.isArray(events) ? events : [events];
|
|
72
|
+
switch (mode) {
|
|
73
|
+
case "batch": return [{
|
|
74
|
+
headers: { "content-type": BATCH },
|
|
75
|
+
body: JSON.stringify(list)
|
|
76
|
+
}];
|
|
77
|
+
case "structured": return list.map((event) => ({
|
|
78
|
+
headers: { "content-type": STRUCTURED },
|
|
79
|
+
body: JSON.stringify(event)
|
|
80
|
+
}));
|
|
81
|
+
case "binary": return list.map((event) => {
|
|
82
|
+
const headers = {};
|
|
83
|
+
for (const [key, value] of Object.entries(event)) {
|
|
84
|
+
if (key === "data" || key === "datacontenttype" || value === void 0) continue;
|
|
85
|
+
headers[`ce-${key}`] = encodeURIComponent(String(value)).replaceAll("%20", " ");
|
|
86
|
+
}
|
|
87
|
+
headers["content-type"] = event.datacontenttype ?? "application/json";
|
|
88
|
+
return {
|
|
89
|
+
headers,
|
|
90
|
+
body: event.data === void 0 ? "" : JSON.stringify(event.data)
|
|
91
|
+
};
|
|
92
|
+
});
|
|
93
|
+
default: throw new TypeError(`Unknown CloudEvents mode ${String(mode)}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function isCloudEvent(value) {
|
|
97
|
+
if (typeof value !== "object" || value === null) return false;
|
|
98
|
+
const event = value;
|
|
99
|
+
return event["specversion"] === "1.0" && typeof event["id"] === "string" && typeof event["source"] === "string" && typeof event["type"] === "string";
|
|
100
|
+
}
|
|
101
|
+
/** Reads CloudEvents from a request in any of the three HTTP modes. */
|
|
102
|
+
async function fromHttp(request) {
|
|
103
|
+
const contentType = request.headers.get("content-type") ?? "";
|
|
104
|
+
const text = await request.text();
|
|
105
|
+
if (contentType.startsWith(BATCH)) {
|
|
106
|
+
const parsed = JSON.parse(text);
|
|
107
|
+
if (!Array.isArray(parsed) || !parsed.every(isCloudEvent)) throw new TypeError("Invalid CloudEvents batch");
|
|
108
|
+
return parsed;
|
|
109
|
+
}
|
|
110
|
+
if (contentType.startsWith(STRUCTURED)) {
|
|
111
|
+
const parsed = JSON.parse(text);
|
|
112
|
+
if (!isCloudEvent(parsed)) throw new TypeError("Invalid CloudEvent");
|
|
113
|
+
return [parsed];
|
|
114
|
+
}
|
|
115
|
+
const event = {};
|
|
116
|
+
for (const [key, value] of request.headers) if (key.startsWith("ce-")) event[key.slice(3)] = decodeURIComponent(value);
|
|
117
|
+
if (contentType) event["datacontenttype"] = contentType;
|
|
118
|
+
if (text) event["data"] = contentType.includes("json") ? JSON.parse(text) : text;
|
|
119
|
+
if (!isCloudEvent(event)) throw new TypeError("Request is not a CloudEvent");
|
|
120
|
+
return [event];
|
|
121
|
+
}
|
|
122
|
+
/** POSTs events to `url`. Non-2xx responses reject. */
|
|
123
|
+
function httpSink(url, options = {}) {
|
|
124
|
+
const send = options.fetch ?? fetch;
|
|
125
|
+
return { async send(events) {
|
|
126
|
+
for (const message of toHttp(events, options.mode ?? "batch")) {
|
|
127
|
+
const response = await send(url, {
|
|
128
|
+
method: "POST",
|
|
129
|
+
headers: {
|
|
130
|
+
...options.headers,
|
|
131
|
+
...message.headers
|
|
132
|
+
},
|
|
133
|
+
body: message.body
|
|
134
|
+
});
|
|
135
|
+
if (!response.ok) throw new Error(`Event sink responded ${String(response.status)}`);
|
|
136
|
+
}
|
|
137
|
+
} };
|
|
138
|
+
}
|
|
139
|
+
//#endregion
|
|
2
140
|
export { ROW_EVENT_TYPES, forwardMutations, fromHttp, httpSink, isCloudEvent, toCloudEvents, toHttp };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { g as Operation } from "./executor-
|
|
2
|
-
import { p as DbError } from "./result-
|
|
3
|
-
import { h as RequestContext, l as MutationKind } from "./plugin-
|
|
1
|
+
import { g as Operation } from "./executor-B5IoUxLE.js";
|
|
2
|
+
import { p as DbError } from "./result-CqEsbgYs.js";
|
|
3
|
+
import { h as RequestContext, l as MutationKind } from "./plugin-bNhvJwRh.js";
|
|
4
4
|
//#region src/core/logger.d.ts
|
|
5
5
|
type LogFields = Readonly<Record<string, unknown>>;
|
|
6
6
|
/**
|
|
@@ -37,7 +37,7 @@ interface MutationNotice {
|
|
|
37
37
|
readonly rows: readonly Readonly<Record<string, unknown>>[];
|
|
38
38
|
readonly context: RequestContext;
|
|
39
39
|
}
|
|
40
|
-
/** A successful RPC registered with `
|
|
40
|
+
/** A successful RPC registered with `betterSupabase.defineRpc(name, { invalidates })`. */
|
|
41
41
|
interface RpcNotice {
|
|
42
42
|
readonly name: string;
|
|
43
43
|
/** App keys of the tables the function changes. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as TableMeta, T as RelationMeta, c as Codec } from "./types-
|
|
2
|
-
import { o as Result, v as ErrorMapper } from "./result-
|
|
1
|
+
import { P as TableMeta, T as RelationMeta, c as Codec } from "./types-DONv9SHN.js";
|
|
2
|
+
import { o as Result, v as ErrorMapper } from "./result-CqEsbgYs.js";
|
|
3
3
|
//#region src/ir/types.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* The query IR. Every repository call becomes one of these operations; the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { c as mapDbError, n as dbError } from "./errors-
|
|
2
|
-
import { i as ok, n as err, o as toDbError } from "./result-
|
|
3
|
-
import { t as compileSql } from "./sql-
|
|
1
|
+
import { c as mapDbError, n as dbError } from "./errors-L0xK41y_.js";
|
|
2
|
+
import { i as ok, n as err, o as toDbError } from "./result-Bj6yt33D.js";
|
|
3
|
+
import { t as compileSql } from "./sql-B34HU2DI.js";
|
|
4
4
|
//#region src/postgres/executor.ts
|
|
5
5
|
function isPgError(value) {
|
|
6
6
|
return typeof value === "object" && value !== null && typeof value.code === "string";
|
|
@@ -25,6 +25,11 @@ async function run(client, plan, op) {
|
|
|
25
25
|
const [first] = await client.queryRaw(plan.count.text, [...plan.count.params]);
|
|
26
26
|
count = first?.count ?? 0;
|
|
27
27
|
} else if (op.kind !== "select") count = rows.length;
|
|
28
|
+
for (const next of plan.chunks ?? []) {
|
|
29
|
+
const more = await run(client, next, op);
|
|
30
|
+
rows.push(...more.rows);
|
|
31
|
+
count = (count ?? 0) + (more.count ?? 0);
|
|
32
|
+
}
|
|
28
33
|
return {
|
|
29
34
|
rows,
|
|
30
35
|
count
|
|
@@ -74,7 +79,7 @@ async function executeOn(client, op, context) {
|
|
|
74
79
|
if (context.signal?.aborted) return err(dbError("aborted", "The request was aborted"));
|
|
75
80
|
let result;
|
|
76
81
|
try {
|
|
77
|
-
result = await run(client, plan, op);
|
|
82
|
+
result = plan.chunks && client.transaction ? await client.transaction((scoped) => run(scoped, plan, op)) : await run(client, plan, op);
|
|
78
83
|
} catch (cause) {
|
|
79
84
|
const raw = fromPgError(cause);
|
|
80
85
|
return err(raw ? mapDbError(raw, context.errorMappers) : toDbError(cause));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { r as Executor } from "./executor-
|
|
2
|
-
import { y as RawDbError } from "./result-
|
|
1
|
+
import { r as Executor } from "./executor-B5IoUxLE.js";
|
|
2
|
+
import { y as RawDbError } from "./result-CqEsbgYs.js";
|
|
3
3
|
//#region src/postgres/executor.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* Anything that runs parameterized SQL: `ctx.postgres` from
|
package/dist/hono/index.d.ts
CHANGED
|
@@ -1,45 +1,38 @@
|
|
|
1
|
-
import { N as TableKey, n as AnyModels, t as AnyFunctions } from "../types-
|
|
2
|
-
import { t as BetterSupabase } from "../define-
|
|
3
|
-
import { r as AuthState } from "../resolve-
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { N as TableKey, n as AnyModels, t as AnyFunctions } from "../types-DONv9SHN.js";
|
|
2
|
+
import { t as BetterSupabase } from "../define-CRS2MRV3.js";
|
|
3
|
+
import { r as AuthState } from "../resolve-BAqQcwMW.js";
|
|
4
|
+
import { f as ServerContext, n as GuardOptions, p as ServerOptions, r as MiddlewareOptions, u as BetterServer } from "../respond-DgIbdrt8.js";
|
|
5
|
+
import { a as ResourceRouteOptions } from "../resource-BJcaIfxi.js";
|
|
6
6
|
import { Context, ErrorHandler, Hono, MiddlewareHandler } from "hono";
|
|
7
7
|
//#region src/hono/index.d.ts
|
|
8
8
|
/** Hono `Env` with the request's better-supabase context in `c.var`. */
|
|
9
|
-
export interface
|
|
9
|
+
export interface HonoEnv<M extends AnyModels, F extends AnyFunctions, E, C = unknown, P = unknown> {
|
|
10
10
|
Variables: {
|
|
11
|
-
readonly bs: ServerContext<M, F, E>;
|
|
12
|
-
readonly db: ServerContext<M, F, E>["db"];
|
|
13
|
-
readonly auth: AuthState
|
|
11
|
+
readonly bs: ServerContext<M, F, E, C, P>;
|
|
12
|
+
readonly db: ServerContext<M, F, E, C, P>["db"];
|
|
13
|
+
readonly auth: AuthState<C, P>;
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
16
|
export interface HonoOptions extends ServerOptions {
|
|
17
17
|
/** Include error details in problem responses. Defaults to `NODE_ENV === 'development'`. */
|
|
18
18
|
readonly exposeErrors?: boolean;
|
|
19
19
|
}
|
|
20
|
-
export interface
|
|
21
|
-
/**
|
|
22
|
-
* Refresh an expired cookie session and send the new cookies. Only for
|
|
23
|
-
* routes browsers call with cookies; bearer tokens never refresh.
|
|
24
|
-
*/
|
|
25
|
-
readonly refresh?: boolean;
|
|
26
|
-
}
|
|
27
|
-
export interface BetterHono<M extends AnyModels, F extends AnyFunctions, E> extends BetterServer<M, F, E> {
|
|
20
|
+
export interface BetterHono<M extends AnyModels, F extends AnyFunctions, E, C = unknown, P = unknown> extends BetterServer<M, F, E, C, P> {
|
|
28
21
|
/** Resolves the caller, enforces `allow` and sets `c.var.bs`, `c.var.db`, `c.var.auth`. */
|
|
29
|
-
middleware(options?: MiddlewareOptions): MiddlewareHandler<
|
|
22
|
+
middleware(options?: MiddlewareOptions): MiddlewareHandler<HonoEnv<M, F, E, C, P>>;
|
|
30
23
|
/** Wraps a handler: `Result`s become JSON or Problem Details, `undefined` becomes 204. */
|
|
31
|
-
|
|
24
|
+
handler(fn: (c: Context<HonoEnv<M, F, E, C, P>>, ctx: ServerContext<M, F, E, C, P>) => unknown, options?: {
|
|
32
25
|
readonly status?: number;
|
|
33
|
-
}): (c: Context<
|
|
26
|
+
}): (c: Context<HonoEnv<M, F, E, C, P>>) => Promise<Response>;
|
|
34
27
|
/** `app.onError(bs.onError)`: `DbException`s (and errors caused by a `DbError`) become Problem Details, others a 500. */
|
|
35
|
-
readonly onError: ErrorHandler<
|
|
28
|
+
readonly onError: ErrorHandler<HonoEnv<M, F, E, C, P>>;
|
|
36
29
|
/**
|
|
37
30
|
* REST routes for a table matching `createOpenApi`. Mount with
|
|
38
31
|
* `app.route('/customers', bs.resource('customers'))` behind `middleware()`.
|
|
39
32
|
*/
|
|
40
|
-
resource<T extends TableKey<M>>(table: T, options?: ResourceRouteOptions<M, T>): Hono<
|
|
33
|
+
resource<T extends TableKey<M>>(table: T, options?: ResourceRouteOptions<M, T>): Hono<HonoEnv<M, F, E, C, P>>;
|
|
41
34
|
}
|
|
42
35
|
/** Hono integration: a server plus middleware, handlers and REST resources. */
|
|
43
|
-
export declare function createHono<M extends AnyModels, D, F extends AnyFunctions, E>(
|
|
36
|
+
export declare function createHono<M extends AnyModels, D, F extends AnyFunctions, E, C = unknown, P = unknown>(betterSupabase: BetterSupabase<M, D, F, E, C, P>, options?: HonoOptions): BetterHono<M, F, E, C, P>;
|
|
44
37
|
//#endregion
|
|
45
|
-
export type { GuardOptions, ResourceRouteOptions };
|
|
38
|
+
export type { GuardOptions, MiddlewareOptions, ResourceRouteOptions };
|
package/dist/hono/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { n as dbError, r as dbErrorOf } from "../errors-
|
|
2
|
-
import { a as problemResponse } from "../problem-
|
|
3
|
-
import {
|
|
4
|
-
import { t as defineResource } from "../resource-
|
|
1
|
+
import { n as dbError, r as dbErrorOf } from "../errors-L0xK41y_.js";
|
|
2
|
+
import { a as problemResponse } from "../problem-BihjPR0t.js";
|
|
3
|
+
import { a as guard, i as defaultExpose, n as extendServer, s as respond, t as createServer } from "../server-BA75B0mT.js";
|
|
4
|
+
import { t as defineResource } from "../resource-My_y-Syh.js";
|
|
5
5
|
import { Hono } from "hono";
|
|
6
6
|
//#region src/hono/index.ts
|
|
7
7
|
function contextOf(c) {
|
|
@@ -10,8 +10,8 @@ function contextOf(c) {
|
|
|
10
10
|
return ctx;
|
|
11
11
|
}
|
|
12
12
|
/** Hono integration: a server plus middleware, handlers and REST resources. */
|
|
13
|
-
function createHono(
|
|
14
|
-
const server = createServer(
|
|
13
|
+
function createHono(betterSupabase, options = {}) {
|
|
14
|
+
const server = createServer(betterSupabase, options);
|
|
15
15
|
const expose = options.exposeErrors ?? defaultExpose();
|
|
16
16
|
const onError = (cause, c) => {
|
|
17
17
|
const instance = new URL(c.req.url).pathname;
|
|
@@ -31,7 +31,7 @@ function createHono(sb, options = {}) {
|
|
|
31
31
|
middleware(middlewareOptions = {}) {
|
|
32
32
|
return async (c, next) => {
|
|
33
33
|
const ctx = await server.context(c.req.raw, { refresh: middlewareOptions.refresh ?? false });
|
|
34
|
-
const denied = guard(ctx.auth, middlewareOptions.allow, middlewareOptions.aal);
|
|
34
|
+
const denied = guard(ctx.auth, middlewareOptions.allow, middlewareOptions.aal, middlewareOptions.scopes);
|
|
35
35
|
if (denied) return ctx.resolution.apply(problemResponse(denied, {
|
|
36
36
|
instance: new URL(c.req.url).pathname,
|
|
37
37
|
expose
|
|
@@ -43,18 +43,18 @@ function createHono(sb, options = {}) {
|
|
|
43
43
|
c.res = ctx.resolution.apply(c.res);
|
|
44
44
|
};
|
|
45
45
|
},
|
|
46
|
-
|
|
46
|
+
handler(fn, handlerOptions = {}) {
|
|
47
47
|
return (c) => {
|
|
48
48
|
const ctx = contextOf(c);
|
|
49
|
-
return respond(() =>
|
|
49
|
+
return respond(() => fn(c, ctx), {
|
|
50
50
|
instance: new URL(c.req.url).pathname,
|
|
51
51
|
expose,
|
|
52
|
-
...
|
|
52
|
+
...handlerOptions
|
|
53
53
|
});
|
|
54
54
|
};
|
|
55
55
|
},
|
|
56
56
|
resource(table, resourceOptions) {
|
|
57
|
-
const resource = defineResource(
|
|
57
|
+
const resource = defineResource(betterSupabase, table, resourceOptions);
|
|
58
58
|
const app = new Hono();
|
|
59
59
|
const serve = (item) => (c) => resource.handle(c.req.raw, contextOf(c).db, item ? c.req.param("id") : void 0, {
|
|
60
60
|
instance: new URL(c.req.url).pathname,
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
1
|
+
/// <reference lib="esnext.temporal" />
|
|
2
|
+
import { b as PermdockBucketPolicy, i as BucketPolicyName } from "./types-DONv9SHN.js";
|
|
3
|
+
import { p as DbError, t as AsyncResult } from "./result-CqEsbgYs.js";
|
|
3
4
|
import { n as StoragePath, t as PathIn } from "./path-Cv__-OTj.js";
|
|
5
|
+
import { t as PermdockCatalog } from "./permdock-sql-DImJULY-.js";
|
|
4
6
|
import { n as TemplateValues, t as TemplateParams } from "./template-mbvlysOo.js";
|
|
5
7
|
import { SupabaseClient } from "@supabase/supabase-js";
|
|
6
|
-
//#region src/storage/
|
|
8
|
+
//#region src/storage/bucket.d.ts
|
|
7
9
|
type BucketPolicy = BucketPolicyName | PermdockBucketPolicy;
|
|
8
10
|
interface BucketConfig<P extends string = string, Id extends string = string> {
|
|
9
11
|
/** Bucket id, e.g. `customer-logos`. */
|
|
@@ -20,10 +22,16 @@ interface BucketConfig<P extends string = string, Id extends string = string> {
|
|
|
20
22
|
* The helpers check role and scope only. Use `permdock` just for
|
|
21
23
|
* permissions whose grants have no row conditions beyond the scope: for a
|
|
22
24
|
* permission with row conditions (e.g. `ownerId = principal.id`) the bucket
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
+
* would grant every object in the scope. Pass `catalog` to refuse those
|
|
26
|
+
* keys here; `better-supabase doctor` (BS214) refuses them from the
|
|
27
|
+
* catalog file.
|
|
25
28
|
*/
|
|
26
29
|
readonly policy?: BucketPolicy;
|
|
30
|
+
/**
|
|
31
|
+
* PermDock's `permissions.catalog.json`. With it, a `permdock` policy
|
|
32
|
+
* naming a permission with `rowConditions: true` throws.
|
|
33
|
+
*/
|
|
34
|
+
readonly catalog?: PermdockCatalog;
|
|
27
35
|
/** `'5MiB'`, `'500KB'` or bytes. */
|
|
28
36
|
readonly fileSizeLimit?: string | number;
|
|
29
37
|
/** `['image/png', 'image/*']`. */
|
|
@@ -105,14 +113,17 @@ interface ReplaceResult<Id extends string = string> {
|
|
|
105
113
|
interface SweepOptions<P extends string> {
|
|
106
114
|
/** Only look under the path prefix these values fill. */
|
|
107
115
|
readonly within?: Partial<TemplateValues<P>>;
|
|
108
|
-
/**
|
|
109
|
-
|
|
116
|
+
/**
|
|
117
|
+
* A cutoff instant, or a minimum age. A duration counts a day as 24 hours
|
|
118
|
+
* and cannot use months or years.
|
|
119
|
+
*/
|
|
120
|
+
readonly olderThan: Temporal.Instant | Temporal.Duration;
|
|
110
121
|
/** Returns which of `paths` are still referenced. */
|
|
111
122
|
readonly referenced: (paths: readonly string[]) => PromiseLike<Iterable<string>> | Iterable<string>;
|
|
112
123
|
readonly dryRun?: boolean;
|
|
113
124
|
readonly batchSize?: number;
|
|
114
125
|
readonly signal?: AbortSignal;
|
|
115
|
-
readonly now?: () =>
|
|
126
|
+
readonly now?: () => Temporal.Instant;
|
|
116
127
|
}
|
|
117
128
|
interface SweepResult {
|
|
118
129
|
readonly scanned: number;
|
|
@@ -191,7 +202,16 @@ interface Bucket<P extends string, Id extends string = string> {
|
|
|
191
202
|
/** A `[storage.buckets.<id>]` section for `supabase/config.toml`. */
|
|
192
203
|
toml(): string;
|
|
193
204
|
drift(actual: ActualBucket | undefined): readonly BucketDrift[];
|
|
194
|
-
connect(client: StorageClient): BucketClient<P, Id>;
|
|
205
|
+
connect(client: StorageClient, options?: BucketConnectOptions): BucketClient<P, Id>;
|
|
206
|
+
}
|
|
207
|
+
interface BucketConnectOptions {
|
|
208
|
+
/**
|
|
209
|
+
* Reuse a signed URL for the same path, `ttl`, `transform` and `download`
|
|
210
|
+
* until shortly before it expires (a tenth of the `ttl`, at most a minute).
|
|
211
|
+
* The cache belongs to this connection; connect once per request so URLs
|
|
212
|
+
* never cross users.
|
|
213
|
+
*/
|
|
214
|
+
readonly cacheSignedUrls?: boolean;
|
|
195
215
|
}
|
|
196
216
|
declare function parseSize(value: string | number): number;
|
|
197
217
|
/** Maps a Storage error (`StorageApiError`, fetch failures) to a `DbError`. */
|
|
@@ -213,4 +233,4 @@ declare function fromStorageError(raw: unknown, table?: string): DbError;
|
|
|
213
233
|
*/
|
|
214
234
|
declare function defineBucket<const P extends string, const Id extends string = string>(config: BucketConfig<P, Id>): Bucket<P, Id>;
|
|
215
235
|
//#endregion
|
|
216
|
-
export {
|
|
236
|
+
export { fromStorageError as C, defineBucket as S, TransformOptions as _, BucketConnectOptions as a, UploadOptions as b, ObjectTarget as c, Reservation as d, StorageClient as f, TTL as g, SweepResult as h, BucketConfig as i, ReplaceOptions as l, SweepOptions as m, Bucket as n, BucketDrift as o, StoredObject as p, BucketClient as r, BucketPolicy as s, ActualBucket as t, ReplaceResult as u, TtlPreset as v, parseSize as w, UrlOptions as x, UploadBody as y };
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import { N as TableKey, O as Row, n as AnyModels, t as AnyFunctions } from "./types-
|
|
2
|
-
import { l as CountMode } from "./executor-
|
|
3
|
-
import { b as ValidationIssue, t as AsyncResult } from "./result-
|
|
4
|
-
import { C as FindExt, Ct as WhereInput, O as RepositoryOf, T as OffsetPageArgs, dt as IncludeArg, gt as OrderByArg, t as BetterSupabase, vt as Payload, w as OffsetPage, xt as SelectArg } from "./define-
|
|
1
|
+
import { N as TableKey, O as Row, n as AnyModels, t as AnyFunctions } from "./types-DONv9SHN.js";
|
|
2
|
+
import { l as CountMode } from "./executor-B5IoUxLE.js";
|
|
3
|
+
import { b as ValidationIssue, t as AsyncResult } from "./result-CqEsbgYs.js";
|
|
4
|
+
import { C as FindExt, Ct as WhereInput, O as RepositoryOf, T as OffsetPageArgs, _ as CursorPage, dt as IncludeArg, gt as OrderByArg, t as BetterSupabase, v as CursorPageArgs, vt as Payload, w as OffsetPage, xt as SelectArg } from "./define-CRS2MRV3.js";
|
|
5
5
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
6
|
-
//#region src/list/
|
|
6
|
+
//#region src/list/list-query.d.ts
|
|
7
7
|
/** Facet value meaning "not set": filters with `is null`. */
|
|
8
8
|
declare const UNSET = "__unset__";
|
|
9
9
|
type Column<M extends AnyModels, T extends keyof M> = Extract<keyof Row<M, T>, string>;
|
|
10
10
|
type TextColumn<M extends AnyModels, T extends keyof M> = { [K in Column<M, T>]: [NonNullable<Row<M, T>[K]>] extends [string] ? K : never; }[Column<M, T>];
|
|
11
|
-
|
|
11
|
+
/** `offset` pages by number; `cursor` continues after the last row (keyset). */
|
|
12
|
+
type ListPagination = "offset" | "cursor";
|
|
13
|
+
interface ListQueryConfig<M extends AnyModels, T extends keyof M, S extends string, F extends string, C extends boolean = boolean, P extends ListPagination = ListPagination> {
|
|
12
14
|
/**
|
|
13
15
|
* Also count rows per facet value, with one grouped aggregate that runs
|
|
14
16
|
* next to the page: 2 calls, 1 wave. Needs PostgREST aggregates.
|
|
@@ -26,6 +28,12 @@ interface ListQueryConfig<M extends AnyModels, T extends keyof M, S extends stri
|
|
|
26
28
|
/** Named orderings. Add a unique column last for stable pages. */
|
|
27
29
|
readonly sorts: { readonly [K in S]: OrderByArg<M, T>; };
|
|
28
30
|
readonly defaultSort: NoInfer<S>;
|
|
31
|
+
/**
|
|
32
|
+
* `cursor` takes an `after` cursor instead of `page` and returns
|
|
33
|
+
* `nextCursor`: the cost of a page stays the same however deep it is, and
|
|
34
|
+
* rows inserted meanwhile don't shift it. Defaults to `offset`.
|
|
35
|
+
*/
|
|
36
|
+
readonly pagination?: P;
|
|
29
37
|
/** Defaults to 50. */
|
|
30
38
|
readonly pageSize?: number;
|
|
31
39
|
/** Defaults to 200. */
|
|
@@ -36,8 +44,10 @@ interface ListQueryConfig<M extends AnyModels, T extends keyof M, S extends stri
|
|
|
36
44
|
interface ListQuery<S extends string, F extends string> {
|
|
37
45
|
readonly q?: string;
|
|
38
46
|
readonly sort: S;
|
|
39
|
-
/** 1-based. */
|
|
47
|
+
/** 1-based. Always 1 with cursor pagination. */
|
|
40
48
|
readonly page: number;
|
|
49
|
+
/** Cursor pagination: the `nextCursor` of the previous page. */
|
|
50
|
+
readonly after?: string;
|
|
41
51
|
readonly size: number;
|
|
42
52
|
readonly facets: { readonly [K in F]?: readonly string[]; };
|
|
43
53
|
}
|
|
@@ -46,6 +56,7 @@ interface ListQueryInput<S extends string, F extends string> {
|
|
|
46
56
|
readonly q?: string;
|
|
47
57
|
readonly sort?: S;
|
|
48
58
|
readonly page?: number;
|
|
59
|
+
readonly after?: string;
|
|
49
60
|
readonly size?: number;
|
|
50
61
|
readonly facets?: { readonly [K in F]?: readonly string[]; };
|
|
51
62
|
}
|
|
@@ -83,13 +94,14 @@ interface FacetInfo {
|
|
|
83
94
|
/** Rows per facet value, keyed by facet and then by value (`UNSET` for empty). */
|
|
84
95
|
type FacetCounts<F extends string> = { readonly [K in F]: Readonly<Record<string, number>>; };
|
|
85
96
|
/** A page of a list; with `facetCounts: true`, also the counts per facet value. */
|
|
86
|
-
type ListPage<R, F extends string, C extends boolean> = OffsetPage<R> & ([C] extends [true] ? {
|
|
97
|
+
type ListPage<R, F extends string, C extends boolean, P extends ListPagination = "offset"> = ([P] extends ["cursor"] ? CursorPage<R> : OffsetPage<R>) & ([C] extends [true] ? {
|
|
87
98
|
readonly facetCounts: FacetCounts<F>;
|
|
88
99
|
} : [C] extends [false] ? unknown : {
|
|
89
100
|
readonly facetCounts?: FacetCounts<F>;
|
|
90
101
|
});
|
|
91
|
-
interface ListDefinition<M extends AnyModels, T extends TableKey<M>, E, S extends string, F extends string, C extends boolean = false> {
|
|
102
|
+
interface ListDefinition<M extends AnyModels, T extends TableKey<M>, E, S extends string, F extends string, C extends boolean = false, P extends ListPagination = "offset"> {
|
|
92
103
|
readonly table: T;
|
|
104
|
+
readonly pagination: P;
|
|
93
105
|
readonly defaults: ListQuery<S, F>;
|
|
94
106
|
readonly facets: readonly FacetInfo[];
|
|
95
107
|
/** Validates typed input or URL params. Also a Standard Schema (`schema`). */
|
|
@@ -100,13 +112,13 @@ interface ListDefinition<M extends AnyModels, T extends TableKey<M>, E, S extend
|
|
|
100
112
|
/** `{ parse, serialize }` per URL key, for routers and nuqs (`list.nuqs(createParser)`). */
|
|
101
113
|
readonly parsers: ListParsers<S, F>;
|
|
102
114
|
nuqs<P>(createParser: (spec: ParserSpec<unknown>) => P): { readonly [K in keyof ListParsers<S, F>]: P; };
|
|
103
|
-
/** Repository arguments: `where`, `orderBy`, `
|
|
104
|
-
args(query: ListQuery<S, F>): OffsetPageArgs<M, T>;
|
|
115
|
+
/** Repository arguments: `where`, `orderBy`, `size`, and `page` and `count` or `after`. */
|
|
116
|
+
args(query: ListQuery<S, F>): [P] extends ["cursor"] ? CursorPageArgs<M, T> : OffsetPageArgs<M, T>;
|
|
105
117
|
/**
|
|
106
118
|
* Runs the list against a table, merging your `select`, `include`, `where`
|
|
107
119
|
* and `count`. One request, plus one for `facetCounts`, always one wave.
|
|
108
120
|
*/
|
|
109
|
-
run<const A extends ListExtra<M, T> & FindExt<E, M, T>>(db: { readonly [K in T]: RepositoryOf<M, K, E>; }, query: ListQuery<S, F>, extra?: A): AsyncResult<ListPage<Payload<M, T, A>, F, C>>;
|
|
121
|
+
run<const A extends ListExtra<M, T> & FindExt<E, M, T>>(db: { readonly [K in T]: RepositoryOf<M, K, E>; }, query: ListQuery<S, F>, extra?: A): AsyncResult<ListPage<Payload<M, T, A>, F, C, P>>;
|
|
110
122
|
/** OpenAPI 3.1 query parameters. */
|
|
111
123
|
readonly openapi: readonly OpenApiParameter[];
|
|
112
124
|
/** JSON Schema of the typed input, e.g. for MCP tool arguments. */
|
|
@@ -123,6 +135,7 @@ type ListParsers<S extends string, F extends string> = {
|
|
|
123
135
|
readonly q: ParserSpec<string>;
|
|
124
136
|
readonly sort: ParserSpec<S>;
|
|
125
137
|
readonly page: ParserSpec<number>;
|
|
138
|
+
readonly after: ParserSpec<string>;
|
|
126
139
|
readonly size: ParserSpec<number>;
|
|
127
140
|
} & { readonly [K in F]: ParserSpec<readonly string[]>; };
|
|
128
141
|
/**
|
|
@@ -131,7 +144,7 @@ type ListParsers<S extends string, F extends string> = {
|
|
|
131
144
|
* OpenAPI parameters and a JSON Schema derived from the same definition.
|
|
132
145
|
*
|
|
133
146
|
* ```ts
|
|
134
|
-
* export const customerList = defineListQuery(
|
|
147
|
+
* export const customerList = defineListQuery(betterSupabase, 'customers', {
|
|
135
148
|
* search: ['name', 'kvk'],
|
|
136
149
|
* facets: { status: 'status' },
|
|
137
150
|
* sorts: { name: { name: 'asc' }, newest: [{ createdAt: 'desc' }, { id: 'asc' }] },
|
|
@@ -139,6 +152,6 @@ type ListParsers<S extends string, F extends string> = {
|
|
|
139
152
|
* });
|
|
140
153
|
* ```
|
|
141
154
|
*/
|
|
142
|
-
declare function defineListQuery<M extends AnyModels, D, Fn extends AnyFunctions, E, T extends TableKey<M>, const S extends string, const F extends string = never, const C extends boolean = false>(
|
|
155
|
+
declare function defineListQuery<M extends AnyModels, D, Fn extends AnyFunctions, E, T extends TableKey<M>, const S extends string, const F extends string = never, const C extends boolean = false, const P extends ListPagination = "offset">(betterSupabase: BetterSupabase<M, D, Fn, E>, table: T, config: ListQueryConfig<M, T, S, F, C, P>): ListDefinition<M, T, E, S, F, C, P>;
|
|
143
156
|
//#endregion
|
|
144
|
-
export { ListPage as a,
|
|
157
|
+
export { ListPage as a, ListParsers as c, ListQueryInput as d, ListSearchParams as f, defineListQuery as g, UNSET as h, ListExtra as i, ListQuery as l, ParserSpec as m, FacetInfo as n, ListPagination as o, OpenApiParameter as p, ListDefinition as r, ListParseResult as s, FacetCounts as t, ListQueryConfig as u };
|
|
@@ -1,61 +1,59 @@
|
|
|
1
|
-
import { p as DbError } from "./result-
|
|
2
|
-
import { Z as QuerySpec } from "./define-
|
|
3
|
-
import { i as
|
|
1
|
+
import { p as DbError } from "./result-CqEsbgYs.js";
|
|
2
|
+
import { Z as QuerySpec } from "./define-CRS2MRV3.js";
|
|
3
|
+
import { i as ClientAuth, t as AuthSnapshot } from "./index-RlLXd8dL.js";
|
|
4
4
|
import { n as TemplateValues } from "./template-mbvlysOo.js";
|
|
5
|
-
import { t as AuthSession } from "./view-
|
|
6
|
-
import { a as LiveSource, g as Topic, h as SubscriptionStatus, p as SubscribeOptions, r as LiveCountSeed, u as EventSchemas, v as TopicHandlers, y as TopicMessage } from "./live-
|
|
7
|
-
import "./entitlements-Dng-ctuT.js";
|
|
8
|
-
import "./session-C3LxnQ0e.js";
|
|
5
|
+
import { t as AuthSession } from "./view-B64o81wp.js";
|
|
6
|
+
import { a as LiveSource, g as Topic, h as SubscriptionStatus, p as SubscribeOptions, r as LiveCountSeed, u as EventSchemas, v as TopicHandlers, y as TopicMessage } from "./live-BHakSWWo.js";
|
|
9
7
|
import { SupabaseClient } from "@supabase/supabase-js";
|
|
10
8
|
import { ReactNode } from "react";
|
|
11
9
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
12
10
|
import { QueryClient } from "@tanstack/query-core";
|
|
13
|
-
//#region src/react/
|
|
14
|
-
/** The parts of `
|
|
15
|
-
interface
|
|
16
|
-
readonly
|
|
11
|
+
//#region src/react/hooks.d.ts
|
|
12
|
+
/** The parts of `createClient()` the provider needs. */
|
|
13
|
+
interface ClientLike {
|
|
14
|
+
readonly betterSupabase: LiveSource;
|
|
17
15
|
readonly supabase: SupabaseClient;
|
|
18
|
-
readonly auth:
|
|
16
|
+
readonly auth: ClientAuth;
|
|
19
17
|
readonly db: object;
|
|
20
18
|
readonly queries: object;
|
|
21
19
|
}
|
|
22
20
|
interface BetterSupabaseProviderProps {
|
|
23
|
-
readonly
|
|
21
|
+
readonly client: ClientLike;
|
|
24
22
|
/** better-supabase queries are removed when the user signs out or changes. */
|
|
25
23
|
readonly queryClient?: QueryClient;
|
|
26
24
|
readonly children?: ReactNode;
|
|
27
25
|
}
|
|
28
|
-
/** Provides the
|
|
26
|
+
/** Provides the better-supabase client to the hooks. */
|
|
29
27
|
declare function BetterSupabaseProvider(props: BetterSupabaseProviderProps): ReactNode;
|
|
30
28
|
/** The session state for UI: `loading`, `signed-out` or `signed-in` with the user and claims. */
|
|
31
29
|
declare function useAuth(): AuthSnapshot;
|
|
32
30
|
declare function useSupabase(): SupabaseClient;
|
|
33
|
-
interface BetterHooks<B extends
|
|
31
|
+
interface BetterHooks<B extends ClientLike> {
|
|
34
32
|
/** Repositories bound to the current session. Re-renders when the user changes. */
|
|
35
33
|
readonly useDb: () => B["db"];
|
|
36
34
|
/** TanStack Query option factories: `useQuery(useQueries().customers.findMany())`. */
|
|
37
35
|
readonly useQueries: () => B["queries"];
|
|
38
36
|
readonly useSupabase: () => SupabaseClient;
|
|
39
37
|
readonly useAuth: () => AuthSnapshot;
|
|
40
|
-
/** `useSession()` typed by the
|
|
38
|
+
/** `useSession()` typed by the client's `betterSupabase.claims(schema)` and `betterSupabase.userMetadata(schema)`. */
|
|
41
39
|
readonly useSession: () => AuthSession<ClaimsOf<B>, ProfileOf<B>>;
|
|
42
40
|
}
|
|
43
|
-
/** The claims type of a
|
|
44
|
-
type ClaimsOf<B extends
|
|
41
|
+
/** The claims type of a client's `betterSupabase.claims(schema)`, `unknown` without one. */
|
|
42
|
+
type ClaimsOf<B extends ClientLike> = B["betterSupabase"] extends {
|
|
45
43
|
readonly claimsSchema: StandardSchemaV1<unknown, infer C> | undefined;
|
|
46
44
|
} ? C : unknown;
|
|
47
|
-
/** The profile type of a
|
|
48
|
-
type ProfileOf<B extends
|
|
45
|
+
/** The profile type of a client's `betterSupabase.userMetadata(schema)`, `unknown` without one. */
|
|
46
|
+
type ProfileOf<B extends ClientLike> = B["betterSupabase"] extends {
|
|
49
47
|
readonly userMetadataSchema: StandardSchemaV1<unknown, infer P> | undefined;
|
|
50
48
|
} ? P : unknown;
|
|
51
49
|
/**
|
|
52
50
|
* Hooks typed for your schema.
|
|
53
51
|
*
|
|
54
52
|
* ```ts
|
|
55
|
-
* export const { useDb, useQueries, useAuth } = createHooks<typeof
|
|
53
|
+
* export const { useDb, useQueries, useAuth } = createHooks<typeof bs>();
|
|
56
54
|
* ```
|
|
57
55
|
*/
|
|
58
|
-
declare function createHooks<B extends
|
|
56
|
+
declare function createHooks<B extends ClientLike>(): BetterHooks<B>;
|
|
59
57
|
interface BroadcastOptions extends Omit<SubscribeOptions, "onStatus"> {
|
|
60
58
|
/**
|
|
61
59
|
* Refetch after each message: table keys (every query that read one of
|
|
@@ -88,7 +86,7 @@ interface LiveQueryHookOptions {
|
|
|
88
86
|
* resubscribes when the spec, tenant or user changes.
|
|
89
87
|
*
|
|
90
88
|
* ```ts
|
|
91
|
-
* const spec =
|
|
89
|
+
* const spec = betterSupabase.spec.customers.findMany({ include: { notes: true } });
|
|
92
90
|
* const { data } = useQuery(q.$spec(spec));
|
|
93
91
|
* useLiveQuery(spec);
|
|
94
92
|
* ```
|
|
@@ -108,13 +106,13 @@ interface LiveCount {
|
|
|
108
106
|
/**
|
|
109
107
|
* A count that stays current: refetches only the `count` spec (a HEAD
|
|
110
108
|
* request) after each debounced change to a table it reads, and after the
|
|
111
|
-
* channel rejoins. Takes a spec or a `
|
|
109
|
+
* channel rejoins. Takes a spec or a `bs.liveCount()` seed; pass `null` to
|
|
112
110
|
* pause. Doesn't need a `QueryClient`.
|
|
113
111
|
*
|
|
114
112
|
* ```tsx
|
|
115
|
-
* const { count } = useLiveCount(seed); // seed = await
|
|
113
|
+
* const { count } = useLiveCount(seed); // seed = await bs.liveCount(spec)
|
|
116
114
|
* ```
|
|
117
115
|
*/
|
|
118
116
|
declare function useLiveCount(source: QuerySpec<string, "count", number> | LiveCountSeed | null | undefined, options?: LiveCountHookOptions): LiveCount;
|
|
119
117
|
//#endregion
|
|
120
|
-
export {
|
|
118
|
+
export { ClaimsOf as a, LiveCountHookOptions as c, createHooks as d, useAuth as f, useSupabase as g, useLiveQuery as h, BroadcastOptions as i, LiveQueryHookOptions as l, useLiveCount as m, BetterSupabaseProvider as n, ClientLike as o, useBroadcast as p, BetterSupabaseProviderProps as r, LiveCount as s, BetterHooks as t, ProfileOf as u };
|