@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/compiler.cjs
CHANGED
|
@@ -1123,14 +1123,20 @@ function requiredArtifactIrVersion(operations) {
|
|
|
1123
1123
|
var CLAIMS_CONTRACT = {
|
|
1124
1124
|
sub: "string",
|
|
1125
1125
|
org: "string?",
|
|
1126
|
+
ext_subject: "string?",
|
|
1127
|
+
ext_email: "string?",
|
|
1126
1128
|
roles: "string[]",
|
|
1127
1129
|
capabilities: "string[]"
|
|
1128
1130
|
};
|
|
1131
|
+
var EXTERNAL_POLICY_CLAIM_KEYS = ["ext_subject", "ext_email"];
|
|
1132
|
+
function isExternalPolicyClaimKey(key) {
|
|
1133
|
+
return EXTERNAL_POLICY_CLAIM_KEYS.includes(key);
|
|
1134
|
+
}
|
|
1129
1135
|
var scalarLiteral = zod.z.union([zod.z.string(), zod.z.number(), zod.z.boolean(), zod.z.null()]);
|
|
1130
1136
|
var valueIrSchema = zod.z.discriminatedUnion("kind", [
|
|
1131
1137
|
zod.z.object({ kind: zod.z.literal("literal"), value: zod.z.union([scalarLiteral, zod.z.array(scalarLiteral)]) }),
|
|
1132
1138
|
zod.z.object({ kind: zod.z.literal("variable"), name: zod.z.string() }),
|
|
1133
|
-
zod.z.object({ kind: zod.z.literal("claim"), key: zod.z.enum(["sub", "org"]) })
|
|
1139
|
+
zod.z.object({ kind: zod.z.literal("claim"), key: zod.z.enum(["sub", "org", ...EXTERNAL_POLICY_CLAIM_KEYS]) })
|
|
1134
1140
|
]);
|
|
1135
1141
|
var CMP_OPS = [
|
|
1136
1142
|
"eq",
|
|
@@ -1850,6 +1856,7 @@ var Parser2 = class {
|
|
|
1850
1856
|
return node;
|
|
1851
1857
|
}
|
|
1852
1858
|
if (this.peekKeyword("exists")) return this.parseExists();
|
|
1859
|
+
if (this.peekKeyword("visible") && this.tokens[this.index + 1]?.kind === "lparen") return this.parseVisible();
|
|
1853
1860
|
return this.parseComparison();
|
|
1854
1861
|
}
|
|
1855
1862
|
parseBooleanCall(fn) {
|
|
@@ -1869,6 +1876,13 @@ var Parser2 = class {
|
|
|
1869
1876
|
this.expect("rparen", "')' closing exists(...)");
|
|
1870
1877
|
return { kind: "exists", path, where };
|
|
1871
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
|
+
}
|
|
1872
1886
|
parseComparison() {
|
|
1873
1887
|
const left = this.parseOperand();
|
|
1874
1888
|
const operator = this.expect("operator", "a comparison operator");
|
|
@@ -1984,6 +1998,24 @@ function parsePolicy(source) {
|
|
|
1984
1998
|
|
|
1985
1999
|
// src/predicate-v2/compile.ts
|
|
1986
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
|
+
}
|
|
1987
2019
|
var CMP_BY_OP = {
|
|
1988
2020
|
"=": "eq",
|
|
1989
2021
|
"!=": "neq",
|
|
@@ -2017,7 +2049,7 @@ function resolveLink(info, ref) {
|
|
|
2017
2049
|
function columnCandidates(info) {
|
|
2018
2050
|
return [...info.columns.map((c) => c.name), ...info.links.map((l) => l.name)];
|
|
2019
2051
|
}
|
|
2020
|
-
var PolicyCompiler = class {
|
|
2052
|
+
var PolicyCompiler = class _PolicyCompiler {
|
|
2021
2053
|
constructor(ctx) {
|
|
2022
2054
|
this.ctx = ctx;
|
|
2023
2055
|
}
|
|
@@ -2029,13 +2061,16 @@ var PolicyCompiler = class {
|
|
|
2029
2061
|
case "or":
|
|
2030
2062
|
return { kind: node.kind, operands: node.operands.map((operand) => this.compile(operand, table, depth)) };
|
|
2031
2063
|
case "not":
|
|
2032
|
-
|
|
2064
|
+
assertNoNegatedExternalClaim(node.operand);
|
|
2065
|
+
return { kind: "not", operand: new _PolicyCompiler({ ...this.ctx, negated: true }).compile(node.operand, table, depth) };
|
|
2033
2066
|
case "capability":
|
|
2034
2067
|
return { kind: "capability", key: node.key };
|
|
2035
2068
|
case "role":
|
|
2036
2069
|
return { kind: "role", key: node.key };
|
|
2037
2070
|
case "exists":
|
|
2038
2071
|
return this.compileExists(node.path, node.where, table, depth);
|
|
2072
|
+
case "visible":
|
|
2073
|
+
return this.compileVisible(node.path, table, depth);
|
|
2039
2074
|
case "compare":
|
|
2040
2075
|
return this.compileCompare(node, table, depth);
|
|
2041
2076
|
}
|
|
@@ -2049,6 +2084,36 @@ var PolicyCompiler = class {
|
|
|
2049
2084
|
}
|
|
2050
2085
|
return pred;
|
|
2051
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
|
+
}
|
|
2052
2117
|
resolveLinkPath(path, table, depth, { toOneOnly }) {
|
|
2053
2118
|
const links = [];
|
|
2054
2119
|
let current = table;
|
|
@@ -2108,6 +2173,10 @@ var PolicyCompiler = class {
|
|
|
2108
2173
|
columnCandidates(info)
|
|
2109
2174
|
);
|
|
2110
2175
|
}
|
|
2176
|
+
if (valueSide.kind === "claim" && isExternalPolicyClaimKey(valueSide.key)) {
|
|
2177
|
+
if (this.ctx.negated) throw negatedExternalClaim(valueSide.key);
|
|
2178
|
+
assertExternalClaimComparison({ op, key: valueSide.key, column: info.columns.find((c) => c.name === column) });
|
|
2179
|
+
}
|
|
2111
2180
|
let cmp = this.buildCmp(op, column, valueSide);
|
|
2112
2181
|
for (let i = links.length - 1; i >= 0; i -= 1) {
|
|
2113
2182
|
cmp = { kind: "exists", link: links[i].name, where: cmp };
|
|
@@ -2128,10 +2197,45 @@ var PolicyCompiler = class {
|
|
|
2128
2197
|
return { kind: "cmp", op: CMP_BY_OP[op], column, value };
|
|
2129
2198
|
}
|
|
2130
2199
|
};
|
|
2200
|
+
function assertExternalClaimComparison({ op, key, column }) {
|
|
2201
|
+
if (op !== "=") {
|
|
2202
|
+
throw new PolicyCompileError(
|
|
2203
|
+
`claim('${key}') supports = comparisons only, got '${op}'`,
|
|
2204
|
+
"unsupported-comparison"
|
|
2205
|
+
);
|
|
2206
|
+
}
|
|
2207
|
+
if (column?.type !== "text") {
|
|
2208
|
+
const actual = column?.type === void 0 ? "of unknown type" : typeof column.type === "string" ? column.type : "an enum";
|
|
2209
|
+
throw new PolicyCompileError(
|
|
2210
|
+
`claim('${key}') is text and compares to a text column only \u2014 '${column?.name ?? "?"}' is ${actual}`,
|
|
2211
|
+
"claim-type-mismatch"
|
|
2212
|
+
);
|
|
2213
|
+
}
|
|
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
|
+
}
|
|
2221
|
+
function assertNoNegatedExternalClaim(operand) {
|
|
2222
|
+
walkPolicy(operand, (node) => {
|
|
2223
|
+
if (node.kind !== "compare") return;
|
|
2224
|
+
const claim = [node.left, node.right].find((side) => side.kind === "claim" && isExternalPolicyClaimKey(side.key));
|
|
2225
|
+
if (claim?.kind === "claim") throw negatedExternalClaim(claim.key);
|
|
2226
|
+
});
|
|
2227
|
+
}
|
|
2131
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 }) {
|
|
2132
2237
|
const parsed = typeof expression === "string" ? parsePolicy(expression) : expression;
|
|
2133
|
-
|
|
2134
|
-
return compiler.compile(parsed.ast, table, 0);
|
|
2238
|
+
return new PolicyCompiler(ctx).compile(parsed.ast, table, depth);
|
|
2135
2239
|
}
|
|
2136
2240
|
function assertClaimsOnlyWhen(parsed) {
|
|
2137
2241
|
walkPolicy(parsed.ast, (node) => {
|
|
@@ -2148,25 +2252,34 @@ function assertClaimsOnlyWhen(parsed) {
|
|
|
2148
2252
|
"when-not-claims-only"
|
|
2149
2253
|
);
|
|
2150
2254
|
}
|
|
2151
|
-
if (node.kind === "exists") {
|
|
2255
|
+
if (node.kind === "exists" || node.kind === "visible") {
|
|
2152
2256
|
throw new PolicyCompileError(
|
|
2153
|
-
|
|
2257
|
+
`variant 'when' guards are claims-only \u2014 ${node.kind}() is not allowed`,
|
|
2154
2258
|
"when-not-claims-only"
|
|
2155
2259
|
);
|
|
2156
2260
|
}
|
|
2157
2261
|
});
|
|
2158
2262
|
}
|
|
2159
|
-
function
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
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 });
|
|
2164
2277
|
if (expression.variants.length === 0) return defaultPred;
|
|
2165
2278
|
const branches = expression.variants.map((variant) => {
|
|
2166
2279
|
const parsedWhen = parsePolicy(variant.when);
|
|
2167
2280
|
assertClaimsOnlyWhen(parsedWhen);
|
|
2168
|
-
const when =
|
|
2169
|
-
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 });
|
|
2170
2283
|
return { kind: "and", operands: [when, grant] };
|
|
2171
2284
|
});
|
|
2172
2285
|
return { kind: "or", operands: [defaultPred, ...branches] };
|
|
@@ -2382,12 +2495,6 @@ var PolicyInjector = class {
|
|
|
2382
2495
|
if (root.op === "resolvedWrite") return this.injectResolvedWrite(root, returning);
|
|
2383
2496
|
if (root.op === "insert" || root.op === "insertOne") {
|
|
2384
2497
|
const insertPolicy = this.insert(table);
|
|
2385
|
-
if (predContainsExists(insertPolicy)) {
|
|
2386
|
-
throw new DocumentCompileError(
|
|
2387
|
-
`table '${table}' cannot be inserted into: v2.0 insert checks are claims + inserted-values only, but its insert policy traverses a link (exists)`,
|
|
2388
|
-
"insert-check-unrepresentable"
|
|
2389
|
-
);
|
|
2390
|
-
}
|
|
2391
2498
|
const pins = (this.ctx.pins.get(table) ?? []).map(
|
|
2392
2499
|
(pin) => ({ column: pin.column, value: { kind: "claim", key: pin.claim } })
|
|
2393
2500
|
);
|
|
@@ -2402,22 +2509,14 @@ var PolicyInjector = class {
|
|
|
2402
2509
|
const updatePolicy = this.update(table);
|
|
2403
2510
|
const where = andPred(updatePolicy, root.where ? this.injectDocPred(root.where, table) : void 0);
|
|
2404
2511
|
if (root.op === "update" || root.op === "updateByPk") {
|
|
2405
|
-
|
|
2406
|
-
return { ...root, where, ...check, returning };
|
|
2512
|
+
return { ...root, where, insertCheck: updatePolicy, returning };
|
|
2407
2513
|
}
|
|
2408
2514
|
if (root.op === "cowOverride") {
|
|
2409
2515
|
const insertPolicy = this.insert(table);
|
|
2410
|
-
if (predContainsExists(insertPolicy)) {
|
|
2411
|
-
throw new DocumentCompileError(
|
|
2412
|
-
`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)`,
|
|
2413
|
-
"insert-check-unrepresentable"
|
|
2414
|
-
);
|
|
2415
|
-
}
|
|
2416
2516
|
const pins = (this.ctx.pins.get(table) ?? []).map(
|
|
2417
2517
|
(pin) => ({ column: pin.column, value: { kind: "claim", key: pin.claim } })
|
|
2418
2518
|
);
|
|
2419
|
-
|
|
2420
|
-
return { ...root, pinnedColumns: pins, cowCopyCheck: insertPolicy, where, ...check, returning };
|
|
2519
|
+
return { ...root, pinnedColumns: pins, cowCopyCheck: insertPolicy, where, insertCheck: updatePolicy, returning };
|
|
2421
2520
|
}
|
|
2422
2521
|
return { ...root, where, returning };
|
|
2423
2522
|
}
|
|
@@ -2639,7 +2738,7 @@ function scopePolicy(pred, namespace) {
|
|
|
2639
2738
|
switch (pred.kind) {
|
|
2640
2739
|
case "capability":
|
|
2641
2740
|
case "role":
|
|
2642
|
-
return { ...pred, namespace };
|
|
2741
|
+
return pred.namespace ? pred : { ...pred, namespace };
|
|
2643
2742
|
case "and":
|
|
2644
2743
|
case "or":
|
|
2645
2744
|
return { ...pred, operands: pred.operands.map((p) => scopePolicy(p, namespace)) };
|
|
@@ -2848,7 +2947,14 @@ function buildCompileContext(rawCanonical) {
|
|
|
2848
2947
|
const updatePolicies = /* @__PURE__ */ new Map();
|
|
2849
2948
|
const pins = /* @__PURE__ */ new Map();
|
|
2850
2949
|
const splitWritePolicyTables = /* @__PURE__ */ new Set();
|
|
2851
|
-
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
|
+
};
|
|
2852
2958
|
for (const table of relations) {
|
|
2853
2959
|
const isView = viewNames.has(table.table);
|
|
2854
2960
|
const compilePolicyFor = (access, store) => {
|
|
@@ -2857,7 +2963,7 @@ function buildCompileContext(rawCanonical) {
|
|
|
2857
2963
|
return;
|
|
2858
2964
|
}
|
|
2859
2965
|
try {
|
|
2860
|
-
const pred = compilePolicy({ expression, table: table.table, resolver });
|
|
2966
|
+
const pred = compilePolicy({ expression, table: table.table, resolver, access });
|
|
2861
2967
|
store.set(table.table, normalized.imports?.length ? scopePolicy(pred, owners.get(table.table).namespace) : pred);
|
|
2862
2968
|
} catch (error) {
|
|
2863
2969
|
if (error instanceof PolicyCompileError || error instanceof PolicyParseError) {
|