@palbase/backend 28.0.0 → 30.0.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/dist/bin/palbase-backend.cjs +166 -12
- package/dist/bin/palbase-backend.cjs.map +1 -1
- package/dist/bin/palbase-backend.js +3 -3
- package/dist/{chunk-RVP6BTEZ.js → chunk-5J5W75A6.js} +15 -4
- package/dist/chunk-5J5W75A6.js.map +1 -0
- package/dist/{chunk-IVZERLTM.js → chunk-ENZ2RFFJ.js} +2 -2
- package/dist/{chunk-SNDXY565.js → chunk-SG4UTNOP.js} +5 -2
- package/dist/chunk-SG4UTNOP.js.map +1 -0
- package/dist/{chunk-75YROPRZ.js → chunk-VN7NMDUH.js} +166 -15
- package/dist/chunk-VN7NMDUH.js.map +1 -0
- package/dist/db/index.cjs +4 -1
- package/dist/db/index.cjs.map +1 -1
- package/dist/db/index.d.cts +1 -1
- package/dist/db/index.d.ts +1 -1
- package/dist/db/index.js +2 -2
- package/dist/engine/index.cjs +166 -12
- package/dist/engine/index.cjs.map +1 -1
- package/dist/engine/index.d.cts +3 -3
- package/dist/engine/index.d.ts +3 -3
- package/dist/engine/index.js +3 -3
- package/dist/{index-dTTLlHIn.d.ts → index-8qy3kIuA.d.ts} +228 -12
- package/dist/{index-DtISj9QX.d.cts → index-DtCgaZAg.d.cts} +228 -12
- package/dist/{index-9C3JHxg-.d.cts → index-Zi7MptvP.d.cts} +30 -8
- package/dist/{index-BbvOoZFr.d.ts → index-b-Q3l7W5.d.ts} +30 -8
- package/dist/index.cjs +66 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +51 -11
- package/dist/index.d.ts +51 -11
- package/dist/index.js +52 -3
- package/dist/index.js.map +1 -1
- package/dist/openapi/index.d.cts +2 -2
- package/dist/openapi/index.d.ts +2 -2
- package/dist/{registry-JQNIX-eA.d.ts → registry-BGJ-Al6F.d.ts} +1 -1
- package/dist/{registry-qIPM5BQe.d.cts → registry-CEYLH-Iz.d.cts} +1 -1
- package/dist/test/index.cjs +56 -0
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.d.cts +1 -1
- package/dist/test/index.d.ts +1 -1
- package/dist/test/index.js +56 -0
- package/dist/test/index.js.map +1 -1
- package/docs/README.md +1 -1
- package/docs/database.md +49 -0
- package/docs/llms-full.txt +50 -1
- package/package.json +1 -1
- package/template/package.json +1 -1
- package/dist/chunk-75YROPRZ.js.map +0 -1
- package/dist/chunk-RVP6BTEZ.js.map +0 -1
- package/dist/chunk-SNDXY565.js.map +0 -1
- /package/dist/{chunk-IVZERLTM.js.map → chunk-ENZ2RFFJ.js.map} +0 -0
package/dist/db/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { f as AnyColumn,
|
|
1
|
+
export { f as AnyColumn, l as ColumnBuilder, m as ColumnDef, n as ColumnMap, o as ColumnType, E as EXTENSION_DEPENDENCIES, t as EmbeddingModelRef, v as EnvServiceDatabase, x as EnvTypedDatabase, O as InsertShape, W as Materialized, a1 as OnDeleteAction, a2 as PALBASE_EXTENSIONS, aC as PalbaseExtension, bz as PolicyBuilder, bA as PolicyCommand, bB as PolicyDef, bE as PolicyMode, bG as RawConstraintDef, bI as Ref, bK as RowShape, S as SchemaDef, bT as TABLE_META, bU as TableDef, bV as TableHandle, bW as TableInput, bY as TxColumnExpr, bZ as TxInsertShape, b$ as TxNow, c0 as TxPlan, T as TxPlanBody, c1 as TxPlanError, c2 as TxPlanHandle, c3 as TxPlanOpResult, c4 as TxPlanRejection, b as TxPlanResponse, c5 as TxRefError, c6 as TxRow, c7 as TxRows, c8 as TxSelectOptions, c9 as TxSetShape, cb as TxTable, cc as TxTables, cd as TxWhere, cj as TypedDB, ck as TypedTable, cl as TypedTx, cu as bigint, cv as boolean, cx as dec, cA as defineSchema, cB as defineTable, cC as enumType, cE as inc, cH as installationRef, cI as integer, cJ as isPalbaseExtension, cL as jsonb, cM as makeTypedDB, cN as now, cR as openai, cS as ownedByUser, cT as policy, cU as raw, cW as text, cX as timestamp, cY as userRef, cZ as uuid, c_ as vector } from '../index-DtCgaZAg.cjs';
|
|
2
2
|
import './env.cjs';
|
|
3
3
|
import '../stack.cjs';
|
|
4
4
|
import 'zod';
|
package/dist/db/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { f as AnyColumn,
|
|
1
|
+
export { f as AnyColumn, l as ColumnBuilder, m as ColumnDef, n as ColumnMap, o as ColumnType, E as EXTENSION_DEPENDENCIES, t as EmbeddingModelRef, v as EnvServiceDatabase, x as EnvTypedDatabase, O as InsertShape, W as Materialized, a1 as OnDeleteAction, a2 as PALBASE_EXTENSIONS, aC as PalbaseExtension, bz as PolicyBuilder, bA as PolicyCommand, bB as PolicyDef, bE as PolicyMode, bG as RawConstraintDef, bI as Ref, bK as RowShape, S as SchemaDef, bT as TABLE_META, bU as TableDef, bV as TableHandle, bW as TableInput, bY as TxColumnExpr, bZ as TxInsertShape, b$ as TxNow, c0 as TxPlan, T as TxPlanBody, c1 as TxPlanError, c2 as TxPlanHandle, c3 as TxPlanOpResult, c4 as TxPlanRejection, b as TxPlanResponse, c5 as TxRefError, c6 as TxRow, c7 as TxRows, c8 as TxSelectOptions, c9 as TxSetShape, cb as TxTable, cc as TxTables, cd as TxWhere, cj as TypedDB, ck as TypedTable, cl as TypedTx, cu as bigint, cv as boolean, cx as dec, cA as defineSchema, cB as defineTable, cC as enumType, cE as inc, cH as installationRef, cI as integer, cJ as isPalbaseExtension, cL as jsonb, cM as makeTypedDB, cN as now, cR as openai, cS as ownedByUser, cT as policy, cU as raw, cW as text, cX as timestamp, cY as userRef, cZ as uuid, c_ as vector } from '../index-8qy3kIuA.js';
|
|
2
2
|
import './env.js';
|
|
3
3
|
import '../stack.js';
|
|
4
4
|
import 'zod';
|
package/dist/db/index.js
CHANGED
package/dist/engine/index.cjs
CHANGED
|
@@ -948,6 +948,7 @@ function makeTableProxy(ops, prefix) {
|
|
|
948
948
|
// DÖRDÜNCÜ FİİL, aynı gerekçeyle: tip söz veriyor, ops katmanı
|
|
949
949
|
// uyguluyor, ve bir handler'ın gerçekten dokunduğu yer BURASI.
|
|
950
950
|
// `runtime-table-verbs` kapısı bunu adıyla saydı.
|
|
951
|
+
aggregate: /* @__PURE__ */ __name((q) => ops().aggregate(name, q), "aggregate"),
|
|
951
952
|
insertMany: /* @__PURE__ */ __name((rows, opts) => ops().insertMany(name, rows, opts), "insertMany"),
|
|
952
953
|
update: /* @__PURE__ */ __name((q) => ops().update(name, q.where.id, q.set), "update"),
|
|
953
954
|
delete: /* @__PURE__ */ __name((id) => ops().delete(name, id), "delete"),
|
|
@@ -1005,6 +1006,7 @@ function makeTypedSurface(raw) {
|
|
|
1005
1006
|
$claim: /* @__PURE__ */ __name((table, unique, extra) => reco.claim(table, unique, extra), "$claim"),
|
|
1006
1007
|
$lockRows: /* @__PURE__ */ __name((table, ids) => reco.lockRows(table, ids), "$lockRows"),
|
|
1007
1008
|
$advisoryXactLock: /* @__PURE__ */ __name((key) => reco.advisoryXactLock(key), "$advisoryXactLock"),
|
|
1009
|
+
$aggregate: /* @__PURE__ */ __name((table, q) => raw.aggregate(table, q), "$aggregate"),
|
|
1008
1010
|
$insertMany: /* @__PURE__ */ __name((table, rows, opts) => raw.insertMany(table, rows, opts), "$insertMany"),
|
|
1009
1011
|
$supersede: /* @__PURE__ */ __name((table, id, row) => raw.supersede(table, id, row), "$supersede")
|
|
1010
1012
|
};
|
|
@@ -1090,6 +1092,7 @@ var Storage = Object.assign({
|
|
|
1090
1092
|
});
|
|
1091
1093
|
var Cache = makeServiceProxy("Cache");
|
|
1092
1094
|
var Secrets = makeServiceProxy("Secrets");
|
|
1095
|
+
var Auth = makeServiceProxy("Auth");
|
|
1093
1096
|
var Log = makeServiceProxy("Log");
|
|
1094
1097
|
var Notifications = makeServiceProxy("Notifications");
|
|
1095
1098
|
var rawFlags = makeServiceProxy("Flags");
|
|
@@ -1339,9 +1342,11 @@ function effectiveAuth(routeAuth, controllerAuth) {
|
|
|
1339
1342
|
};
|
|
1340
1343
|
const o = spec;
|
|
1341
1344
|
const role = typeof o.role === "string" && o.role.trim() !== "" ? o.role.trim() : void 0;
|
|
1345
|
+
const permission = typeof o.permission === "string" && o.permission.trim() !== "" ? o.permission.trim() : void 0;
|
|
1342
1346
|
return {
|
|
1343
1347
|
required: o.required !== false,
|
|
1344
1348
|
role,
|
|
1349
|
+
permission,
|
|
1345
1350
|
verifiedEmail: o.verifiedEmail === true
|
|
1346
1351
|
};
|
|
1347
1352
|
}
|
|
@@ -2329,6 +2334,26 @@ function buildValueTuple(table, cols, data, add, caller) {
|
|
|
2329
2334
|
}).join(", ");
|
|
2330
2335
|
}
|
|
2331
2336
|
__name(buildValueTuple, "buildValueTuple");
|
|
2337
|
+
function columnTypeOf(table, col) {
|
|
2338
|
+
const def = tableDefOf(table);
|
|
2339
|
+
const c = def?.columns?.[col];
|
|
2340
|
+
if (c === void 0 || c === null) return null;
|
|
2341
|
+
const d = typeof c === "object" && "_def" in c ? c._def : c;
|
|
2342
|
+
return d !== null && typeof d === "object" && typeof d.type === "string" ? d.type : null;
|
|
2343
|
+
}
|
|
2344
|
+
__name(columnTypeOf, "columnTypeOf");
|
|
2345
|
+
var NUMERIC_PG_TYPES = /* @__PURE__ */ new Set([
|
|
2346
|
+
"integer",
|
|
2347
|
+
"bigint",
|
|
2348
|
+
"numeric"
|
|
2349
|
+
]);
|
|
2350
|
+
function aggregateExpr(table, fn, col, alias) {
|
|
2351
|
+
const q = `t.${quoteIdent(col)}`;
|
|
2352
|
+
const t = columnTypeOf(table, col);
|
|
2353
|
+
if (fn === "sum" || fn === "avg") return `${fn}(${q})::text AS ${quoteIdent(alias)}`;
|
|
2354
|
+
return t !== null && NUMERIC_PG_TYPES.has(t) && t !== "integer" ? `${fn}(${q})::text AS ${quoteIdent(alias)}` : `${fn}(${q}) AS ${quoteIdent(alias)}`;
|
|
2355
|
+
}
|
|
2356
|
+
__name(aggregateExpr, "aggregateExpr");
|
|
2332
2357
|
function createOps(tx) {
|
|
2333
2358
|
const at = /* @__PURE__ */ __name(() => resolveTx(tx), "at");
|
|
2334
2359
|
const ops = {
|
|
@@ -2609,7 +2634,7 @@ function createOps(tx) {
|
|
|
2609
2634
|
* should not silently succeed. Callers who mean every row say so with a
|
|
2610
2635
|
* predicate that is true for every row.
|
|
2611
2636
|
*/
|
|
2612
|
-
async updateMany(table, where, set) {
|
|
2637
|
+
async updateMany(table, where, set, opts) {
|
|
2613
2638
|
assertNotAppendOnly("updateMany", table);
|
|
2614
2639
|
const cols = Object.keys(set);
|
|
2615
2640
|
if (cols.length === 0) {
|
|
@@ -2632,8 +2657,15 @@ function createOps(tx) {
|
|
|
2632
2657
|
const whereSql = compileWhere(table, known, where, add, "updateMany");
|
|
2633
2658
|
assertHasPredicate(whereSql, "updateMany", table);
|
|
2634
2659
|
const pk = primaryKeyOf(table);
|
|
2635
|
-
const
|
|
2636
|
-
|
|
2660
|
+
const wantRows = opts?.returning !== false;
|
|
2661
|
+
const tail = wantRows ? "RETURNING *" : "RETURNING 1";
|
|
2662
|
+
const simple = isSinglePkEquality(where, pk) || pk === null;
|
|
2663
|
+
const body = simple ? `UPDATE ${quoteTable(table)} AS t SET ${assignments} WHERE true${whereSql} ${tail}` : `UPDATE ${quoteTable(table)} AS t SET ${assignments} FROM locked WHERE t.${quoteIdent(pk)} = locked.${quoteIdent(pk)} ` + (wantRows ? "RETURNING t.*" : "RETURNING 1");
|
|
2664
|
+
const lockedCte = simple ? "" : `WITH locked AS (SELECT t.${quoteIdent(pk)} FROM ${quoteTable(table)} t WHERE true${whereSql} ORDER BY t.${quoteIdent(pk)} FOR NO KEY UPDATE) `;
|
|
2665
|
+
const sql = wantRows ? `${lockedCte}${body}` : simple ? `WITH upd AS (${body}) SELECT count(*)::int AS n FROM upd` : `${lockedCte.replace(/ $/, "")}, upd AS (${body}) SELECT count(*)::int AS n FROM upd`;
|
|
2666
|
+
const out = await (await at()).unsafe(sql, params);
|
|
2667
|
+
if (!wantRows) return Number(out[0]?.["n"] ?? 0);
|
|
2668
|
+
return asTableRows(table, out);
|
|
2637
2669
|
},
|
|
2638
2670
|
/**
|
|
2639
2671
|
* Delete every row the filter matches, in ONE statement; resolves to how
|
|
@@ -2663,6 +2695,78 @@ function createOps(tx) {
|
|
|
2663
2695
|
* An empty filter is legitimate HERE: counting a whole table is a read, and
|
|
2664
2696
|
* reads do not destroy anything.
|
|
2665
2697
|
*/
|
|
2698
|
+
/**
|
|
2699
|
+
* Toplama — `count`'un yanına `sum`/`avg`/`min`/`max` ve `groupBy`.
|
|
2700
|
+
*
|
|
2701
|
+
* Var olma nedeni: bunlar olmadan "bu hesabın toplamı" ya `$query`'ye ya da
|
|
2702
|
+
* TÜM satırları çekip JS'te toplamaya düşüyor. İkincisi hem N+1 hem de para
|
|
2703
|
+
* için YANLIŞ — `numeric` JS `number`'ına uğradığı anda kesinliğini
|
|
2704
|
+
* kaybeder.
|
|
2705
|
+
*
|
|
2706
|
+
* `groupBy` verilirse SATIRLAR döner, verilmezse TEK sonuç. Boş kümede
|
|
2707
|
+
* Postgres NULL döndürür, 0 değil — ve bu SDK onu `null` olarak geçiriyor:
|
|
2708
|
+
* "hiç satır yoktu" ile "toplam sıfırdı" farklı şeyler.
|
|
2709
|
+
*/
|
|
2710
|
+
async aggregate(table, q = {}) {
|
|
2711
|
+
const known = schemaColumns(table);
|
|
2712
|
+
const check = /* @__PURE__ */ __name((col, where) => {
|
|
2713
|
+
if (known !== null && !known.has(col)) {
|
|
2714
|
+
throw new Error(`aggregate(${table}): ${where} kolonu "${col}" tabloda yok (FR-016)`);
|
|
2715
|
+
}
|
|
2716
|
+
}, "check");
|
|
2717
|
+
const parts = [];
|
|
2718
|
+
const wants = [];
|
|
2719
|
+
for (const fn of [
|
|
2720
|
+
"sum",
|
|
2721
|
+
"avg",
|
|
2722
|
+
"min",
|
|
2723
|
+
"max"
|
|
2724
|
+
]) {
|
|
2725
|
+
for (const col of q[fn] ?? []) {
|
|
2726
|
+
check(col, fn);
|
|
2727
|
+
if (fn === "sum" || fn === "avg") {
|
|
2728
|
+
const t = columnTypeOf(table, col);
|
|
2729
|
+
if (t !== null && !NUMERIC_PG_TYPES.has(t)) {
|
|
2730
|
+
throw new Error(`aggregate(${table}): ${fn}("${col}") say\u0131sal olmayan bir kolonda \u2014 "${col}" bir ${t} kolonu. Toplama yaln\u0131z integer/bigint/numeric kolonlarda anlaml\u0131; ka\xE7 sat\u0131r oldu\u011Funu saymak i\xE7in count: true kullan\u0131n.`);
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2733
|
+
const alias = `${fn}__${col}`;
|
|
2734
|
+
wants.push({
|
|
2735
|
+
fn,
|
|
2736
|
+
col,
|
|
2737
|
+
alias
|
|
2738
|
+
});
|
|
2739
|
+
parts.push(aggregateExpr(table, fn, col, alias));
|
|
2740
|
+
}
|
|
2741
|
+
}
|
|
2742
|
+
if (q.count === true) parts.push('count(*) AS "_count"');
|
|
2743
|
+
if (parts.length === 0) {
|
|
2744
|
+
throw new Error(`aggregate(${table}): hi\xE7bir toplama istenmedi \u2014 sonu\xE7 bo\u015F bir nesne olurdu. sum/avg/min/max kolonlar\u0131n\u0131 ya da count: true'yu verin.`);
|
|
2745
|
+
}
|
|
2746
|
+
const groups = q.groupBy ?? [];
|
|
2747
|
+
for (const col of groups) check(col, "groupBy");
|
|
2748
|
+
const groupSel = groups.map((c) => `t.${quoteIdent(c)} AS ${quoteIdent(`g__${c}`)}`);
|
|
2749
|
+
const params = [];
|
|
2750
|
+
const add = /* @__PURE__ */ __name((v) => `$${params.push(v)}`, "add");
|
|
2751
|
+
const whereSql = compileWhere(table, known, q.where ?? {}, add, "aggregate");
|
|
2752
|
+
const groupSql = groups.length > 0 ? ` GROUP BY ${groups.map((c) => `t.${quoteIdent(c)}`).join(", ")}` : "";
|
|
2753
|
+
const sql = `SELECT ${[
|
|
2754
|
+
...groupSel,
|
|
2755
|
+
...parts
|
|
2756
|
+
].join(", ")} FROM ${quoteTable(table)} t WHERE true${whereSql}${groupSql}`;
|
|
2757
|
+
const rows = await (await at()).unsafe(sql, params);
|
|
2758
|
+
const shape = /* @__PURE__ */ __name((r) => {
|
|
2759
|
+
const out = {};
|
|
2760
|
+
for (const c of groups) out[c] = r[`g__${c}`];
|
|
2761
|
+
for (const w of wants) {
|
|
2762
|
+
const bucket = out[w.fn] ??= {};
|
|
2763
|
+
bucket[w.col] = r[w.alias] ?? null;
|
|
2764
|
+
}
|
|
2765
|
+
if (q.count === true) out["count"] = Number(r["_count"] ?? 0);
|
|
2766
|
+
return out;
|
|
2767
|
+
}, "shape");
|
|
2768
|
+
return groups.length > 0 ? rows.map(shape) : shape(rows[0] ?? {});
|
|
2769
|
+
},
|
|
2666
2770
|
async count(table, where = {}) {
|
|
2667
2771
|
const params = [];
|
|
2668
2772
|
const add = /* @__PURE__ */ __name((v) => {
|
|
@@ -4505,6 +4609,32 @@ var AUTHOR_ANSWERED = /* @__PURE__ */ new Set([
|
|
|
4505
4609
|
409,
|
|
4506
4610
|
429
|
|
4507
4611
|
]);
|
|
4612
|
+
async function refuseUnlessAuthorized(client, spec, requestId, rolesOfCaller) {
|
|
4613
|
+
if (spec.permission !== void 0) {
|
|
4614
|
+
const [row] = await client.query("select auth.has_permission($1) as ok", [
|
|
4615
|
+
spec.permission
|
|
4616
|
+
]);
|
|
4617
|
+
if (row?.ok !== true) {
|
|
4618
|
+
return envelope("forbidden", `This endpoint requires the "${spec.permission}" permission`, 403, requestId);
|
|
4619
|
+
}
|
|
4620
|
+
}
|
|
4621
|
+
if (spec.role !== void 0) {
|
|
4622
|
+
const held = await rolesOfCaller();
|
|
4623
|
+
if (!held.includes(spec.role)) {
|
|
4624
|
+
return envelope("forbidden", `This endpoint requires the "${spec.role}" role`, 403, requestId);
|
|
4625
|
+
}
|
|
4626
|
+
}
|
|
4627
|
+
return null;
|
|
4628
|
+
}
|
|
4629
|
+
__name(refuseUnlessAuthorized, "refuseUnlessAuthorized");
|
|
4630
|
+
function makeRoleResolver(client) {
|
|
4631
|
+
let pending = null;
|
|
4632
|
+
return () => {
|
|
4633
|
+
pending ??= client.query("select unnest(auth.app_roles()) as role").then((rows) => rows.map((r) => r.role));
|
|
4634
|
+
return pending;
|
|
4635
|
+
};
|
|
4636
|
+
}
|
|
4637
|
+
__name(makeRoleResolver, "makeRoleResolver");
|
|
4508
4638
|
async function createApp(opts) {
|
|
4509
4639
|
const { config, controllers } = opts;
|
|
4510
4640
|
const container = buildContainer();
|
|
@@ -4553,6 +4683,7 @@ async function createApp(opts) {
|
|
|
4553
4683
|
Database: db?.client ?? stubModule("Database (a start hook runs before any request \u2014 open your own connection here)"),
|
|
4554
4684
|
Cache: cache,
|
|
4555
4685
|
Log: log,
|
|
4686
|
+
Auth: modules.Auth ?? stubModule("Auth"),
|
|
4556
4687
|
Documents: modules.Documents ?? stubModule("Documents"),
|
|
4557
4688
|
Storage: modules.Storage ?? stubModule("Storage"),
|
|
4558
4689
|
Notifications: modules.Notifications ?? stubModule("Notifications"),
|
|
@@ -4596,8 +4727,23 @@ async function createApp(opts) {
|
|
|
4596
4727
|
if (spec.required && !callerClaims) {
|
|
4597
4728
|
return envelope("unauthorized", "A valid access token is required", 401, requestId);
|
|
4598
4729
|
}
|
|
4599
|
-
if (callerClaims
|
|
4600
|
-
|
|
4730
|
+
if (callerClaims) {
|
|
4731
|
+
const uploadDb = createRequestDatabase(sql, {
|
|
4732
|
+
role: config.dbRole,
|
|
4733
|
+
serviceRole: config.dbServiceRole,
|
|
4734
|
+
claimsJson: JSON.stringify(callerClaims)
|
|
4735
|
+
});
|
|
4736
|
+
try {
|
|
4737
|
+
const refusal = await refuseUnlessAuthorized(uploadDb.client, spec, requestId, makeRoleResolver(uploadDb.client));
|
|
4738
|
+
if (refusal) {
|
|
4739
|
+
await uploadDb.commit();
|
|
4740
|
+
return refusal;
|
|
4741
|
+
}
|
|
4742
|
+
await uploadDb.commit();
|
|
4743
|
+
} catch (err) {
|
|
4744
|
+
await uploadDb.rollback(err);
|
|
4745
|
+
throw err;
|
|
4746
|
+
}
|
|
4601
4747
|
}
|
|
4602
4748
|
const grant = grantFor(target?.entry, {
|
|
4603
4749
|
userId: typeof callerClaims?.sub === "string" ? callerClaims.sub : null,
|
|
@@ -4630,8 +4776,15 @@ async function createApp(opts) {
|
|
|
4630
4776
|
return envelope("unauthorized", "A valid access token is required", 401, requestId);
|
|
4631
4777
|
}
|
|
4632
4778
|
const userId = typeof claims?.sub === "string" ? claims.sub : void 0;
|
|
4633
|
-
|
|
4634
|
-
|
|
4779
|
+
const db = createRequestDatabase(sql, {
|
|
4780
|
+
role: config.dbRole,
|
|
4781
|
+
serviceRole: config.dbServiceRole,
|
|
4782
|
+
claimsJson: JSON.stringify(claims ?? {})
|
|
4783
|
+
});
|
|
4784
|
+
const rolesOfCaller = makeRoleResolver(db.client);
|
|
4785
|
+
if (claims) {
|
|
4786
|
+
const refusal = await refuseUnlessAuthorized(db.client, spec, requestId, rolesOfCaller);
|
|
4787
|
+
if (refusal) return refusal;
|
|
4635
4788
|
}
|
|
4636
4789
|
if (claims && spec.verifiedEmail && claims.email_verified !== true) {
|
|
4637
4790
|
return envelope("email_not_verified", "A verified email address is required", 403, requestId);
|
|
@@ -4783,6 +4936,12 @@ async function createApp(opts) {
|
|
|
4783
4936
|
role: claims.role,
|
|
4784
4937
|
emailVerified: claims.email_verified === true,
|
|
4785
4938
|
metadata: claims.metadata ?? {},
|
|
4939
|
+
// The caller's APPLICATION roles, from `auth.user_roles` — the
|
|
4940
|
+
// same resolution the gate used, so the handler cannot read a
|
|
4941
|
+
// different set than the one it was admitted on. Never from a
|
|
4942
|
+
// claim: a revoked role has to bite on the next request, and a
|
|
4943
|
+
// token says only what was true when it was minted.
|
|
4944
|
+
roles: await rolesOfCaller(),
|
|
4786
4945
|
// Server-owned and only ever filled from a verdict palauth
|
|
4787
4946
|
// returned. Never from a raw header, never from JWT metadata —
|
|
4788
4947
|
// the type has said so since it was written and this is the
|
|
@@ -4852,11 +5011,6 @@ async function createApp(opts) {
|
|
|
4852
5011
|
args[p.index] = void 0;
|
|
4853
5012
|
}
|
|
4854
5013
|
}
|
|
4855
|
-
const db = createRequestDatabase(sql, {
|
|
4856
|
-
role: config.dbRole,
|
|
4857
|
-
serviceRole: config.dbServiceRole,
|
|
4858
|
-
claimsJson: JSON.stringify(claims ?? {})
|
|
4859
|
-
});
|
|
4860
5014
|
try {
|
|
4861
5015
|
const services = buildServices(db);
|
|
4862
5016
|
if (meta.options?.sseConfig !== void 0) {
|