Rocky_Mountain_Vending/.pnpm-store/v10/files/9f/dc2ee320eab5cf338f3bcd25e61cdbe0eb37b839ffb7d6ba57663050a4817405fb5555cac206a22add6eb2949414290dbb99cf112b863ff861eb64c14b42b6
DMleadgen 46d973904b
Initial commit: Rocky Mountain Vending website
Next.js website for Rocky Mountain Vending company featuring:
- Product catalog with Stripe integration
- Service areas and parts pages
- Admin dashboard with Clerk authentication
- SEO optimized pages with JSON-LD structured data

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 16:22:15 -07:00

277 lines
No EOL
10 KiB
Text

// Copyright 2024 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import * as Core from '../core/core.js';
/**
* @file This class encapsulates logic for handling resources and tasks used to model the
* execution dependency graph of the page. A node has a unique identifier and can depend on other
* nodes/be depended on. The construction of the graph maintains some important invariants that are
* inherent to the model:
*
* 1. The graph is a DAG, there are no cycles.
* 2. There is always a root node upon which all other nodes eventually depend.
*
* This allows particular optimizations in this class so that we do no need to check for cycles as
* these methods are called and we can always start traversal at the root node.
*/
class BaseNode {
static types = {
NETWORK: 'network',
CPU: 'cpu',
};
_id;
_isMainDocument;
dependents;
dependencies;
constructor(id) {
this._id = id;
this._isMainDocument = false;
this.dependents = [];
this.dependencies = [];
}
get id() {
return this._id;
}
get type() {
throw new Core.LanternError('Unimplemented');
}
/**
* In microseconds
*/
get startTime() {
throw new Core.LanternError('Unimplemented');
}
/**
* In microseconds
*/
get endTime() {
throw new Core.LanternError('Unimplemented');
}
setIsMainDocument(value) {
this._isMainDocument = value;
}
isMainDocument() {
return this._isMainDocument;
}
getDependents() {
return this.dependents.slice();
}
getNumberOfDependents() {
return this.dependents.length;
}
getDependencies() {
return this.dependencies.slice();
}
getNumberOfDependencies() {
return this.dependencies.length;
}
getRootNode() {
let rootNode = this;
while (rootNode.dependencies.length) {
rootNode = rootNode.dependencies[0];
}
return rootNode;
}
addDependent(node) {
node.addDependency(this);
}
addDependency(node) {
// @ts-expect-error - in checkJs, ts doesn't know that CPUNode and NetworkNode *are* BaseNodes.
if (node === this) {
throw new Core.LanternError('Cannot add dependency on itself');
}
if (this.dependencies.includes(node)) {
return;
}
node.dependents.push(this);
this.dependencies.push(node);
}
removeDependent(node) {
node.removeDependency(this);
}
removeDependency(node) {
if (!this.dependencies.includes(node)) {
return;
}
const thisIndex = node.dependents.indexOf(this);
node.dependents.splice(thisIndex, 1);
this.dependencies.splice(this.dependencies.indexOf(node), 1);
}
// Unused in devtools, but used in LH.
removeAllDependencies() {
for (const node of this.dependencies.slice()) {
this.removeDependency(node);
}
}
/**
* Computes whether the given node is anywhere in the dependency graph of this node.
* While this method can prevent cycles, it walks the graph and should be used sparingly.
* Nodes are always considered dependent on themselves for the purposes of cycle detection.
*/
isDependentOn(node) {
let isDependentOnNode = false;
this.traverse(currentNode => {
if (isDependentOnNode) {
return;
}
isDependentOnNode = currentNode === node;
}, currentNode => {
// If we've already found the dependency, don't traverse further.
if (isDependentOnNode) {
return [];
}
// Otherwise, traverse the dependencies.
return currentNode.getDependencies();
});
return isDependentOnNode;
}
/**
* Clones the node's information without adding any dependencies/dependents.
*/
cloneWithoutRelationships() {
const node = new BaseNode(this.id);
node.setIsMainDocument(this._isMainDocument);
return node;
}
/**
* Clones the entire graph connected to this node filtered by the optional predicate. If a node is
* included by the predicate, all nodes along the paths between the node and the root will be included. If the
* node this was called on is not included in the resulting filtered graph, the method will throw.
*
* This does not clone NetworkNode's `record` or `rawRecord` fields. It may be reasonable to clone the former,
* to assist in graph construction, but the latter should never be cloned as one constraint of Lantern is that
* the underlying data records are accessible for plain object reference equality checks.
*/
cloneWithRelationships(predicate) {
const rootNode = this.getRootNode();
const idsToIncludedClones = new Map();
// Walk down dependents.
rootNode.traverse(node => {
if (idsToIncludedClones.has(node.id)) {
return;
}
if (predicate === undefined) {
// No condition for entry, so clone every node.
idsToIncludedClones.set(node.id, node.cloneWithoutRelationships());
return;
}
if (predicate(node)) {
// Node included, so walk back up dependencies, cloning nodes from here back to the root.
node.traverse(node => idsToIncludedClones.set(node.id, node.cloneWithoutRelationships()),
// Dependencies already cloned have already cloned ancestors, so no need to visit again.
node => node.dependencies.filter(parent => !idsToIncludedClones.has(parent.id)));
}
});
// Copy dependencies between nodes.
rootNode.traverse(originalNode => {
const clonedNode = idsToIncludedClones.get(originalNode.id);
if (!clonedNode) {
return;
}
for (const dependency of originalNode.dependencies) {
const clonedDependency = idsToIncludedClones.get(dependency.id);
if (!clonedDependency) {
throw new Core.LanternError('Dependency somehow not cloned');
}
clonedNode.addDependency(clonedDependency);
}
});
const clonedThisNode = idsToIncludedClones.get(this.id);
if (!clonedThisNode) {
throw new Core.LanternError('Cloned graph missing node');
}
return clonedThisNode;
}
/**
* Traverses all connected nodes in BFS order, calling `callback` exactly once
* on each. `traversalPath` is the shortest (though not necessarily unique)
* path from `node` to the root of the iteration.
*
* The `getNextNodes` function takes a visited node and returns which nodes to
* visit next. It defaults to returning the node's dependents.
*/
traverse(callback, getNextNodes) {
for (const { node, traversalPath } of this.traverseGenerator(getNextNodes)) {
callback(node, traversalPath);
}
}
/**
* @see BaseNode.traverse
*/
// clang-format off
*traverseGenerator(getNextNodes) {
// clang-format on
if (!getNextNodes) {
getNextNodes = node => node.getDependents();
}
// @ts-expect-error - only traverses graphs of Node, so force tsc to treat `this` as one
const queue = [[this]];
const visited = new Set([this.id]);
while (queue.length) {
// @ts-expect-error - queue has length so it's guaranteed to have an item
const traversalPath = queue.shift();
const node = traversalPath[0];
yield { node, traversalPath };
for (const nextNode of getNextNodes(node)) {
if (visited.has(nextNode.id)) {
continue;
}
visited.add(nextNode.id);
queue.push([nextNode, ...traversalPath]);
}
}
}
/**
* If the given node has a cycle, returns a path representing that cycle.
* Else returns null.
*
* Does a DFS on in its dependent graph.
*/
static findCycle(node, direction = 'both') {
// Checking 'both' is the default entrypoint to recursively check both directions
if (direction === 'both') {
return BaseNode.findCycle(node, 'dependents') || BaseNode.findCycle(node, 'dependencies');
}
const visited = new Set();
const currentPath = [];
const toVisit = [node];
const depthAdded = new Map([[node, 0]]);
// Keep going while we have nodes to visit in the stack
while (toVisit.length) {
// Get the last node in the stack (DFS uses stack, not queue)
// @ts-expect-error - toVisit has length so it's guaranteed to have an item
const currentNode = toVisit.pop();
// We've hit a cycle if the node we're visiting is in our current dependency path
if (currentPath.includes(currentNode)) {
return currentPath;
}
// If we've already visited the node, no need to revisit it
if (visited.has(currentNode)) {
continue;
}
// Since we're visiting this node, clear out any nodes in our path that we had to backtrack
// @ts-expect-error
while (currentPath.length > depthAdded.get(currentNode)) {
currentPath.pop();
}
// Update our data structures to reflect that we're adding this node to our path
visited.add(currentNode);
currentPath.push(currentNode);
// Add all of its dependents to our toVisit stack
const nodesToExplore = direction === 'dependents' ? currentNode.dependents : currentNode.dependencies;
for (const nextNode of nodesToExplore) {
if (toVisit.includes(nextNode)) {
continue;
}
toVisit.push(nextNode);
depthAdded.set(nextNode, currentPath.length);
}
}
return null;
}
canDependOn(node) {
return node.startTime <= this.startTime;
}
}
export { BaseNode };
//# sourceMappingURL=BaseNode.js.map