better-supabase 0.2.0 → 0.3.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 +34 -31
- package/dist/base64-CQeB1eSf.js +42 -0
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +4 -14
- package/dist/{compiler-CZ9nIuqy.d.ts → compiler-2zqGzxVZ.d.ts} +2 -2
- package/dist/config/index.d.ts +4 -3
- package/dist/config/index.js +5 -3
- package/dist/config-Be4GwsqI.js +129 -0
- package/dist/{index-DOsjFvMf.d.ts → config-C1CYcm6o.d.ts} +49 -49
- package/dist/{config-ClDXmVtY.js → config-DOsGYSiT.js} +14 -137
- package/dist/{define-BXMYD9AY.js → define-DF341GDj.js} +71 -51
- package/dist/{define-e3zI4_4g.d.ts → define-DUbydmJa.d.ts} +10 -9
- package/dist/edge/index.d.ts +4 -4
- package/dist/edge/index.js +4 -4
- package/dist/{entitlements-Dng-ctuT.d.ts → entitlements-DvJXoI02.d.ts} +1 -1
- package/dist/env/index.js +203 -1
- package/dist/events/index.d.ts +1 -1
- package/dist/events/index.js +139 -1
- package/dist/{events-DOt3nSO-.d.ts → events-Do8jXh4D.d.ts} +3 -3
- package/dist/{executor-BYXQ9AGn.d.ts → executor-B26lgbHD.d.ts} +2 -2
- package/dist/{executor-DeppvirK.js → executor-C57jx2pn.js} +7 -2
- package/dist/{executor-CGdNQ27g.d.ts → executor-DynmIWhq.d.ts} +2 -2
- package/dist/hono/index.d.ts +5 -5
- package/dist/hono/index.js +4 -4
- package/dist/{index-CBDufH1M.d.ts → index-BNsABq7P.d.ts} +8 -8
- package/dist/{index-Bw1bwtC0.d.ts → index-CADlNHK-.d.ts} +27 -14
- package/dist/{index-DuP14VHo.d.ts → index-CTTMS6_1.d.ts} +19 -8
- package/dist/index-Cf-D2jk1.d.ts +50 -0
- package/dist/{index-K-3WM2Hp.d.ts → index-CriFFJ_p.d.ts} +4 -4
- package/dist/{index-Ciu5Xlml.d.ts → index-GV77NpRP.d.ts} +5 -4
- package/dist/{index-D4v7sncu.d.ts → index-UJ-LUxtI.d.ts} +3 -3
- package/dist/{index-CG69s15v.d.ts → index-kpgopLNv.d.ts} +4 -3
- package/dist/{index-DbxFr7XG.d.ts → index-qriRM9cZ.d.ts} +7 -2
- package/dist/index.d.ts +12 -12
- package/dist/index.js +16 -7
- package/dist/jobs/index.d.ts +25 -24
- package/dist/jobs/index.js +16 -10
- package/dist/{json-schema-BpYqkCKf.js → json-schema--aOXAGfU.js} +7 -2
- package/dist/lint/index.js +1 -1
- package/dist/list/index.d.ts +2 -2
- package/dist/list/index.js +128 -93
- package/dist/{live-DJyZMD6w.d.ts → live-QE3UPbGm.d.ts} +16 -6
- package/dist/mcp/index.d.ts +24 -17
- package/dist/mcp/index.js +41 -12
- package/dist/{mfa-CpLfJSde.js → mfa-BxoR1RkN.js} +74 -19
- package/dist/{mfa-TVia554c.d.ts → mfa-x3V3vGh-.d.ts} +1 -1
- package/dist/next/index.d.ts +39 -26
- package/dist/next/index.js +28 -23
- package/dist/openapi/index.d.ts +1 -1
- package/dist/openapi/index.js +57 -34
- package/dist/orpc/index.d.ts +6 -6
- package/dist/orpc/index.js +3 -3
- package/dist/otel/index.d.ts +2 -2
- package/dist/otel/index.js +4 -2
- package/dist/{permdock-sql-CghlHPuH.js → permdock-sql-Bta_G0v-.js} +11 -6
- package/dist/permdock-sql-DImJULY-.d.ts +17 -0
- package/dist/{plugin-C4TR9J0Z.d.ts → plugin-DVRB8qNE.d.ts} +5 -4
- package/dist/plugins/actor/index.d.ts +1 -1
- package/dist/plugins/rules/index.d.ts +2 -2
- package/dist/plugins/rules/index.js +4 -4
- package/dist/plugins/soft-delete/index.d.ts +3 -3
- package/dist/plugins/soft-delete/index.js +2 -2
- package/dist/plugins/tenant/index.d.ts +2 -2
- package/dist/plugins/timestamps/index.d.ts +1 -1
- package/dist/plugins/timestamps/index.js +1 -1
- package/dist/plugins/validation/index.d.ts +2 -2
- package/dist/pool-B9qsLyFR.d.ts +71 -0
- package/dist/postgres/index.d.ts +10 -5
- package/dist/postgres/index.js +36 -16
- package/dist/{postgrest-cAEQ_IQN.d.ts → postgrest-DmBlevh-.d.ts} +1 -1
- package/dist/{problem-BTkTtRV9.js → problem-CGt1ADix.js} +10 -4
- package/dist/{problem-DlgB0lYl.d.ts → problem-Z2RTQjZD.d.ts} +6 -4
- package/dist/query/index.d.ts +2 -2
- package/dist/query/index.js +1 -1
- package/dist/{query-ChvDbsDE.js → query-BdKLTPlj.js} +2 -2
- package/dist/react/index.d.ts +7 -7
- package/dist/react/index.js +2 -2
- package/dist/react/server.d.ts +3 -3
- package/dist/react/server.js +1 -1
- package/dist/react/session.d.ts +21 -2
- package/dist/react/session.js +19 -1
- package/dist/{react-C83BdBGt.js → react-DNBYNazu.js} +20 -16
- package/dist/{read-set-4lI3W_gZ.js → read-set-D0qBcboM.js} +3 -3
- package/dist/realtime/index.d.ts +4 -3
- package/dist/realtime/index.js +7 -6
- package/dist/{resolve-CjHILk2w.d.ts → resolve-BRciX2jK.d.ts} +6 -5
- package/dist/{resource-CgZa3YG_.d.ts → resource-CwVgiI5q.d.ts} +16 -7
- package/dist/{resource-BHEWmQJK.js → resource-D30rp0GN.js} +38 -16
- package/dist/{respond-DMcbQwfN.js → respond-C44EZAJo.js} +35 -19
- package/dist/{respond-Dne1ARSa.d.ts → respond-DAkPIdTH.d.ts} +15 -9
- package/dist/{result-CTkCZ6JA.d.ts → result-c2OYgCV2.d.ts} +13 -8
- package/dist/server/index.d.ts +35 -10
- package/dist/server/index.js +38 -9
- package/dist/{simplify-bwJpLnaS.js → simplify-LakqK1vH.js} +10 -16
- package/dist/sql/index.d.ts +139 -0
- package/dist/sql/index.js +1983 -0
- package/dist/{sql-DTjA1LPj.js → sql-BIgk12ew.js} +72 -16
- package/dist/ssr/index.d.ts +4 -4
- package/dist/ssr/index.js +2 -2
- package/dist/{standard-CyhRzctb.d.ts → standard-eudBiRk1.d.ts} +1 -1
- package/dist/{stats-C5W-rBfR.js → stats-D78UQ8Yu.js} +8 -7
- package/dist/storage/index.d.ts +4 -3
- package/dist/storage/index.js +530 -1
- package/dist/{tables-rZxRxNPW.js → tables-Dhha3BXM.js} +5 -5
- package/dist/temporal-BwshQk0W.js +27 -0
- package/dist/temporal-required-CMxeSVPo.js +19 -0
- package/dist/testing/index.d.ts +13 -11
- package/dist/testing/index.js +128 -29
- package/dist/{types-C83v5SQz.d.ts → types-DONv9SHN.d.ts} +14 -11
- package/dist/{version-Cqq1m3UY.js → version-BfpG2gks.js} +1 -1
- package/dist/{view-D1s0wa0T.d.ts → view-3Po_PVlr.d.ts} +34 -4
- package/dist/{view-aOYizTeE.js → view-wZ-9qIUz.js} +11 -2
- package/dist/webhooks/index.d.ts +18 -16
- package/dist/webhooks/index.js +1 -1
- package/dist/{webhooks-DMipjhSP.js → webhooks-CAIFQSTM.js} +11 -9
- package/package.json +24 -28
- 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 +23 -11
- package/skills/better-supabase/references/plugins.md +21 -18
- package/skills/better-supabase/references/troubleshooting.md +15 -15
- package/skills/better-supabase-api/SKILL.md +7 -7
- package/skills/better-supabase-api/references/adapters.md +30 -25
- package/skills/better-supabase-testing/SKILL.md +11 -8
- package/bin/better-supabase.js +0 -4
- package/dist/casing-da0uRTqt.js +0 -14
- package/dist/cli/bin.d.ts +0 -1
- package/dist/cli/bin.js +0 -15
- package/dist/cli/index.d.ts +0 -226
- package/dist/cli/index.js +0 -3
- 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/env/index.js
CHANGED
|
@@ -1,2 +1,204 @@
|
|
|
1
|
-
|
|
1
|
+
//#region src/env/index.ts
|
|
2
|
+
const PREFIXES = [
|
|
3
|
+
"",
|
|
4
|
+
"NEXT_PUBLIC_",
|
|
5
|
+
"VITE_",
|
|
6
|
+
"PUBLIC_",
|
|
7
|
+
"EXPO_PUBLIC_",
|
|
8
|
+
"NUXT_PUBLIC_"
|
|
9
|
+
];
|
|
10
|
+
const ENV_VARIABLES = {
|
|
11
|
+
url: PREFIXES.map((prefix) => `${prefix}SUPABASE_URL`),
|
|
12
|
+
publishableKey: PREFIXES.flatMap((prefix) => [`${prefix}SUPABASE_PUBLISHABLE_KEY`, `${prefix}SUPABASE_PUBLISHABLE_DEFAULT_KEY`]),
|
|
13
|
+
secretKey: ["SUPABASE_SECRET_KEY"],
|
|
14
|
+
dbUrl: ["SUPABASE_DB_URL", "DATABASE_URL"],
|
|
15
|
+
jwksUrl: ["SUPABASE_JWKS_URL"],
|
|
16
|
+
readUrl: ["SUPABASE_READ_URL"]
|
|
17
|
+
};
|
|
18
|
+
const LOOPBACK = /^(localhost|127(?:\.\d{1,3}){3}|\[::1\])$/;
|
|
19
|
+
const HOSTED = /^([a-z0-9]{20})\.supabase\.co$/;
|
|
20
|
+
var EnvValidationError = class extends Error {
|
|
21
|
+
name = "EnvValidationError";
|
|
22
|
+
issues;
|
|
23
|
+
constructor(issues) {
|
|
24
|
+
super(`Invalid Supabase environment:\n${issues.map((issue) => ` - ${issue.variables[0] ?? "env"}: ${issue.message}`).join("\n")}`);
|
|
25
|
+
this.issues = issues;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
function first(source, names) {
|
|
29
|
+
for (const name of names) {
|
|
30
|
+
const value = source[name]?.trim();
|
|
31
|
+
if (value) return value;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/** `SUPABASE_*_KEYS` JSON objects, as `@supabase/server` reads them. */
|
|
35
|
+
function keySet(source, name, kind, issues) {
|
|
36
|
+
const raw = source[name];
|
|
37
|
+
if (!raw) return void 0;
|
|
38
|
+
let parsed;
|
|
39
|
+
try {
|
|
40
|
+
parsed = JSON.parse(raw);
|
|
41
|
+
} catch {
|
|
42
|
+
parsed = void 0;
|
|
43
|
+
}
|
|
44
|
+
const entries = typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? Object.entries(parsed) : [];
|
|
45
|
+
if (entries.length === 0 || entries.some(([, value]) => typeof value !== "string")) {
|
|
46
|
+
issues.push({
|
|
47
|
+
variables: [name],
|
|
48
|
+
message: "must be a JSON object of key names to keys"
|
|
49
|
+
});
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const keys = {};
|
|
53
|
+
for (const [key, value] of entries) if (checkKey(value, kind, [name], issues)) keys[key] = value;
|
|
54
|
+
return keys;
|
|
55
|
+
}
|
|
56
|
+
function preferred(keys) {
|
|
57
|
+
if (!keys) return void 0;
|
|
58
|
+
return keys["default"] ?? Object.values(keys)[0];
|
|
59
|
+
}
|
|
60
|
+
function checkUrl(value, variables, issues) {
|
|
61
|
+
let url;
|
|
62
|
+
try {
|
|
63
|
+
url = new URL(value);
|
|
64
|
+
} catch {
|
|
65
|
+
issues.push({
|
|
66
|
+
variables,
|
|
67
|
+
message: "is not a valid URL"
|
|
68
|
+
});
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (url.protocol === "https:") return url;
|
|
72
|
+
if (url.protocol === "http:" && LOOPBACK.test(url.hostname)) return url;
|
|
73
|
+
issues.push({
|
|
74
|
+
variables,
|
|
75
|
+
message: "must use https (http is only allowed for localhost / 127.0.0.1 / [::1])"
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
function checkKey(value, kind, variables, issues) {
|
|
79
|
+
if (value.startsWith(`sb_${kind}_`)) return true;
|
|
80
|
+
if (value.startsWith("eyJ")) issues.push({
|
|
81
|
+
variables,
|
|
82
|
+
message: `is a legacy JWT key; use the new sb_${kind}_ key (Project Settings > API Keys)`
|
|
83
|
+
});
|
|
84
|
+
else if (value.startsWith(kind === "publishable" ? "sb_secret_" : "sb_publishable_")) issues.push({
|
|
85
|
+
variables,
|
|
86
|
+
message: kind === "publishable" ? "holds a secret key; never expose sb_secret_ keys as publishable" : "holds a publishable key, not a secret key"
|
|
87
|
+
});
|
|
88
|
+
else issues.push({
|
|
89
|
+
variables,
|
|
90
|
+
message: `must start with sb_${kind}_`
|
|
91
|
+
});
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Reads and validates Supabase settings. Accepts the `SUPABASE_`,
|
|
96
|
+
* `NEXT_PUBLIC_`, `VITE_`, `PUBLIC_`, `EXPO_PUBLIC_` and `NUXT_PUBLIC_`
|
|
97
|
+
* spellings. Issues name variables, never values.
|
|
98
|
+
*/
|
|
99
|
+
function parseEnv(source, options = {}) {
|
|
100
|
+
const issues = [];
|
|
101
|
+
const required = new Set(options.require ?? []);
|
|
102
|
+
const rawUrl = first(source, ENV_VARIABLES.url);
|
|
103
|
+
const url = rawUrl ? checkUrl(rawUrl, ENV_VARIABLES.url, issues) : void 0;
|
|
104
|
+
if (!rawUrl) issues.push({
|
|
105
|
+
variables: ENV_VARIABLES.url,
|
|
106
|
+
message: "is not set"
|
|
107
|
+
});
|
|
108
|
+
const publishableKeys = keySet(source, "SUPABASE_PUBLISHABLE_KEYS", "publishable", issues);
|
|
109
|
+
const singlePublishable = first(source, ENV_VARIABLES.publishableKey);
|
|
110
|
+
const publishableKey = singlePublishable ?? preferred(publishableKeys);
|
|
111
|
+
if (!publishableKey) issues.push({
|
|
112
|
+
variables: ENV_VARIABLES.publishableKey,
|
|
113
|
+
message: "is not set"
|
|
114
|
+
});
|
|
115
|
+
else if (singlePublishable) checkKey(publishableKey, "publishable", ENV_VARIABLES.publishableKey, issues);
|
|
116
|
+
const namedSecrets = keySet(source, "SUPABASE_SECRET_KEYS", "secret", issues);
|
|
117
|
+
const singleSecret = first(source, ENV_VARIABLES.secretKey);
|
|
118
|
+
const secretKey = singleSecret ?? preferred(namedSecrets);
|
|
119
|
+
if (singleSecret) checkKey(singleSecret, "secret", ENV_VARIABLES.secretKey, issues);
|
|
120
|
+
else if (!secretKey && required.has("secretKey")) issues.push({
|
|
121
|
+
variables: ENV_VARIABLES.secretKey,
|
|
122
|
+
message: "is not set"
|
|
123
|
+
});
|
|
124
|
+
const dbUrl = first(source, ENV_VARIABLES.dbUrl);
|
|
125
|
+
if (dbUrl && !/^postgres(ql)?:\/\//.test(dbUrl)) issues.push({
|
|
126
|
+
variables: ENV_VARIABLES.dbUrl,
|
|
127
|
+
message: "must be a postgres:// connection string"
|
|
128
|
+
});
|
|
129
|
+
else if (!dbUrl && required.has("dbUrl")) issues.push({
|
|
130
|
+
variables: ENV_VARIABLES.dbUrl,
|
|
131
|
+
message: "is not set"
|
|
132
|
+
});
|
|
133
|
+
const rawJwks = first(source, ENV_VARIABLES.jwksUrl);
|
|
134
|
+
const jwksOverride = rawJwks ? checkUrl(rawJwks, ENV_VARIABLES.jwksUrl, issues) : void 0;
|
|
135
|
+
const rawRead = first(source, ENV_VARIABLES.readUrl);
|
|
136
|
+
const readUrl = rawRead ? checkUrl(rawRead, ENV_VARIABLES.readUrl, issues) : void 0;
|
|
137
|
+
if (issues.length > 0 || !url || !publishableKey) return {
|
|
138
|
+
ok: false,
|
|
139
|
+
issues
|
|
140
|
+
};
|
|
141
|
+
const base = url.href.replace(/\/+$/, "");
|
|
142
|
+
const projectRef = HOSTED.exec(url.hostname)?.[1];
|
|
143
|
+
return {
|
|
144
|
+
ok: true,
|
|
145
|
+
env: {
|
|
146
|
+
url: base,
|
|
147
|
+
publishableKey,
|
|
148
|
+
jwksUrl: jwksOverride ?? new URL(`${base}/auth/v1/.well-known/jwks.json`),
|
|
149
|
+
...secretKey ? {
|
|
150
|
+
secretKey,
|
|
151
|
+
secretKeys: {
|
|
152
|
+
...namedSecrets,
|
|
153
|
+
default: secretKey
|
|
154
|
+
}
|
|
155
|
+
} : {},
|
|
156
|
+
...dbUrl ? { dbUrl } : {},
|
|
157
|
+
...projectRef ? { projectRef } : {},
|
|
158
|
+
...readUrl ? { readUrl: readUrl.href.replace(/\/+$/, "") } : {}
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function processEnv() {
|
|
163
|
+
return globalThis.process?.env ?? {};
|
|
164
|
+
}
|
|
165
|
+
/** Like `parseEnv`, but throws `EnvValidationError`. Reads `process.env` by default. */
|
|
166
|
+
function loadEnv(source = processEnv(), options = {}) {
|
|
167
|
+
const result = parseEnv(source, options);
|
|
168
|
+
if (!result.ok) throw new EnvValidationError(result.issues);
|
|
169
|
+
return result.env;
|
|
170
|
+
}
|
|
171
|
+
/** The env validator as a Standard Schema, for t3-env, framework config or any validator slot. */
|
|
172
|
+
function envSchema(options = {}) {
|
|
173
|
+
return { "~standard": {
|
|
174
|
+
version: 1,
|
|
175
|
+
vendor: "better-supabase",
|
|
176
|
+
validate(value) {
|
|
177
|
+
if (typeof value !== "object" || value === null) return { issues: [{ message: "Expected an environment object" }] };
|
|
178
|
+
const result = parseEnv(value, options);
|
|
179
|
+
if (result.ok) return { value: result.env };
|
|
180
|
+
return { issues: result.issues.map((issue) => ({
|
|
181
|
+
message: `${issue.variables[0] ?? "env"} ${issue.message}`,
|
|
182
|
+
path: issue.variables.slice(0, 1)
|
|
183
|
+
})) };
|
|
184
|
+
}
|
|
185
|
+
} };
|
|
186
|
+
}
|
|
187
|
+
/** The parts that are safe in client bundles. */
|
|
188
|
+
function publicEnv(env) {
|
|
189
|
+
return {
|
|
190
|
+
url: env.url,
|
|
191
|
+
publishableKey: env.publishableKey
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
/** Converts to `@supabase/server`'s `SupabaseEnv` (the `env` option of `withSupabase`). */
|
|
195
|
+
function toServerEnv(env) {
|
|
196
|
+
return {
|
|
197
|
+
url: env.url,
|
|
198
|
+
publishableKeys: { default: env.publishableKey },
|
|
199
|
+
secretKeys: env.secretKeys ?? (env.secretKey ? { default: env.secretKey } : {}),
|
|
200
|
+
jwks: env.jwksUrl
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
//#endregion
|
|
2
204
|
export { ENV_VARIABLES, EnvValidationError, envSchema, loadEnv, parseEnv, publicEnv, toServerEnv };
|
package/dist/events/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as HttpMode, c as RowEventData, d as httpSink, f as isCloudEvent, i as ForwardOptions, l as forwardMutations, m as toHttp, n as CloudEventOptions, o as HttpSinkOptions, p as toCloudEvents, r as EventSink, s as ROW_EVENT_TYPES, t as CloudEvent, u as fromHttp } from "../index-
|
|
1
|
+
import { a as HttpMode, c as RowEventData, d as httpSink, f as isCloudEvent, i as ForwardOptions, l as forwardMutations, m as toHttp, n as CloudEventOptions, o as HttpSinkOptions, p as toCloudEvents, r as EventSink, s as ROW_EVENT_TYPES, t as CloudEvent, u as fromHttp } from "../index-GV77NpRP.js";
|
|
2
2
|
export { CloudEvent, CloudEventOptions, EventSink, ForwardOptions, HttpMode, HttpSinkOptions, ROW_EVENT_TYPES, RowEventData, forwardMutations, fromHttp, httpSink, isCloudEvent, toCloudEvents, toHttp };
|
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(sb, sink, options) {
|
|
48
|
+
return sb.events.on("mutation", (notice) => {
|
|
49
|
+
if (options.filter && !options.filter(notice)) return;
|
|
50
|
+
const events = toCloudEvents(notice, {
|
|
51
|
+
...options,
|
|
52
|
+
meta: sb.meta
|
|
53
|
+
});
|
|
54
|
+
if (events.length === 0) return;
|
|
55
|
+
const report = (error) => {
|
|
56
|
+
(options.onError ?? ((cause) => {
|
|
57
|
+
sb.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-DynmIWhq.js";
|
|
2
|
+
import { p as DbError } from "./result-c2OYgCV2.js";
|
|
3
|
+
import { h as RequestContext, l as MutationKind } from "./plugin-DVRB8qNE.js";
|
|
4
4
|
//#region src/core/logger.d.ts
|
|
5
5
|
type LogFields = Readonly<Record<string, unknown>>;
|
|
6
6
|
/**
|
|
@@ -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-DynmIWhq.js";
|
|
2
|
+
import { y as RawDbError } from "./result-c2OYgCV2.js";
|
|
3
3
|
//#region src/postgres/executor.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* Anything that runs parameterized SQL: `ctx.postgres` from
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { c as mapDbError, n as dbError } from "./errors-Bl3rkOzo.js";
|
|
2
2
|
import { i as ok, n as err, o as toDbError } from "./result-DWXatkd6.js";
|
|
3
|
-
import { t as compileSql } from "./sql-
|
|
3
|
+
import { t as compileSql } from "./sql-BIgk12ew.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 { 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-c2OYgCV2.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
|
package/dist/hono/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
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 { d as ServerContext, f as ServerOptions, l as BetterServer, n as GuardOptions } from "../respond-
|
|
5
|
-
import {
|
|
1
|
+
import { N as TableKey, n as AnyModels, t as AnyFunctions } from "../types-DONv9SHN.js";
|
|
2
|
+
import { t as BetterSupabase } from "../define-DUbydmJa.js";
|
|
3
|
+
import { r as AuthState } from "../resolve-BRciX2jK.js";
|
|
4
|
+
import { d as ServerContext, f as ServerOptions, l as BetterServer, n as GuardOptions } from "../respond-DAkPIdTH.js";
|
|
5
|
+
import { a as ResourceRouteOptions } from "../resource-CwVgiI5q.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`. */
|
package/dist/hono/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { n as dbError, r as dbErrorOf } from "../errors-Bl3rkOzo.js";
|
|
2
|
-
import { a as problemResponse } from "../problem-
|
|
3
|
-
import { i as respond, n as guard, o as createServer, s as extendServer, t as defaultExpose } from "../respond-
|
|
4
|
-
import { t as defineResource } from "../resource-
|
|
2
|
+
import { a as problemResponse } from "../problem-CGt1ADix.js";
|
|
3
|
+
import { i as respond, n as guard, o as createServer, s as extendServer, t as defaultExpose } from "../respond-C44EZAJo.js";
|
|
4
|
+
import { t as defineResource } from "../resource-D30rp0GN.js";
|
|
5
5
|
import { Hono } from "hono";
|
|
6
6
|
//#region src/hono/index.ts
|
|
7
7
|
function contextOf(c) {
|
|
@@ -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
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { p as DbError } from "./result-
|
|
2
|
-
import { Z as QuerySpec } from "./define-
|
|
3
|
-
import { i as BrowserAuth, t as AuthSnapshot } from "./index-
|
|
1
|
+
import { p as DbError } from "./result-c2OYgCV2.js";
|
|
2
|
+
import { Z as QuerySpec } from "./define-DUbydmJa.js";
|
|
3
|
+
import { i as BrowserAuth, t as AuthSnapshot } from "./index-UJ-LUxtI.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-
|
|
8
|
-
import "./session
|
|
5
|
+
import { t as AuthSession } from "./view-3Po_PVlr.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-QE3UPbGm.js";
|
|
7
|
+
import "./entitlements-DvJXoI02.js";
|
|
8
|
+
import "./react/session.js";
|
|
9
9
|
import { SupabaseClient } from "@supabase/supabase-js";
|
|
10
10
|
import { ReactNode } from "react";
|
|
11
11
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
12
12
|
import { QueryClient } from "@tanstack/query-core";
|
|
13
|
-
//#region src/react/
|
|
13
|
+
//#region src/react/hooks.d.ts
|
|
14
14
|
/** The parts of `createBrowser()` the provider needs. */
|
|
15
15
|
interface BrowserLike {
|
|
16
16
|
readonly sb: LiveSource;
|
|
@@ -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-DynmIWhq.js";
|
|
3
|
+
import { b as ValidationIssue, t as AsyncResult } from "./result-c2OYgCV2.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-DUbydmJa.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
|
/**
|
|
@@ -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>(sb: BetterSupabase<M, D, Fn, E>, table: T, config: ListQueryConfig<M, T, S, F, C>): ListDefinition<M, T, E, S, F, C>;
|
|
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">(sb: 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,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-c2OYgCV2.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;
|