@palbase/backend 34.0.0 → 34.1.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/LICENSE +21 -0
- package/dist/bin/palbase-backend.cjs +275 -30
- package/dist/bin/palbase-backend.cjs.map +1 -1
- package/dist/bin/palbase-backend.js +4 -4
- package/dist/{chunk-SEM36BWY.js → chunk-5UGPXKMJ.js} +60 -12
- package/dist/chunk-5UGPXKMJ.js.map +1 -0
- package/dist/{chunk-FOWA7PF4.js → chunk-P7MGSAFT.js} +6 -1
- package/dist/{chunk-FOWA7PF4.js.map → chunk-P7MGSAFT.js.map} +1 -1
- package/dist/{chunk-YX7UA7VC.js → chunk-X2UYIXDS.js} +8 -2
- package/dist/chunk-X2UYIXDS.js.map +1 -0
- package/dist/{chunk-MNGZE2MM.js → chunk-XZPDPYVY.js} +279 -34
- package/dist/chunk-XZPDPYVY.js.map +1 -0
- package/dist/{chunk-LOGL2ZHD.js → chunk-YO5GYM73.js} +2 -2
- package/dist/db/index.cjs +34 -10
- package/dist/db/index.cjs.map +1 -1
- package/dist/db/index.d.cts +2 -2
- package/dist/db/index.d.ts +2 -2
- package/dist/db/index.js +2 -2
- package/dist/{endpoint-DpNUx-ON.d.ts → endpoint-BXPdsTA5.d.ts} +56 -2
- package/dist/{endpoint-kYgEHrrV.d.cts → endpoint-DqhUP7RT.d.cts} +56 -2
- package/dist/engine/index.cjs +275 -30
- 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 +4 -4
- package/dist/{index-pnDsEfxJ.d.cts → index-Co0Hj_aO.d.cts} +2 -2
- package/dist/{index-JrHqNsjQ.d.ts → index-MhCL7ll4.d.ts} +2 -2
- package/dist/index.cjs +79 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +6 -4
- 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-BcOd8Zhf.d.cts → registry-BkIVHPzB.d.cts} +1 -1
- package/dist/{registry-BoX5iNyY.d.ts → registry-CvGLR5j1.d.ts} +1 -1
- 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 +2 -2
- package/dist/test/index.js.map +1 -1
- package/docs/auth.md +58 -0
- package/docs/llms-full.txt +73 -0
- package/docs/schema.md +15 -0
- package/package.json +15 -15
- package/template/db/public.ts +12 -0
- package/template/scripts/test.sh +0 -0
- package/dist/chunk-MNGZE2MM.js.map +0 -1
- package/dist/chunk-SEM36BWY.js.map +0 -1
- package/dist/chunk-YX7UA7VC.js.map +0 -1
- /package/dist/{chunk-LOGL2ZHD.js.map → chunk-YO5GYM73.js.map} +0 -0
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
buildContainer,
|
|
6
6
|
buildRelations,
|
|
7
7
|
uniqueKeysOf
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-X2UYIXDS.js";
|
|
9
9
|
import {
|
|
10
10
|
validateInsertManyOptions
|
|
11
11
|
} from "./chunk-GHNC2UHS.js";
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
__requestALS,
|
|
14
14
|
__runStartHooks,
|
|
15
15
|
__runWithRuntime
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-YO5GYM73.js";
|
|
17
17
|
import {
|
|
18
18
|
CODECS,
|
|
19
19
|
assertReadContract
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
retryBudget,
|
|
35
35
|
validateAtomicOptions,
|
|
36
36
|
validateWorkload
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-P7MGSAFT.js";
|
|
38
38
|
import {
|
|
39
39
|
getRoutes,
|
|
40
40
|
resolveEffectiveAuth
|
|
@@ -1370,10 +1370,10 @@ function applyFromDb(row, transforms) {
|
|
|
1370
1370
|
__name(applyFromDb, "applyFromDb");
|
|
1371
1371
|
function asTableRow(table, row) {
|
|
1372
1372
|
const revived = reviveVectors(asWireRow(row), vectorColumnsOf(currentSchema, table));
|
|
1373
|
-
return applyFromDb(revived, transformsOf(currentSchema, table));
|
|
1373
|
+
return foldCan(table, applyFromDb(revived, transformsOf(currentSchema, table)), false);
|
|
1374
1374
|
}
|
|
1375
1375
|
__name(asTableRow, "asTableRow");
|
|
1376
|
-
function asTableRows(table, rows, withSpec) {
|
|
1376
|
+
function asTableRows(table, rows, withSpec, partial = false) {
|
|
1377
1377
|
const vectorCols = vectorColumnsOf(currentSchema, table);
|
|
1378
1378
|
const transforms = transformsOf(currentSchema, table);
|
|
1379
1379
|
const result = rows.map((row) => applyFromDb(reviveVectors(asWireRow(row), vectorCols), transforms));
|
|
@@ -1389,6 +1389,7 @@ function asTableRows(table, rows, withSpec) {
|
|
|
1389
1389
|
], nested)[0];
|
|
1390
1390
|
}
|
|
1391
1391
|
}
|
|
1392
|
+
for (const row of result) foldCan(table, row, partial);
|
|
1392
1393
|
return result;
|
|
1393
1394
|
}
|
|
1394
1395
|
__name(asTableRows, "asTableRows");
|
|
@@ -1687,21 +1688,248 @@ function projectedColumn(table, column, alias) {
|
|
|
1687
1688
|
return columnTypeOf(table, column) === "timestamp" ? `${expr} AS ${quoteIdent(column)}` : expr;
|
|
1688
1689
|
}
|
|
1689
1690
|
__name(projectedColumn, "projectedColumn");
|
|
1691
|
+
var CAN_PREFIX = "can:";
|
|
1692
|
+
var canMemo = /* @__PURE__ */ new WeakMap();
|
|
1693
|
+
function canRoleGate(roles) {
|
|
1694
|
+
if (roles.length === 0) return null;
|
|
1695
|
+
const names = roles.flatMap((r) => [
|
|
1696
|
+
r,
|
|
1697
|
+
`backend_${r}`
|
|
1698
|
+
]).map((r) => `'${r.replace(/'/g, "''")}'`);
|
|
1699
|
+
return `(current_user = ANY (ARRAY[${names.join(", ")}]))`;
|
|
1700
|
+
}
|
|
1701
|
+
__name(canRoleGate, "canRoleGate");
|
|
1702
|
+
function canEntries(def, alias) {
|
|
1703
|
+
const policies = def.policies ?? [];
|
|
1704
|
+
const dropped = [];
|
|
1705
|
+
const restrictive = [];
|
|
1706
|
+
for (const p of policies) {
|
|
1707
|
+
if (p.permissive !== false || p.command !== "update" && p.command !== "all") continue;
|
|
1708
|
+
const r = renderCanUsing(p, alias, def.name);
|
|
1709
|
+
if (r === null) {
|
|
1710
|
+
return {
|
|
1711
|
+
entries: [],
|
|
1712
|
+
dropped: policies.filter((q) => q.command === "update" && q.permissive !== false).map((q) => ({
|
|
1713
|
+
name: q.name,
|
|
1714
|
+
reason: `restrictive policy "${p.name}" cannot be evaluated as a column`
|
|
1715
|
+
}))
|
|
1716
|
+
};
|
|
1717
|
+
}
|
|
1718
|
+
const rGate = canRoleGate(p.roles ?? []);
|
|
1719
|
+
restrictive.push(rGate === null ? r : `((NOT ${rGate}) OR ${r})`);
|
|
1720
|
+
}
|
|
1721
|
+
const entries = [];
|
|
1722
|
+
for (const p of policies) {
|
|
1723
|
+
if (p.command !== "update" || p.permissive === false) continue;
|
|
1724
|
+
if (Buffer.byteLength(CAN_PREFIX + p.name, "utf8") > 63) {
|
|
1725
|
+
dropped.push({
|
|
1726
|
+
name: p.name,
|
|
1727
|
+
reason: "its name is longer than 59 bytes and Postgres would truncate the column alias"
|
|
1728
|
+
});
|
|
1729
|
+
continue;
|
|
1730
|
+
}
|
|
1731
|
+
const using = renderCanUsing(p, alias, def.name);
|
|
1732
|
+
if (using === null) {
|
|
1733
|
+
dropped.push({
|
|
1734
|
+
name: p.name,
|
|
1735
|
+
reason: "its using expression cannot be selected as a column (null; a raw string that is table-qualified, carries a comment or unbalanced parentheses, or calls auth.has_permission outside a (select \u2026) wrapper; or a shape this engine does not render)"
|
|
1736
|
+
});
|
|
1737
|
+
continue;
|
|
1738
|
+
}
|
|
1739
|
+
const gate = canRoleGate(p.roles ?? []);
|
|
1740
|
+
const parts = [
|
|
1741
|
+
...gate === null ? [] : [
|
|
1742
|
+
gate
|
|
1743
|
+
],
|
|
1744
|
+
using,
|
|
1745
|
+
...restrictive
|
|
1746
|
+
];
|
|
1747
|
+
entries.push({
|
|
1748
|
+
name: p.name,
|
|
1749
|
+
using: parts.length === 1 ? using : `(${parts.join(" AND ")})`
|
|
1750
|
+
});
|
|
1751
|
+
}
|
|
1752
|
+
return {
|
|
1753
|
+
entries,
|
|
1754
|
+
dropped
|
|
1755
|
+
};
|
|
1756
|
+
}
|
|
1757
|
+
__name(canEntries, "canEntries");
|
|
1758
|
+
var CanRenderError = class CanRenderError2 extends Error {
|
|
1759
|
+
static {
|
|
1760
|
+
__name(this, "CanRenderError");
|
|
1761
|
+
}
|
|
1762
|
+
};
|
|
1763
|
+
var ENGINE_ALIASES = /^(t|locked|w\d+|wj\d+|x|q|g|p|upd)$/;
|
|
1764
|
+
function renderCanUsing(p, qualifier, tableName) {
|
|
1765
|
+
if (p.using === null || p.using === void 0) return null;
|
|
1766
|
+
if (typeof p.using === "string") {
|
|
1767
|
+
const withoutLiterals = p.using.replace(/'(?:[^']|'')*'/g, "''");
|
|
1768
|
+
if (/--|\/\*/.test(withoutLiterals)) return null;
|
|
1769
|
+
if (withoutLiterals.replace(/''/g, "").includes("'")) return null;
|
|
1770
|
+
let depth = 0;
|
|
1771
|
+
for (const ch of withoutLiterals) {
|
|
1772
|
+
if (ch === "(") depth++;
|
|
1773
|
+
else if (ch === ")" && --depth < 0) return null;
|
|
1774
|
+
}
|
|
1775
|
+
if (depth !== 0) return null;
|
|
1776
|
+
const tbl = tableName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1777
|
+
const qualified = new RegExp(`(^|[^A-Za-z0-9_])(?:"?[A-Za-z_][A-Za-z0-9_]*"?\\s*\\.\\s*)?"?${tbl}"?\\s*\\.`);
|
|
1778
|
+
if (qualified.test(withoutLiterals)) return null;
|
|
1779
|
+
const calls = withoutLiterals.match(/auth\s*\.\s*"?has_permission"?\s*\(/gi) ?? [];
|
|
1780
|
+
const wrapped = withoutLiterals.match(/\(\s*select\s*[(\s]*auth\s*\.\s*"?has_permission"?\s*\(/gi) ?? [];
|
|
1781
|
+
if (calls.length !== wrapped.length) return null;
|
|
1782
|
+
return `(${p.using})`;
|
|
1783
|
+
}
|
|
1784
|
+
try {
|
|
1785
|
+
const rendered = canExprSql(p.using, qualifier);
|
|
1786
|
+
const selfParenthesized = p.using.kind === "binop" || p.using.kind === "and" || p.using.kind === "or" || p.using.kind === "not" || p.using.kind === "isNull" || p.using.kind === "exists";
|
|
1787
|
+
return selfParenthesized ? rendered : `(${rendered})`;
|
|
1788
|
+
} catch (err) {
|
|
1789
|
+
if (err instanceof CanRenderError) return null;
|
|
1790
|
+
throw err;
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
__name(renderCanUsing, "renderCanUsing");
|
|
1794
|
+
var CAN_BINOPS = {
|
|
1795
|
+
eq: "=",
|
|
1796
|
+
neq: "<>",
|
|
1797
|
+
gt: ">",
|
|
1798
|
+
gte: ">=",
|
|
1799
|
+
lt: "<",
|
|
1800
|
+
lte: "<="
|
|
1801
|
+
};
|
|
1802
|
+
function canLiteral(v) {
|
|
1803
|
+
if (v === null) return "NULL";
|
|
1804
|
+
if (typeof v === "boolean") return v ? "TRUE" : "FALSE";
|
|
1805
|
+
if (typeof v === "number") {
|
|
1806
|
+
if (!Number.isFinite(v)) throw new CanRenderError("can: non-finite literal");
|
|
1807
|
+
return String(v);
|
|
1808
|
+
}
|
|
1809
|
+
return `'${v.replace(/'/g, "''")}'`;
|
|
1810
|
+
}
|
|
1811
|
+
__name(canLiteral, "canLiteral");
|
|
1812
|
+
function canExprSql(e, qualifier) {
|
|
1813
|
+
switch (e.kind) {
|
|
1814
|
+
case "col":
|
|
1815
|
+
return e.alias !== void 0 && e.alias !== "" ? `${quoteIdent(e.alias)}.${quoteIdent(e.name)}` : `${qualifier}.${quoteIdent(e.name)}`;
|
|
1816
|
+
case "authUid":
|
|
1817
|
+
return "(SELECT auth.uid())";
|
|
1818
|
+
case "lit":
|
|
1819
|
+
return canLiteral(e.value);
|
|
1820
|
+
case "binop": {
|
|
1821
|
+
const op = CAN_BINOPS[e.op];
|
|
1822
|
+
if (op === void 0) throw new CanRenderError(`can: unknown operator ${String(e.op)}`);
|
|
1823
|
+
return `(${canExprSql(e.left, qualifier)} ${op} ${canExprSql(e.right, qualifier)})`;
|
|
1824
|
+
}
|
|
1825
|
+
case "and":
|
|
1826
|
+
return e.parts.length === 0 ? "(true)" : `(${e.parts.map((p) => canExprSql(p, qualifier)).join(" AND ")})`;
|
|
1827
|
+
case "or":
|
|
1828
|
+
return e.parts.length === 0 ? "(false)" : `(${e.parts.map((p) => canExprSql(p, qualifier)).join(" OR ")})`;
|
|
1829
|
+
case "not":
|
|
1830
|
+
return `(NOT ${canExprSql(e.expr, qualifier)})`;
|
|
1831
|
+
case "isNull":
|
|
1832
|
+
return `(${canExprSql(e.expr, qualifier)} IS ${e.negated ? "NOT " : ""}NULL)`;
|
|
1833
|
+
case "exists":
|
|
1834
|
+
if (e.alias === "" || ENGINE_ALIASES.test(e.alias)) {
|
|
1835
|
+
throw new CanRenderError(`can: exists alias "${e.alias}" collides with the engine's own aliases \u2014 the correlation would silently collapse`);
|
|
1836
|
+
}
|
|
1837
|
+
return `EXISTS (SELECT 1 FROM ${quoteTable(e.table)} ${quoteIdent(e.alias)} WHERE ${canExprSql(e.predicate, qualifier)})`;
|
|
1838
|
+
default:
|
|
1839
|
+
throw new CanRenderError(`can: unknown policy expression kind ${String(e.kind)}`);
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
__name(canExprSql, "canExprSql");
|
|
1843
|
+
function canEntriesOf(table, alias) {
|
|
1844
|
+
const byAlias = memoTable(canMemo, currentSchema, table, () => /* @__PURE__ */ new Map());
|
|
1845
|
+
const hit = byAlias.get(alias);
|
|
1846
|
+
if (hit !== void 0) return hit;
|
|
1847
|
+
const def = tableDefOf(table);
|
|
1848
|
+
const warnOnce = /* @__PURE__ */ __name((msg) => {
|
|
1849
|
+
if (byAlias.has("__warned")) return;
|
|
1850
|
+
byAlias.set("__warned", []);
|
|
1851
|
+
console.warn(msg);
|
|
1852
|
+
}, "warnOnce");
|
|
1853
|
+
if (def === null || def.policies === void 0) {
|
|
1854
|
+
byAlias.set(alias, []);
|
|
1855
|
+
return [];
|
|
1856
|
+
}
|
|
1857
|
+
if (hasCanColumn(table)) {
|
|
1858
|
+
warnOnce(`can(${table}): the table declares a column or relation named "can", so the policy echo is disabled for it \u2014 rename it to get can`);
|
|
1859
|
+
byAlias.set(alias, []);
|
|
1860
|
+
return [];
|
|
1861
|
+
}
|
|
1862
|
+
const { entries, dropped } = canEntries(def, alias === "" ? quoteTable(table) : alias);
|
|
1863
|
+
if (dropped.length > 0) {
|
|
1864
|
+
warnOnce(dropped.map((d) => `can(${table}): policy "${d.name}" is not exposed in can \u2014 ${d.reason}; the database still enforces it`).join("\n"));
|
|
1865
|
+
}
|
|
1866
|
+
byAlias.set(alias, entries);
|
|
1867
|
+
return entries;
|
|
1868
|
+
}
|
|
1869
|
+
__name(canEntriesOf, "canEntriesOf");
|
|
1870
|
+
function canOuter(table) {
|
|
1871
|
+
const cols = canProjection(table, "q");
|
|
1872
|
+
return cols.length === 0 ? "" : `, ${cols.join(", ")}`;
|
|
1873
|
+
}
|
|
1874
|
+
__name(canOuter, "canOuter");
|
|
1875
|
+
function canProjection(table, alias) {
|
|
1876
|
+
return canEntriesOf(table, alias).map((e) => `${e.using} AS ${quoteIdent(CAN_PREFIX + e.name)}`);
|
|
1877
|
+
}
|
|
1878
|
+
__name(canProjection, "canProjection");
|
|
1879
|
+
function canJsonPairs(table, alias) {
|
|
1880
|
+
return canEntriesOf(table, alias).map((e) => `${quoteLiteral(CAN_PREFIX + e.name)}, ${e.using}`);
|
|
1881
|
+
}
|
|
1882
|
+
__name(canJsonPairs, "canJsonPairs");
|
|
1883
|
+
function foldCan(table, row, partial) {
|
|
1884
|
+
let can = null;
|
|
1885
|
+
for (const key2 of Object.keys(row)) {
|
|
1886
|
+
if (!key2.startsWith(CAN_PREFIX)) continue;
|
|
1887
|
+
can ??= [];
|
|
1888
|
+
if (row[key2] === true) can.push(key2.slice(CAN_PREFIX.length));
|
|
1889
|
+
delete row[key2];
|
|
1890
|
+
}
|
|
1891
|
+
if (hasCanColumn(table)) return row;
|
|
1892
|
+
if (can !== null) row.can = can;
|
|
1893
|
+
else if (!partial && tableDefOf(table) !== null) {
|
|
1894
|
+
if (canEntriesOf(table, "").length > 0) {
|
|
1895
|
+
const flags = memoTable(canMemo, currentSchema, table, () => /* @__PURE__ */ new Map());
|
|
1896
|
+
if (!flags.has("__missing_warned")) {
|
|
1897
|
+
flags.set("__missing_warned", []);
|
|
1898
|
+
console.warn(`can(${table}): a full-row read returned no "can:*" columns although the table declares update policies \u2014 this statement shape does not carry can; the row gets can: []`);
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
row.can = [];
|
|
1902
|
+
}
|
|
1903
|
+
return row;
|
|
1904
|
+
}
|
|
1905
|
+
__name(foldCan, "foldCan");
|
|
1906
|
+
function hasCanColumn(table) {
|
|
1907
|
+
const cols = tableDefOf(table)?.columns;
|
|
1908
|
+
if (cols !== void 0 && Object.prototype.hasOwnProperty.call(cols, "can")) return true;
|
|
1909
|
+
return (currentRelations.get(table) ?? []).some((rel) => rel.name === "can");
|
|
1910
|
+
}
|
|
1911
|
+
__name(hasCanColumn, "hasCanColumn");
|
|
1690
1912
|
function rowProjection(table, alias, wildcard = alias ? `${alias}.*` : "*") {
|
|
1691
1913
|
const cols = schemaColumns(table);
|
|
1692
|
-
|
|
1914
|
+
let base = wildcard;
|
|
1915
|
+
if (cols && memoTable(timestampMemo, currentSchema, table, () => [
|
|
1693
1916
|
...cols
|
|
1694
|
-
].some((c) => columnTypeOf(table, c) === "timestamp")))
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1917
|
+
].some((c) => columnTypeOf(table, c) === "timestamp"))) {
|
|
1918
|
+
const projections = memoTable(projectionMemo, currentSchema, table, () => /* @__PURE__ */ new Map());
|
|
1919
|
+
const key2 = alias ?? "";
|
|
1920
|
+
let sql = projections.get(key2);
|
|
1921
|
+
if (sql === void 0) {
|
|
1922
|
+
sql = [
|
|
1923
|
+
...cols
|
|
1924
|
+
].map((c) => projectedColumn(table, c, alias)).join(", ");
|
|
1925
|
+
projections.set(key2, sql);
|
|
1926
|
+
}
|
|
1927
|
+
base = sql;
|
|
1928
|
+
}
|
|
1929
|
+
const can = canProjection(table, alias ?? "");
|
|
1930
|
+
if (can.length === 0) return base;
|
|
1931
|
+
const rows = base === "*" && alias !== void 0 ? `${alias}.*` : base;
|
|
1932
|
+
return `${rows}, ${can.join(", ")}`;
|
|
1705
1933
|
}
|
|
1706
1934
|
__name(rowProjection, "rowProjection");
|
|
1707
1935
|
function colRefSql(table, known, ref, caller, alias = "t") {
|
|
@@ -2122,7 +2350,10 @@ function withClauses(table, spec, add, caller, parentAlias = "t", depth = 0, cou
|
|
|
2122
2350
|
jsonPairs: []
|
|
2123
2351
|
} : withClauses(rel.to, nestedSpec, add, caller, childAlias, depth + 1, counter);
|
|
2124
2352
|
const nestedJoins = nested.joins.length ? ` ${nested.joins.join(" ")}` : "";
|
|
2125
|
-
const obj = jsonObjectFor(rel.to, childCols, childAlias,
|
|
2353
|
+
const obj = jsonObjectFor(rel.to, childCols, childAlias, [
|
|
2354
|
+
...nested.jsonPairs,
|
|
2355
|
+
...canJsonPairs(rel.to, childAlias)
|
|
2356
|
+
]);
|
|
2126
2357
|
const innerWhere = o["where"] !== void 0 && o["where"] !== null ? compileWhereBare(rel.to, childKnown, o["where"], add, caller, childAlias, i) : "";
|
|
2127
2358
|
const link = `${childAlias}.${quoteIdent(theirCol)} = ${parentAlias}.${quoteIdent(ourCol)}`;
|
|
2128
2359
|
const cond = innerWhere === "" ? link : `${link} AND ${innerWhere}`;
|
|
@@ -2515,8 +2746,8 @@ function createOps(tx, context = {}) {
|
|
|
2515
2746
|
const wantRows = opts?.returning !== false;
|
|
2516
2747
|
const tail = wantRows ? `RETURNING ${rowProjection(table, "t", "*")}` : "RETURNING 1";
|
|
2517
2748
|
const simple = isSinglePkEquality(where, pk) || pk === null;
|
|
2518
|
-
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
|
|
2519
|
-
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) `;
|
|
2749
|
+
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."__pb_lock" ` + (wantRows ? `RETURNING ${rowProjection(table, "t")}` : "RETURNING 1");
|
|
2750
|
+
const lockedCte = simple ? "" : `WITH locked AS (SELECT t.${quoteIdent(pk)} AS "__pb_lock" FROM ${quoteTable(table)} t WHERE true${whereSql} ORDER BY t.${quoteIdent(pk)} FOR NO KEY UPDATE) `;
|
|
2520
2751
|
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`;
|
|
2521
2752
|
const out = await (await at()).unsafe(sql, params);
|
|
2522
2753
|
if (!wantRows) return Number(out[0]?.["n"] ?? 0);
|
|
@@ -2661,7 +2892,7 @@ function createOps(tx, context = {}) {
|
|
|
2661
2892
|
const compiled = ops.compileQuery(query);
|
|
2662
2893
|
return asTableRows(query.table, await (await at()).unsafe(compiled.sql, compiled.params, {
|
|
2663
2894
|
name: query.name
|
|
2664
|
-
}));
|
|
2895
|
+
}), void 0, true);
|
|
2665
2896
|
},
|
|
2666
2897
|
async explainQuery(query) {
|
|
2667
2898
|
const compiled = ops.compileQuery(query);
|
|
@@ -2671,7 +2902,7 @@ function createOps(tx, context = {}) {
|
|
|
2671
2902
|
},
|
|
2672
2903
|
async findMany(table, query = {}, opts = {}) {
|
|
2673
2904
|
const compiled = compileFindMany(table, query, opts);
|
|
2674
|
-
return asTableRows(table, await (await at()).unsafe(compiled.sql, compiled.params), opts.with);
|
|
2905
|
+
return asTableRows(table, await (await at()).unsafe(compiled.sql, compiled.params), opts.with, opts.select !== void 0);
|
|
2675
2906
|
},
|
|
2676
2907
|
async page(table, query = {}, opts = {}) {
|
|
2677
2908
|
const secret = cursorKey();
|
|
@@ -2739,7 +2970,7 @@ function createOps(tx, context = {}) {
|
|
|
2739
2970
|
other
|
|
2740
2971
|
]) delete row[name];
|
|
2741
2972
|
return {
|
|
2742
|
-
rows: asTableRows(table, rows, opts.with),
|
|
2973
|
+
rows: asTableRows(table, rows, opts.with, opts.select !== void 0),
|
|
2743
2974
|
pageInfo
|
|
2744
2975
|
};
|
|
2745
2976
|
},
|
|
@@ -2846,7 +3077,9 @@ function createOps(tx, context = {}) {
|
|
|
2846
3077
|
kwC = `SELECT c.${pidQ} AS pid, c.chunk_seq, ROW_NUMBER() OVER (ORDER BY ts_rank_cd(c.palbase_fts, websearch_to_tsquery('simple', ${qpC})) DESC) AS r FROM ${quoteTable(ck.table)} c${parentJoinC}c.palbase_fts @@ websearch_to_tsquery('simple', ${qpC}) ORDER BY r LIMIT ${cpool}`;
|
|
2847
3078
|
}
|
|
2848
3079
|
const msC = params.minScore === void 0 ? "" : addC(params.minScore);
|
|
2849
|
-
const colListC =
|
|
3080
|
+
const colListC = [
|
|
3081
|
+
...cfg.cols.map((c) => columnTypeOf(table, c) === "timestamp" ? projectedColumn(table, c, "t") : `t.${quoteIdent(c)}`)
|
|
3082
|
+
].join(", ");
|
|
2850
3083
|
const scoreC = scoreMul === "" ? "g._score" : `(g._score)${scoreMul}`;
|
|
2851
3084
|
const assembleC = /* @__PURE__ */ __name((kwIn, withKwn) => {
|
|
2852
3085
|
const kwnCol = withKwn ? `, (SELECT count(*) FROM kw)::int AS _kwn` : "";
|
|
@@ -2859,7 +3092,7 @@ function createOps(tx, context = {}) {
|
|
|
2859
3092
|
fusedSrc = `WITH kw AS (${kwIn}), fused AS (SELECT kw.pid, kw.chunk_seq, (1.0/(${K2} + kw.r))::float8 AS cscore FROM kw)`;
|
|
2860
3093
|
}
|
|
2861
3094
|
const inner = `${fusedSrc}, ranked AS (SELECT f.pid, f.chunk_seq, f.cscore, ROW_NUMBER() OVER (PARTITION BY f.pid ORDER BY f.cscore DESC, f.chunk_seq) AS rn FROM fused f), grouped AS (SELECT r.pid, MAX(r.cscore) AS _score, json_agg(json_build_object('content', c.content, 'score', r.cscore, 'chunkSeq', r.chunk_seq, 'charStart', c.char_start) ORDER BY r.cscore DESC, r.chunk_seq) FILTER (WHERE r.rn <= ${bpr}) AS blocks FROM ranked r JOIN ${quoteTable(ck.table)} c ON c.${pidQ} = r.pid AND c.chunk_seq = r.chunk_seq GROUP BY r.pid) SELECT ${colListC}, ${scoreC}::float8 AS _score, g.blocks AS blocks${kwnCol} FROM grouped g JOIN ${quoteTable(table)} t ON t.${quoteIdent(cfg.pk)} = g.pid`;
|
|
2862
|
-
return msC === "" ? `SELECT
|
|
3095
|
+
return msC === "" ? `SELECT q.*${canOuter(table)} FROM (${inner}) q ORDER BY q._score DESC, q.${quoteIdent(cfg.pk)} LIMIT ${limit}` : `SELECT q.*${canOuter(table)} FROM (${inner}) q WHERE q._score >= ${msC} ORDER BY q._score DESC, q.${quoteIdent(cfg.pk)} LIMIT ${limit}`;
|
|
2863
3096
|
}, "assembleC");
|
|
2864
3097
|
let rowsC = await liveC.unsafe(assembleC(kwC, kwC !== ""), bindC);
|
|
2865
3098
|
if (wantTextC && kwC !== "" && (rowsC.length === 0 || rowsC[0]._kwn === 0)) {
|
|
@@ -2928,26 +3161,26 @@ function createOps(tx, context = {}) {
|
|
|
2928
3161
|
qpRef = qp;
|
|
2929
3162
|
kwSql = `SELECT t.${quoteIdent(cfg.pk)} AS id, ROW_NUMBER() OVER (ORDER BY ts_rank_cd(t.palbase_fts, websearch_to_tsquery('simple', ${qp})) DESC) AS r FROM ${quoteTable(table)} t WHERE t.palbase_fts @@ websearch_to_tsquery('simple', ${qp})${whereSql} ORDER BY r LIMIT ${pool}`;
|
|
2930
3163
|
}
|
|
2931
|
-
const colList =
|
|
3164
|
+
const colList = [
|
|
3165
|
+
...cfg.cols.map((c) => columnTypeOf(table, c) === "timestamp" ? projectedColumn(table, c, "t") : `t.${quoteIdent(c)}`)
|
|
3166
|
+
].join(", ");
|
|
2932
3167
|
const msP = params.minScore === void 0 ? "" : add(params.minScore);
|
|
2933
3168
|
const hlCol = params.highlight === true && wantText ? `, ts_headline('simple', ${ftsExprOf(cfg.ftsCols)}, websearch_to_tsquery('simple', ${qpRef})) AS _highlight` : "";
|
|
2934
3169
|
const assemble = /* @__PURE__ */ __name((kwIn, withKwn) => {
|
|
2935
3170
|
const kwnCol = withKwn ? `, (SELECT count(*) FROM kw)::int AS _kwn` : "";
|
|
2936
3171
|
let inner;
|
|
2937
|
-
let orderScore;
|
|
2938
3172
|
if (semSql !== "" && kwIn !== "") {
|
|
2939
3173
|
const score = scoreMul === "" ? "fused._score" : `(fused._score)${scoreMul}`;
|
|
2940
3174
|
inner = `WITH sem AS (${semSql}), kw AS (${kwIn}), fused AS (SELECT COALESCE(sem.id, kw.id) AS id, (COALESCE(1.0/(${K} + sem.r), 0) + COALESCE(1.0/(${K} + kw.r), 0))::float8 AS _score FROM sem FULL OUTER JOIN kw ON sem.id = kw.id) SELECT ${colList}, ${score} AS _score${hlCol}${kwnCol} FROM fused JOIN ${quoteTable(table)} t ON t.${quoteIdent(cfg.pk)} = fused.id`;
|
|
2941
|
-
orderScore = scoreMul === "" ? "fused._score" : "_score";
|
|
2942
3175
|
} else {
|
|
2943
3176
|
const single = semSql !== "" ? `sem AS (${semSql})` : `kw AS (${kwIn})`;
|
|
2944
3177
|
const alias = semSql !== "" ? "sem" : "kw";
|
|
2945
3178
|
const base = `(1.0/(${K} + ${alias}.r))::float8`;
|
|
2946
3179
|
const score = scoreMul === "" ? base : `(${base})${scoreMul}`;
|
|
2947
3180
|
inner = `WITH ${single} SELECT ${colList}, ${score} AS _score${hlCol}${semSql !== "" ? "" : kwnCol} FROM ${alias} JOIN ${quoteTable(table)} t ON t.${quoteIdent(cfg.pk)} = ${alias}.id`;
|
|
2948
|
-
orderScore = "_score";
|
|
2949
3181
|
}
|
|
2950
|
-
|
|
3182
|
+
const ms = msP === "" ? "" : ` WHERE q._score >= ${msP}`;
|
|
3183
|
+
return `SELECT q.*${canOuter(table)} FROM (${inner}) q${ms} ORDER BY q._score DESC, q.${quoteIdent(cfg.pk)} LIMIT ${limit}`;
|
|
2951
3184
|
}, "assemble");
|
|
2952
3185
|
let rows = await live.unsafe(assemble(kwSql, kwSql !== ""), bind);
|
|
2953
3186
|
if (wantText && kwSql !== "" && (rows.length === 0 || rows[0]._kwn === 0)) {
|
|
@@ -3783,7 +4016,7 @@ function compilePlanOp(op, results) {
|
|
|
3783
4016
|
const assignments = cols.map((c) => `${quoteIdent(c)} = ${renderValue(op.set[c], c, args, results, opVectorCols, opTransforms)}`);
|
|
3784
4017
|
const planned = planWhere(op.table, op.where, args, results);
|
|
3785
4018
|
const planPk = primaryKeyOf(op.table);
|
|
3786
|
-
sql = isSinglePkEquality(planned.resolved, planPk) || planPk === null ? `UPDATE ${table} AS t SET ${assignments.join(", ")} WHERE true${planned.sql} RETURNING ${rowProjection(op.table, "t")}` : `WITH locked AS (SELECT t.${quoteIdent(planPk)} FROM ${table} t WHERE true${planned.sql} ORDER BY t.${quoteIdent(planPk)} FOR NO KEY UPDATE) UPDATE ${table} AS t SET ${assignments.join(", ")} FROM locked WHERE t.${quoteIdent(planPk)} = locked
|
|
4019
|
+
sql = isSinglePkEquality(planned.resolved, planPk) || planPk === null ? `UPDATE ${table} AS t SET ${assignments.join(", ")} WHERE true${planned.sql} RETURNING ${rowProjection(op.table, "t")}` : `WITH locked AS (SELECT t.${quoteIdent(planPk)} AS "__pb_lock" FROM ${table} t WHERE true${planned.sql} ORDER BY t.${quoteIdent(planPk)} FOR NO KEY UPDATE) UPDATE ${table} AS t SET ${assignments.join(", ")} FROM locked WHERE t.${quoteIdent(planPk)} = locked."__pb_lock" RETURNING ${rowProjection(op.table, "t")}`;
|
|
3787
4020
|
break;
|
|
3788
4021
|
}
|
|
3789
4022
|
case "delete": {
|
|
@@ -4519,13 +4752,25 @@ async function refuseUnlessAuthorized(client, spec, requestId, rolesOfCaller) {
|
|
|
4519
4752
|
spec.permission
|
|
4520
4753
|
]);
|
|
4521
4754
|
if (row?.ok !== true) {
|
|
4522
|
-
return envelope(
|
|
4755
|
+
return envelope(
|
|
4756
|
+
"forbidden",
|
|
4757
|
+
`This endpoint requires the "${spec.permission}" permission`,
|
|
4758
|
+
403,
|
|
4759
|
+
requestId,
|
|
4760
|
+
// FR-008 / D-7: reddin ÇARESİ gövdede — istemci `required`'ı görünce ilgili
|
|
4761
|
+
// nesneyi yeniden çeker (Entra CAE deseni); cümle değişmez.
|
|
4762
|
+
{
|
|
4763
|
+
required: spec.permission
|
|
4764
|
+
}
|
|
4765
|
+
);
|
|
4523
4766
|
}
|
|
4524
4767
|
}
|
|
4525
4768
|
if (spec.role !== void 0) {
|
|
4526
4769
|
const held = await rolesOfCaller();
|
|
4527
4770
|
if (!held.includes(spec.role)) {
|
|
4528
|
-
return envelope("forbidden", `This endpoint requires the "${spec.role}" role`, 403, requestId
|
|
4771
|
+
return envelope("forbidden", `This endpoint requires the "${spec.role}" role`, 403, requestId, {
|
|
4772
|
+
required_role: spec.role
|
|
4773
|
+
});
|
|
4529
4774
|
}
|
|
4530
4775
|
}
|
|
4531
4776
|
return null;
|
|
@@ -5144,4 +5389,4 @@ export {
|
|
|
5144
5389
|
installEgressFence,
|
|
5145
5390
|
createApp
|
|
5146
5391
|
};
|
|
5147
|
-
//# sourceMappingURL=chunk-
|
|
5392
|
+
//# sourceMappingURL=chunk-XZPDPYVY.js.map
|