UNPKG

@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
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 }); } }