better-supabase 0.4.0 → 0.5.1
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 +135 -16
- package/dist/actor-DaJjqD5R.d.ts +43 -0
- package/dist/channel-FQW-CnGR.d.ts +70 -0
- package/dist/cli/bin.js +1 -1
- package/dist/cli/codemod-DqV7612m.js +388 -0
- package/dist/cli/{config-D9R0o1LH.js → config-CPVpXP5I.js} +78 -8
- package/dist/cli/{config-SSKSTUl2.js → config-dtoekM0v.js} +1 -1
- package/dist/cli/{read-sets-CMa29t9a.js → diff-SjkO5rNr.js} +1 -220
- package/dist/cli/{dist-CFgGknmZ.js → dist-DBPdlPYK.js} +4 -4
- package/dist/cli/{dist-B1MvLv-M.js → dist-DreZ24Sz.js} +1 -1
- package/dist/cli/{dist-Du7VnEUG.js → dist-xn4wlboc.js} +112 -1
- package/dist/cli/{doctor-BOym3mbL.js → doctor-C_A-KEAz.js} +533 -50
- package/dist/cli/{env-BZ0uy8XK.js → env-DmKv_G4A.js} +1 -1
- package/dist/cli/gen-CIdSQZad.js +2 -0
- package/dist/cli/{gen-Ccn_p90L.js → gen-CjElD1Vw.js} +321 -88
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +4 -4
- package/dist/cli/{init-ClnxRYBF.js → init-DMa6M7hV.js} +2 -2
- package/dist/cli/{introspect-DxqfkVIv.js → introspect-BKlaIu_M.js} +2 -2
- package/dist/cli/{openapi-8WHjxiTJ.js → openapi-Cp5Fyta-.js} +1 -1
- package/dist/cli/read-sets-VQcPgxU9.js +436 -0
- package/dist/cli/{run-CP20LKk_.js → run-Bo_NLT5n.js} +16 -12
- package/dist/cli/{seed-5J6eu_Xg.js → seed-CUlqWh-8.js} +1 -1
- package/dist/cli/sql-CVx-1O8G.js +373 -0
- package/dist/cli/{version-5mW_8sY1.js → version-BCtlgDQx.js} +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +3 -10
- package/dist/clone-BfgmLU90.js +21 -0
- package/dist/{compiler-DB3aSFOA.d.ts → compiler-BXH_aZ7N.d.ts} +2 -2
- package/dist/config/index.d.ts +4 -4
- package/dist/config/index.js +4 -4
- package/dist/{config-C1CYcm6o.d.ts → config-Bv7gvfzC.d.ts} +185 -11
- package/dist/{config-CbvFmGD8.js → config-DreERlKo.js} +1 -1
- package/dist/{define-CRS2MRV3.d.ts → define-Bp_BZa5h.d.ts} +11 -7
- package/dist/{define-Bh0AqTik.js → define-cNKzqwL_.js} +202 -16
- package/dist/edge/index.d.ts +16 -5
- package/dist/edge/index.js +10 -7
- package/dist/{entitlements-Dm-FHpNH.d.ts → entitlements-BiJZ4t49.d.ts} +1 -1
- package/dist/env/index.d.ts +2 -2
- package/dist/env/index.js +34 -1
- package/dist/{errors-L0xK41y_.js → errors-DtwXa6b8.js} +1 -0
- package/dist/events/index.d.ts +3 -2
- package/dist/events/index.js +65 -12
- package/dist/events-aIO7vsIk.d.ts +230 -0
- package/dist/{result-CqEsbgYs.d.ts → executor-BunPOO-k.d.ts} +200 -1
- package/dist/{executor-CZ7ImRiD.d.ts → executor-Cghdstwu.d.ts} +1 -2
- package/dist/{executor-CVt6dD0B.js → executor-XycESQPH.js} +3 -3
- package/dist/hono/index.d.ts +17 -6
- package/dist/hono/index.js +40 -5
- package/dist/impersonation-BgSHAYLf.js +133 -0
- package/dist/{resolve-BAqQcwMW.d.ts → impersonation-BmePsuxz.d.ts} +36 -6
- package/dist/{index-BhHEC04r.d.ts → index-C7f8cOBM.d.ts} +2 -2
- package/dist/{index-Dy6kuF0P.d.ts → index-CLg87OBW.d.ts} +6 -1
- package/dist/{index-Cf-D2jk1.d.ts → index-CWMDdEr4.d.ts} +2 -2
- package/dist/{index-D5IfUIDh.d.ts → index-DOESKG27.d.ts} +33 -8
- package/dist/{index-DFmOpBGC.d.ts → index-DgILJRXj.d.ts} +35 -9
- package/dist/{index-RlLXd8dL.d.ts → index-DmXSxbLi.d.ts} +4 -4
- package/dist/{index-BBdvo-5d.d.ts → index-Dp2VDwog.d.ts} +49 -6
- package/dist/{index-528Ugmyp.d.ts → index-SaTsEfGP.d.ts} +76 -8
- package/dist/{index-BAy4tsAP.d.ts → index-fag8VJZ2.d.ts} +3 -4
- package/dist/index.d.ts +30 -13
- package/dist/index.js +10 -9
- package/dist/jobs/index.d.ts +147 -101
- package/dist/jobs/index.js +744 -237
- package/dist/{json-schema-DP6V7cbr.js → json-schema-m1fiwKgN.js} +22 -1
- 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-BpxDfnbg.js → kits-CGLLrNPd.js} +37 -9
- package/dist/list/index.d.ts +1 -1
- package/dist/{live-BHakSWWo.d.ts → live-D9W2M5aO.d.ts} +6 -5
- package/dist/mcp/index.d.ts +25 -8
- package/dist/mcp/index.js +23 -14
- package/dist/{mfa-C7MNQ1Ob.d.ts → mfa-Cu96oJdw.d.ts} +2 -16
- package/dist/next/index.d.ts +31 -10
- package/dist/next/index.js +136 -27
- 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 +2 -2
- package/dist/orgs/index.d.ts +132 -0
- package/dist/orgs/index.js +245 -0
- package/dist/orpc/index.d.ts +28 -8
- package/dist/orpc/index.js +16 -3
- package/dist/otel/index.d.ts +21 -2
- package/dist/otel/index.js +50 -9
- 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-bNhvJwRh.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 +15 -10
- 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 +2 -2
- package/dist/plugins/tenant/index.js +38 -20
- 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-DFwEQwmN.d.ts → pool-C9voM4Kw.d.ts} +17 -6
- package/dist/postgres/index.d.ts +5 -5
- package/dist/postgres/index.js +20 -11
- package/dist/{postgrest-Bc-0Xw62.d.ts → postgrest-CruYKAvV.d.ts} +1 -1
- package/dist/{problem-BihjPR0t.js → problem-BW1KhzSG.js} +2 -1
- package/dist/{problem-U8UEKccv.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-BMMSWbI7.js → query-CCbo44lG.js} +2 -2
- 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 +14 -3
- package/dist/react/server.js +10 -2
- package/dist/react/session.d.ts +7 -1
- package/dist/react/session.js +9 -1
- package/dist/{react-CQHMaESR.js → react-C_s7kiNz.js} +103 -11
- package/dist/{read-set-FWK8qQ58.js → read-set-DQFt1N_Y.js} +22 -1
- package/dist/realtime/index.d.ts +3 -3
- package/dist/realtime/index.js +5 -4
- package/dist/{refresh-BwYkyNOQ.d.ts → refresh-CFPX0Qft.d.ts} +1 -1
- package/dist/{resource-BJcaIfxi.d.ts → resource-DNRFP7KR.d.ts} +5 -5
- package/dist/{resource-My_y-Syh.js → resource-mqfJW4YF.js} +22 -7
- package/dist/respond-BR1fJWOB.d.ts +377 -0
- package/dist/{result-Bj6yt33D.js → result-BKiliPY7.js} +1 -1
- package/dist/server/index.d.ts +15 -12
- package/dist/server/index.js +11 -17
- package/dist/{server-BA75B0mT.js → server-B1mZ7CgQ.js} +259 -37
- package/dist/{shared-DnoCdMDr.js → shared-D1dRUrAg.js} +16 -2
- package/dist/signers-Cc6r98SW.d.ts +162 -0
- package/dist/{simplify-Cqvfr9dj.js → simplify-_0oUKqCd.js} +4 -115
- package/dist/sql/index.d.ts +283 -11
- package/dist/sql/index.js +8705 -775
- package/dist/{sql-B34HU2DI.js → sql-Coh6n4cC.js} +9 -2
- package/dist/ssr/index.d.ts +4 -4
- package/dist/ssr/index.js +1 -2
- package/dist/{standard-BRyBGTtp.js → standard-D7r6dMUu.js} +6 -3
- package/dist/{standard-BsaCYQJc.d.ts → standard-J4YgMOLm.d.ts} +1 -1
- package/dist/{stats-C_YxA2r1.js → stats-Bqob3WqT.js} +4 -4
- package/dist/storage/index.d.ts +4 -4
- package/dist/storage/index.js +155 -26
- package/dist/support-CEPwlw2G.d.ts +89 -0
- package/dist/support-view-B8SlToQX.d.ts +17 -0
- package/dist/support-view-g4YLBeqX.js +18 -0
- package/dist/support-wHnK6zp-.js +293 -0
- package/dist/{permdock-sql-C4KFzLc9.js → template-TxrGI5jR.js} +2 -31
- package/dist/{temporal-BwshQk0W.js → temporal-CVlIV2DZ.js} +17 -4
- package/dist/{temporal-required-CBNHT6ni.js → temporal-required-CAt4Dc9Z.js} +2 -2
- package/dist/testing/index.d.ts +132 -10
- package/dist/testing/index.js +476 -8
- package/dist/{types-DONv9SHN.d.ts → types-Dnf_wRDz.d.ts} +26 -3
- package/dist/user-change-CHFEUro1.js +49 -0
- package/dist/{webhooks-Bh_Jf_-l.js → verify-Ce49ETPz.js} +5 -5
- package/dist/{version-Bmf7_FzM.js → version-DOYTYwHP.js} +1 -1
- package/dist/{mfa-byfjruR9.js → view-Bn6HNKUe.js} +97 -98
- package/dist/{view-B64o81wp.d.ts → view-CbgObAeb.d.ts} +22 -31
- package/dist/webhooks/index.d.ts +135 -4
- package/dist/webhooks/index.js +588 -2
- package/dist/wire-D2dT86vi.js +35 -0
- package/package.json +27 -19
- package/schemas/config-v1.json +131 -1
- package/schemas/doctor-report-v1.json +11 -0
- package/skills/better-supabase/SKILL.md +9 -6
- package/skills/better-supabase/references/plugins.md +6 -3
- package/skills/better-supabase/references/troubleshooting.md +1 -0
- package/skills/better-supabase-api/SKILL.md +9 -2
- package/skills/better-supabase-api/references/adapters.md +11 -8
- package/skills/better-supabase-auth/SKILL.md +18 -7
- package/skills/better-supabase-auth/references/permdock.md +78 -11
- package/dist/cli/gen-D5cLCULx.js +0 -2
- package/dist/cli/sql-D4VbqmXi.js +0 -199
- package/dist/events-B21UOhvP.d.ts +0 -84
- package/dist/executor-B5IoUxLE.d.ts +0 -195
- package/dist/invalidate-CgxmRamI.js +0 -24
- package/dist/permdock-sql-DImJULY-.d.ts +0 -17
- package/dist/plugin-BqR6wKMB.js +0 -23
- package/dist/respond-DgIbdrt8.d.ts +0 -194
- package/dist/view-BrJI2LKp.js +0 -45
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as CliError } from "./errors-4v4DTtda.js";
|
|
2
|
-
import {
|
|
2
|
+
import { d as pipe, h as string, l as optional, v as url } from "./dist-xn4wlboc.js";
|
|
3
3
|
//#region ../../node_modules/.pnpm/@t3-oss+env-core@0.13.11_arktype@2.2.3_typescript@7.0.2_valibot@1.5.0_typescript@7.0.2__zod@4.6.5/node_modules/@t3-oss/env-core/dist/standard.js
|
|
4
4
|
function ensureSynchronous(value, message) {
|
|
5
5
|
if (value instanceof Promise) throw new Error(message);
|
|
@@ -2,9 +2,10 @@ import { r as defineCliCommand } from "./command-CK14fP91.js";
|
|
|
2
2
|
import { n as CliError } from "./errors-4v4DTtda.js";
|
|
3
3
|
import { n as importPath, r as writeIfChanged, t as display } from "./io-k41_z-jT.js";
|
|
4
4
|
import { r as painter } from "./style-C0NFdJRL.js";
|
|
5
|
-
import { t as VERSION } from "./version-
|
|
6
|
-
import { d as readSupabaseToml,
|
|
7
|
-
import {
|
|
5
|
+
import { t as VERSION } from "./version-BCtlgDQx.js";
|
|
6
|
+
import { d as pgFunctionHooks, f as readSupabaseToml, m as tomlGet, s as stdinDatabaseUrl, t as databaseUrl } from "./config-CPVpXP5I.js";
|
|
7
|
+
import { t as fileDiff } from "./diff-SjkO5rNr.js";
|
|
8
|
+
import { a as entitlementsMode, c as parseHookMarker, d as scopeMemberships, f as unsafeKey, i as entitlementRequirements, n as accessPermdockMode, o as kitKeyProblems, r as accessRequirements, s as parseGrantsMarker, t as compiledReadSets, u as readPermdock } from "./read-sets-VQcPgxU9.js";
|
|
8
9
|
import { t as byCodePoint } from "./compare-BmIWVEui.js";
|
|
9
10
|
import { n as withSpinner } from "./prompts-BNcNfAve.js";
|
|
10
11
|
import { t as connect } from "./db-DOBPt660.js";
|
|
@@ -12,7 +13,7 @@ import { basename, dirname, resolve } from "node:path";
|
|
|
12
13
|
import { existsSync } from "node:fs";
|
|
13
14
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
14
15
|
import { setTimeout } from "node:timers/promises";
|
|
15
|
-
import { kitLayout, permdockKeys, renderKit, sameKitFile } from "better-supabase/sql";
|
|
16
|
+
import { PERMDOCK_SCHEMA, kitLayout, kitPermissionKeys, permdockKeys, renderKit, resolveModules, sameKitFile } from "better-supabase/sql";
|
|
16
17
|
import { DEFAULT_CLAIMS, tenantClaimPaths } from "better-supabase/config";
|
|
17
18
|
import { toCamel, toSnake } from "better-supabase";
|
|
18
19
|
import { generateTypescript, pgTypeToTsType, sortGeneratorMetadata } from "@supabase/postgrest-typegen/generation";
|
|
@@ -198,7 +199,15 @@ function catalogOf(context) {
|
|
|
198
199
|
}
|
|
199
200
|
return catalog;
|
|
200
201
|
}
|
|
201
|
-
|
|
202
|
+
/** `[api] schemas` from `config.toml`, the schemas PostgREST serves, or `config.schemas` without one. */
|
|
203
|
+
function exposedSchemas(context) {
|
|
204
|
+
const listed = context.configToml ? tomlGet(context.configToml.document, ["api", "schemas"]) : void 0;
|
|
205
|
+
return Array.isArray(listed) && listed.every((entry) => typeof entry === "string") ? listed : context.config.schemas;
|
|
206
|
+
}
|
|
207
|
+
const exposed = (context) => {
|
|
208
|
+
const schemas = exposedSchemas(context);
|
|
209
|
+
return catalogOf(context).tables.filter((table) => schemas.includes(table.schema));
|
|
210
|
+
};
|
|
202
211
|
const qualified = (table) => `${table.schema}.${table.name}`;
|
|
203
212
|
const tableObject = (table) => ({
|
|
204
213
|
kind: "table",
|
|
@@ -232,7 +241,8 @@ function lineOf(text, pattern) {
|
|
|
232
241
|
/** Tables whose policies PermDock's helpers can't row-check (PermDock's PD037 scans the same ones). */
|
|
233
242
|
const HELPER_TABLES = /* @__PURE__ */ new Set(["storage.objects", "realtime.messages"]);
|
|
234
243
|
const POLICY = /create\s+policy\s+("(?:[^"]|"")+"|[\w$]+)\s+on\s+((?:"[^"]+"|\w+)\s*\.\s*(?:"[^"]+"|\w+))([\s\S]*?);/gi;
|
|
235
|
-
|
|
244
|
+
/** A helper call: optional schema, `permitted_<scope>_ids` or `permdock_has`, and the key, positional or named. */
|
|
245
|
+
const HELPER_CALL = /(?:(?:"((?:[^"]|"")+)"|([a-z_][\w$]*))\s*\.\s*)?"?(?:permitted_(\w+?)_ids|(permdock_has))"?\s*\(\s*(?:\w+\s*=>\s*)?'((?:[^']|'')*)'/gi;
|
|
236
246
|
const unquote = (name) => name.replaceAll(/^"|"$/g, "").replaceAll("\"\"", "\"");
|
|
237
247
|
/**
|
|
238
248
|
* Policies better-supabase generates (`bs_` names) on `storage.objects` and
|
|
@@ -250,99 +260,303 @@ function helperPolicies(files) {
|
|
|
250
260
|
const id = `${table}.${name}`;
|
|
251
261
|
if (seen.has(id)) continue;
|
|
252
262
|
seen.add(id);
|
|
253
|
-
const
|
|
254
|
-
|
|
263
|
+
const calls = [...match[3].matchAll(HELPER_CALL)].map((call) => {
|
|
264
|
+
const schema = call[1]?.replaceAll("\"\"", "\"") ?? call[2];
|
|
265
|
+
return {
|
|
266
|
+
key: call[5].replaceAll("''", "'"),
|
|
267
|
+
scope: call[4] ? "global" : call[3],
|
|
268
|
+
...schema ? { schema } : {}
|
|
269
|
+
};
|
|
270
|
+
});
|
|
271
|
+
if (calls.length === 0) continue;
|
|
255
272
|
found.push({
|
|
256
273
|
name,
|
|
257
274
|
table,
|
|
258
|
-
keys: [...new Set(
|
|
275
|
+
keys: [...new Set(calls.map((call) => call.key))],
|
|
276
|
+
calls,
|
|
259
277
|
file: file.path,
|
|
260
278
|
line: file.text.slice(0, match.index).split("\n").length
|
|
261
279
|
});
|
|
262
280
|
}
|
|
263
281
|
return found;
|
|
264
282
|
}
|
|
265
|
-
/** `buckets` entries with a PermDock policy,
|
|
283
|
+
/** `buckets` entries with a PermDock policy, the keys they name, their scope and helper schema. */
|
|
266
284
|
function configuredPermdockKeys(context) {
|
|
267
|
-
return Object.entries(context.config.buckets).flatMap(([bucket, config]) => config.policy !== void 0 && typeof config.policy !== "string" ? [{
|
|
285
|
+
return Object.entries(context.config.buckets).flatMap(([bucket, config]) => config.policy !== void 0 && typeof config.policy !== "string" && "permdock" in config.policy ? [{
|
|
268
286
|
bucket,
|
|
269
|
-
keys: permdockKeys(config.policy)
|
|
287
|
+
keys: permdockKeys(config.policy),
|
|
288
|
+
scope: config.policy.scope,
|
|
289
|
+
schema: config.policy.schema ?? PERMDOCK_SCHEMA
|
|
270
290
|
}] : []);
|
|
271
291
|
}
|
|
292
|
+
/** A scope name or PermDock's alias for it (`tenant` is the first scope, `team` the second), resolved against the manifest. */
|
|
293
|
+
function resolveManifestScope(manifest, name) {
|
|
294
|
+
const scopes = manifest?.rls?.scopes;
|
|
295
|
+
if (!scopes) return void 0;
|
|
296
|
+
if (scopes.some((scope) => scope.name === name)) return name;
|
|
297
|
+
if (name === "tenant") return scopes[0]?.name;
|
|
298
|
+
if (name === "team") return scopes[1]?.name;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Why a policy's helper schema or scope can't work with PermDock's
|
|
302
|
+
* manifest and catalog: a schema other than `rls.schema`, a scope the
|
|
303
|
+
* manifest doesn't declare, or a key the catalog declares at another scope.
|
|
304
|
+
*/
|
|
305
|
+
function placementProblems(project, catalog, call, where) {
|
|
306
|
+
const rls = project.manifest?.rls;
|
|
307
|
+
const problems = [];
|
|
308
|
+
if (rls && call.schema !== void 0 && call.schema !== rls.schema) problems.push(`${where} calls PermDock's helpers in schema ${call.schema}, but ${project.manifestPath} puts them in ${rls.schema}. Set schema: '${rls.schema}' on the policy.`);
|
|
309
|
+
if (call.scope === "global") return problems;
|
|
310
|
+
if (rls && !rls.scopes.some((scope) => scope.name === call.scope)) {
|
|
311
|
+
const resolved = resolveManifestScope(project.manifest, call.scope);
|
|
312
|
+
problems.push(`${where} uses scope "${call.scope}", which ${project.manifestPath} doesn't declare, so permitted_${call.scope}_ids doesn't exist. ${resolved ? `Use "${resolved}", the scope "${call.scope}" stands for.` : `Use one of ${rls.scopes.map((scope) => scope.name).join(", ") || "its scopes"}.`}`);
|
|
313
|
+
return problems;
|
|
314
|
+
}
|
|
315
|
+
const declared = catalog.permissions.find((permission) => permission.key === call.key)?.scope;
|
|
316
|
+
if (declared === void 0) return problems;
|
|
317
|
+
const resolved = resolveManifestScope(project.manifest, declared) ?? declared;
|
|
318
|
+
if (resolved !== call.scope) problems.push(`${where} checks "${call.key}" at scope "${call.scope}", but ${project.catalogPath} declares it at "${resolved}", so PermDock's helpers never grant it there. Use scope "${resolved}".`);
|
|
319
|
+
return problems;
|
|
320
|
+
}
|
|
272
321
|
/** PermDock's helpers for the `entitlements` kit module, when the manifest allows PermDock mode. */
|
|
273
322
|
function entitlementsKit(context) {
|
|
274
323
|
const mode = entitlementsMode(context.config, context.permdock);
|
|
275
324
|
return mode.kind === "permdock" ? mode.permdock : void 0;
|
|
276
325
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
severity: "info",
|
|
296
|
-
message: `
|
|
326
|
+
/** PermDock's helpers for the `permdock` access model, when `sql.kit` installs `access`. */
|
|
327
|
+
function accessKit(context) {
|
|
328
|
+
if (!resolveModules(context.config.sql.kit, {}).some((module) => module.name === "access")) return { kind: "off" };
|
|
329
|
+
return accessPermdockMode(context.config, context.permdock);
|
|
330
|
+
}
|
|
331
|
+
const PERMDOCK_RULES = [
|
|
332
|
+
{
|
|
333
|
+
code: "BS214",
|
|
334
|
+
severity: "error",
|
|
335
|
+
title: "PermDock permission with row conditions in a Storage or Realtime policy",
|
|
336
|
+
description: "PermDock's SQL helpers (`permitted_<scope>_ids`, `permdock_has`) check role and scope, not row conditions. A bucket or topic policy that names a permission whose catalog entry has `rowConditions: true` grants every object or topic in the scope. Use PermDock's generated policies for those permissions. A key whose entry has no boolean `rowConditions` (an older `permdock catalog`) or that the catalog doesn't list is unknown and reported the same way, and so are all of them when the catalog is missing or unreadable. With the manifest, a helper schema other than `rls.schema`, a scope the manifest doesn't declare and a key checked at another scope than its catalog entry's are reported too.",
|
|
337
|
+
check: (context) => {
|
|
338
|
+
const project = context.permdock;
|
|
339
|
+
if (!project) return [];
|
|
340
|
+
const fromConfig = configuredPermdockKeys(context);
|
|
341
|
+
const fromSql = helperPolicies(context.sqlFiles ?? []);
|
|
342
|
+
const uses = fromConfig.length > 0 || fromSql.length > 0;
|
|
343
|
+
const findings = project.problems.filter((problem) => problem.startsWith(project.catalogPath)).map((problem) => ({
|
|
344
|
+
...uses ? {} : { severity: "info" },
|
|
345
|
+
message: `Could not read PermDock's catalog: ${problem}`,
|
|
297
346
|
target: project.catalogPath
|
|
298
|
-
});
|
|
347
|
+
}));
|
|
348
|
+
const catalog = project.catalog;
|
|
349
|
+
if (!catalog) {
|
|
350
|
+
if (findings.length === 0 && uses) findings.push({
|
|
351
|
+
message: `Storage or Realtime policies call PermDock's helpers, but there is no ${project.catalogPath}, so whether their permissions have row conditions is unknown. Run \`permdock catalog\`, or set permdock.catalog in the config.`,
|
|
352
|
+
target: project.catalogPath
|
|
353
|
+
});
|
|
354
|
+
return findings;
|
|
355
|
+
}
|
|
356
|
+
for (const { bucket, keys, scope, schema } of fromConfig) for (const key of keys) {
|
|
357
|
+
for (const message of placementProblems(project, catalog, {
|
|
358
|
+
key,
|
|
359
|
+
scope,
|
|
360
|
+
schema
|
|
361
|
+
}, `buckets.${bucket} ("${key}")`)) findings.push({
|
|
362
|
+
message,
|
|
363
|
+
target: `buckets.${bucket}:${key}`
|
|
364
|
+
});
|
|
365
|
+
const problem = unsafeKey(catalog, key, project.catalogPath);
|
|
366
|
+
if (!problem) continue;
|
|
367
|
+
findings.push({
|
|
368
|
+
message: `buckets.${bucket} uses PermDock permission "${key}", which ${problem.reason}. The bucket policy could grant every object in the scope. ${problem.fix}`,
|
|
369
|
+
target: `buckets.${bucket}:${key}`
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
for (const policy of fromSql) {
|
|
373
|
+
const seen = /* @__PURE__ */ new Set();
|
|
374
|
+
for (const call of policy.calls) for (const message of placementProblems(project, catalog, call, `Policy "${policy.name}" on ${policy.table}`)) {
|
|
375
|
+
if (seen.has(message)) continue;
|
|
376
|
+
seen.add(message);
|
|
377
|
+
findings.push({
|
|
378
|
+
message,
|
|
379
|
+
target: `${policy.table}.${policy.name}:${call.key}`,
|
|
380
|
+
location: {
|
|
381
|
+
file: policy.file,
|
|
382
|
+
line: policy.line
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
for (const key of policy.keys) {
|
|
387
|
+
const problem = unsafeKey(catalog, key, project.catalogPath);
|
|
388
|
+
if (!problem) continue;
|
|
389
|
+
findings.push({
|
|
390
|
+
message: `Policy "${policy.name}" on ${policy.table} passes "${key}" to PermDock's helpers, but it ${problem.reason}. ${problem.fix}`,
|
|
391
|
+
target: `${policy.table}.${policy.name}:${key}`,
|
|
392
|
+
location: {
|
|
393
|
+
file: policy.file,
|
|
394
|
+
line: policy.line
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
}
|
|
299
399
|
return findings;
|
|
300
400
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
}
|
|
316
|
-
}, {
|
|
317
|
-
code: "BS408",
|
|
318
|
-
severity: "warning",
|
|
319
|
-
title: "PermDock helpers the entitlements module calls are missing",
|
|
320
|
-
description: "With a PermDock manifest, the `entitlements` kit module reads memberships from PermDock's `member_<scope>_ids()` (in `has_entitlement`) and `member_<scope>_ids_for(uuid)` (in `feature_claims`). Doctor warns when `entitlements.permdock.scope` is not one of the manifest's scopes, when the manifest's `rls.helpers` lacks a helper (run `permdock rls generate` with a current PermDock), or when the snapshot lacks it (apply the migration it wrote).",
|
|
321
|
-
check: (context) => {
|
|
322
|
-
if (!context.config.sql.kit.includes("entitlements")) return [];
|
|
323
|
-
const mode = entitlementsMode(context.config, context.permdock);
|
|
324
|
-
if (mode.kind === "tenant") return [];
|
|
325
|
-
const manifest = context.config.permdock.manifest;
|
|
326
|
-
if (mode.kind === "invalid") return [{
|
|
327
|
-
message: mode.problem,
|
|
328
|
-
target: "entitlements.permdock"
|
|
329
|
-
}];
|
|
330
|
-
const listed = new Set((context.permdock?.manifest?.rls?.helpers ?? []).map((helper) => `${mode.permdock.schema}.${helper.name}`));
|
|
331
|
-
const read = context.snapshot.schemas.includes(mode.permdock.schema);
|
|
332
|
-
const present = new Set(catalogOf(context).functions.map((fn) => `${fn.schema}.${fn.name}`));
|
|
333
|
-
return entitlementHelpers(mode.permdock).flatMap((helper) => {
|
|
334
|
-
if (!listed.has(helper)) return [{
|
|
335
|
-
message: `${manifest} lists no ${helper}, which the entitlements module calls. Run \`permdock rls generate\` (PermDock writes member_<scope>_ids and member_<scope>_ids_for for every scope), then \`permdock supabase inspect --out\`.`,
|
|
336
|
-
target: helper
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
code: "BS408",
|
|
404
|
+
severity: "warning",
|
|
405
|
+
title: "PermDock helpers the entitlements module calls are missing",
|
|
406
|
+
description: "With a PermDock manifest, the `entitlements` kit module reads memberships from PermDock's `member_<scope>_ids()` (in `has_entitlement`, as `authenticated`) and `member_<scope>_ids_for(uuid)` (in `feature_claims`, which PermDock's hook calls as `supabase_auth_admin`). Doctor warns when a PermDock project has no readable manifest or no `rls` block, when the scope is not one of the manifest's scopes or can't be chosen, when the scope's id type is missing or not `uuid`, `text`, `bigint` or `integer`, when the manifest has no `claims.features` claim filled by `better_supabase.feature_claims`, when no membership source covers the scope, when the manifest's `rls.helpers` lacks one of those helpers or doesn't grant it to that role (naming the `permdock.config.ts` setting that adds it), or when the snapshot lacks it (apply the migration `permdock rls generate` wrote).",
|
|
407
|
+
check: (context) => {
|
|
408
|
+
if (!context.config.sql.kit.includes("entitlements")) return [];
|
|
409
|
+
const mode = entitlementsMode(context.config, context.permdock);
|
|
410
|
+
if (mode.kind === "tenant") return [];
|
|
411
|
+
const manifest = context.config.permdock.manifest;
|
|
412
|
+
if (mode.kind === "invalid") return [{
|
|
413
|
+
message: mode.problem,
|
|
414
|
+
target: "entitlements.permdock"
|
|
337
415
|
}];
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
416
|
+
const listed = new Map((context.permdock?.manifest?.rls?.helpers ?? []).map((helper) => [`${mode.permdock.schema}.${helper.name}`, helper]));
|
|
417
|
+
const read = context.snapshot.schemas.includes(mode.permdock.schema);
|
|
418
|
+
const present = new Set(catalogOf(context).functions.map((fn) => `${fn.schema}.${fn.name}`));
|
|
419
|
+
const regenerate = `then run \`permdock rls generate\`, \`permdock supabase hook generate\` and \`permdock supabase inspect --out\`.`;
|
|
420
|
+
const findings = [];
|
|
421
|
+
const project = context.permdock;
|
|
422
|
+
const features = context.config.claims.features;
|
|
423
|
+
const claims = project?.manifest?.claims ?? [];
|
|
424
|
+
const featureClaim = claims.find((claim) => claim.name === features);
|
|
425
|
+
if (featureClaim?.source !== "better_supabase.feature_claims") {
|
|
426
|
+
const registered = claims.find((claim) => claim.source === "better_supabase.feature_claims");
|
|
427
|
+
findings.push({
|
|
428
|
+
message: registered ? `${manifest} registers better_supabase.feature_claims as the "${registered.name}" claim, but claims.features is "${features}", so hasEntitlement() never finds the plan features. Set claims.features to "${registered.name}", or rename the claim in permdock.config.ts (supabase.hook.claims).` : featureClaim ? `${manifest} fills the "${features}" claim from ${featureClaim.source}, not better_supabase.feature_claims, so the claim doesn't carry the entitlements module's features. Point supabase.hook.claims.${features} in permdock.config.ts at 'better_supabase.feature_claims', ${regenerate}` : `${manifest} has no "${features}" claim, so PermDock's hook never writes the plan features hasEntitlement() reads. Add \`supabase.hook.claims: { ${features}: 'better_supabase.feature_claims' }\` to permdock.config.ts, ${regenerate}`,
|
|
429
|
+
target: `claims.${features}`
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
const scope = mode.permdock.scope;
|
|
433
|
+
const memberships = project?.manifest ? scopeMemberships(project.manifest, scope) : {
|
|
434
|
+
sources: [],
|
|
435
|
+
from: "hook"
|
|
436
|
+
};
|
|
437
|
+
if (memberships.from === "hook") findings.push({
|
|
438
|
+
severity: "info",
|
|
439
|
+
message: `${manifest} has no rls.memberships, so entitlement_members() reads the hook's membership sources, which can differ from the tables member_${scope}_ids_for reads. Run \`permdock supabase inspect --out\` with a current PermDock.`,
|
|
440
|
+
target: `entitlements.permdock.${scope}`
|
|
441
|
+
});
|
|
442
|
+
if (memberships.sources.length === 0) findings.push({
|
|
443
|
+
message: memberships.from === "rls" ? `${manifest} maps no rls.memberships table to scope "${scope}", so entitlement_members() finds no users and plan changes never invalidate sessions. Map a table for "${scope}" (\`rls.memberships\` or \`rls.membershipSources\` in permdock.config.ts), ${regenerate}` : `No membership source in ${manifest} covers scope "${scope}", so entitlement_members() finds no users and plan changes never invalidate sessions. Add a source for "${scope}" (\`supabase.hook.memberships\` in permdock.config.ts), ${regenerate}`,
|
|
444
|
+
target: `entitlements.permdock.${scope}`
|
|
445
|
+
});
|
|
446
|
+
return findings.concat(entitlementRequirements(mode.permdock).flatMap((requirement) => {
|
|
447
|
+
const { helper, caller, role } = requirement;
|
|
448
|
+
const entry = listed.get(helper);
|
|
449
|
+
if (!entry) return [{
|
|
450
|
+
message: requirement.kind === "member" ? `${manifest} lists no ${helper}, but ${caller} calls it. Run \`permdock rls generate\` with a current PermDock, then \`permdock supabase inspect --out\`.` : `${manifest} lists no ${helper}, but ${caller} calls it. PermDock writes it only for a scope with a membership source: add one for "${mode.permdock.scope}" (\`supabase.hook.memberships\`, \`rls.membershipSources\` or a mapped \`rls.memberships\` table in permdock.config.ts), ${regenerate}`,
|
|
451
|
+
target: helper
|
|
452
|
+
}];
|
|
453
|
+
if (!entry.execute.includes(role)) {
|
|
454
|
+
const message = requirement.kind === "member" ? `${manifest} says ${role} may not execute ${helper}, but ${caller} calls it as ${role}. Run \`permdock rls generate\` with a current PermDock, then \`permdock supabase inspect --out\`.` : `${manifest} says ${role} may not execute ${helper}. feature_claims runs as its owner, so this only matters when something else calls it as ${role}; PermDock grants it when the hook has claims (\`supabase.hook.claims\` in permdock.config.ts), ${regenerate}`;
|
|
455
|
+
return [{
|
|
456
|
+
...requirement.kind === "member-for" ? { severity: "info" } : {},
|
|
457
|
+
message,
|
|
458
|
+
target: helper
|
|
459
|
+
}];
|
|
460
|
+
}
|
|
461
|
+
if (read && !present.has(helper)) return [{
|
|
462
|
+
message: `${helper} is in ${manifest} but not in the database. Apply the migration \`permdock rls generate\` wrote before the entitlements module.`,
|
|
463
|
+
target: helper
|
|
464
|
+
}];
|
|
465
|
+
return [];
|
|
466
|
+
}));
|
|
467
|
+
}
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
code: "BS409",
|
|
471
|
+
severity: "warning",
|
|
472
|
+
title: "PermDock's manifest and the better-supabase config disagree",
|
|
473
|
+
description: "PermDock's hook and helpers read the active tenant from the manifest's `rls.tenantClaim`, and its generated files start with a `-- permdock:hook v1` or `-- permdock:grants v1` marker. Doctor warns when `claims.tenant` names another claim (the tenant plugin, guards and RLS would read a claim PermDock never writes), reports an error for a marker version this release can't read, and notes when `claims.scope` resolves to another scope than the manifest's root scope.",
|
|
474
|
+
check: (context) => {
|
|
475
|
+
const project = context.permdock;
|
|
476
|
+
if (!project) return [];
|
|
477
|
+
const findings = [];
|
|
478
|
+
const manifest = project.manifest;
|
|
479
|
+
const tenantClaim = manifest?.rls?.tenantClaim ?? manifest?.tenantClaim;
|
|
480
|
+
if (tenantClaim !== void 0 && tenantClaim !== context.config.claims.tenant) findings.push({
|
|
481
|
+
message: `${project.manifestPath} says PermDock writes the active tenant to "${tenantClaim}", but claims.tenant is "${context.config.claims.tenant}". Set claims.tenant to "${tenantClaim}", or change rls.tenantClaim in permdock.config.ts.`,
|
|
482
|
+
target: "claims.tenant"
|
|
483
|
+
});
|
|
484
|
+
for (const file of context.sqlFiles ?? []) {
|
|
485
|
+
const markers = [["hook", parseHookMarker(file.text)?.version], ["grants", parseGrantsMarker(file.text)?.version]];
|
|
486
|
+
for (const [kind, version] of markers) {
|
|
487
|
+
if (version === void 0 || version === 1) continue;
|
|
488
|
+
findings.push({
|
|
489
|
+
severity: "error",
|
|
490
|
+
message: `${file.path} has a \`-- permdock:${kind} v${String(version)}\` marker, but this release reads only v1, so doctor can't trust what it says about PermDock's ${kind === "hook" ? "hook" : "grants"}. Upgrade better-supabase, or regenerate the file with the PermDock version that matches it.`,
|
|
491
|
+
target: file.path,
|
|
492
|
+
location: {
|
|
493
|
+
file: file.path,
|
|
494
|
+
line: 1
|
|
495
|
+
}
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
const root = manifest?.rls?.scopes.find((scope) => scope.within === void 0)?.name;
|
|
500
|
+
const scope = context.config.claims.scope;
|
|
501
|
+
const resolved = resolveManifestScope(manifest, scope);
|
|
502
|
+
if (root !== void 0 && resolved !== root) findings.push({
|
|
503
|
+
severity: "info",
|
|
504
|
+
message: `claims.scope is "${scope}", which ${resolved ? `stands for "${resolved}"` : "is not a scope"} in ${project.manifestPath}, whose root scope is "${root}". The tenant module's membership claims then use another scope than PermDock's. Set claims.scope to "${root}" (or "tenant").`,
|
|
505
|
+
target: "claims.scope"
|
|
506
|
+
});
|
|
507
|
+
return findings;
|
|
508
|
+
}
|
|
509
|
+
},
|
|
510
|
+
{
|
|
511
|
+
code: "BS411",
|
|
512
|
+
severity: "error",
|
|
513
|
+
title: "The permdock access model doesn't match PermDock's manifest",
|
|
514
|
+
description: "With `kits.access.model: 'permdock'`, the `access` module calls PermDock's `<rls.schema>.permitted_<scope>_ids(permission)` for tenant checks and `<rls.schema>.permdock_has(permission)` for platform checks, with the manifest's root scope unless `kits.access.permdock.scope` names another. Doctor reports a project without a readable manifest or `rls` block, no single root scope, a scope, schema or id type the manifest doesn't declare, a helper the manifest doesn't list, doesn't let `authenticated` execute or the database lacks, and every kit permission key (from `kitPermissionKeys`) whose catalog entry isn't `rowConditions: false`, since the helpers check role and scope only. It warns when `kits.access.functions.canAssign` is not set, because only the service role then assigns roles, and notes a `permdock_can_assign` template the manifest's helpers lack.",
|
|
515
|
+
check: (context) => {
|
|
516
|
+
const mode = accessKit(context);
|
|
517
|
+
if (mode.kind === "off") return [];
|
|
518
|
+
if (mode.kind === "invalid") return [{
|
|
519
|
+
message: mode.problem,
|
|
520
|
+
target: "kits.access.permdock"
|
|
341
521
|
}];
|
|
342
|
-
|
|
343
|
-
|
|
522
|
+
const project = context.permdock;
|
|
523
|
+
const manifest = context.config.permdock.manifest;
|
|
524
|
+
const findings = [];
|
|
525
|
+
const listed = new Map((project?.manifest?.rls?.helpers ?? []).map((helper) => [`${mode.access.schema}.${helper.name}`, helper]));
|
|
526
|
+
const read = context.snapshot.schemas.includes(mode.access.schema);
|
|
527
|
+
const present = new Set(catalogOf(context).functions.map((fn) => `${fn.schema}.${fn.name}`));
|
|
528
|
+
for (const { helper, role } of accessRequirements(mode.access)) {
|
|
529
|
+
const entry = listed.get(helper);
|
|
530
|
+
if (!entry) findings.push({
|
|
531
|
+
message: `${manifest} lists no ${helper}, but the permdock access model calls it from can() and the kit modules. Run \`permdock rls generate\` with a current PermDock, then \`permdock supabase inspect --out\`.`,
|
|
532
|
+
target: helper
|
|
533
|
+
});
|
|
534
|
+
else if (!entry.execute.includes(role)) findings.push({
|
|
535
|
+
message: `${manifest} says ${role} may not execute ${helper}, but the permdock access model calls it as ${role}. Run \`permdock rls generate\` with a current PermDock, then \`permdock supabase inspect --out\`.`,
|
|
536
|
+
target: helper
|
|
537
|
+
});
|
|
538
|
+
else if (read && !present.has(helper)) findings.push({
|
|
539
|
+
message: `${helper} is in ${manifest} but not in the database. Apply the migration \`permdock rls generate\` wrote before the access module.`,
|
|
540
|
+
target: helper
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
if (project) findings.push(...kitKeyProblems(project, kitPermissionKeys(context.config.kits, context.config.sql.kit), mode.access));
|
|
544
|
+
const canAssign = context.config.kits.access?.functions?.canAssign;
|
|
545
|
+
const recipe = `${mode.access.schema}.permdock_can_assign({role}, {tenant}::text)`;
|
|
546
|
+
if (canAssign === void 0) findings.push({
|
|
547
|
+
severity: "warning",
|
|
548
|
+
message: `kits.access.functions.canAssign is not set, so under the permdock model only the service role assigns roles: the memberships guard refuses every membership a member adds or changes${context.config.sql.kit.includes("tenant") ? "" : " (the owner-role fallback needs the tenant module, which PermDock projects don't install)"}. Set canAssign: "${recipe}", which PermDock writes when a role declares \`assigns\`.`,
|
|
549
|
+
target: "kits.access.functions.canAssign"
|
|
550
|
+
});
|
|
551
|
+
else if (canAssign.includes("permdock_can_assign") && !listed.has(`${mode.access.schema}.permdock_can_assign`)) findings.push({
|
|
552
|
+
severity: "info",
|
|
553
|
+
message: `kits.access.functions.canAssign calls permdock_can_assign, but ${manifest} doesn't list ${mode.access.schema}.permdock_can_assign. PermDock writes it only when a role in permdock.config.ts declares \`assigns\`; add one, then run \`permdock rls generate\` and \`permdock supabase inspect --out\`.`,
|
|
554
|
+
target: "kits.access.functions.canAssign"
|
|
555
|
+
});
|
|
556
|
+
return findings;
|
|
557
|
+
}
|
|
344
558
|
}
|
|
345
|
-
|
|
559
|
+
];
|
|
346
560
|
//#endregion
|
|
347
561
|
//#region src/cli/gen/shared.ts
|
|
348
562
|
const q = (value) => JSON.stringify(value);
|
|
@@ -731,8 +945,8 @@ async function formatTypes(code) {
|
|
|
731
945
|
return formatted;
|
|
732
946
|
}
|
|
733
947
|
/**
|
|
734
|
-
* Renders `database.types.ts
|
|
735
|
-
*
|
|
948
|
+
* Renders `database.types.ts` as `supabase gen types` prints it when oxfmt is
|
|
949
|
+
* installed, plus the `ComputedFields` keys Supabase CLI 2.119 doesn't write.
|
|
736
950
|
*/
|
|
737
951
|
function generateDatabaseTypes(metadata, options) {
|
|
738
952
|
return generateTypescript(sortGeneratorMetadata(restrictSchemas(metadata, options.schemas)), {
|
|
@@ -792,6 +1006,7 @@ function pruneTypes(metadata, schemas) {
|
|
|
792
1006
|
}
|
|
793
1007
|
//#endregion
|
|
794
1008
|
//#region src/cli/gen/model.ts
|
|
1009
|
+
const SOFT_DELETE_UDTS = /* @__PURE__ */ new Set(["timestamptz", "timestamp"]);
|
|
795
1010
|
const applyCasing = (name, casing) => casing === "camel" ? toCamel(name) : name;
|
|
796
1011
|
function tableKey(table, casing, schemas) {
|
|
797
1012
|
const base = applyCasing(table.name, casing);
|
|
@@ -973,8 +1188,13 @@ function buildModel(snapshot, config) {
|
|
|
973
1188
|
const has = (db) => columns.find((col) => col.db === db)?.app;
|
|
974
1189
|
const flags = {};
|
|
975
1190
|
if (flagsConfig.softDelete) {
|
|
976
|
-
const
|
|
977
|
-
|
|
1191
|
+
const db = flagsConfig.softDelete.column;
|
|
1192
|
+
const column = columns.find((col) => col.db === db);
|
|
1193
|
+
if (column) {
|
|
1194
|
+
const udt = column.snapshot.udt;
|
|
1195
|
+
if (column.snapshot.isArray || !SOFT_DELETE_UDTS.has(udt)) throw new TypeError(`plugins.softDelete.column: ${table.schema}.${table.name}.${db} is ${udt}, but softDelete() writes a timestamp. Use timestamptz or timestamp.`);
|
|
1196
|
+
flags.softDelete = column.app;
|
|
1197
|
+
}
|
|
978
1198
|
}
|
|
979
1199
|
if (flagsConfig.timestamps) {
|
|
980
1200
|
const createdAt = has(flagsConfig.timestamps.createdAt);
|
|
@@ -991,9 +1211,11 @@ function buildModel(snapshot, config) {
|
|
|
991
1211
|
if (flagsConfig.actor) {
|
|
992
1212
|
const createdBy = has(flagsConfig.actor.createdBy);
|
|
993
1213
|
const updatedBy = has(flagsConfig.actor.updatedBy);
|
|
994
|
-
|
|
1214
|
+
const impersonatedBy = has(flagsConfig.actor.impersonatedBy);
|
|
1215
|
+
if (createdBy || updatedBy || impersonatedBy) flags.actor = {
|
|
995
1216
|
...createdBy ? { createdBy } : {},
|
|
996
|
-
...updatedBy ? { updatedBy } : {}
|
|
1217
|
+
...updatedBy ? { updatedBy } : {},
|
|
1218
|
+
...impersonatedBy ? { impersonatedBy } : {}
|
|
997
1219
|
};
|
|
998
1220
|
}
|
|
999
1221
|
const key = keyOf.get(id) ?? table.name;
|
|
@@ -1815,22 +2037,33 @@ async function readSetFile(config) {
|
|
|
1815
2037
|
const readSets = await compiledReadSets(config);
|
|
1816
2038
|
return renderKit(["read-sets"], kitLayout(config, config.sql.testsDir, readSets))[0];
|
|
1817
2039
|
}
|
|
1818
|
-
/**
|
|
2040
|
+
/**
|
|
2041
|
+
* `buckets` keys the catalog doesn't mark `rowConditions: false`, as error
|
|
2042
|
+
* lines. Without a readable catalog every PermDock bucket is refused, since
|
|
2043
|
+
* whether its keys have row conditions is unknown.
|
|
2044
|
+
*/
|
|
1819
2045
|
async function rowConditionedBuckets(config) {
|
|
1820
2046
|
const configured = configuredPermdockKeys({ config });
|
|
1821
2047
|
if (configured.length === 0) return [];
|
|
1822
2048
|
const project = await readPermdock(config.root, config.permdock);
|
|
1823
|
-
const
|
|
1824
|
-
if (!
|
|
1825
|
-
|
|
2049
|
+
const catalog = project?.catalog;
|
|
2050
|
+
if (!catalog) {
|
|
2051
|
+
const path = project?.catalogPath ?? config.permdock.catalog;
|
|
2052
|
+
const problem = project?.problems.find((entry) => entry.startsWith(path));
|
|
2053
|
+
return [` ${configured.map(({ bucket }) => `buckets.${bucket}`).join(", ")}: ${problem ? `could not read PermDock's catalog (${problem})` : `there is no ${path}`}, so whether the keys have row conditions is unknown. Run \`permdock catalog\`, or set permdock.catalog in the config.`];
|
|
2054
|
+
}
|
|
2055
|
+
return configured.flatMap(({ bucket, keys }) => keys.flatMap((key) => {
|
|
2056
|
+
const problem = unsafeKey(catalog, key, project.catalogPath);
|
|
2057
|
+
return problem ? [` buckets.${bucket}: "${key}" ${problem.reason}. ${problem.fix}`] : [];
|
|
2058
|
+
}));
|
|
1826
2059
|
}
|
|
1827
|
-
const UNFORMATTED = "oxfmt is not installed, so database.types.ts is not formatted like `supabase gen types` output. Install it: pnpm add -D oxfmt";
|
|
2060
|
+
const UNFORMATTED = "oxfmt is not installed, so database.types.ts is not formatted like `supabase gen types` output. Install it: pnpm add -D oxfmt. @supabase/postgrest-typegen pins oxfmt 0.66.0 as its peer; to allow a newer one, add it to peerDependencyRules.allowedVersions in pnpm-workspace.yaml, keyed \"@supabase/postgrest-typegen>oxfmt\".";
|
|
1828
2061
|
async function runGen(options) {
|
|
1829
2062
|
const { config } = options;
|
|
1830
2063
|
const refused = await rowConditionedBuckets(config);
|
|
1831
2064
|
if (refused.length > 0) return {
|
|
1832
2065
|
code: 1,
|
|
1833
|
-
error: `PermDock's SQL helpers don't check row conditions, so these bucket policies
|
|
2066
|
+
error: `PermDock's SQL helpers don't check row conditions, so these bucket policies could grant every object in the scope:\n${refused.join("\n")}\nSee doctor BS214.`
|
|
1834
2067
|
};
|
|
1835
2068
|
const snapshot = options.snapshot ?? await loadSnapshot(config, options.env, options);
|
|
1836
2069
|
const files = await renderFiles(config, snapshot);
|
|
@@ -2005,4 +2238,4 @@ const genCommand = defineCliCommand({
|
|
|
2005
2238
|
}
|
|
2006
2239
|
});
|
|
2007
2240
|
//#endregion
|
|
2008
|
-
export {
|
|
2241
|
+
export { tableObject as A, entitlementsKit as C, lineOf as D, exposedSchemas as E, policyObject as O, accessKit as S, exposed as T, restrictSchemas as _, sourceArgs as a, emitModule as b, openSource as c, snapshotFile as d, introspect as f, buildModel as g, CACHE_DIR as h, runGen as i, toCatalog as j, qualified as k, parseSnapshot as l, pgSource as m, genCommand as n, loadSnapshot as o, managementSource as p, renderFiles as r, managementTarget as s, SOURCE_ARGS as t, serializeSnapshot as u, serializeGenerator as v, catalogOf as w, PERMDOCK_RULES as x, GENERATED_HEADER as y };
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -488,6 +488,6 @@ export declare function emitModule(model: Model, options: EmitOptions): string;
|
|
|
488
488
|
export declare function loadConfig(cwd: string, explicit?: string): Promise<ResolvedConfig>;
|
|
489
489
|
//#endregion
|
|
490
490
|
//#region src/cli/version.d.ts
|
|
491
|
-
export declare const VERSION = "0.
|
|
491
|
+
export declare const VERSION = "0.5.1";
|
|
492
492
|
//#endregion
|
|
493
493
|
export type { Catalog, CatalogCheck, CatalogColumn, CatalogColumnGrant, CatalogEnum, CatalogForeignKey, CatalogFunction, CatalogFunctionArg, CatalogGrant, CatalogIndex, CatalogPolicy, CatalogTable, CatalogTrigger, CatalogUnique, CliArgs, CliCommandDef, CliContext, CliEnv, CliErrorCode, CliErrorOptions, CliIo, CliProblem, CliProblemExtensions, Command, CommandContext, CommandResult, ExitCode, ExtrasFunction, ExtrasHook, ExtrasHookFunction, ExtrasTable, ForeignKeyAction, GeneratorMetadata, IntrospectionSource, ManagementSourceOptions, ParsedArgs, Queryable, RunOptions, RunResult, Snapshot, SnapshotBucket, SnapshotExtras };
|
package/dist/cli/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { a as list, r as defineCliCommand } from "./command-CK14fP91.js";
|
|
2
2
|
import { n as CliError, t as CLI_ERRORS_URL } from "./errors-4v4DTtda.js";
|
|
3
|
-
import { i as run, n as help, r as registerCommand, t as commandNames } from "./run-
|
|
4
|
-
import { t as VERSION } from "./version-
|
|
5
|
-
import { a as loadConfig } from "./config-
|
|
6
|
-
import { b as emitModule, f as introspect, g as buildModel, i as runGen,
|
|
3
|
+
import { i as run, n as help, r as registerCommand, t as commandNames } from "./run-Bo_NLT5n.js";
|
|
4
|
+
import { t as VERSION } from "./version-BCtlgDQx.js";
|
|
5
|
+
import { a as loadConfig } from "./config-CPVpXP5I.js";
|
|
6
|
+
import { b as emitModule, f as introspect, g as buildModel, i as runGen, j as toCatalog, l as parseSnapshot, m as pgSource, o as loadSnapshot, p as managementSource, r as renderFiles, u as serializeSnapshot, y as GENERATED_HEADER } from "./gen-CjElD1Vw.js";
|
|
7
7
|
//#region src/cli/introspect/from-catalog.ts
|
|
8
8
|
/**
|
|
9
9
|
* Builds a snapshot from catalog records: the inverse of `toCatalog`. Tests
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as list, r as defineCliCommand } from "./command-CK14fP91.js";
|
|
2
2
|
import { r as writeIfChanged, t as display } from "./io-k41_z-jT.js";
|
|
3
|
-
import { t as VERSION } from "./version-
|
|
4
|
-
import { n as findConfig } from "./config-
|
|
3
|
+
import { t as VERSION } from "./version-BCtlgDQx.js";
|
|
4
|
+
import { n as findConfig } from "./config-CPVpXP5I.js";
|
|
5
5
|
import { n as installCommand, t as detectProject } from "./project-B_GeXyHh.js";
|
|
6
6
|
import { posix, resolve } from "node:path";
|
|
7
7
|
import { existsSync } from "node:fs";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { r as defineCliCommand } from "./command-CK14fP91.js";
|
|
2
2
|
import { r as writeIfChanged, t as display } from "./io-k41_z-jT.js";
|
|
3
3
|
import { r as painter } from "./style-C0NFdJRL.js";
|
|
4
|
-
import {
|
|
5
|
-
import { _ as restrictSchemas, a as sourceArgs, o as loadSnapshot, t as SOURCE_ARGS, u as serializeSnapshot, v as serializeGenerator } from "./gen-
|
|
4
|
+
import { t as fileDiff } from "./diff-SjkO5rNr.js";
|
|
5
|
+
import { _ as restrictSchemas, a as sourceArgs, o as loadSnapshot, t as SOURCE_ARGS, u as serializeSnapshot, v as serializeGenerator } from "./gen-CjElD1Vw.js";
|
|
6
6
|
import { n as withSpinner } from "./prompts-BNcNfAve.js";
|
|
7
7
|
import { resolve } from "node:path";
|
|
8
8
|
import { readFile } from "node:fs/promises";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { r as defineCliCommand } from "./command-CK14fP91.js";
|
|
2
2
|
import { r as writeIfChanged, t as display } from "./io-k41_z-jT.js";
|
|
3
|
-
import { r as importExport } from "./config-
|
|
3
|
+
import { r as importExport } from "./config-CPVpXP5I.js";
|
|
4
4
|
import { resolve } from "node:path";
|
|
5
5
|
import { existsSync } from "node:fs";
|
|
6
6
|
import { readFile } from "node:fs/promises";
|