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