@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/cli.cjs
CHANGED
|
@@ -1429,14 +1429,20 @@ function requiredArtifactIrVersion(operations) {
|
|
|
1429
1429
|
var CLAIMS_CONTRACT = {
|
|
1430
1430
|
sub: "string",
|
|
1431
1431
|
org: "string?",
|
|
1432
|
+
ext_subject: "string?",
|
|
1433
|
+
ext_email: "string?",
|
|
1432
1434
|
roles: "string[]",
|
|
1433
1435
|
capabilities: "string[]"
|
|
1434
1436
|
};
|
|
1437
|
+
var EXTERNAL_POLICY_CLAIM_KEYS = ["ext_subject", "ext_email"];
|
|
1438
|
+
function isExternalPolicyClaimKey(key) {
|
|
1439
|
+
return EXTERNAL_POLICY_CLAIM_KEYS.includes(key);
|
|
1440
|
+
}
|
|
1435
1441
|
var scalarLiteral = zod.z.union([zod.z.string(), zod.z.number(), zod.z.boolean(), zod.z.null()]);
|
|
1436
1442
|
var valueIrSchema = zod.z.discriminatedUnion("kind", [
|
|
1437
1443
|
zod.z.object({ kind: zod.z.literal("literal"), value: zod.z.union([scalarLiteral, zod.z.array(scalarLiteral)]) }),
|
|
1438
1444
|
zod.z.object({ kind: zod.z.literal("variable"), name: zod.z.string() }),
|
|
1439
|
-
zod.z.object({ kind: zod.z.literal("claim"), key: zod.z.enum(["sub", "org"]) })
|
|
1445
|
+
zod.z.object({ kind: zod.z.literal("claim"), key: zod.z.enum(["sub", "org", ...EXTERNAL_POLICY_CLAIM_KEYS]) })
|
|
1440
1446
|
]);
|
|
1441
1447
|
var CMP_OPS = [
|
|
1442
1448
|
"eq",
|
|
@@ -2174,6 +2180,7 @@ var Parser2 = class {
|
|
|
2174
2180
|
return node;
|
|
2175
2181
|
}
|
|
2176
2182
|
if (this.peekKeyword("exists")) return this.parseExists();
|
|
2183
|
+
if (this.peekKeyword("visible") && this.tokens[this.index + 1]?.kind === "lparen") return this.parseVisible();
|
|
2177
2184
|
return this.parseComparison();
|
|
2178
2185
|
}
|
|
2179
2186
|
parseBooleanCall(fn) {
|
|
@@ -2193,6 +2200,13 @@ var Parser2 = class {
|
|
|
2193
2200
|
this.expect("rparen", "')' closing exists(...)");
|
|
2194
2201
|
return { kind: "exists", path, where };
|
|
2195
2202
|
}
|
|
2203
|
+
parseVisible() {
|
|
2204
|
+
this.advance();
|
|
2205
|
+
this.expect("lparen", "'(' after visible");
|
|
2206
|
+
const path = this.parsePath("a link path in visible(...)");
|
|
2207
|
+
this.expect("rparen", "')' closing visible(...)");
|
|
2208
|
+
return { kind: "visible", path };
|
|
2209
|
+
}
|
|
2196
2210
|
parseComparison() {
|
|
2197
2211
|
const left = this.parseOperand();
|
|
2198
2212
|
const operator = this.expect("operator", "a comparison operator");
|
|
@@ -2308,6 +2322,24 @@ function parsePolicy(source) {
|
|
|
2308
2322
|
|
|
2309
2323
|
// src/predicate-v2/compile.ts
|
|
2310
2324
|
var POLICY_MAX_LINK_DEPTH = 3;
|
|
2325
|
+
function scopeToNamespace(pred, namespace) {
|
|
2326
|
+
switch (pred.kind) {
|
|
2327
|
+
case "capability":
|
|
2328
|
+
case "role":
|
|
2329
|
+
return pred.namespace ? pred : { ...pred, namespace };
|
|
2330
|
+
case "and":
|
|
2331
|
+
case "or":
|
|
2332
|
+
return { ...pred, operands: pred.operands.map((operand) => scopeToNamespace(operand, namespace)) };
|
|
2333
|
+
case "not":
|
|
2334
|
+
return { ...pred, operand: scopeToNamespace(pred.operand, namespace) };
|
|
2335
|
+
case "exists":
|
|
2336
|
+
return { ...pred, where: scopeToNamespace(pred.where, namespace) };
|
|
2337
|
+
case "aggPredicate":
|
|
2338
|
+
return pred.where ? { ...pred, where: scopeToNamespace(pred.where, namespace) } : pred;
|
|
2339
|
+
default:
|
|
2340
|
+
return pred;
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2311
2343
|
var CMP_BY_OP = {
|
|
2312
2344
|
"=": "eq",
|
|
2313
2345
|
"!=": "neq",
|
|
@@ -2341,7 +2373,7 @@ function resolveLink(info, ref) {
|
|
|
2341
2373
|
function columnCandidates(info) {
|
|
2342
2374
|
return [...info.columns.map((c) => c.name), ...info.links.map((l) => l.name)];
|
|
2343
2375
|
}
|
|
2344
|
-
var PolicyCompiler = class {
|
|
2376
|
+
var PolicyCompiler = class _PolicyCompiler {
|
|
2345
2377
|
constructor(ctx) {
|
|
2346
2378
|
this.ctx = ctx;
|
|
2347
2379
|
}
|
|
@@ -2353,13 +2385,16 @@ var PolicyCompiler = class {
|
|
|
2353
2385
|
case "or":
|
|
2354
2386
|
return { kind: node.kind, operands: node.operands.map((operand) => this.compile(operand, table, depth)) };
|
|
2355
2387
|
case "not":
|
|
2356
|
-
|
|
2388
|
+
assertNoNegatedExternalClaim(node.operand);
|
|
2389
|
+
return { kind: "not", operand: new _PolicyCompiler({ ...this.ctx, negated: true }).compile(node.operand, table, depth) };
|
|
2357
2390
|
case "capability":
|
|
2358
2391
|
return { kind: "capability", key: node.key };
|
|
2359
2392
|
case "role":
|
|
2360
2393
|
return { kind: "role", key: node.key };
|
|
2361
2394
|
case "exists":
|
|
2362
2395
|
return this.compileExists(node.path, node.where, table, depth);
|
|
2396
|
+
case "visible":
|
|
2397
|
+
return this.compileVisible(node.path, table, depth);
|
|
2363
2398
|
case "compare":
|
|
2364
2399
|
return this.compileCompare(node, table, depth);
|
|
2365
2400
|
}
|
|
@@ -2373,6 +2408,36 @@ var PolicyCompiler = class {
|
|
|
2373
2408
|
}
|
|
2374
2409
|
return pred;
|
|
2375
2410
|
}
|
|
2411
|
+
/** `visible(path)` → `exists(path, <final target's read policy>)`. */
|
|
2412
|
+
compileVisible(path, table, depth) {
|
|
2413
|
+
const { links, finalTable } = this.resolveLinkPath(path, table, depth, { toOneOnly: false });
|
|
2414
|
+
const readPolicy = this.ctx.resolver.readPolicy;
|
|
2415
|
+
if (!readPolicy) {
|
|
2416
|
+
throw new PolicyCompileError(
|
|
2417
|
+
`visible(${path.join(".")}) needs the target's read policy, which this compile context does not provide`,
|
|
2418
|
+
"visible-unresolvable"
|
|
2419
|
+
);
|
|
2420
|
+
}
|
|
2421
|
+
if (this.ctx.visiting.includes(finalTable)) {
|
|
2422
|
+
throw new PolicyCompileError(
|
|
2423
|
+
`visible(${path.join(".")}) on '${table}' reaches '${finalTable}' whose read policy is already being expanded (${[...this.ctx.visiting, finalTable].join(" \u2192 ")})`,
|
|
2424
|
+
"visible-cycle"
|
|
2425
|
+
);
|
|
2426
|
+
}
|
|
2427
|
+
const expression = readPolicy(finalTable);
|
|
2428
|
+
let pred = expression === void 0 ? { kind: "const", value: true } : compilePolicyAt({
|
|
2429
|
+
expression,
|
|
2430
|
+
table: finalTable,
|
|
2431
|
+
ctx: { ...this.ctx, visiting: [...this.ctx.visiting, finalTable] },
|
|
2432
|
+
depth: depth + links.length
|
|
2433
|
+
});
|
|
2434
|
+
const owner = this.ctx.resolver.policyNamespace?.(finalTable);
|
|
2435
|
+
if (owner) pred = scopeToNamespace(pred, owner);
|
|
2436
|
+
for (let i = links.length - 1; i >= 0; i -= 1) {
|
|
2437
|
+
pred = { kind: "exists", link: links[i].name, where: pred };
|
|
2438
|
+
}
|
|
2439
|
+
return pred;
|
|
2440
|
+
}
|
|
2376
2441
|
resolveLinkPath(path, table, depth, { toOneOnly }) {
|
|
2377
2442
|
const links = [];
|
|
2378
2443
|
let current = table;
|
|
@@ -2432,6 +2497,10 @@ var PolicyCompiler = class {
|
|
|
2432
2497
|
columnCandidates(info)
|
|
2433
2498
|
);
|
|
2434
2499
|
}
|
|
2500
|
+
if (valueSide.kind === "claim" && isExternalPolicyClaimKey(valueSide.key)) {
|
|
2501
|
+
if (this.ctx.negated) throw negatedExternalClaim(valueSide.key);
|
|
2502
|
+
assertExternalClaimComparison({ op, key: valueSide.key, column: info.columns.find((c) => c.name === column) });
|
|
2503
|
+
}
|
|
2435
2504
|
let cmp = this.buildCmp(op, column, valueSide);
|
|
2436
2505
|
for (let i = links.length - 1; i >= 0; i -= 1) {
|
|
2437
2506
|
cmp = { kind: "exists", link: links[i].name, where: cmp };
|
|
@@ -2452,10 +2521,37 @@ var PolicyCompiler = class {
|
|
|
2452
2521
|
return { kind: "cmp", op: CMP_BY_OP[op], column, value };
|
|
2453
2522
|
}
|
|
2454
2523
|
};
|
|
2455
|
-
function
|
|
2524
|
+
function assertExternalClaimComparison({ op, key, column }) {
|
|
2525
|
+
if (op !== "=") {
|
|
2526
|
+
throw new PolicyCompileError(
|
|
2527
|
+
`claim('${key}') supports = comparisons only, got '${op}'`,
|
|
2528
|
+
"unsupported-comparison"
|
|
2529
|
+
);
|
|
2530
|
+
}
|
|
2531
|
+
if (column?.type !== "text") {
|
|
2532
|
+
const actual = column?.type === void 0 ? "of unknown type" : typeof column.type === "string" ? column.type : "an enum";
|
|
2533
|
+
throw new PolicyCompileError(
|
|
2534
|
+
`claim('${key}') is text and compares to a text column only \u2014 '${column?.name ?? "?"}' is ${actual}`,
|
|
2535
|
+
"claim-type-mismatch"
|
|
2536
|
+
);
|
|
2537
|
+
}
|
|
2538
|
+
}
|
|
2539
|
+
function negatedExternalClaim(key) {
|
|
2540
|
+
return new PolicyCompileError(
|
|
2541
|
+
`claim('${key}') cannot appear under not(): a negated borrower comparison admits other borrowers' rows`,
|
|
2542
|
+
"unsupported-comparison"
|
|
2543
|
+
);
|
|
2544
|
+
}
|
|
2545
|
+
function assertNoNegatedExternalClaim(operand) {
|
|
2546
|
+
walkPolicy(operand, (node) => {
|
|
2547
|
+
if (node.kind !== "compare") return;
|
|
2548
|
+
const claim = [node.left, node.right].find((side) => side.kind === "claim" && isExternalPolicyClaimKey(side.key));
|
|
2549
|
+
if (claim?.kind === "claim") throw negatedExternalClaim(claim.key);
|
|
2550
|
+
});
|
|
2551
|
+
}
|
|
2552
|
+
function compilePredicateAt({ expression, table, ctx, depth }) {
|
|
2456
2553
|
const parsed = typeof expression === "string" ? parsePolicy(expression) : expression;
|
|
2457
|
-
|
|
2458
|
-
return compiler.compile(parsed.ast, table, 0);
|
|
2554
|
+
return new PolicyCompiler(ctx).compile(parsed.ast, table, depth);
|
|
2459
2555
|
}
|
|
2460
2556
|
function assertClaimsOnlyWhen(parsed) {
|
|
2461
2557
|
walkPolicy(parsed.ast, (node) => {
|
|
@@ -2472,25 +2568,34 @@ function assertClaimsOnlyWhen(parsed) {
|
|
|
2472
2568
|
"when-not-claims-only"
|
|
2473
2569
|
);
|
|
2474
2570
|
}
|
|
2475
|
-
if (node.kind === "exists") {
|
|
2571
|
+
if (node.kind === "exists" || node.kind === "visible") {
|
|
2476
2572
|
throw new PolicyCompileError(
|
|
2477
|
-
|
|
2573
|
+
`variant 'when' guards are claims-only \u2014 ${node.kind}() is not allowed`,
|
|
2478
2574
|
"when-not-claims-only"
|
|
2479
2575
|
);
|
|
2480
2576
|
}
|
|
2481
2577
|
});
|
|
2482
2578
|
}
|
|
2483
|
-
function
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2579
|
+
function initialVisiting({ table, access }) {
|
|
2580
|
+
return (access ?? "read") === "read" ? [table] : [];
|
|
2581
|
+
}
|
|
2582
|
+
function compilePolicy({ expression, table, resolver, maxLinkDepth, access }) {
|
|
2583
|
+
return compilePolicyAt({
|
|
2584
|
+
expression,
|
|
2585
|
+
table,
|
|
2586
|
+
depth: 0,
|
|
2587
|
+
ctx: { resolver, maxLinkDepth: maxLinkDepth ?? POLICY_MAX_LINK_DEPTH, visiting: initialVisiting({ table, access }) }
|
|
2588
|
+
});
|
|
2589
|
+
}
|
|
2590
|
+
function compilePolicyAt({ expression, table, ctx, depth }) {
|
|
2591
|
+
if (typeof expression === "string") return compilePredicateAt({ expression, table, ctx, depth });
|
|
2592
|
+
const defaultPred = compilePredicateAt({ expression: expression.default, table, ctx, depth });
|
|
2488
2593
|
if (expression.variants.length === 0) return defaultPred;
|
|
2489
2594
|
const branches = expression.variants.map((variant) => {
|
|
2490
2595
|
const parsedWhen = parsePolicy(variant.when);
|
|
2491
2596
|
assertClaimsOnlyWhen(parsedWhen);
|
|
2492
|
-
const when =
|
|
2493
|
-
const grant = variant.allow === true ? { kind: "const", value: true } :
|
|
2597
|
+
const when = compilePredicateAt({ expression: parsedWhen, table, ctx, depth });
|
|
2598
|
+
const grant = variant.allow === true ? { kind: "const", value: true } : compilePredicateAt({ expression: variant.policy, table, ctx, depth });
|
|
2494
2599
|
return { kind: "and", operands: [when, grant] };
|
|
2495
2600
|
});
|
|
2496
2601
|
return { kind: "or", operands: [defaultPred, ...branches] };
|
|
@@ -2528,33 +2633,6 @@ function andPred(...preds) {
|
|
|
2528
2633
|
if (operands.length === 1) return operands[0];
|
|
2529
2634
|
return { kind: "and", operands };
|
|
2530
2635
|
}
|
|
2531
|
-
function walkPred(pred, visit2) {
|
|
2532
|
-
visit2(pred);
|
|
2533
|
-
switch (pred.kind) {
|
|
2534
|
-
case "and":
|
|
2535
|
-
case "or":
|
|
2536
|
-
for (const operand of pred.operands) walkPred(operand, visit2);
|
|
2537
|
-
return;
|
|
2538
|
-
case "not":
|
|
2539
|
-
walkPred(pred.operand, visit2);
|
|
2540
|
-
return;
|
|
2541
|
-
case "exists":
|
|
2542
|
-
walkPred(pred.where, visit2);
|
|
2543
|
-
return;
|
|
2544
|
-
case "aggPredicate":
|
|
2545
|
-
if (pred.where) walkPred(pred.where, visit2);
|
|
2546
|
-
return;
|
|
2547
|
-
default:
|
|
2548
|
-
return;
|
|
2549
|
-
}
|
|
2550
|
-
}
|
|
2551
|
-
function predContainsExists(pred) {
|
|
2552
|
-
let found = false;
|
|
2553
|
-
walkPred(pred, (node) => {
|
|
2554
|
-
if (node.kind === "exists" || node.kind === "aggPredicate") found = true;
|
|
2555
|
-
});
|
|
2556
|
-
return found;
|
|
2557
|
-
}
|
|
2558
2636
|
|
|
2559
2637
|
// src/compiler-gql/inject.ts
|
|
2560
2638
|
function collectPinnedColumns(writePolicy) {
|
|
@@ -2698,12 +2776,6 @@ var PolicyInjector = class {
|
|
|
2698
2776
|
if (root.op === "resolvedWrite") return this.injectResolvedWrite(root, returning);
|
|
2699
2777
|
if (root.op === "insert" || root.op === "insertOne") {
|
|
2700
2778
|
const insertPolicy = this.insert(table);
|
|
2701
|
-
if (predContainsExists(insertPolicy)) {
|
|
2702
|
-
throw new DocumentCompileError(
|
|
2703
|
-
`table '${table}' cannot be inserted into: v2.0 insert checks are claims + inserted-values only, but its insert policy traverses a link (exists)`,
|
|
2704
|
-
"insert-check-unrepresentable"
|
|
2705
|
-
);
|
|
2706
|
-
}
|
|
2707
2779
|
const pins = (this.ctx.pins.get(table) ?? []).map(
|
|
2708
2780
|
(pin) => ({ column: pin.column, value: { kind: "claim", key: pin.claim } })
|
|
2709
2781
|
);
|
|
@@ -2718,22 +2790,14 @@ var PolicyInjector = class {
|
|
|
2718
2790
|
const updatePolicy = this.update(table);
|
|
2719
2791
|
const where = andPred(updatePolicy, root.where ? this.injectDocPred(root.where, table) : void 0);
|
|
2720
2792
|
if (root.op === "update" || root.op === "updateByPk") {
|
|
2721
|
-
|
|
2722
|
-
return { ...root, where, ...check, returning };
|
|
2793
|
+
return { ...root, where, insertCheck: updatePolicy, returning };
|
|
2723
2794
|
}
|
|
2724
2795
|
if (root.op === "cowOverride") {
|
|
2725
2796
|
const insertPolicy = this.insert(table);
|
|
2726
|
-
if (predContainsExists(insertPolicy)) {
|
|
2727
|
-
throw new DocumentCompileError(
|
|
2728
|
-
`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)`,
|
|
2729
|
-
"insert-check-unrepresentable"
|
|
2730
|
-
);
|
|
2731
|
-
}
|
|
2732
2797
|
const pins = (this.ctx.pins.get(table) ?? []).map(
|
|
2733
2798
|
(pin) => ({ column: pin.column, value: { kind: "claim", key: pin.claim } })
|
|
2734
2799
|
);
|
|
2735
|
-
|
|
2736
|
-
return { ...root, pinnedColumns: pins, cowCopyCheck: insertPolicy, where, ...check, returning };
|
|
2800
|
+
return { ...root, pinnedColumns: pins, cowCopyCheck: insertPolicy, where, insertCheck: updatePolicy, returning };
|
|
2737
2801
|
}
|
|
2738
2802
|
return { ...root, where, returning };
|
|
2739
2803
|
}
|
|
@@ -2967,7 +3031,7 @@ function scopePolicy(pred, namespace) {
|
|
|
2967
3031
|
switch (pred.kind) {
|
|
2968
3032
|
case "capability":
|
|
2969
3033
|
case "role":
|
|
2970
|
-
return { ...pred, namespace };
|
|
3034
|
+
return pred.namespace ? pred : { ...pred, namespace };
|
|
2971
3035
|
case "and":
|
|
2972
3036
|
case "or":
|
|
2973
3037
|
return { ...pred, operands: pred.operands.map((p) => scopePolicy(p, namespace)) };
|
|
@@ -3176,7 +3240,14 @@ function buildCompileContext(rawCanonical) {
|
|
|
3176
3240
|
const updatePolicies = /* @__PURE__ */ new Map();
|
|
3177
3241
|
const pins = /* @__PURE__ */ new Map();
|
|
3178
3242
|
const splitWritePolicyTables = /* @__PURE__ */ new Set();
|
|
3179
|
-
const
|
|
3243
|
+
const readPolicyExpressions = new Map(relations.map((relation) => [relation.table, relation.policies.read]));
|
|
3244
|
+
const resolver = {
|
|
3245
|
+
tables,
|
|
3246
|
+
readPolicy: (table) => readPolicyExpressions.get(table),
|
|
3247
|
+
// In a composed namespace, an imported table's read policy (inlined by
|
|
3248
|
+
// visible()) is checked against the namespace that owns it.
|
|
3249
|
+
...normalized.imports?.length ? { policyNamespace: (table) => owners.get(table)?.namespace } : {}
|
|
3250
|
+
};
|
|
3180
3251
|
for (const table of relations) {
|
|
3181
3252
|
const isView = viewNames.has(table.table);
|
|
3182
3253
|
const compilePolicyFor = (access, store) => {
|
|
@@ -3185,7 +3256,7 @@ function buildCompileContext(rawCanonical) {
|
|
|
3185
3256
|
return;
|
|
3186
3257
|
}
|
|
3187
3258
|
try {
|
|
3188
|
-
const pred = compilePolicy({ expression, table: table.table, resolver });
|
|
3259
|
+
const pred = compilePolicy({ expression, table: table.table, resolver, access });
|
|
3189
3260
|
store.set(table.table, normalized.imports?.length ? scopePolicy(pred, owners.get(table.table).namespace) : pred);
|
|
3190
3261
|
} catch (error) {
|
|
3191
3262
|
if (error instanceof PolicyCompileError || error instanceof PolicyParseError) {
|