@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.
- package/dist/cli.cjs +100 -66
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +100 -66
- package/dist/cli.js.map +1 -1
- package/dist/compiler.cjs +107 -38
- 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 +107 -38
- package/dist/compiler.js.map +1 -1
- package/dist/predicate-v2.cjs +95 -19
- package/dist/predicate-v2.cjs.map +1 -1
- package/dist/predicate-v2.d.cts +40 -3
- package/dist/predicate-v2.d.ts +40 -3
- package/dist/predicate-v2.js +95 -19
- package/dist/predicate-v2.js.map +1 -1
- package/package.json +1 -1
package/dist/predicate-v2.d.cts
CHANGED
|
@@ -50,6 +50,16 @@ type PolicyNode = {
|
|
|
50
50
|
kind: 'exists';
|
|
51
51
|
path: readonly string[];
|
|
52
52
|
where: PolicyNode;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* `visible(<link-path>)` — a related row exists AND the caller may read it:
|
|
56
|
+
* the final target's own read policy is compiled in as the inner predicate.
|
|
57
|
+
* Lets a write policy say "only on a parent you can see" without restating
|
|
58
|
+
* the parent's rule (and without drifting from it).
|
|
59
|
+
*/
|
|
60
|
+
| {
|
|
61
|
+
kind: 'visible';
|
|
62
|
+
path: readonly string[];
|
|
53
63
|
};
|
|
54
64
|
interface ParsedPolicy {
|
|
55
65
|
readonly source: string;
|
|
@@ -63,7 +73,7 @@ declare class PolicyParseError extends Error {
|
|
|
63
73
|
readonly name = "PolicyParseError";
|
|
64
74
|
constructor(message: string, position: number);
|
|
65
75
|
}
|
|
66
|
-
type PolicyCompileErrorCode = 'unknown-table' | 'unknown-column' | 'unknown-link' | 'not-to-one' | 'link-depth-exceeded' | 'unsupported-comparison' | 'invalid-null-comparison' | 'when-not-claims-only' | 'claim-type-mismatch';
|
|
76
|
+
type PolicyCompileErrorCode = 'unknown-table' | 'unknown-column' | 'unknown-link' | 'not-to-one' | 'link-depth-exceeded' | 'unsupported-comparison' | 'invalid-null-comparison' | 'when-not-claims-only' | 'claim-type-mismatch' | 'visible-unresolvable' | 'visible-cycle';
|
|
67
77
|
/**
|
|
68
78
|
* Compile-time failure — the parsed policy references something the table
|
|
69
79
|
* doesn't declare, or uses a shape the v2.0 IR cannot represent. Carries
|
|
@@ -96,6 +106,11 @@ declare function parsePolicy(source: string): ParsedPolicy;
|
|
|
96
106
|
* - variant `when` guards are claims-only: capability()/role() under
|
|
97
107
|
* and/or/not. (claim() comparisons in `when` are not representable in the
|
|
98
108
|
* v2.0 IR — a targeted error says so.)
|
|
109
|
+
* - `visible(<link-path>)` lowers to `exists(<link-path>, <target read policy>)`,
|
|
110
|
+
* compiled from the target's own read policy expression at the combined
|
|
111
|
+
* depth. A read policy that reaches itself again through visible() is a
|
|
112
|
+
* cycle and refused; a target with no read policy is readable to everyone
|
|
113
|
+
* who may use the namespace, so visibility reduces to existence.
|
|
99
114
|
*/
|
|
100
115
|
|
|
101
116
|
declare const POLICY_MAX_LINK_DEPTH = 3;
|
|
@@ -120,6 +135,19 @@ interface PolicyTableInfo {
|
|
|
120
135
|
/** `{ tables }` keyed by table name — TableMetaIr's record is assignable. */
|
|
121
136
|
interface PolicyResolver {
|
|
122
137
|
readonly tables: Record<string, PolicyTableInfo>;
|
|
138
|
+
/**
|
|
139
|
+
* A table's read policy expression, for `visible()`. Return `undefined` when
|
|
140
|
+
* the table has none (readable to every namespace user). A resolver without
|
|
141
|
+
* this callback cannot compile `visible()`: it is refused, never guessed.
|
|
142
|
+
*/
|
|
143
|
+
readonly readPolicy?: (table: string) => PolicyExpression | undefined;
|
|
144
|
+
/**
|
|
145
|
+
* The namespace that owns a table's policies, in a composed namespace. A
|
|
146
|
+
* visible() expansion of an imported table's read policy evaluates its
|
|
147
|
+
* capability() and role() checks in that namespace, not the importer's.
|
|
148
|
+
* Omitted (or undefined for a table) leaves them unscoped.
|
|
149
|
+
*/
|
|
150
|
+
readonly policyNamespace?: (table: string) => string | undefined;
|
|
123
151
|
}
|
|
124
152
|
interface CompilePredicateInput {
|
|
125
153
|
/** DSL source text, or an already-parsed policy. */
|
|
@@ -131,11 +159,20 @@ interface CompilePredicateInput {
|
|
|
131
159
|
declare function compilePredicateV2({ expression, table, resolver, maxLinkDepth }: CompilePredicateInput): PredIr;
|
|
132
160
|
/** Variant `when` guards may only test claims: capability()/role() under and/or/not. */
|
|
133
161
|
declare function assertClaimsOnlyWhen(parsed: ParsedPolicy): void;
|
|
162
|
+
/** Which of a table's policies is being compiled. */
|
|
163
|
+
type PolicyAccess = 'read' | 'write' | 'insert' | 'update';
|
|
134
164
|
interface CompilePolicyInput {
|
|
135
165
|
expression: PolicyExpression;
|
|
136
166
|
table: string;
|
|
137
167
|
resolver: PolicyResolver;
|
|
138
168
|
maxLinkDepth?: number;
|
|
169
|
+
/**
|
|
170
|
+
* Default 'read'. A read policy that reaches its own table through visible()
|
|
171
|
+
* is a cycle; a write policy that follows a self-link (a subtask written
|
|
172
|
+
* under a parent the caller can read) only expands the read policy, so it
|
|
173
|
+
* does not start the cycle check at its own table.
|
|
174
|
+
*/
|
|
175
|
+
access?: PolicyAccess;
|
|
139
176
|
}
|
|
140
177
|
/**
|
|
141
178
|
* Policy expression (string or `{default, variants}`) → one PredIr under §4c
|
|
@@ -146,6 +183,6 @@ interface CompilePolicyInput {
|
|
|
146
183
|
* Granting a role/capability only ever widens access; `allow: true`
|
|
147
184
|
* short-circuits its branch to the guard alone.
|
|
148
185
|
*/
|
|
149
|
-
declare function compilePolicy({ expression, table, resolver, maxLinkDepth }: CompilePolicyInput): PredIr;
|
|
186
|
+
declare function compilePolicy({ expression, table, resolver, maxLinkDepth, access }: CompilePolicyInput): PredIr;
|
|
150
187
|
|
|
151
|
-
export { type CompilePolicyInput, type CompilePredicateInput, POLICY_COMPARE_OPS, POLICY_MAX_LINK_DEPTH, type ParsedPolicy, type PolicyColumnInfo, type PolicyCompareOp, PolicyCompileError, type PolicyCompileErrorCode, type PolicyLinkInfo, type PolicyNode, type PolicyOperand, PolicyParseError, type PolicyResolver, type PolicyTableInfo, assertClaimsOnlyWhen, compilePolicy, compilePredicateV2, parsePolicy, walkPolicy };
|
|
188
|
+
export { type CompilePolicyInput, type CompilePredicateInput, POLICY_COMPARE_OPS, POLICY_MAX_LINK_DEPTH, type ParsedPolicy, type PolicyAccess, type PolicyColumnInfo, type PolicyCompareOp, PolicyCompileError, type PolicyCompileErrorCode, type PolicyLinkInfo, type PolicyNode, type PolicyOperand, PolicyParseError, type PolicyResolver, type PolicyTableInfo, assertClaimsOnlyWhen, compilePolicy, compilePredicateV2, parsePolicy, walkPolicy };
|
package/dist/predicate-v2.d.ts
CHANGED
|
@@ -50,6 +50,16 @@ type PolicyNode = {
|
|
|
50
50
|
kind: 'exists';
|
|
51
51
|
path: readonly string[];
|
|
52
52
|
where: PolicyNode;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* `visible(<link-path>)` — a related row exists AND the caller may read it:
|
|
56
|
+
* the final target's own read policy is compiled in as the inner predicate.
|
|
57
|
+
* Lets a write policy say "only on a parent you can see" without restating
|
|
58
|
+
* the parent's rule (and without drifting from it).
|
|
59
|
+
*/
|
|
60
|
+
| {
|
|
61
|
+
kind: 'visible';
|
|
62
|
+
path: readonly string[];
|
|
53
63
|
};
|
|
54
64
|
interface ParsedPolicy {
|
|
55
65
|
readonly source: string;
|
|
@@ -63,7 +73,7 @@ declare class PolicyParseError extends Error {
|
|
|
63
73
|
readonly name = "PolicyParseError";
|
|
64
74
|
constructor(message: string, position: number);
|
|
65
75
|
}
|
|
66
|
-
type PolicyCompileErrorCode = 'unknown-table' | 'unknown-column' | 'unknown-link' | 'not-to-one' | 'link-depth-exceeded' | 'unsupported-comparison' | 'invalid-null-comparison' | 'when-not-claims-only' | 'claim-type-mismatch';
|
|
76
|
+
type PolicyCompileErrorCode = 'unknown-table' | 'unknown-column' | 'unknown-link' | 'not-to-one' | 'link-depth-exceeded' | 'unsupported-comparison' | 'invalid-null-comparison' | 'when-not-claims-only' | 'claim-type-mismatch' | 'visible-unresolvable' | 'visible-cycle';
|
|
67
77
|
/**
|
|
68
78
|
* Compile-time failure — the parsed policy references something the table
|
|
69
79
|
* doesn't declare, or uses a shape the v2.0 IR cannot represent. Carries
|
|
@@ -96,6 +106,11 @@ declare function parsePolicy(source: string): ParsedPolicy;
|
|
|
96
106
|
* - variant `when` guards are claims-only: capability()/role() under
|
|
97
107
|
* and/or/not. (claim() comparisons in `when` are not representable in the
|
|
98
108
|
* v2.0 IR — a targeted error says so.)
|
|
109
|
+
* - `visible(<link-path>)` lowers to `exists(<link-path>, <target read policy>)`,
|
|
110
|
+
* compiled from the target's own read policy expression at the combined
|
|
111
|
+
* depth. A read policy that reaches itself again through visible() is a
|
|
112
|
+
* cycle and refused; a target with no read policy is readable to everyone
|
|
113
|
+
* who may use the namespace, so visibility reduces to existence.
|
|
99
114
|
*/
|
|
100
115
|
|
|
101
116
|
declare const POLICY_MAX_LINK_DEPTH = 3;
|
|
@@ -120,6 +135,19 @@ interface PolicyTableInfo {
|
|
|
120
135
|
/** `{ tables }` keyed by table name — TableMetaIr's record is assignable. */
|
|
121
136
|
interface PolicyResolver {
|
|
122
137
|
readonly tables: Record<string, PolicyTableInfo>;
|
|
138
|
+
/**
|
|
139
|
+
* A table's read policy expression, for `visible()`. Return `undefined` when
|
|
140
|
+
* the table has none (readable to every namespace user). A resolver without
|
|
141
|
+
* this callback cannot compile `visible()`: it is refused, never guessed.
|
|
142
|
+
*/
|
|
143
|
+
readonly readPolicy?: (table: string) => PolicyExpression | undefined;
|
|
144
|
+
/**
|
|
145
|
+
* The namespace that owns a table's policies, in a composed namespace. A
|
|
146
|
+
* visible() expansion of an imported table's read policy evaluates its
|
|
147
|
+
* capability() and role() checks in that namespace, not the importer's.
|
|
148
|
+
* Omitted (or undefined for a table) leaves them unscoped.
|
|
149
|
+
*/
|
|
150
|
+
readonly policyNamespace?: (table: string) => string | undefined;
|
|
123
151
|
}
|
|
124
152
|
interface CompilePredicateInput {
|
|
125
153
|
/** DSL source text, or an already-parsed policy. */
|
|
@@ -131,11 +159,20 @@ interface CompilePredicateInput {
|
|
|
131
159
|
declare function compilePredicateV2({ expression, table, resolver, maxLinkDepth }: CompilePredicateInput): PredIr;
|
|
132
160
|
/** Variant `when` guards may only test claims: capability()/role() under and/or/not. */
|
|
133
161
|
declare function assertClaimsOnlyWhen(parsed: ParsedPolicy): void;
|
|
162
|
+
/** Which of a table's policies is being compiled. */
|
|
163
|
+
type PolicyAccess = 'read' | 'write' | 'insert' | 'update';
|
|
134
164
|
interface CompilePolicyInput {
|
|
135
165
|
expression: PolicyExpression;
|
|
136
166
|
table: string;
|
|
137
167
|
resolver: PolicyResolver;
|
|
138
168
|
maxLinkDepth?: number;
|
|
169
|
+
/**
|
|
170
|
+
* Default 'read'. A read policy that reaches its own table through visible()
|
|
171
|
+
* is a cycle; a write policy that follows a self-link (a subtask written
|
|
172
|
+
* under a parent the caller can read) only expands the read policy, so it
|
|
173
|
+
* does not start the cycle check at its own table.
|
|
174
|
+
*/
|
|
175
|
+
access?: PolicyAccess;
|
|
139
176
|
}
|
|
140
177
|
/**
|
|
141
178
|
* Policy expression (string or `{default, variants}`) → one PredIr under §4c
|
|
@@ -146,6 +183,6 @@ interface CompilePolicyInput {
|
|
|
146
183
|
* Granting a role/capability only ever widens access; `allow: true`
|
|
147
184
|
* short-circuits its branch to the guard alone.
|
|
148
185
|
*/
|
|
149
|
-
declare function compilePolicy({ expression, table, resolver, maxLinkDepth }: CompilePolicyInput): PredIr;
|
|
186
|
+
declare function compilePolicy({ expression, table, resolver, maxLinkDepth, access }: CompilePolicyInput): PredIr;
|
|
150
187
|
|
|
151
|
-
export { type CompilePolicyInput, type CompilePredicateInput, POLICY_COMPARE_OPS, POLICY_MAX_LINK_DEPTH, type ParsedPolicy, type PolicyColumnInfo, type PolicyCompareOp, PolicyCompileError, type PolicyCompileErrorCode, type PolicyLinkInfo, type PolicyNode, type PolicyOperand, PolicyParseError, type PolicyResolver, type PolicyTableInfo, assertClaimsOnlyWhen, compilePolicy, compilePredicateV2, parsePolicy, walkPolicy };
|
|
188
|
+
export { type CompilePolicyInput, type CompilePredicateInput, POLICY_COMPARE_OPS, POLICY_MAX_LINK_DEPTH, type ParsedPolicy, type PolicyAccess, type PolicyColumnInfo, type PolicyCompareOp, PolicyCompileError, type PolicyCompileErrorCode, type PolicyLinkInfo, type PolicyNode, type PolicyOperand, PolicyParseError, type PolicyResolver, type PolicyTableInfo, assertClaimsOnlyWhen, compilePolicy, compilePredicateV2, parsePolicy, walkPolicy };
|
package/dist/predicate-v2.js
CHANGED
|
@@ -1640,6 +1640,7 @@ var Parser2 = class {
|
|
|
1640
1640
|
return node;
|
|
1641
1641
|
}
|
|
1642
1642
|
if (this.peekKeyword("exists")) return this.parseExists();
|
|
1643
|
+
if (this.peekKeyword("visible") && this.tokens[this.index + 1]?.kind === "lparen") return this.parseVisible();
|
|
1643
1644
|
return this.parseComparison();
|
|
1644
1645
|
}
|
|
1645
1646
|
parseBooleanCall(fn) {
|
|
@@ -1659,6 +1660,13 @@ var Parser2 = class {
|
|
|
1659
1660
|
this.expect("rparen", "')' closing exists(...)");
|
|
1660
1661
|
return { kind: "exists", path, where };
|
|
1661
1662
|
}
|
|
1663
|
+
parseVisible() {
|
|
1664
|
+
this.advance();
|
|
1665
|
+
this.expect("lparen", "'(' after visible");
|
|
1666
|
+
const path = this.parsePath("a link path in visible(...)");
|
|
1667
|
+
this.expect("rparen", "')' closing visible(...)");
|
|
1668
|
+
return { kind: "visible", path };
|
|
1669
|
+
}
|
|
1662
1670
|
parseComparison() {
|
|
1663
1671
|
const left = this.parseOperand();
|
|
1664
1672
|
const operator = this.expect("operator", "a comparison operator");
|
|
@@ -1774,6 +1782,24 @@ function parsePolicy(source) {
|
|
|
1774
1782
|
|
|
1775
1783
|
// src/predicate-v2/compile.ts
|
|
1776
1784
|
var POLICY_MAX_LINK_DEPTH = 3;
|
|
1785
|
+
function scopeToNamespace(pred, namespace) {
|
|
1786
|
+
switch (pred.kind) {
|
|
1787
|
+
case "capability":
|
|
1788
|
+
case "role":
|
|
1789
|
+
return pred.namespace ? pred : { ...pred, namespace };
|
|
1790
|
+
case "and":
|
|
1791
|
+
case "or":
|
|
1792
|
+
return { ...pred, operands: pred.operands.map((operand) => scopeToNamespace(operand, namespace)) };
|
|
1793
|
+
case "not":
|
|
1794
|
+
return { ...pred, operand: scopeToNamespace(pred.operand, namespace) };
|
|
1795
|
+
case "exists":
|
|
1796
|
+
return { ...pred, where: scopeToNamespace(pred.where, namespace) };
|
|
1797
|
+
case "aggPredicate":
|
|
1798
|
+
return pred.where ? { ...pred, where: scopeToNamespace(pred.where, namespace) } : pred;
|
|
1799
|
+
default:
|
|
1800
|
+
return pred;
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1777
1803
|
var CMP_BY_OP = {
|
|
1778
1804
|
"=": "eq",
|
|
1779
1805
|
"!=": "neq",
|
|
@@ -1807,7 +1833,7 @@ function resolveLink(info, ref) {
|
|
|
1807
1833
|
function columnCandidates(info) {
|
|
1808
1834
|
return [...info.columns.map((c) => c.name), ...info.links.map((l) => l.name)];
|
|
1809
1835
|
}
|
|
1810
|
-
var PolicyCompiler = class {
|
|
1836
|
+
var PolicyCompiler = class _PolicyCompiler {
|
|
1811
1837
|
constructor(ctx) {
|
|
1812
1838
|
this.ctx = ctx;
|
|
1813
1839
|
}
|
|
@@ -1820,13 +1846,15 @@ var PolicyCompiler = class {
|
|
|
1820
1846
|
return { kind: node.kind, operands: node.operands.map((operand) => this.compile(operand, table, depth)) };
|
|
1821
1847
|
case "not":
|
|
1822
1848
|
assertNoNegatedExternalClaim(node.operand);
|
|
1823
|
-
return { kind: "not", operand: this.compile(node.operand, table, depth) };
|
|
1849
|
+
return { kind: "not", operand: new _PolicyCompiler({ ...this.ctx, negated: true }).compile(node.operand, table, depth) };
|
|
1824
1850
|
case "capability":
|
|
1825
1851
|
return { kind: "capability", key: node.key };
|
|
1826
1852
|
case "role":
|
|
1827
1853
|
return { kind: "role", key: node.key };
|
|
1828
1854
|
case "exists":
|
|
1829
1855
|
return this.compileExists(node.path, node.where, table, depth);
|
|
1856
|
+
case "visible":
|
|
1857
|
+
return this.compileVisible(node.path, table, depth);
|
|
1830
1858
|
case "compare":
|
|
1831
1859
|
return this.compileCompare(node, table, depth);
|
|
1832
1860
|
}
|
|
@@ -1840,6 +1868,36 @@ var PolicyCompiler = class {
|
|
|
1840
1868
|
}
|
|
1841
1869
|
return pred;
|
|
1842
1870
|
}
|
|
1871
|
+
/** `visible(path)` → `exists(path, <final target's read policy>)`. */
|
|
1872
|
+
compileVisible(path, table, depth) {
|
|
1873
|
+
const { links, finalTable } = this.resolveLinkPath(path, table, depth, { toOneOnly: false });
|
|
1874
|
+
const readPolicy = this.ctx.resolver.readPolicy;
|
|
1875
|
+
if (!readPolicy) {
|
|
1876
|
+
throw new PolicyCompileError(
|
|
1877
|
+
`visible(${path.join(".")}) needs the target's read policy, which this compile context does not provide`,
|
|
1878
|
+
"visible-unresolvable"
|
|
1879
|
+
);
|
|
1880
|
+
}
|
|
1881
|
+
if (this.ctx.visiting.includes(finalTable)) {
|
|
1882
|
+
throw new PolicyCompileError(
|
|
1883
|
+
`visible(${path.join(".")}) on '${table}' reaches '${finalTable}' whose read policy is already being expanded (${[...this.ctx.visiting, finalTable].join(" \u2192 ")})`,
|
|
1884
|
+
"visible-cycle"
|
|
1885
|
+
);
|
|
1886
|
+
}
|
|
1887
|
+
const expression = readPolicy(finalTable);
|
|
1888
|
+
let pred = expression === void 0 ? { kind: "const", value: true } : compilePolicyAt({
|
|
1889
|
+
expression,
|
|
1890
|
+
table: finalTable,
|
|
1891
|
+
ctx: { ...this.ctx, visiting: [...this.ctx.visiting, finalTable] },
|
|
1892
|
+
depth: depth + links.length
|
|
1893
|
+
});
|
|
1894
|
+
const owner = this.ctx.resolver.policyNamespace?.(finalTable);
|
|
1895
|
+
if (owner) pred = scopeToNamespace(pred, owner);
|
|
1896
|
+
for (let i = links.length - 1; i >= 0; i -= 1) {
|
|
1897
|
+
pred = { kind: "exists", link: links[i].name, where: pred };
|
|
1898
|
+
}
|
|
1899
|
+
return pred;
|
|
1900
|
+
}
|
|
1843
1901
|
resolveLinkPath(path, table, depth, { toOneOnly }) {
|
|
1844
1902
|
const links = [];
|
|
1845
1903
|
let current = table;
|
|
@@ -1900,6 +1958,7 @@ var PolicyCompiler = class {
|
|
|
1900
1958
|
);
|
|
1901
1959
|
}
|
|
1902
1960
|
if (valueSide.kind === "claim" && isExternalPolicyClaimKey(valueSide.key)) {
|
|
1961
|
+
if (this.ctx.negated) throw negatedExternalClaim(valueSide.key);
|
|
1903
1962
|
assertExternalClaimComparison({ op, key: valueSide.key, column: info.columns.find((c) => c.name === column) });
|
|
1904
1963
|
}
|
|
1905
1964
|
let cmp = this.buildCmp(op, column, valueSide);
|
|
@@ -1937,22 +1996,30 @@ function assertExternalClaimComparison({ op, key, column }) {
|
|
|
1937
1996
|
);
|
|
1938
1997
|
}
|
|
1939
1998
|
}
|
|
1999
|
+
function negatedExternalClaim(key) {
|
|
2000
|
+
return new PolicyCompileError(
|
|
2001
|
+
`claim('${key}') cannot appear under not(): a negated borrower comparison admits other borrowers' rows`,
|
|
2002
|
+
"unsupported-comparison"
|
|
2003
|
+
);
|
|
2004
|
+
}
|
|
1940
2005
|
function assertNoNegatedExternalClaim(operand) {
|
|
1941
2006
|
walkPolicy(operand, (node) => {
|
|
1942
2007
|
if (node.kind !== "compare") return;
|
|
1943
2008
|
const claim = [node.left, node.right].find((side) => side.kind === "claim" && isExternalPolicyClaimKey(side.key));
|
|
1944
|
-
if (claim?.kind === "claim")
|
|
1945
|
-
throw new PolicyCompileError(
|
|
1946
|
-
`claim('${claim.key}') cannot appear under not(): a negated borrower comparison admits other borrowers' rows`,
|
|
1947
|
-
"unsupported-comparison"
|
|
1948
|
-
);
|
|
1949
|
-
}
|
|
2009
|
+
if (claim?.kind === "claim") throw negatedExternalClaim(claim.key);
|
|
1950
2010
|
});
|
|
1951
2011
|
}
|
|
1952
2012
|
function compilePredicateV2({ expression, table, resolver, maxLinkDepth }) {
|
|
2013
|
+
return compilePredicateAt({
|
|
2014
|
+
expression,
|
|
2015
|
+
table,
|
|
2016
|
+
depth: 0,
|
|
2017
|
+
ctx: { resolver, maxLinkDepth: maxLinkDepth ?? POLICY_MAX_LINK_DEPTH, visiting: [table] }
|
|
2018
|
+
});
|
|
2019
|
+
}
|
|
2020
|
+
function compilePredicateAt({ expression, table, ctx, depth }) {
|
|
1953
2021
|
const parsed = typeof expression === "string" ? parsePolicy(expression) : expression;
|
|
1954
|
-
|
|
1955
|
-
return compiler.compile(parsed.ast, table, 0);
|
|
2022
|
+
return new PolicyCompiler(ctx).compile(parsed.ast, table, depth);
|
|
1956
2023
|
}
|
|
1957
2024
|
function assertClaimsOnlyWhen(parsed) {
|
|
1958
2025
|
walkPolicy(parsed.ast, (node) => {
|
|
@@ -1969,25 +2036,34 @@ function assertClaimsOnlyWhen(parsed) {
|
|
|
1969
2036
|
"when-not-claims-only"
|
|
1970
2037
|
);
|
|
1971
2038
|
}
|
|
1972
|
-
if (node.kind === "exists") {
|
|
2039
|
+
if (node.kind === "exists" || node.kind === "visible") {
|
|
1973
2040
|
throw new PolicyCompileError(
|
|
1974
|
-
|
|
2041
|
+
`variant 'when' guards are claims-only \u2014 ${node.kind}() is not allowed`,
|
|
1975
2042
|
"when-not-claims-only"
|
|
1976
2043
|
);
|
|
1977
2044
|
}
|
|
1978
2045
|
});
|
|
1979
2046
|
}
|
|
1980
|
-
function
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
2047
|
+
function initialVisiting({ table, access }) {
|
|
2048
|
+
return (access ?? "read") === "read" ? [table] : [];
|
|
2049
|
+
}
|
|
2050
|
+
function compilePolicy({ expression, table, resolver, maxLinkDepth, access }) {
|
|
2051
|
+
return compilePolicyAt({
|
|
2052
|
+
expression,
|
|
2053
|
+
table,
|
|
2054
|
+
depth: 0,
|
|
2055
|
+
ctx: { resolver, maxLinkDepth: maxLinkDepth ?? POLICY_MAX_LINK_DEPTH, visiting: initialVisiting({ table, access }) }
|
|
2056
|
+
});
|
|
2057
|
+
}
|
|
2058
|
+
function compilePolicyAt({ expression, table, ctx, depth }) {
|
|
2059
|
+
if (typeof expression === "string") return compilePredicateAt({ expression, table, ctx, depth });
|
|
2060
|
+
const defaultPred = compilePredicateAt({ expression: expression.default, table, ctx, depth });
|
|
1985
2061
|
if (expression.variants.length === 0) return defaultPred;
|
|
1986
2062
|
const branches = expression.variants.map((variant) => {
|
|
1987
2063
|
const parsedWhen = parsePolicy(variant.when);
|
|
1988
2064
|
assertClaimsOnlyWhen(parsedWhen);
|
|
1989
|
-
const when =
|
|
1990
|
-
const grant = variant.allow === true ? { kind: "const", value: true } :
|
|
2065
|
+
const when = compilePredicateAt({ expression: parsedWhen, table, ctx, depth });
|
|
2066
|
+
const grant = variant.allow === true ? { kind: "const", value: true } : compilePredicateAt({ expression: variant.policy, table, ctx, depth });
|
|
1991
2067
|
return { kind: "and", operands: [when, grant] };
|
|
1992
2068
|
});
|
|
1993
2069
|
return { kind: "or", operands: [defaultPred, ...branches] };
|