@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/compiler.cjs
CHANGED
|
@@ -1856,6 +1856,7 @@ var Parser2 = class {
|
|
|
1856
1856
|
return node;
|
|
1857
1857
|
}
|
|
1858
1858
|
if (this.peekKeyword("exists")) return this.parseExists();
|
|
1859
|
+
if (this.peekKeyword("visible") && this.tokens[this.index + 1]?.kind === "lparen") return this.parseVisible();
|
|
1859
1860
|
return this.parseComparison();
|
|
1860
1861
|
}
|
|
1861
1862
|
parseBooleanCall(fn) {
|
|
@@ -1875,6 +1876,13 @@ var Parser2 = class {
|
|
|
1875
1876
|
this.expect("rparen", "')' closing exists(...)");
|
|
1876
1877
|
return { kind: "exists", path, where };
|
|
1877
1878
|
}
|
|
1879
|
+
parseVisible() {
|
|
1880
|
+
this.advance();
|
|
1881
|
+
this.expect("lparen", "'(' after visible");
|
|
1882
|
+
const path = this.parsePath("a link path in visible(...)");
|
|
1883
|
+
this.expect("rparen", "')' closing visible(...)");
|
|
1884
|
+
return { kind: "visible", path };
|
|
1885
|
+
}
|
|
1878
1886
|
parseComparison() {
|
|
1879
1887
|
const left = this.parseOperand();
|
|
1880
1888
|
const operator = this.expect("operator", "a comparison operator");
|
|
@@ -1990,6 +1998,24 @@ function parsePolicy(source) {
|
|
|
1990
1998
|
|
|
1991
1999
|
// src/predicate-v2/compile.ts
|
|
1992
2000
|
var POLICY_MAX_LINK_DEPTH = 3;
|
|
2001
|
+
function scopeToNamespace(pred, namespace) {
|
|
2002
|
+
switch (pred.kind) {
|
|
2003
|
+
case "capability":
|
|
2004
|
+
case "role":
|
|
2005
|
+
return pred.namespace ? pred : { ...pred, namespace };
|
|
2006
|
+
case "and":
|
|
2007
|
+
case "or":
|
|
2008
|
+
return { ...pred, operands: pred.operands.map((operand) => scopeToNamespace(operand, namespace)) };
|
|
2009
|
+
case "not":
|
|
2010
|
+
return { ...pred, operand: scopeToNamespace(pred.operand, namespace) };
|
|
2011
|
+
case "exists":
|
|
2012
|
+
return { ...pred, where: scopeToNamespace(pred.where, namespace) };
|
|
2013
|
+
case "aggPredicate":
|
|
2014
|
+
return pred.where ? { ...pred, where: scopeToNamespace(pred.where, namespace) } : pred;
|
|
2015
|
+
default:
|
|
2016
|
+
return pred;
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
1993
2019
|
var CMP_BY_OP = {
|
|
1994
2020
|
"=": "eq",
|
|
1995
2021
|
"!=": "neq",
|
|
@@ -2023,7 +2049,7 @@ function resolveLink(info, ref) {
|
|
|
2023
2049
|
function columnCandidates(info) {
|
|
2024
2050
|
return [...info.columns.map((c) => c.name), ...info.links.map((l) => l.name)];
|
|
2025
2051
|
}
|
|
2026
|
-
var PolicyCompiler = class {
|
|
2052
|
+
var PolicyCompiler = class _PolicyCompiler {
|
|
2027
2053
|
constructor(ctx) {
|
|
2028
2054
|
this.ctx = ctx;
|
|
2029
2055
|
}
|
|
@@ -2036,13 +2062,15 @@ var PolicyCompiler = class {
|
|
|
2036
2062
|
return { kind: node.kind, operands: node.operands.map((operand) => this.compile(operand, table, depth)) };
|
|
2037
2063
|
case "not":
|
|
2038
2064
|
assertNoNegatedExternalClaim(node.operand);
|
|
2039
|
-
return { kind: "not", operand: this.compile(node.operand, table, depth) };
|
|
2065
|
+
return { kind: "not", operand: new _PolicyCompiler({ ...this.ctx, negated: true }).compile(node.operand, table, depth) };
|
|
2040
2066
|
case "capability":
|
|
2041
2067
|
return { kind: "capability", key: node.key };
|
|
2042
2068
|
case "role":
|
|
2043
2069
|
return { kind: "role", key: node.key };
|
|
2044
2070
|
case "exists":
|
|
2045
2071
|
return this.compileExists(node.path, node.where, table, depth);
|
|
2072
|
+
case "visible":
|
|
2073
|
+
return this.compileVisible(node.path, table, depth);
|
|
2046
2074
|
case "compare":
|
|
2047
2075
|
return this.compileCompare(node, table, depth);
|
|
2048
2076
|
}
|
|
@@ -2056,6 +2084,36 @@ var PolicyCompiler = class {
|
|
|
2056
2084
|
}
|
|
2057
2085
|
return pred;
|
|
2058
2086
|
}
|
|
2087
|
+
/** `visible(path)` → `exists(path, <final target's read policy>)`. */
|
|
2088
|
+
compileVisible(path, table, depth) {
|
|
2089
|
+
const { links, finalTable } = this.resolveLinkPath(path, table, depth, { toOneOnly: false });
|
|
2090
|
+
const readPolicy = this.ctx.resolver.readPolicy;
|
|
2091
|
+
if (!readPolicy) {
|
|
2092
|
+
throw new PolicyCompileError(
|
|
2093
|
+
`visible(${path.join(".")}) needs the target's read policy, which this compile context does not provide`,
|
|
2094
|
+
"visible-unresolvable"
|
|
2095
|
+
);
|
|
2096
|
+
}
|
|
2097
|
+
if (this.ctx.visiting.includes(finalTable)) {
|
|
2098
|
+
throw new PolicyCompileError(
|
|
2099
|
+
`visible(${path.join(".")}) on '${table}' reaches '${finalTable}' whose read policy is already being expanded (${[...this.ctx.visiting, finalTable].join(" \u2192 ")})`,
|
|
2100
|
+
"visible-cycle"
|
|
2101
|
+
);
|
|
2102
|
+
}
|
|
2103
|
+
const expression = readPolicy(finalTable);
|
|
2104
|
+
let pred = expression === void 0 ? { kind: "const", value: true } : compilePolicyAt({
|
|
2105
|
+
expression,
|
|
2106
|
+
table: finalTable,
|
|
2107
|
+
ctx: { ...this.ctx, visiting: [...this.ctx.visiting, finalTable] },
|
|
2108
|
+
depth: depth + links.length
|
|
2109
|
+
});
|
|
2110
|
+
const owner = this.ctx.resolver.policyNamespace?.(finalTable);
|
|
2111
|
+
if (owner) pred = scopeToNamespace(pred, owner);
|
|
2112
|
+
for (let i = links.length - 1; i >= 0; i -= 1) {
|
|
2113
|
+
pred = { kind: "exists", link: links[i].name, where: pred };
|
|
2114
|
+
}
|
|
2115
|
+
return pred;
|
|
2116
|
+
}
|
|
2059
2117
|
resolveLinkPath(path, table, depth, { toOneOnly }) {
|
|
2060
2118
|
const links = [];
|
|
2061
2119
|
let current = table;
|
|
@@ -2116,6 +2174,7 @@ var PolicyCompiler = class {
|
|
|
2116
2174
|
);
|
|
2117
2175
|
}
|
|
2118
2176
|
if (valueSide.kind === "claim" && isExternalPolicyClaimKey(valueSide.key)) {
|
|
2177
|
+
if (this.ctx.negated) throw negatedExternalClaim(valueSide.key);
|
|
2119
2178
|
assertExternalClaimComparison({ op, key: valueSide.key, column: info.columns.find((c) => c.name === column) });
|
|
2120
2179
|
}
|
|
2121
2180
|
let cmp = this.buildCmp(op, column, valueSide);
|
|
@@ -2153,22 +2212,30 @@ function assertExternalClaimComparison({ op, key, column }) {
|
|
|
2153
2212
|
);
|
|
2154
2213
|
}
|
|
2155
2214
|
}
|
|
2215
|
+
function negatedExternalClaim(key) {
|
|
2216
|
+
return new PolicyCompileError(
|
|
2217
|
+
`claim('${key}') cannot appear under not(): a negated borrower comparison admits other borrowers' rows`,
|
|
2218
|
+
"unsupported-comparison"
|
|
2219
|
+
);
|
|
2220
|
+
}
|
|
2156
2221
|
function assertNoNegatedExternalClaim(operand) {
|
|
2157
2222
|
walkPolicy(operand, (node) => {
|
|
2158
2223
|
if (node.kind !== "compare") return;
|
|
2159
2224
|
const claim = [node.left, node.right].find((side) => side.kind === "claim" && isExternalPolicyClaimKey(side.key));
|
|
2160
|
-
if (claim?.kind === "claim")
|
|
2161
|
-
throw new PolicyCompileError(
|
|
2162
|
-
`claim('${claim.key}') cannot appear under not(): a negated borrower comparison admits other borrowers' rows`,
|
|
2163
|
-
"unsupported-comparison"
|
|
2164
|
-
);
|
|
2165
|
-
}
|
|
2225
|
+
if (claim?.kind === "claim") throw negatedExternalClaim(claim.key);
|
|
2166
2226
|
});
|
|
2167
2227
|
}
|
|
2168
2228
|
function compilePredicateV2({ expression, table, resolver, maxLinkDepth }) {
|
|
2229
|
+
return compilePredicateAt({
|
|
2230
|
+
expression,
|
|
2231
|
+
table,
|
|
2232
|
+
depth: 0,
|
|
2233
|
+
ctx: { resolver, maxLinkDepth: maxLinkDepth ?? POLICY_MAX_LINK_DEPTH, visiting: [table] }
|
|
2234
|
+
});
|
|
2235
|
+
}
|
|
2236
|
+
function compilePredicateAt({ expression, table, ctx, depth }) {
|
|
2169
2237
|
const parsed = typeof expression === "string" ? parsePolicy(expression) : expression;
|
|
2170
|
-
|
|
2171
|
-
return compiler.compile(parsed.ast, table, 0);
|
|
2238
|
+
return new PolicyCompiler(ctx).compile(parsed.ast, table, depth);
|
|
2172
2239
|
}
|
|
2173
2240
|
function assertClaimsOnlyWhen(parsed) {
|
|
2174
2241
|
walkPolicy(parsed.ast, (node) => {
|
|
@@ -2185,25 +2252,34 @@ function assertClaimsOnlyWhen(parsed) {
|
|
|
2185
2252
|
"when-not-claims-only"
|
|
2186
2253
|
);
|
|
2187
2254
|
}
|
|
2188
|
-
if (node.kind === "exists") {
|
|
2255
|
+
if (node.kind === "exists" || node.kind === "visible") {
|
|
2189
2256
|
throw new PolicyCompileError(
|
|
2190
|
-
|
|
2257
|
+
`variant 'when' guards are claims-only \u2014 ${node.kind}() is not allowed`,
|
|
2191
2258
|
"when-not-claims-only"
|
|
2192
2259
|
);
|
|
2193
2260
|
}
|
|
2194
2261
|
});
|
|
2195
2262
|
}
|
|
2196
|
-
function
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2263
|
+
function initialVisiting({ table, access }) {
|
|
2264
|
+
return (access ?? "read") === "read" ? [table] : [];
|
|
2265
|
+
}
|
|
2266
|
+
function compilePolicy({ expression, table, resolver, maxLinkDepth, access }) {
|
|
2267
|
+
return compilePolicyAt({
|
|
2268
|
+
expression,
|
|
2269
|
+
table,
|
|
2270
|
+
depth: 0,
|
|
2271
|
+
ctx: { resolver, maxLinkDepth: maxLinkDepth ?? POLICY_MAX_LINK_DEPTH, visiting: initialVisiting({ table, access }) }
|
|
2272
|
+
});
|
|
2273
|
+
}
|
|
2274
|
+
function compilePolicyAt({ expression, table, ctx, depth }) {
|
|
2275
|
+
if (typeof expression === "string") return compilePredicateAt({ expression, table, ctx, depth });
|
|
2276
|
+
const defaultPred = compilePredicateAt({ expression: expression.default, table, ctx, depth });
|
|
2201
2277
|
if (expression.variants.length === 0) return defaultPred;
|
|
2202
2278
|
const branches = expression.variants.map((variant) => {
|
|
2203
2279
|
const parsedWhen = parsePolicy(variant.when);
|
|
2204
2280
|
assertClaimsOnlyWhen(parsedWhen);
|
|
2205
|
-
const when =
|
|
2206
|
-
const grant = variant.allow === true ? { kind: "const", value: true } :
|
|
2281
|
+
const when = compilePredicateAt({ expression: parsedWhen, table, ctx, depth });
|
|
2282
|
+
const grant = variant.allow === true ? { kind: "const", value: true } : compilePredicateAt({ expression: variant.policy, table, ctx, depth });
|
|
2207
2283
|
return { kind: "and", operands: [when, grant] };
|
|
2208
2284
|
});
|
|
2209
2285
|
return { kind: "or", operands: [defaultPred, ...branches] };
|
|
@@ -2419,12 +2495,6 @@ var PolicyInjector = class {
|
|
|
2419
2495
|
if (root.op === "resolvedWrite") return this.injectResolvedWrite(root, returning);
|
|
2420
2496
|
if (root.op === "insert" || root.op === "insertOne") {
|
|
2421
2497
|
const insertPolicy = this.insert(table);
|
|
2422
|
-
if (predContainsExists(insertPolicy)) {
|
|
2423
|
-
throw new DocumentCompileError(
|
|
2424
|
-
`table '${table}' cannot be inserted into: v2.0 insert checks are claims + inserted-values only, but its insert policy traverses a link (exists)`,
|
|
2425
|
-
"insert-check-unrepresentable"
|
|
2426
|
-
);
|
|
2427
|
-
}
|
|
2428
2498
|
const pins = (this.ctx.pins.get(table) ?? []).map(
|
|
2429
2499
|
(pin) => ({ column: pin.column, value: { kind: "claim", key: pin.claim } })
|
|
2430
2500
|
);
|
|
@@ -2439,22 +2509,14 @@ var PolicyInjector = class {
|
|
|
2439
2509
|
const updatePolicy = this.update(table);
|
|
2440
2510
|
const where = andPred(updatePolicy, root.where ? this.injectDocPred(root.where, table) : void 0);
|
|
2441
2511
|
if (root.op === "update" || root.op === "updateByPk") {
|
|
2442
|
-
|
|
2443
|
-
return { ...root, where, ...check, returning };
|
|
2512
|
+
return { ...root, where, insertCheck: updatePolicy, returning };
|
|
2444
2513
|
}
|
|
2445
2514
|
if (root.op === "cowOverride") {
|
|
2446
2515
|
const insertPolicy = this.insert(table);
|
|
2447
|
-
if (predContainsExists(insertPolicy)) {
|
|
2448
|
-
throw new DocumentCompileError(
|
|
2449
|
-
`table '${table}' cannot be copy-on-write overridden: v2.0 insert checks are claims + inserted-values only, but its insert policy traverses a link (exists)`,
|
|
2450
|
-
"insert-check-unrepresentable"
|
|
2451
|
-
);
|
|
2452
|
-
}
|
|
2453
2516
|
const pins = (this.ctx.pins.get(table) ?? []).map(
|
|
2454
2517
|
(pin) => ({ column: pin.column, value: { kind: "claim", key: pin.claim } })
|
|
2455
2518
|
);
|
|
2456
|
-
|
|
2457
|
-
return { ...root, pinnedColumns: pins, cowCopyCheck: insertPolicy, where, ...check, returning };
|
|
2519
|
+
return { ...root, pinnedColumns: pins, cowCopyCheck: insertPolicy, where, insertCheck: updatePolicy, returning };
|
|
2458
2520
|
}
|
|
2459
2521
|
return { ...root, where, returning };
|
|
2460
2522
|
}
|
|
@@ -2676,7 +2738,7 @@ function scopePolicy(pred, namespace) {
|
|
|
2676
2738
|
switch (pred.kind) {
|
|
2677
2739
|
case "capability":
|
|
2678
2740
|
case "role":
|
|
2679
|
-
return { ...pred, namespace };
|
|
2741
|
+
return pred.namespace ? pred : { ...pred, namespace };
|
|
2680
2742
|
case "and":
|
|
2681
2743
|
case "or":
|
|
2682
2744
|
return { ...pred, operands: pred.operands.map((p) => scopePolicy(p, namespace)) };
|
|
@@ -2885,7 +2947,14 @@ function buildCompileContext(rawCanonical) {
|
|
|
2885
2947
|
const updatePolicies = /* @__PURE__ */ new Map();
|
|
2886
2948
|
const pins = /* @__PURE__ */ new Map();
|
|
2887
2949
|
const splitWritePolicyTables = /* @__PURE__ */ new Set();
|
|
2888
|
-
const
|
|
2950
|
+
const readPolicyExpressions = new Map(relations.map((relation) => [relation.table, relation.policies.read]));
|
|
2951
|
+
const resolver = {
|
|
2952
|
+
tables,
|
|
2953
|
+
readPolicy: (table) => readPolicyExpressions.get(table),
|
|
2954
|
+
// In a composed namespace, an imported table's read policy (inlined by
|
|
2955
|
+
// visible()) is checked against the namespace that owns it.
|
|
2956
|
+
...normalized.imports?.length ? { policyNamespace: (table) => owners.get(table)?.namespace } : {}
|
|
2957
|
+
};
|
|
2889
2958
|
for (const table of relations) {
|
|
2890
2959
|
const isView = viewNames.has(table.table);
|
|
2891
2960
|
const compilePolicyFor = (access, store) => {
|
|
@@ -2894,7 +2963,7 @@ function buildCompileContext(rawCanonical) {
|
|
|
2894
2963
|
return;
|
|
2895
2964
|
}
|
|
2896
2965
|
try {
|
|
2897
|
-
const pred = compilePolicy({ expression, table: table.table, resolver });
|
|
2966
|
+
const pred = compilePolicy({ expression, table: table.table, resolver, access });
|
|
2898
2967
|
store.set(table.table, normalized.imports?.length ? scopePolicy(pred, owners.get(table.table).namespace) : pred);
|
|
2899
2968
|
} catch (error) {
|
|
2900
2969
|
if (error instanceof PolicyCompileError || error instanceof PolicyParseError) {
|