js-rigor: transitive dependency-index invalidation; multi_hop/parent usage tracking
- invalidateRuleResultCacheByRelation now walks the transitive closure of the dependency index (owner -> is_owner -> computed can_read): mutating a base relation left computed/multi_hop results stale - _collectRelationUsages registers multi_hop and parent rule relations (parentRelation + subject relation) so their caches invalidate on base-relation mutations - advanced-rule-kinds.test.js: multi_hop (min-over-path, depth-limited), computed (userset alias), parent (subject relation on the target's parent) through the full check() path under random mutations
This commit is contained in:
+28
-3
@@ -471,10 +471,22 @@ export class Arbiter {
|
||||
|
||||
invalidateRuleResultCacheByRelation(relation) {
|
||||
if (!this.ruleResultCache) return;
|
||||
// Transitive closure over the dependency index: a mutation of a base
|
||||
// relation (e.g. owner) must invalidate cached results of every rule
|
||||
// that depends on it through any chain (owner -> is_owner ->
|
||||
// can_read/computed), not just direct dependents.
|
||||
const affected = new Set([relation]);
|
||||
const entry = this.dependencyIndex.get(relation);
|
||||
if (entry?.all) {
|
||||
for (const rel of entry.all) affected.add(rel);
|
||||
const queue = [relation];
|
||||
while (queue.length) {
|
||||
const rel = queue.pop();
|
||||
const entry = this.dependencyIndex.get(rel);
|
||||
if (!entry || !entry.all) continue;
|
||||
for (const dep of entry.all) {
|
||||
if (!affected.has(dep)) {
|
||||
affected.add(dep);
|
||||
queue.push(dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const rel of affected) {
|
||||
@@ -529,6 +541,19 @@ export class Arbiter {
|
||||
acc.set(`${rule.relation}|false`, { relation: rule.relation, reverse: false });
|
||||
}
|
||||
|
||||
if (rule.type === 'multi_hop' && rule.relation) {
|
||||
acc.set(`${rule.relation}|false`, { relation: rule.relation, reverse: false });
|
||||
}
|
||||
|
||||
if (rule.type === 'parent') {
|
||||
if (rule.parentRelation) {
|
||||
acc.set(`${rule.parentRelation}|false`, { relation: rule.parentRelation, reverse: false });
|
||||
}
|
||||
if (rule.relation && rule.relation !== rule.parentRelation) {
|
||||
acc.set(`${rule.relation}|false`, { relation: rule.relation, reverse: false });
|
||||
}
|
||||
}
|
||||
|
||||
const unionRules = Array.isArray(rule.union) ? rule.union : rule.union?.rules;
|
||||
if (Array.isArray(unionRules)) {
|
||||
for (const child of unionRules) this._collectRelationUsages(child, acc);
|
||||
|
||||
Reference in New Issue
Block a user