better-supabase 0.5.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 +133 -16
- package/dist/actor-DaJjqD5R.d.ts +43 -0
- package/dist/cli/bin.js +1 -1
- package/dist/cli/{doctor-L7Z8mYTx.js → doctor-C_A-KEAz.js} +6 -4
- package/dist/cli/gen-CIdSQZad.js +2 -0
- package/dist/cli/{gen-C2osRcr5.js → gen-CjElD1Vw.js} +72 -35
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +3 -3
- package/dist/cli/{init-Saf5-R9T.js → init-DMa6M7hV.js} +1 -1
- package/dist/cli/{introspect--H7oGmcd.js → introspect-BKlaIu_M.js} +1 -1
- package/dist/cli/{read-sets-BfMQcKy8.js → read-sets-VQcPgxU9.js} +173 -26
- package/dist/cli/{run-BKR2W_79.js → run-Bo_NLT5n.js} +7 -7
- package/dist/cli/{sql-DzPbfngx.js → sql-CVx-1O8G.js} +30 -5
- package/dist/cli/{version-BDJuO0UR.js → version-BCtlgDQx.js} +1 -1
- package/dist/client/index.js +1 -1
- package/dist/config/index.d.ts +2 -2
- package/dist/{config-T_ejwyd3.d.ts → config-Bv7gvfzC.d.ts} +10 -1
- package/dist/edge/index.d.ts +2 -2
- package/dist/edge/index.js +2 -2
- package/dist/{entitlements-DUxU4oHe.d.ts → entitlements-BiJZ4t49.d.ts} +1 -1
- package/dist/hono/index.d.ts +3 -3
- package/dist/hono/index.js +2 -2
- package/dist/impersonation-BgSHAYLf.js +133 -0
- package/dist/{resolve-C15TJq77.d.ts → impersonation-BmePsuxz.d.ts} +25 -1
- package/dist/{index-Dxn2a5cw.d.ts → index-CWMDdEr4.d.ts} +1 -1
- package/dist/{index-BGdgBPdf.d.ts → index-Dp2VDwog.d.ts} +1 -1
- package/dist/mcp/index.d.ts +2 -2
- package/dist/mcp/index.js +2 -2
- package/dist/{mfa-CusDgPaD.d.ts → mfa-Cu96oJdw.d.ts} +1 -19
- package/dist/next/index.d.ts +6 -6
- package/dist/next/index.js +3 -3
- package/dist/orpc/index.d.ts +2 -2
- package/dist/orpc/index.js +1 -1
- package/dist/otel/index.js +5 -5
- package/dist/react/index.d.ts +5 -5
- package/dist/react/server.d.ts +5 -5
- package/dist/react/session.d.ts +2 -2
- package/dist/{refresh-BTrQ_ZTp.d.ts → refresh-CFPX0Qft.d.ts} +1 -1
- package/dist/{resource-D8iqWMr-.d.ts → resource-DNRFP7KR.d.ts} +1 -1
- package/dist/{resource-CRxxwkQq.js → resource-mqfJW4YF.js} +1 -1
- package/dist/{respond-D2OQl62-.d.ts → respond-BR1fJWOB.d.ts} +12 -6
- package/dist/server/index.d.ts +9 -8
- package/dist/server/index.js +5 -5
- package/dist/{server-Csx2sRjg.js → server-B1mZ7CgQ.js} +11 -6
- package/dist/sql/index.d.ts +38 -3
- package/dist/sql/index.js +237 -118
- package/dist/ssr/index.d.ts +4 -4
- package/dist/ssr/index.js +1 -1
- package/dist/{support-DT-OhSxD.d.ts → support-CEPwlw2G.d.ts} +2 -1
- package/dist/{support-view-Da5peKFg.d.ts → support-view-B8SlToQX.d.ts} +1 -1
- package/dist/{support-Dk6vdmEw.js → support-wHnK6zp-.js} +4 -2
- package/dist/testing/index.d.ts +36 -4
- package/dist/testing/index.js +141 -1
- package/dist/{version-DM_Roffb.js → version-DOYTYwHP.js} +1 -1
- package/dist/{view-Bq64ki8-.js → view-Bn6HNKUe.js} +7 -65
- package/dist/{view-BEf0diC5.d.ts → view-CbgObAeb.d.ts} +17 -31
- package/package.json +13 -13
- package/schemas/doctor-report-v1.json +1 -0
- package/skills/better-supabase-auth/SKILL.md +15 -7
- package/skills/better-supabase-auth/references/permdock.md +22 -0
- package/dist/cli/gen-BJc9vdyu.js +0 -2
- package/dist/impersonation-VfaGc4Q_.js +0 -41
|
@@ -2,7 +2,7 @@ import { i as importModule } from "./config-CPVpXP5I.js";
|
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
import { readFile } from "node:fs/promises";
|
|
5
|
-
import { KIT_ID_TYPES, compileReadSets, kitIdType } from "better-supabase/sql";
|
|
5
|
+
import { KIT_ID_TYPES, compileReadSets, kitIdType, permdockKeyStatus } from "better-supabase/sql";
|
|
6
6
|
import { isReadSet } from "better-supabase";
|
|
7
7
|
//#region src/cli/permdock.ts
|
|
8
8
|
const PERMDOCK_CONFIGS = [
|
|
@@ -81,7 +81,11 @@ function parseManifest(json) {
|
|
|
81
81
|
args: helper["args"],
|
|
82
82
|
returns: helper["returns"],
|
|
83
83
|
execute: strings(helper["execute"])
|
|
84
|
-
}] : [])
|
|
84
|
+
}] : []),
|
|
85
|
+
...Array.isArray(rls["memberships"]) ? { memberships: rls["memberships"].flatMap((entry) => {
|
|
86
|
+
const parsed = membership(entry);
|
|
87
|
+
return parsed ? [parsed] : [];
|
|
88
|
+
}) } : {}
|
|
85
89
|
} } : {},
|
|
86
90
|
decidingColumns: strings(json["decidingColumns"])
|
|
87
91
|
};
|
|
@@ -186,17 +190,13 @@ function parseGrantsMarker(sql) {
|
|
|
186
190
|
};
|
|
187
191
|
}
|
|
188
192
|
/**
|
|
189
|
-
* PermDock
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
* PermDock project without a readable manifest or without its `rls` block
|
|
194
|
-
* is invalid too, so the module never falls back to `tenant` on its own.
|
|
193
|
+
* The PermDock scope tenants are: `explicit`, which must be one of the
|
|
194
|
+
* manifest's `rls.scopes`, else the one scope without `within`. A missing or
|
|
195
|
+
* unreadable manifest, no `rls` block, no root scope or several, and a scope
|
|
196
|
+
* without a type this kit renders are invalid rather than a guess.
|
|
195
197
|
*/
|
|
196
|
-
function
|
|
197
|
-
const setting =
|
|
198
|
-
if (setting === false || !project) return { kind: "tenant" };
|
|
199
|
-
const optOut = "or set entitlements.permdock: false to keep the tenant module's memberships.";
|
|
198
|
+
function manifestScope(project, explicit, reader) {
|
|
199
|
+
const { subject, helpers, setting, optOut } = reader;
|
|
200
200
|
if (!project.manifest) {
|
|
201
201
|
const problem = project.problems.find((entry) => entry.startsWith(project.manifestPath));
|
|
202
202
|
return {
|
|
@@ -207,41 +207,82 @@ function entitlementsMode(config, project) {
|
|
|
207
207
|
const rls = project.manifest.rls;
|
|
208
208
|
if (!rls) return {
|
|
209
209
|
kind: "invalid",
|
|
210
|
-
problem: `${project.manifestPath} has no rls block, so
|
|
210
|
+
problem: `${project.manifestPath} has no rls block, so ${subject} can't find PermDock's ${helpers}. Run \`permdock rls generate\`, then \`permdock supabase inspect --out\`, ${optOut}`
|
|
211
211
|
};
|
|
212
212
|
const names = rls.scopes.map((scope) => scope.name).join(", ") || "none";
|
|
213
213
|
let scope;
|
|
214
|
-
if (
|
|
214
|
+
if (explicit === void 0) {
|
|
215
215
|
const roots = rls.scopes.filter((entry) => entry.within === void 0);
|
|
216
216
|
if (roots.length !== 1) return {
|
|
217
217
|
kind: "invalid",
|
|
218
|
-
problem: `${project.manifestPath} has no single root scope (${roots.map((entry) => entry.name).join(", ") || "none"}), so
|
|
218
|
+
problem: `${project.manifestPath} has no single root scope (${roots.map((entry) => entry.name).join(", ") || "none"}), so ${subject} can't tell which scope tenants are. Set ${setting}: { scope } to one of ${names}.`
|
|
219
219
|
};
|
|
220
220
|
scope = roots[0].name;
|
|
221
221
|
} else {
|
|
222
|
-
scope =
|
|
222
|
+
scope = explicit;
|
|
223
223
|
if (!rls.scopes.some((entry) => entry.name === scope)) return {
|
|
224
224
|
kind: "invalid",
|
|
225
|
-
problem:
|
|
225
|
+
problem: `${setting}.scope is "${scope}", but ${project.manifestPath} has the scopes ${names}. Set ${setting}: { scope } to one of them, or remove it to use the root scope.`
|
|
226
226
|
};
|
|
227
227
|
}
|
|
228
228
|
const declared = rls.scopes.find((entry) => entry.name === scope)?.type;
|
|
229
229
|
if (declared === void 0) return {
|
|
230
230
|
kind: "invalid",
|
|
231
|
-
problem: `${project.manifestPath} gives scope "${scope}" no type, so
|
|
231
|
+
problem: `${project.manifestPath} gives scope "${scope}" no type, so ${subject} can't tell its id type. Run \`permdock supabase inspect --out\` with a current PermDock.`
|
|
232
232
|
};
|
|
233
|
-
const
|
|
234
|
-
if (
|
|
233
|
+
const idType = kitIdType(declared);
|
|
234
|
+
if (idType === void 0) return {
|
|
235
235
|
kind: "invalid",
|
|
236
|
-
problem: `${project.manifestPath} gives scope "${scope}" the type ${declared}, but
|
|
236
|
+
problem: `${project.manifestPath} gives scope "${scope}" the type ${declared}, but ${subject} renders only ${KIT_ID_TYPES.join(", ").replace(/, (?=[^,]*$)/, " or ")} ids.`
|
|
237
|
+
};
|
|
238
|
+
return {
|
|
239
|
+
kind: "ok",
|
|
240
|
+
manifest: project.manifest,
|
|
241
|
+
rls,
|
|
242
|
+
scope,
|
|
243
|
+
idType
|
|
237
244
|
};
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* The membership tables PermDock's `member_<scope>_ids_for` reads for
|
|
248
|
+
* `scope`: the manifest's `rls.memberships` that can cover it (a scope
|
|
249
|
+
* column, or a fixed scope equal to `scope`). A manifest from a PermDock
|
|
250
|
+
* without `rls.memberships` falls back to the hook's sources.
|
|
251
|
+
*/
|
|
252
|
+
function scopeMemberships(manifest, scope) {
|
|
253
|
+
const rls = manifest.rls?.memberships;
|
|
254
|
+
return {
|
|
255
|
+
sources: (rls ?? manifest.memberships).filter((source) => !("value" in source.scope) || source.scope.value === scope),
|
|
256
|
+
from: rls === void 0 ? "hook" : "rls"
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* PermDock mode, when the project uses PermDock and `entitlements.permdock`
|
|
261
|
+
* is not `false`. The scope is `entitlements.permdock.scope`, which must be
|
|
262
|
+
* one of the manifest's `rls.scopes`, else the one scope without `within`.
|
|
263
|
+
* No root scope, or more than one, is invalid rather than a guess. A
|
|
264
|
+
* PermDock project without a readable manifest or without its `rls` block
|
|
265
|
+
* is invalid too, so the module never falls back to `tenant` on its own.
|
|
266
|
+
* Memberships come from `rls.memberships`, the tables PermDock's helpers
|
|
267
|
+
* read, and from the hook's sources only when the manifest has none.
|
|
268
|
+
*/
|
|
269
|
+
function entitlementsMode(config, project) {
|
|
270
|
+
const setting = config.entitlements.permdock;
|
|
271
|
+
if (setting === false || !project) return { kind: "tenant" };
|
|
272
|
+
const chosen = manifestScope(project, setting.scope, {
|
|
273
|
+
subject: "the entitlements module",
|
|
274
|
+
helpers: "membership helpers",
|
|
275
|
+
setting: "entitlements.permdock",
|
|
276
|
+
optOut: "or set entitlements.permdock: false to keep the tenant module's memberships."
|
|
277
|
+
});
|
|
278
|
+
if (chosen.kind === "invalid") return chosen;
|
|
238
279
|
return {
|
|
239
280
|
kind: "permdock",
|
|
240
281
|
permdock: {
|
|
241
|
-
schema: rls.schema,
|
|
242
|
-
scope,
|
|
243
|
-
idType:
|
|
244
|
-
memberships:
|
|
282
|
+
schema: chosen.rls.schema,
|
|
283
|
+
scope: chosen.scope,
|
|
284
|
+
idType: chosen.idType,
|
|
285
|
+
memberships: scopeMemberships(chosen.manifest, chosen.scope).sources.map((source) => ({
|
|
245
286
|
table: source.table,
|
|
246
287
|
userColumn: source.user.column,
|
|
247
288
|
scope: source.scope,
|
|
@@ -251,6 +292,112 @@ function entitlementsMode(config, project) {
|
|
|
251
292
|
};
|
|
252
293
|
}
|
|
253
294
|
/**
|
|
295
|
+
* The `permdock` access model's helpers, read from the manifest whatever
|
|
296
|
+
* `entitlements` says: `rls.schema`, the root scope (or
|
|
297
|
+
* `kits.access.permdock.scope`, which the manifest must declare) and that
|
|
298
|
+
* scope's id type. `off` when another model is chosen. Without a PermDock
|
|
299
|
+
* project or a usable manifest the model is invalid, never a default.
|
|
300
|
+
*/
|
|
301
|
+
function accessPermdockMode(config, project) {
|
|
302
|
+
const access = config.kits.access;
|
|
303
|
+
if (access?.model !== "permdock") return { kind: "off" };
|
|
304
|
+
const optOut = "or choose another kits.access.model.";
|
|
305
|
+
if (!project) return {
|
|
306
|
+
kind: "invalid",
|
|
307
|
+
problem: `kits.access.model is "permdock", but there is no PermDock project here (no permdock.config.ts and no manifest). Run \`permdock supabase inspect --out\` to write the manifest, ${optOut}`
|
|
308
|
+
};
|
|
309
|
+
const chosen = manifestScope(project, access.permdock?.scope, {
|
|
310
|
+
subject: "the permdock access model",
|
|
311
|
+
helpers: "permission helpers",
|
|
312
|
+
setting: "kits.access.permdock",
|
|
313
|
+
optOut
|
|
314
|
+
});
|
|
315
|
+
if (chosen.kind === "invalid") return chosen;
|
|
316
|
+
const schema = access.permdock?.schema;
|
|
317
|
+
if (schema !== void 0 && schema !== chosen.rls.schema) return {
|
|
318
|
+
kind: "invalid",
|
|
319
|
+
problem: `kits.access.permdock.schema is "${schema}", but ${project.manifestPath} puts PermDock's helpers in "${chosen.rls.schema}". Set it to "${chosen.rls.schema}", or remove it to use the manifest's.`
|
|
320
|
+
};
|
|
321
|
+
const idType = access.idType === void 0 ? void 0 : kitIdType(access.idType);
|
|
322
|
+
if (idType !== void 0 && idType !== chosen.idType) return {
|
|
323
|
+
kind: "invalid",
|
|
324
|
+
problem: `kits.access.idType is "${access.idType}", but ${project.manifestPath} gives scope "${chosen.scope}" the type ${chosen.idType}. Set kits.access.idType to "${chosen.idType}", or remove it to use the manifest's.`
|
|
325
|
+
};
|
|
326
|
+
return {
|
|
327
|
+
kind: "permdock",
|
|
328
|
+
access: {
|
|
329
|
+
schema: chosen.rls.schema,
|
|
330
|
+
scope: chosen.scope,
|
|
331
|
+
idType: chosen.idType
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
const ROW_CONDITIONS_FIX = "Use the policies `permdock rls generate` writes for it, or a permission whose catalog entry has rowConditions: false.";
|
|
336
|
+
const REGENERATE_FIX = "Regenerate it with a current `permdock catalog`, which writes rowConditions for every permission.";
|
|
337
|
+
/**
|
|
338
|
+
* Why the SQL helpers can't be trusted with `key`, or `undefined` when the
|
|
339
|
+
* catalog marks it `rowConditions: false`. An entry without the flag and a
|
|
340
|
+
* key the catalog doesn't list are unknown, so they count as unsafe.
|
|
341
|
+
*/
|
|
342
|
+
function unsafeKey(catalog, key, catalogPath) {
|
|
343
|
+
const status = permdockKeyStatus(catalog, key);
|
|
344
|
+
switch (status) {
|
|
345
|
+
case "scope-only": return;
|
|
346
|
+
case "row-conditions": return {
|
|
347
|
+
status,
|
|
348
|
+
reason: `has row conditions in ${catalogPath}`,
|
|
349
|
+
fix: ROW_CONDITIONS_FIX
|
|
350
|
+
};
|
|
351
|
+
case "no-flag": return {
|
|
352
|
+
status,
|
|
353
|
+
reason: `has no rowConditions flag in ${catalogPath}, so whether it has row conditions is unknown`,
|
|
354
|
+
fix: REGENERATE_FIX
|
|
355
|
+
};
|
|
356
|
+
case "missing": return {
|
|
357
|
+
status,
|
|
358
|
+
reason: `is not in ${catalogPath}`,
|
|
359
|
+
fix: REGENERATE_FIX
|
|
360
|
+
};
|
|
361
|
+
default: return status;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Checks the keys the kit modules pass to PermDock's helpers against
|
|
366
|
+
* `permissions.catalog.json`, with the statuses bucket policies use: only
|
|
367
|
+
* `rowConditions: false` passes. A missing or unreadable catalog is a
|
|
368
|
+
* problem too, since every key is then unknown.
|
|
369
|
+
*/
|
|
370
|
+
function kitKeyProblems(project, keys, access) {
|
|
371
|
+
if (keys.length === 0) return [];
|
|
372
|
+
const catalog = project.catalog;
|
|
373
|
+
if (!catalog) {
|
|
374
|
+
const unreadable = project.problems.find((problem) => problem.startsWith(project.catalogPath));
|
|
375
|
+
return [{
|
|
376
|
+
target: project.catalogPath,
|
|
377
|
+
message: unreadable ? `Could not read PermDock's catalog: ${unreadable}. The permdock access model passes ${keys.length} kit permission keys to PermDock's helpers, so whether they have row conditions is unknown. Run \`permdock catalog\` with a current PermDock.` : `The permdock access model passes ${keys.length} kit permission keys to PermDock's helpers, but there is no ${project.catalogPath}, so whether they have row conditions is unknown. Run \`permdock catalog\`, or set permdock.catalog in the config.`
|
|
378
|
+
}];
|
|
379
|
+
}
|
|
380
|
+
return keys.flatMap((entry) => {
|
|
381
|
+
const problem = unsafeKey(catalog, entry.key, project.catalogPath);
|
|
382
|
+
if (!problem) return [];
|
|
383
|
+
const helper = entry.scope === "platform" ? `${access.schema}.permdock_has` : `${access.schema}.permitted_${access.scope}_ids`;
|
|
384
|
+
const setting = `kits.${entry.module}.permissions.${entry.action}`;
|
|
385
|
+
const fix = problem.status === "row-conditions" ? `PermDock's SQL helpers check role and scope, not row conditions, so the module would grant it everywhere in the scope. Set ${setting} to a permission whose catalog entry has rowConditions: false.` : `${problem.fix} If PermDock doesn't define it, set ${setting} to a key the catalog lists.`;
|
|
386
|
+
return [{
|
|
387
|
+
target: setting,
|
|
388
|
+
message: `The ${entry.module} module checks "${entry.key}" (${entry.action}) with ${helper}, but it ${problem.reason}. ${fix}`
|
|
389
|
+
}];
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
/** A helper the `permdock` access model calls, as `schema.name`, and the role it runs as. */
|
|
393
|
+
const accessRequirements = (access) => [{
|
|
394
|
+
helper: `${access.schema}.permitted_${access.scope}_ids`,
|
|
395
|
+
role: "authenticated"
|
|
396
|
+
}, {
|
|
397
|
+
helper: `${access.schema}.permdock_has`,
|
|
398
|
+
role: "authenticated"
|
|
399
|
+
}];
|
|
400
|
+
/**
|
|
254
401
|
* The helpers the `entitlements` module calls for the chosen scope:
|
|
255
402
|
* `member_<scope>_ids` from `has_entitlement` as `authenticated`, and
|
|
256
403
|
* `member_<scope>_ids_for` from `feature_claims`, which PermDock's hook runs
|
|
@@ -286,4 +433,4 @@ async function compiledReadSets(config) {
|
|
|
286
433
|
return compileReadSets(await loadReadSets(config));
|
|
287
434
|
}
|
|
288
435
|
//#endregion
|
|
289
|
-
export {
|
|
436
|
+
export { entitlementsMode as a, parseHookMarker as c, scopeMemberships as d, unsafeKey as f, entitlementRequirements as i, permdockSource as l, accessPermdockMode as n, kitKeyProblems as o, accessRequirements as r, parseGrantsMarker as s, compiledReadSets as t, readPermdock as u };
|
|
@@ -2,7 +2,7 @@ import { n as __toESM } from "./rolldown-runtime-BPOCksWG.js";
|
|
|
2
2
|
import { c as parseArgs$2, i as joinRepeated, l as renderUsage, n as cliContext, o as listArgs, s as defineCommand, t as GLOBAL_ARGS, u as runCommand } from "./command-CK14fP91.js";
|
|
3
3
|
import { n as CliError, r as toCliError } from "./errors-4v4DTtda.js";
|
|
4
4
|
import { n as highlight, r as painter } from "./style-C0NFdJRL.js";
|
|
5
|
-
import { t as VERSION } from "./version-
|
|
5
|
+
import { t as VERSION } from "./version-BCtlgDQx.js";
|
|
6
6
|
import { stripVTControlCharacters } from "node:util";
|
|
7
7
|
import { resolve } from "node:path";
|
|
8
8
|
//#region src/cli/legacy.ts
|
|
@@ -134,11 +134,11 @@ function renderError(error, options) {
|
|
|
134
134
|
//#region src/cli/run.ts
|
|
135
135
|
const COMMANDS = /* @__PURE__ */ new Map([
|
|
136
136
|
["init", {
|
|
137
|
-
load: () => import("./init-
|
|
137
|
+
load: () => import("./init-DMa6M7hV.js").then((m) => m.initCommand),
|
|
138
138
|
description: "Writes better-supabase.config.ts, src/lib/supabase/index.ts and glue for the frameworks in package.json"
|
|
139
139
|
}],
|
|
140
140
|
["add", {
|
|
141
|
-
load: () => import("./init-
|
|
141
|
+
load: () => import("./init-DMa6M7hV.js").then((m) => m.addCommand),
|
|
142
142
|
description: "Adds glue for an integration to an existing project"
|
|
143
143
|
}],
|
|
144
144
|
["env", {
|
|
@@ -154,11 +154,11 @@ const COMMANDS = /* @__PURE__ */ new Map([
|
|
|
154
154
|
description: "Installs the Agent Skills that ship with this version of better-supabase"
|
|
155
155
|
}],
|
|
156
156
|
["gen", {
|
|
157
|
-
load: () => import("./gen-
|
|
157
|
+
load: () => import("./gen-CIdSQZad.js").then((m) => m.genCommand),
|
|
158
158
|
description: "Writes database.types.ts, generated.ts and its metadata module from the database"
|
|
159
159
|
}],
|
|
160
160
|
["introspect", {
|
|
161
|
-
load: () => import("./introspect
|
|
161
|
+
load: () => import("./introspect-BKlaIu_M.js").then((m) => m.introspectCommand),
|
|
162
162
|
description: "Saves the database schema as a snapshot for offline gen"
|
|
163
163
|
}],
|
|
164
164
|
["openapi", {
|
|
@@ -170,11 +170,11 @@ const COMMANDS = /* @__PURE__ */ new Map([
|
|
|
170
170
|
description: "Renders the fixtures from defineSeed() (better-supabase/testing) to SQL"
|
|
171
171
|
}],
|
|
172
172
|
["doctor", {
|
|
173
|
-
load: () => import("./doctor-
|
|
173
|
+
load: () => import("./doctor-C_A-KEAz.js").then((m) => m.doctorCommand),
|
|
174
174
|
description: "Checks RLS, indexes, drift, auth config and env files; exits 1 on errors, or on warnings with --strict"
|
|
175
175
|
}],
|
|
176
176
|
["sql", {
|
|
177
|
-
load: () => import("./sql-
|
|
177
|
+
load: () => import("./sql-CVx-1O8G.js").then((m) => m.sqlCommand),
|
|
178
178
|
description: "Lists, adds, syncs, upgrades and prints SQL kit modules"
|
|
179
179
|
}],
|
|
180
180
|
["codemod", {
|
|
@@ -3,11 +3,11 @@ import { r as writeIfChanged, t as display } from "./io-k41_z-jT.js";
|
|
|
3
3
|
import { i as plain, r as painter } from "./style-C0NFdJRL.js";
|
|
4
4
|
import { f as readSupabaseToml, p as schemaPaths, u as migrationCommand } from "./config-CPVpXP5I.js";
|
|
5
5
|
import { t as fileDiff } from "./diff-SjkO5rNr.js";
|
|
6
|
-
import {
|
|
6
|
+
import { a as entitlementsMode, l as permdockSource, n as accessPermdockMode, o as kitKeyProblems, t as compiledReadSets, u as readPermdock } from "./read-sets-VQcPgxU9.js";
|
|
7
7
|
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
8
8
|
import { existsSync } from "node:fs";
|
|
9
9
|
import { readFile, readdir } from "node:fs/promises";
|
|
10
|
-
import { SQL_MODULES, contractSignature, customContracts, kitFileVersion, kitLayout, moduleBody, renderKit, resolveModules, sameKitFile, upgradePlan } from "better-supabase/sql";
|
|
10
|
+
import { SQL_MODULES, contractSignature, customContracts, kitFilePaths, kitFileVersion, kitLayout, kitPermissionKeys, moduleBody, renderKit, resolveModules, sameKitFile, upgradePlan } from "better-supabase/sql";
|
|
11
11
|
//#region src/cli/commands/sql.ts
|
|
12
12
|
const SQL_ARGS = {
|
|
13
13
|
action: {
|
|
@@ -53,14 +53,39 @@ async function permdockFor(config, names) {
|
|
|
53
53
|
default: return mode;
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* PermDock's helpers for the `access` module's `permdock` model, read from
|
|
58
|
+
* the manifest whatever `entitlements` says. When `names` resolve to
|
|
59
|
+
* `access`, an invalid mode throws, and so does a kit permission key the
|
|
60
|
+
* catalog doesn't mark `rowConditions: false`. `sql list` (no `names`)
|
|
61
|
+
* doesn't render, so it doesn't throw.
|
|
62
|
+
*/
|
|
63
|
+
async function accessPermdockFor(config, names) {
|
|
64
|
+
const project = await readPermdock(config.root, config.permdock);
|
|
65
|
+
const mode = accessPermdockMode(config, project);
|
|
66
|
+
const renders = names !== void 0 && resolveModules(names, {}).some((module) => module.name === "access");
|
|
67
|
+
switch (mode.kind) {
|
|
68
|
+
case "off": return;
|
|
69
|
+
case "permdock": {
|
|
70
|
+
if (!renders || !project) return mode.access;
|
|
71
|
+
const problems = kitKeyProblems(project, kitPermissionKeys(config.kits, names), mode.access);
|
|
72
|
+
if (problems.length > 0) throw new TypeError(problems.map((problem) => problem.message).join("\n"));
|
|
73
|
+
return mode.access;
|
|
74
|
+
}
|
|
75
|
+
case "invalid":
|
|
76
|
+
if (!renders) return void 0;
|
|
77
|
+
throw new TypeError(mode.problem);
|
|
78
|
+
default: return mode;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
56
81
|
async function layout(config, args, names) {
|
|
57
|
-
return kitLayout(config, args["tests-dir"], [], await permdockFor(config, names));
|
|
82
|
+
return kitLayout(config, args["tests-dir"], [], await permdockFor(config, names), await accessPermdockFor(config, names));
|
|
58
83
|
}
|
|
59
84
|
/** The layout, with `config.readSets` compiled when `names` includes `read-sets`. */
|
|
60
85
|
async function layoutFor(config, args, names) {
|
|
61
86
|
const permdock = await permdockFor(config, names);
|
|
62
87
|
const needsReadSets = resolveModules(names, { ...permdock ? { permdock } : {} }).some((module) => module.name === "read-sets");
|
|
63
|
-
return kitLayout(config, args["tests-dir"], needsReadSets ? await compiledReadSets(config) : [], permdock);
|
|
88
|
+
return kitLayout(config, args["tests-dir"], needsReadSets ? await compiledReadSets(config) : [], permdock, await accessPermdockFor(config, names));
|
|
64
89
|
}
|
|
65
90
|
/**
|
|
66
91
|
* The migrations folder next to the `config.toml` found walking up from
|
|
@@ -231,7 +256,7 @@ async function runSql(config, args, paint = plain) {
|
|
|
231
256
|
switch (action) {
|
|
232
257
|
case "list": {
|
|
233
258
|
const kit = await layout(config, args);
|
|
234
|
-
const files =
|
|
259
|
+
const files = kitFilePaths(Object.keys(SQL_MODULES), kit);
|
|
235
260
|
return {
|
|
236
261
|
code: 0,
|
|
237
262
|
output: `${Object.values(SQL_MODULES).map((module) => {
|
package/dist/client/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as decodeJwtPayload } from "../base64-D2gsioCY.js";
|
|
2
|
-
import { r as userContext } from "../impersonation-
|
|
2
|
+
import { r as userContext } from "../impersonation-BgSHAYLf.js";
|
|
3
3
|
import { EnvValidationError, parseEnv } from "../env/index.js";
|
|
4
4
|
import { t as createQueries } from "../query-CCbo44lG.js";
|
|
5
5
|
import { createBrowserClient } from "@supabase/ssr";
|
package/dist/config/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { o as Casing } from "../types-Dnf_wRDz.js";
|
|
2
|
-
import { $ as GeneratorMetadata, A as VectorSearchConfig, B as CatalogFunction, C as SoftDeleteConfig, D as TenantConfig, E as TableConfig, F as CatalogCheck, G as CatalogTable, H as CatalogGrant, I as CatalogColumn, J as ExtrasFunction, K as CatalogTrigger, L as CatalogColumnGrant, M as resolveConfig, N as resolveJsonSchema, O as TimestampsConfig, P as Catalog, Q as ForeignKeyAction, R as CatalogEnum, S as SeedConfig, T as SqlConfig, U as CatalogIndex, V as CatalogFunctionArg, W as CatalogPolicy, X as ExtrasHookFunction, Y as ExtrasHook, Z as ExtrasTable, _ as PluginFlagsConfig, a as ClaimsConfig, at as DEFAULT_ACTIVE_TENANT, b as ResolvedConfig, c as EntitlementsConfig, ct as KitsConfig, d as Generator, et as Snapshot, f as GeneratorInput, g as PermdockPathsConfig, h as OpenApiConfig, i as CONFIG_SCHEMA_URL, it as ActiveTenantSource, j as defineConfig, k as VectorDistance, l as ExposeConfig, m as JsonTypeConfig, n as BetterSupabaseConfig, nt as SnapshotExtras, o as CodecsConfig, ot as KitMode, p as JsonSchemaSource, q as CatalogUnique, r as BucketConfig, rt as AccessKitConfig, s as DoctorConfig, st as KitModuleConfig, t as ActorConfig, tt as SnapshotBucket, u as GeneratedFile, v as Privilege, w as SourceConfig, x as ResolvedExpose, y as RealtimeConfig, z as CatalogForeignKey } from "../config-
|
|
3
|
-
import { a as JsonSchemaGeneratorOptions, c as DEFAULT_CLAIMS, i as valibot, l as tenantClaimPaths, n as zod, o as buildJsonSchema, r as ValibotGeneratorOptions, s as jsonSchema, t as ZodGeneratorOptions } from "../index-
|
|
2
|
+
import { $ as GeneratorMetadata, A as VectorSearchConfig, B as CatalogFunction, C as SoftDeleteConfig, D as TenantConfig, E as TableConfig, F as CatalogCheck, G as CatalogTable, H as CatalogGrant, I as CatalogColumn, J as ExtrasFunction, K as CatalogTrigger, L as CatalogColumnGrant, M as resolveConfig, N as resolveJsonSchema, O as TimestampsConfig, P as Catalog, Q as ForeignKeyAction, R as CatalogEnum, S as SeedConfig, T as SqlConfig, U as CatalogIndex, V as CatalogFunctionArg, W as CatalogPolicy, X as ExtrasHookFunction, Y as ExtrasHook, Z as ExtrasTable, _ as PluginFlagsConfig, a as ClaimsConfig, at as DEFAULT_ACTIVE_TENANT, b as ResolvedConfig, c as EntitlementsConfig, ct as KitsConfig, d as Generator, et as Snapshot, f as GeneratorInput, g as PermdockPathsConfig, h as OpenApiConfig, i as CONFIG_SCHEMA_URL, it as ActiveTenantSource, j as defineConfig, k as VectorDistance, l as ExposeConfig, m as JsonTypeConfig, n as BetterSupabaseConfig, nt as SnapshotExtras, o as CodecsConfig, ot as KitMode, p as JsonSchemaSource, q as CatalogUnique, r as BucketConfig, rt as AccessKitConfig, s as DoctorConfig, st as KitModuleConfig, t as ActorConfig, tt as SnapshotBucket, u as GeneratedFile, v as Privilege, w as SourceConfig, x as ResolvedExpose, y as RealtimeConfig, z as CatalogForeignKey } from "../config-Bv7gvfzC.js";
|
|
3
|
+
import { a as JsonSchemaGeneratorOptions, c as DEFAULT_CLAIMS, i as valibot, l as tenantClaimPaths, n as zod, o as buildJsonSchema, r as ValibotGeneratorOptions, s as jsonSchema, t as ZodGeneratorOptions } from "../index-CWMDdEr4.js";
|
|
4
4
|
export { type AccessKitConfig, type ActiveTenantSource, type ActorConfig, type BetterSupabaseConfig, type BucketConfig, CONFIG_SCHEMA_URL, type Casing, type Catalog, type CatalogCheck, type CatalogColumn, type CatalogColumnGrant, type CatalogEnum, type CatalogForeignKey, type CatalogFunction, type CatalogFunctionArg, type CatalogGrant, type CatalogIndex, type CatalogPolicy, type CatalogTable, type CatalogTrigger, type CatalogUnique, type ClaimsConfig, type CodecsConfig, DEFAULT_ACTIVE_TENANT, DEFAULT_CLAIMS, type DoctorConfig, type EntitlementsConfig, type ExposeConfig, type ExtrasFunction, type ExtrasHook, type ExtrasHookFunction, type ExtrasTable, type ForeignKeyAction, type GeneratedFile, type Generator, type GeneratorInput, type GeneratorMetadata, type JsonSchemaGeneratorOptions, type JsonSchemaSource, type JsonTypeConfig, type KitMode, type KitModuleConfig, type KitsConfig, type OpenApiConfig, type PermdockPathsConfig, type PluginFlagsConfig, type Privilege, type RealtimeConfig, type ResolvedConfig, type ResolvedExpose, type SeedConfig, type Snapshot, type SnapshotBucket, type SnapshotExtras, type SoftDeleteConfig, type SourceConfig, type SqlConfig, type TableConfig, type TenantConfig, type TimestampsConfig, type ValibotGeneratorOptions, type VectorDistance, type VectorSearchConfig, type ZodGeneratorOptions, buildJsonSchema, defineConfig, jsonSchema, resolveConfig, resolveJsonSchema, tenantClaimPaths, valibot, zod };
|
|
@@ -96,7 +96,16 @@ interface AccessKitConfig extends KitModuleConfig {
|
|
|
96
96
|
readonly tenantIdsWith?: string;
|
|
97
97
|
readonly isPlatform?: string;
|
|
98
98
|
readonly canUser?: string;
|
|
99
|
-
/**
|
|
99
|
+
/**
|
|
100
|
+
* `{tenant}` and `{role}`: whether the caller may assign the role.
|
|
101
|
+
* Required by the `custom` model. For the `permdock` model, use
|
|
102
|
+
* PermDock's assignment rule:
|
|
103
|
+
* `'permdock.permdock_can_assign({role}, {tenant}::text)'` (with the
|
|
104
|
+
* manifest's `rls.schema`). Without it, the `permdock` model lets only
|
|
105
|
+
* the service role assign roles: PermDock projects don't install the
|
|
106
|
+
* `tenant` module (doctor BS407), whose owner-role fallback is the only
|
|
107
|
+
* other rule, and doctor BS411 warns.
|
|
108
|
+
*/
|
|
100
109
|
readonly canAssign?: string;
|
|
101
110
|
/** `{user}`: the permission claim for the access token hook. */
|
|
102
111
|
readonly permissionClaims?: string;
|
package/dist/edge/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { P as TableKey, n as AnyFunctions, r as AnyModels } from "../types-Dnf_wRDz.js";
|
|
2
2
|
import { t as BetterSupabase } from "../define-Bp_BZa5h.js";
|
|
3
|
-
import { f as ServerContext, n as GuardOptions, p as ServerOptions, r as MiddlewareOptions, u as BetterServer } from "../respond-
|
|
4
|
-
import { a as ResourceRouteOptions } from "../resource-
|
|
3
|
+
import { f as ServerContext, n as GuardOptions, p as ServerOptions, r as MiddlewareOptions, u as BetterServer } from "../respond-BR1fJWOB.js";
|
|
4
|
+
import { a as ResourceRouteOptions } from "../resource-DNRFP7KR.js";
|
|
5
5
|
//#region src/edge/index.d.ts
|
|
6
6
|
export interface CorsOptions {
|
|
7
7
|
/** Allowed origins, or `*`. Defaults to `*`. */
|
package/dist/edge/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { n as dbError } from "../errors-DtwXa6b8.js";
|
|
2
2
|
import { a as problemResponse } from "../problem-BW1KhzSG.js";
|
|
3
|
-
import { d as guard, n as createServer, p as respond, r as extendServer, u as defaultExpose } from "../server-
|
|
4
|
-
import { t as defineResource } from "../resource-
|
|
3
|
+
import { d as guard, n as createServer, p as respond, r as extendServer, u as defaultExpose } from "../server-B1mZ7CgQ.js";
|
|
4
|
+
import { t as defineResource } from "../resource-mqfJW4YF.js";
|
|
5
5
|
//#region src/edge/index.ts
|
|
6
6
|
const SUPABASE_CORS_HEADERS = [
|
|
7
7
|
"authorization",
|
package/dist/hono/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { P as TableKey, n as AnyFunctions, r as AnyModels } from "../types-Dnf_wRDz.js";
|
|
2
2
|
import { t as BetterSupabase } from "../define-Bp_BZa5h.js";
|
|
3
|
-
import {
|
|
4
|
-
import { f as ServerContext, n as GuardOptions, p as ServerOptions, r as MiddlewareOptions, u as BetterServer } from "../respond-
|
|
5
|
-
import { a as ResourceRouteOptions } from "../resource-
|
|
3
|
+
import { o as AuthState } from "../impersonation-BmePsuxz.js";
|
|
4
|
+
import { f as ServerContext, n as GuardOptions, p as ServerOptions, r as MiddlewareOptions, u as BetterServer } from "../respond-BR1fJWOB.js";
|
|
5
|
+
import { a as ResourceRouteOptions } from "../resource-DNRFP7KR.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-DtwXa6b8.js";
|
|
2
2
|
import { a as problemResponse } from "../problem-BW1KhzSG.js";
|
|
3
|
-
import { d as guard, i as rememberVerifiedFor, n as createServer, p as respond, r as extendServer, u as defaultExpose } from "../server-
|
|
4
|
-
import { t as defineResource } from "../resource-
|
|
3
|
+
import { d as guard, i as rememberVerifiedFor, n as createServer, p as respond, r as extendServer, u as defaultExpose } from "../server-B1mZ7CgQ.js";
|
|
4
|
+
import { t as defineResource } from "../resource-mqfJW4YF.js";
|
|
5
5
|
import { Hono } from "hono";
|
|
6
6
|
import { HTTPException } from "hono/http-exception";
|
|
7
7
|
//#region src/hono/index.ts
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
//#region src/auth/actor.ts
|
|
2
|
+
function isRecord(value) {
|
|
3
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4
|
+
}
|
|
5
|
+
function copyAct(level) {
|
|
6
|
+
const entries = Object.entries(level).map(([key, value]) => [key, key === "act" && isRecord(value) ? copyAct(value) : value]);
|
|
7
|
+
return Object.fromEntries(entries);
|
|
8
|
+
}
|
|
9
|
+
const INVALID = {
|
|
10
|
+
ok: false,
|
|
11
|
+
reason: "invalid-chain"
|
|
12
|
+
};
|
|
13
|
+
function actorAt(outer, id) {
|
|
14
|
+
const kind = outer["kind"];
|
|
15
|
+
const sessionId = outer["session_id"];
|
|
16
|
+
const readOnly = outer["read_only"];
|
|
17
|
+
const reason = outer["reason"];
|
|
18
|
+
if (kind === "support" || kind === void 0 && sessionId !== void 0) {
|
|
19
|
+
if (typeof sessionId !== "string" || sessionId === "") return INVALID;
|
|
20
|
+
if (readOnly !== void 0 && typeof readOnly !== "boolean") return INVALID;
|
|
21
|
+
return {
|
|
22
|
+
ok: true,
|
|
23
|
+
actor: {
|
|
24
|
+
kind: "support",
|
|
25
|
+
id,
|
|
26
|
+
sessionId,
|
|
27
|
+
readOnly: readOnly ?? true,
|
|
28
|
+
...typeof reason === "string" ? { reason } : {}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
if (kind === "impersonation") return {
|
|
33
|
+
ok: true,
|
|
34
|
+
actor: {
|
|
35
|
+
kind: "impersonation",
|
|
36
|
+
id,
|
|
37
|
+
...typeof reason === "string" ? { reason } : {}
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
if (kind !== void 0) return INVALID;
|
|
41
|
+
return {
|
|
42
|
+
ok: true,
|
|
43
|
+
actor: {
|
|
44
|
+
kind: "oauth-client",
|
|
45
|
+
id,
|
|
46
|
+
chain: copyAct(outer)
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Reads only `act` and `client_id`, the way PermDock's `actorOf` does. An
|
|
52
|
+
* `act` that is not a chain of objects each with a non-empty `sub`, a
|
|
53
|
+
* `kind` other than `support` or `impersonation`, or a support level without
|
|
54
|
+
* a `session_id` is `{ ok: false }`: the session must not pass as the user
|
|
55
|
+
* alone.
|
|
56
|
+
*/
|
|
57
|
+
function actorOf(claims) {
|
|
58
|
+
const outer = claims["act"];
|
|
59
|
+
if (outer !== void 0) {
|
|
60
|
+
if (!isRecord(outer)) return INVALID;
|
|
61
|
+
const id = outer["sub"];
|
|
62
|
+
if (typeof id !== "string" || id === "") return INVALID;
|
|
63
|
+
let level = outer["act"];
|
|
64
|
+
while (level !== void 0) {
|
|
65
|
+
if (!isRecord(level)) return INVALID;
|
|
66
|
+
const sub = level["sub"];
|
|
67
|
+
if (typeof sub !== "string" || sub === "") return INVALID;
|
|
68
|
+
level = level["act"];
|
|
69
|
+
}
|
|
70
|
+
return actorAt(outer, id);
|
|
71
|
+
}
|
|
72
|
+
const client = claims["client_id"];
|
|
73
|
+
if (typeof client === "string" && client !== "") return {
|
|
74
|
+
ok: true,
|
|
75
|
+
actor: {
|
|
76
|
+
id: client,
|
|
77
|
+
kind: "oauth-client"
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
return { ok: true };
|
|
81
|
+
}
|
|
82
|
+
/** The OAuth `scope` claim (a space-separated string or a list); `undefined` when empty. */
|
|
83
|
+
function delegationOf(claims) {
|
|
84
|
+
const scope = claims["scope"];
|
|
85
|
+
const scopes = typeof scope === "string" ? scope.split(/\s+/u).filter(Boolean) : Array.isArray(scope) ? scope.filter((item) => typeof item === "string") : [];
|
|
86
|
+
return scopes.length > 0 ? { scopes } : void 0;
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region src/auth/impersonation.ts
|
|
90
|
+
/** The `act` claim for an impersonated session. */
|
|
91
|
+
function actClaim(options) {
|
|
92
|
+
return {
|
|
93
|
+
kind: "impersonation",
|
|
94
|
+
sub: options.actor,
|
|
95
|
+
reason: options.reason
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The admin in `claims.act`: a support session or an impersonated session
|
|
100
|
+
* (`actorOf` reads the same level, so the two always agree). `undefined`
|
|
101
|
+
* for a normal session, an OAuth client or agent chain, and an invalid `act`.
|
|
102
|
+
*/
|
|
103
|
+
function impersonatorOf(claims) {
|
|
104
|
+
const outcome = actorOf(claims);
|
|
105
|
+
const actor = outcome.ok ? outcome.actor : void 0;
|
|
106
|
+
if (actor === void 0) return void 0;
|
|
107
|
+
switch (actor.kind) {
|
|
108
|
+
case "oauth-client": return;
|
|
109
|
+
case "support":
|
|
110
|
+
case "impersonation": return { ...actor };
|
|
111
|
+
default: return actor;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* The repository context of a signed-in user: the actor, with the
|
|
116
|
+
* impersonator from `act`, and the claims. Every adapter builds it here, so
|
|
117
|
+
* plugins see the same actor on the server, in middleware and in the browser.
|
|
118
|
+
*/
|
|
119
|
+
function userContext(user, claims) {
|
|
120
|
+
const impersonator = impersonatorOf(claims);
|
|
121
|
+
return {
|
|
122
|
+
actor: {
|
|
123
|
+
id: user.id,
|
|
124
|
+
kind: "user",
|
|
125
|
+
...user.role ? { role: user.role } : {},
|
|
126
|
+
...user.email ? { email: user.email } : {},
|
|
127
|
+
...impersonator ? { impersonator: impersonator.id } : {}
|
|
128
|
+
},
|
|
129
|
+
claims
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
//#endregion
|
|
133
|
+
export { delegationOf as a, actorOf as i, impersonatorOf as n, userContext as r, actClaim as t };
|
|
@@ -179,4 +179,28 @@ declare function resolveAuth(request: Request, options: ResolveAuthOptions): Pro
|
|
|
179
179
|
/** The repository context (`actor`, `claims`) for an auth state. */
|
|
180
180
|
declare function authContext(auth: AuthState): RequestContext;
|
|
181
181
|
//#endregion
|
|
182
|
-
|
|
182
|
+
//#region src/auth/impersonation.d.ts
|
|
183
|
+
/** The admin acting as the user, from the RFC 8693 `act` claim. */
|
|
184
|
+
interface Impersonator {
|
|
185
|
+
/** `act.kind`: a support session or an impersonated session. */
|
|
186
|
+
readonly kind: "support" | "impersonation";
|
|
187
|
+
readonly id: string;
|
|
188
|
+
readonly reason?: string;
|
|
189
|
+
/** The support session, when the admin started one (`act.session_id`). */
|
|
190
|
+
readonly sessionId?: string;
|
|
191
|
+
/** Whether the support session blocks writes (`act.read_only`). */
|
|
192
|
+
readonly readOnly?: boolean;
|
|
193
|
+
}
|
|
194
|
+
/** `actingAs(userId, claims, { actor, reason })`: who acts, and why (recorded by the audit module). */
|
|
195
|
+
interface ImpersonationOptions {
|
|
196
|
+
readonly actor: string;
|
|
197
|
+
readonly reason: string;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* The admin in `claims.act`: a support session or an impersonated session
|
|
201
|
+
* (`actorOf` reads the same level, so the two always agree). `undefined`
|
|
202
|
+
* for a normal session, an OAuth client or agent chain, and an invalid `act`.
|
|
203
|
+
*/
|
|
204
|
+
declare function impersonatorOf(claims: Readonly<Record<string, unknown>>): Impersonator | undefined;
|
|
205
|
+
//#endregion
|
|
206
|
+
export { writeSession as C, sessionCookieName as S, StoredSession as _, AuthResolver as a, readSession as b, ResolveAuthOptions as c, clientIp as d, resolveAuth as f, CookieWrite as g, CookieRecord as h, AuthResolution as i, ResolvedState as l, CookieOptions as m, Impersonator as n, AuthState as o, AUTH_CACHE_HEADERS as p, impersonatorOf as r, InvalidReason as s, ImpersonationOptions as t, authContext as u, applyCookieWrites as v, serializeCookie as x, parseCookies as y };
|