@statezero/core
Version:
The type-safe frontend client for StateZero - connect directly to your backend models with zero boilerplate
96 lines (95 loc) • 3.53 kB
JavaScript
import { Graph } from "graphlib";
/**
* Simple graph for tracking queryset store ancestry
*/
export class QuerysetStoreGraph {
constructor(hasStoreFn = null) {
this.graph = new Graph({ directed: true });
this.hasStoreFn = hasStoreFn || (() => false);
this.processedQuerysets = new Set(); // Track UUIDs of processed querysets
}
setHasStoreFn(hasStoreFn) {
this.hasStoreFn = hasStoreFn;
}
/**
* Add a queryset and its parent relationship to the graph
*/
addQueryset(queryset) {
if (!queryset)
return;
if (this.processedQuerysets.has(queryset.key)) {
return; // Already processed, skip
}
let current = queryset;
while (current && !this.processedQuerysets.has(current.key)) {
const currentKey = current.semanticKey;
const currentUuid = current.key;
this.processedQuerysets.add(currentUuid);
this.graph.setNode(currentKey);
if (current.__parent) {
const parentKey = current.__parent.semanticKey;
this.graph.setNode(parentKey);
if (currentKey !== parentKey) {
this.graph.setEdge(currentKey, parentKey);
}
current = current.__parent;
}
else {
break;
}
}
}
/**
* Find the root store for a queryset
* @param {Object} queryset - The queryset to analyze
* @returns {Object} { isRoot: boolean, root: semanticKey|null }
*/
findRoot(queryset) {
// Validate input - null/undefined is a programming error
if (!queryset) {
throw new Error("findRoot was called with a null object, instead of a queryset");
}
// Handle queryset without semanticKey
if (!queryset.semanticKey) {
throw new Error("findRoot was called on an object without a semanticKey, which means its not a queryset. findRoot only works on querysets");
}
const semanticKey = queryset.semanticKey;
if (!this.graph.hasNode(semanticKey)) {
this.addQueryset(queryset);
}
// Traverse ALL the way up to find the HIGHEST ancestor with a store
const visited = new Set();
let current = semanticKey;
let highestAncestorWithStore = null;
while (current && !visited.has(current)) {
visited.add(current);
// Check if current node has a store
if (this.hasStoreFn(current)) {
highestAncestorWithStore = current;
}
// Move to parent
const parents = this.graph.successors(current) || [];
if (parents.length > 0) {
current = parents[0]; // Follow the parent chain
}
else {
break; // No more parents
}
}
if (highestAncestorWithStore) {
if (highestAncestorWithStore === semanticKey) {
// This queryset itself is the highest with a store
return { isRoot: true, root: semanticKey };
}
else {
// Found a higher ancestor with a store
return { isRoot: false, root: highestAncestorWithStore };
}
}
// No stores found anywhere in the chain
return { isRoot: true, root: null };
}
clear() {
this.graph = new Graph({ directed: true });
}
}