@sequenceholdings/orm 2.2.0 → 2.4.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/cli.cjs +132 -61
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +132 -61
- package/dist/cli.js.map +1 -1
- package/dist/compiler.cjs +139 -33
- package/dist/compiler.cjs.map +1 -1
- package/dist/compiler.d.cts +6 -4
- package/dist/compiler.d.ts +6 -4
- package/dist/compiler.js +139 -33
- package/dist/compiler.js.map +1 -1
- package/dist/index.cjs +9 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/ir.cjs +9 -1
- package/dist/ir.cjs.map +1 -1
- package/dist/ir.d.cts +89 -3
- package/dist/ir.d.ts +89 -3
- package/dist/ir.js +8 -2
- package/dist/ir.js.map +1 -1
- package/dist/predicate-v2.cjs +127 -14
- package/dist/predicate-v2.cjs.map +1 -1
- package/dist/predicate-v2.d.cts +49 -4
- package/dist/predicate-v2.d.ts +49 -4
- package/dist/predicate-v2.js +127 -14
- package/dist/predicate-v2.js.map +1 -1
- package/dist/sdl.cjs +2 -1
- package/dist/sdl.cjs.map +1 -1
- package/dist/sdl.js +2 -1
- package/dist/sdl.js.map +1 -1
- package/package.json +1 -1
package/dist/predicate-v2.cjs
CHANGED
|
@@ -1058,14 +1058,20 @@ zod.z.object({ irVersion: irVersionSchema });
|
|
|
1058
1058
|
var CLAIMS_CONTRACT = {
|
|
1059
1059
|
sub: "string",
|
|
1060
1060
|
org: "string?",
|
|
1061
|
+
ext_subject: "string?",
|
|
1062
|
+
ext_email: "string?",
|
|
1061
1063
|
roles: "string[]",
|
|
1062
1064
|
capabilities: "string[]"
|
|
1063
1065
|
};
|
|
1066
|
+
var EXTERNAL_POLICY_CLAIM_KEYS = ["ext_subject", "ext_email"];
|
|
1067
|
+
function isExternalPolicyClaimKey(key) {
|
|
1068
|
+
return EXTERNAL_POLICY_CLAIM_KEYS.includes(key);
|
|
1069
|
+
}
|
|
1064
1070
|
var scalarLiteral = zod.z.union([zod.z.string(), zod.z.number(), zod.z.boolean(), zod.z.null()]);
|
|
1065
1071
|
var valueIrSchema = zod.z.discriminatedUnion("kind", [
|
|
1066
1072
|
zod.z.object({ kind: zod.z.literal("literal"), value: zod.z.union([scalarLiteral, zod.z.array(scalarLiteral)]) }),
|
|
1067
1073
|
zod.z.object({ kind: zod.z.literal("variable"), name: zod.z.string() }),
|
|
1068
|
-
zod.z.object({ kind: zod.z.literal("claim"), key: zod.z.enum(["sub", "org"]) })
|
|
1074
|
+
zod.z.object({ kind: zod.z.literal("claim"), key: zod.z.enum(["sub", "org", ...EXTERNAL_POLICY_CLAIM_KEYS]) })
|
|
1069
1075
|
]);
|
|
1070
1076
|
var CMP_OPS = [
|
|
1071
1077
|
"eq",
|
|
@@ -1636,6 +1642,7 @@ var Parser2 = class {
|
|
|
1636
1642
|
return node;
|
|
1637
1643
|
}
|
|
1638
1644
|
if (this.peekKeyword("exists")) return this.parseExists();
|
|
1645
|
+
if (this.peekKeyword("visible") && this.tokens[this.index + 1]?.kind === "lparen") return this.parseVisible();
|
|
1639
1646
|
return this.parseComparison();
|
|
1640
1647
|
}
|
|
1641
1648
|
parseBooleanCall(fn) {
|
|
@@ -1655,6 +1662,13 @@ var Parser2 = class {
|
|
|
1655
1662
|
this.expect("rparen", "')' closing exists(...)");
|
|
1656
1663
|
return { kind: "exists", path, where };
|
|
1657
1664
|
}
|
|
1665
|
+
parseVisible() {
|
|
1666
|
+
this.advance();
|
|
1667
|
+
this.expect("lparen", "'(' after visible");
|
|
1668
|
+
const path = this.parsePath("a link path in visible(...)");
|
|
1669
|
+
this.expect("rparen", "')' closing visible(...)");
|
|
1670
|
+
return { kind: "visible", path };
|
|
1671
|
+
}
|
|
1658
1672
|
parseComparison() {
|
|
1659
1673
|
const left = this.parseOperand();
|
|
1660
1674
|
const operator = this.expect("operator", "a comparison operator");
|
|
@@ -1770,6 +1784,24 @@ function parsePolicy(source) {
|
|
|
1770
1784
|
|
|
1771
1785
|
// src/predicate-v2/compile.ts
|
|
1772
1786
|
var POLICY_MAX_LINK_DEPTH = 3;
|
|
1787
|
+
function scopeToNamespace(pred, namespace) {
|
|
1788
|
+
switch (pred.kind) {
|
|
1789
|
+
case "capability":
|
|
1790
|
+
case "role":
|
|
1791
|
+
return pred.namespace ? pred : { ...pred, namespace };
|
|
1792
|
+
case "and":
|
|
1793
|
+
case "or":
|
|
1794
|
+
return { ...pred, operands: pred.operands.map((operand) => scopeToNamespace(operand, namespace)) };
|
|
1795
|
+
case "not":
|
|
1796
|
+
return { ...pred, operand: scopeToNamespace(pred.operand, namespace) };
|
|
1797
|
+
case "exists":
|
|
1798
|
+
return { ...pred, where: scopeToNamespace(pred.where, namespace) };
|
|
1799
|
+
case "aggPredicate":
|
|
1800
|
+
return pred.where ? { ...pred, where: scopeToNamespace(pred.where, namespace) } : pred;
|
|
1801
|
+
default:
|
|
1802
|
+
return pred;
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1773
1805
|
var CMP_BY_OP = {
|
|
1774
1806
|
"=": "eq",
|
|
1775
1807
|
"!=": "neq",
|
|
@@ -1803,7 +1835,7 @@ function resolveLink(info, ref) {
|
|
|
1803
1835
|
function columnCandidates(info) {
|
|
1804
1836
|
return [...info.columns.map((c) => c.name), ...info.links.map((l) => l.name)];
|
|
1805
1837
|
}
|
|
1806
|
-
var PolicyCompiler = class {
|
|
1838
|
+
var PolicyCompiler = class _PolicyCompiler {
|
|
1807
1839
|
constructor(ctx) {
|
|
1808
1840
|
this.ctx = ctx;
|
|
1809
1841
|
}
|
|
@@ -1815,13 +1847,16 @@ var PolicyCompiler = class {
|
|
|
1815
1847
|
case "or":
|
|
1816
1848
|
return { kind: node.kind, operands: node.operands.map((operand) => this.compile(operand, table, depth)) };
|
|
1817
1849
|
case "not":
|
|
1818
|
-
|
|
1850
|
+
assertNoNegatedExternalClaim(node.operand);
|
|
1851
|
+
return { kind: "not", operand: new _PolicyCompiler({ ...this.ctx, negated: true }).compile(node.operand, table, depth) };
|
|
1819
1852
|
case "capability":
|
|
1820
1853
|
return { kind: "capability", key: node.key };
|
|
1821
1854
|
case "role":
|
|
1822
1855
|
return { kind: "role", key: node.key };
|
|
1823
1856
|
case "exists":
|
|
1824
1857
|
return this.compileExists(node.path, node.where, table, depth);
|
|
1858
|
+
case "visible":
|
|
1859
|
+
return this.compileVisible(node.path, table, depth);
|
|
1825
1860
|
case "compare":
|
|
1826
1861
|
return this.compileCompare(node, table, depth);
|
|
1827
1862
|
}
|
|
@@ -1835,6 +1870,36 @@ var PolicyCompiler = class {
|
|
|
1835
1870
|
}
|
|
1836
1871
|
return pred;
|
|
1837
1872
|
}
|
|
1873
|
+
/** `visible(path)` → `exists(path, <final target's read policy>)`. */
|
|
1874
|
+
compileVisible(path, table, depth) {
|
|
1875
|
+
const { links, finalTable } = this.resolveLinkPath(path, table, depth, { toOneOnly: false });
|
|
1876
|
+
const readPolicy = this.ctx.resolver.readPolicy;
|
|
1877
|
+
if (!readPolicy) {
|
|
1878
|
+
throw new PolicyCompileError(
|
|
1879
|
+
`visible(${path.join(".")}) needs the target's read policy, which this compile context does not provide`,
|
|
1880
|
+
"visible-unresolvable"
|
|
1881
|
+
);
|
|
1882
|
+
}
|
|
1883
|
+
if (this.ctx.visiting.includes(finalTable)) {
|
|
1884
|
+
throw new PolicyCompileError(
|
|
1885
|
+
`visible(${path.join(".")}) on '${table}' reaches '${finalTable}' whose read policy is already being expanded (${[...this.ctx.visiting, finalTable].join(" \u2192 ")})`,
|
|
1886
|
+
"visible-cycle"
|
|
1887
|
+
);
|
|
1888
|
+
}
|
|
1889
|
+
const expression = readPolicy(finalTable);
|
|
1890
|
+
let pred = expression === void 0 ? { kind: "const", value: true } : compilePolicyAt({
|
|
1891
|
+
expression,
|
|
1892
|
+
table: finalTable,
|
|
1893
|
+
ctx: { ...this.ctx, visiting: [...this.ctx.visiting, finalTable] },
|
|
1894
|
+
depth: depth + links.length
|
|
1895
|
+
});
|
|
1896
|
+
const owner = this.ctx.resolver.policyNamespace?.(finalTable);
|
|
1897
|
+
if (owner) pred = scopeToNamespace(pred, owner);
|
|
1898
|
+
for (let i = links.length - 1; i >= 0; i -= 1) {
|
|
1899
|
+
pred = { kind: "exists", link: links[i].name, where: pred };
|
|
1900
|
+
}
|
|
1901
|
+
return pred;
|
|
1902
|
+
}
|
|
1838
1903
|
resolveLinkPath(path, table, depth, { toOneOnly }) {
|
|
1839
1904
|
const links = [];
|
|
1840
1905
|
let current = table;
|
|
@@ -1894,6 +1959,10 @@ var PolicyCompiler = class {
|
|
|
1894
1959
|
columnCandidates(info)
|
|
1895
1960
|
);
|
|
1896
1961
|
}
|
|
1962
|
+
if (valueSide.kind === "claim" && isExternalPolicyClaimKey(valueSide.key)) {
|
|
1963
|
+
if (this.ctx.negated) throw negatedExternalClaim(valueSide.key);
|
|
1964
|
+
assertExternalClaimComparison({ op, key: valueSide.key, column: info.columns.find((c) => c.name === column) });
|
|
1965
|
+
}
|
|
1897
1966
|
let cmp = this.buildCmp(op, column, valueSide);
|
|
1898
1967
|
for (let i = links.length - 1; i >= 0; i -= 1) {
|
|
1899
1968
|
cmp = { kind: "exists", link: links[i].name, where: cmp };
|
|
@@ -1914,10 +1983,45 @@ var PolicyCompiler = class {
|
|
|
1914
1983
|
return { kind: "cmp", op: CMP_BY_OP[op], column, value };
|
|
1915
1984
|
}
|
|
1916
1985
|
};
|
|
1986
|
+
function assertExternalClaimComparison({ op, key, column }) {
|
|
1987
|
+
if (op !== "=") {
|
|
1988
|
+
throw new PolicyCompileError(
|
|
1989
|
+
`claim('${key}') supports = comparisons only, got '${op}'`,
|
|
1990
|
+
"unsupported-comparison"
|
|
1991
|
+
);
|
|
1992
|
+
}
|
|
1993
|
+
if (column?.type !== "text") {
|
|
1994
|
+
const actual = column?.type === void 0 ? "of unknown type" : typeof column.type === "string" ? column.type : "an enum";
|
|
1995
|
+
throw new PolicyCompileError(
|
|
1996
|
+
`claim('${key}') is text and compares to a text column only \u2014 '${column?.name ?? "?"}' is ${actual}`,
|
|
1997
|
+
"claim-type-mismatch"
|
|
1998
|
+
);
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
function negatedExternalClaim(key) {
|
|
2002
|
+
return new PolicyCompileError(
|
|
2003
|
+
`claim('${key}') cannot appear under not(): a negated borrower comparison admits other borrowers' rows`,
|
|
2004
|
+
"unsupported-comparison"
|
|
2005
|
+
);
|
|
2006
|
+
}
|
|
2007
|
+
function assertNoNegatedExternalClaim(operand) {
|
|
2008
|
+
walkPolicy(operand, (node) => {
|
|
2009
|
+
if (node.kind !== "compare") return;
|
|
2010
|
+
const claim = [node.left, node.right].find((side) => side.kind === "claim" && isExternalPolicyClaimKey(side.key));
|
|
2011
|
+
if (claim?.kind === "claim") throw negatedExternalClaim(claim.key);
|
|
2012
|
+
});
|
|
2013
|
+
}
|
|
1917
2014
|
function compilePredicateV2({ expression, table, resolver, maxLinkDepth }) {
|
|
2015
|
+
return compilePredicateAt({
|
|
2016
|
+
expression,
|
|
2017
|
+
table,
|
|
2018
|
+
depth: 0,
|
|
2019
|
+
ctx: { resolver, maxLinkDepth: maxLinkDepth ?? POLICY_MAX_LINK_DEPTH, visiting: [table] }
|
|
2020
|
+
});
|
|
2021
|
+
}
|
|
2022
|
+
function compilePredicateAt({ expression, table, ctx, depth }) {
|
|
1918
2023
|
const parsed = typeof expression === "string" ? parsePolicy(expression) : expression;
|
|
1919
|
-
|
|
1920
|
-
return compiler.compile(parsed.ast, table, 0);
|
|
2024
|
+
return new PolicyCompiler(ctx).compile(parsed.ast, table, depth);
|
|
1921
2025
|
}
|
|
1922
2026
|
function assertClaimsOnlyWhen(parsed) {
|
|
1923
2027
|
walkPolicy(parsed.ast, (node) => {
|
|
@@ -1934,25 +2038,34 @@ function assertClaimsOnlyWhen(parsed) {
|
|
|
1934
2038
|
"when-not-claims-only"
|
|
1935
2039
|
);
|
|
1936
2040
|
}
|
|
1937
|
-
if (node.kind === "exists") {
|
|
2041
|
+
if (node.kind === "exists" || node.kind === "visible") {
|
|
1938
2042
|
throw new PolicyCompileError(
|
|
1939
|
-
|
|
2043
|
+
`variant 'when' guards are claims-only \u2014 ${node.kind}() is not allowed`,
|
|
1940
2044
|
"when-not-claims-only"
|
|
1941
2045
|
);
|
|
1942
2046
|
}
|
|
1943
2047
|
});
|
|
1944
2048
|
}
|
|
1945
|
-
function
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
2049
|
+
function initialVisiting({ table, access }) {
|
|
2050
|
+
return (access ?? "read") === "read" ? [table] : [];
|
|
2051
|
+
}
|
|
2052
|
+
function compilePolicy({ expression, table, resolver, maxLinkDepth, access }) {
|
|
2053
|
+
return compilePolicyAt({
|
|
2054
|
+
expression,
|
|
2055
|
+
table,
|
|
2056
|
+
depth: 0,
|
|
2057
|
+
ctx: { resolver, maxLinkDepth: maxLinkDepth ?? POLICY_MAX_LINK_DEPTH, visiting: initialVisiting({ table, access }) }
|
|
2058
|
+
});
|
|
2059
|
+
}
|
|
2060
|
+
function compilePolicyAt({ expression, table, ctx, depth }) {
|
|
2061
|
+
if (typeof expression === "string") return compilePredicateAt({ expression, table, ctx, depth });
|
|
2062
|
+
const defaultPred = compilePredicateAt({ expression: expression.default, table, ctx, depth });
|
|
1950
2063
|
if (expression.variants.length === 0) return defaultPred;
|
|
1951
2064
|
const branches = expression.variants.map((variant) => {
|
|
1952
2065
|
const parsedWhen = parsePolicy(variant.when);
|
|
1953
2066
|
assertClaimsOnlyWhen(parsedWhen);
|
|
1954
|
-
const when =
|
|
1955
|
-
const grant = variant.allow === true ? { kind: "const", value: true } :
|
|
2067
|
+
const when = compilePredicateAt({ expression: parsedWhen, table, ctx, depth });
|
|
2068
|
+
const grant = variant.allow === true ? { kind: "const", value: true } : compilePredicateAt({ expression: variant.policy, table, ctx, depth });
|
|
1956
2069
|
return { kind: "and", operands: [when, grant] };
|
|
1957
2070
|
});
|
|
1958
2071
|
return { kind: "or", operands: [defaultPred, ...branches] };
|