@sequenceholdings/orm 2.3.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.
@@ -1642,6 +1642,7 @@ var Parser2 = class {
1642
1642
  return node;
1643
1643
  }
1644
1644
  if (this.peekKeyword("exists")) return this.parseExists();
1645
+ if (this.peekKeyword("visible") && this.tokens[this.index + 1]?.kind === "lparen") return this.parseVisible();
1645
1646
  return this.parseComparison();
1646
1647
  }
1647
1648
  parseBooleanCall(fn) {
@@ -1661,6 +1662,13 @@ var Parser2 = class {
1661
1662
  this.expect("rparen", "')' closing exists(...)");
1662
1663
  return { kind: "exists", path, where };
1663
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
+ }
1664
1672
  parseComparison() {
1665
1673
  const left = this.parseOperand();
1666
1674
  const operator = this.expect("operator", "a comparison operator");
@@ -1776,6 +1784,24 @@ function parsePolicy(source) {
1776
1784
 
1777
1785
  // src/predicate-v2/compile.ts
1778
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
+ }
1779
1805
  var CMP_BY_OP = {
1780
1806
  "=": "eq",
1781
1807
  "!=": "neq",
@@ -1809,7 +1835,7 @@ function resolveLink(info, ref) {
1809
1835
  function columnCandidates(info) {
1810
1836
  return [...info.columns.map((c) => c.name), ...info.links.map((l) => l.name)];
1811
1837
  }
1812
- var PolicyCompiler = class {
1838
+ var PolicyCompiler = class _PolicyCompiler {
1813
1839
  constructor(ctx) {
1814
1840
  this.ctx = ctx;
1815
1841
  }
@@ -1822,13 +1848,15 @@ var PolicyCompiler = class {
1822
1848
  return { kind: node.kind, operands: node.operands.map((operand) => this.compile(operand, table, depth)) };
1823
1849
  case "not":
1824
1850
  assertNoNegatedExternalClaim(node.operand);
1825
- return { kind: "not", operand: this.compile(node.operand, table, depth) };
1851
+ return { kind: "not", operand: new _PolicyCompiler({ ...this.ctx, negated: true }).compile(node.operand, table, depth) };
1826
1852
  case "capability":
1827
1853
  return { kind: "capability", key: node.key };
1828
1854
  case "role":
1829
1855
  return { kind: "role", key: node.key };
1830
1856
  case "exists":
1831
1857
  return this.compileExists(node.path, node.where, table, depth);
1858
+ case "visible":
1859
+ return this.compileVisible(node.path, table, depth);
1832
1860
  case "compare":
1833
1861
  return this.compileCompare(node, table, depth);
1834
1862
  }
@@ -1842,6 +1870,36 @@ var PolicyCompiler = class {
1842
1870
  }
1843
1871
  return pred;
1844
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
+ }
1845
1903
  resolveLinkPath(path, table, depth, { toOneOnly }) {
1846
1904
  const links = [];
1847
1905
  let current = table;
@@ -1902,6 +1960,7 @@ var PolicyCompiler = class {
1902
1960
  );
1903
1961
  }
1904
1962
  if (valueSide.kind === "claim" && isExternalPolicyClaimKey(valueSide.key)) {
1963
+ if (this.ctx.negated) throw negatedExternalClaim(valueSide.key);
1905
1964
  assertExternalClaimComparison({ op, key: valueSide.key, column: info.columns.find((c) => c.name === column) });
1906
1965
  }
1907
1966
  let cmp = this.buildCmp(op, column, valueSide);
@@ -1939,22 +1998,30 @@ function assertExternalClaimComparison({ op, key, column }) {
1939
1998
  );
1940
1999
  }
1941
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
+ }
1942
2007
  function assertNoNegatedExternalClaim(operand) {
1943
2008
  walkPolicy(operand, (node) => {
1944
2009
  if (node.kind !== "compare") return;
1945
2010
  const claim = [node.left, node.right].find((side) => side.kind === "claim" && isExternalPolicyClaimKey(side.key));
1946
- if (claim?.kind === "claim") {
1947
- throw new PolicyCompileError(
1948
- `claim('${claim.key}') cannot appear under not(): a negated borrower comparison admits other borrowers' rows`,
1949
- "unsupported-comparison"
1950
- );
1951
- }
2011
+ if (claim?.kind === "claim") throw negatedExternalClaim(claim.key);
1952
2012
  });
1953
2013
  }
1954
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 }) {
1955
2023
  const parsed = typeof expression === "string" ? parsePolicy(expression) : expression;
1956
- const compiler = new PolicyCompiler({ resolver, maxLinkDepth: maxLinkDepth ?? POLICY_MAX_LINK_DEPTH });
1957
- return compiler.compile(parsed.ast, table, 0);
2024
+ return new PolicyCompiler(ctx).compile(parsed.ast, table, depth);
1958
2025
  }
1959
2026
  function assertClaimsOnlyWhen(parsed) {
1960
2027
  walkPolicy(parsed.ast, (node) => {
@@ -1971,25 +2038,34 @@ function assertClaimsOnlyWhen(parsed) {
1971
2038
  "when-not-claims-only"
1972
2039
  );
1973
2040
  }
1974
- if (node.kind === "exists") {
2041
+ if (node.kind === "exists" || node.kind === "visible") {
1975
2042
  throw new PolicyCompileError(
1976
- "variant 'when' guards are claims-only \u2014 exists() is not allowed",
2043
+ `variant 'when' guards are claims-only \u2014 ${node.kind}() is not allowed`,
1977
2044
  "when-not-claims-only"
1978
2045
  );
1979
2046
  }
1980
2047
  });
1981
2048
  }
1982
- function compilePolicy({ expression, table, resolver, maxLinkDepth }) {
1983
- if (typeof expression === "string") {
1984
- return compilePredicateV2({ expression, table, resolver, maxLinkDepth });
1985
- }
1986
- const defaultPred = compilePredicateV2({ expression: expression.default, table, resolver, maxLinkDepth });
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 });
1987
2063
  if (expression.variants.length === 0) return defaultPred;
1988
2064
  const branches = expression.variants.map((variant) => {
1989
2065
  const parsedWhen = parsePolicy(variant.when);
1990
2066
  assertClaimsOnlyWhen(parsedWhen);
1991
- const when = compilePredicateV2({ expression: parsedWhen, table, resolver, maxLinkDepth });
1992
- const grant = variant.allow === true ? { kind: "const", value: true } : compilePredicateV2({ expression: variant.policy, table, resolver, maxLinkDepth });
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 });
1993
2069
  return { kind: "and", operands: [when, grant] };
1994
2070
  });
1995
2071
  return { kind: "or", operands: [defaultPred, ...branches] };