Update dashboard, kb, memory +4 more (+28 ~18 -1)

This commit is contained in:
Echo
2026-02-06 14:25:10 +00:00
parent 7f64d5054a
commit 19d178268a
6767 changed files with 1346472 additions and 1282 deletions

View File

@@ -0,0 +1,41 @@
/**
* @license
* Copyright 2022 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
declare global {
interface Window {
/**
* @internal
*/
__ariaQuerySelector(root: Node, selector: string): Promise<Node | null>;
/**
* @internal
*/
__ariaQuerySelectorAll(root: Node, selector: string): Promise<Node[]>;
}
}
/**
* @internal
*/
export const ariaQuerySelector = (
root: Node,
selector: string,
): Promise<Node | null> => {
// In Firefox sandboxes globalThis !== window and we expose bindings on globalThis.
return (globalThis as unknown as Window).__ariaQuerySelector(root, selector);
};
/**
* @internal
*/
export const ariaQuerySelectorAll = async function* (
root: Node,
selector: string,
): AsyncIterable<Node> {
// In Firefox sandboxes globalThis !== window and we expose bindings on globalThis.
yield* await (globalThis as unknown as Window).__ariaQuerySelectorAll(
root,
selector,
);
};

View File

@@ -0,0 +1,26 @@
/**
* @license
* Copyright 2024 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
*/
export const cssQuerySelector = (
root: Node,
selector: string,
): Element | null => {
// @ts-expect-error assume element root
return root.querySelector(selector);
};
/**
* @internal
*/
export const cssQuerySelectorAll = function (
root: Node,
selector: string,
): Iterable<Element> {
// @ts-expect-error assume element root
return root.querySelectorAll(selector);
};

View File

@@ -0,0 +1,65 @@
/**
* @license
* Copyright 2023 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import type {CustomQueryHandler} from '../common/CustomQueryHandler.js';
import type {Awaitable, AwaitableIterable} from '../common/types.js';
/**
* @public
*/
export interface CustomQuerySelector {
querySelector(root: Node, selector: string): Awaitable<Node | null>;
querySelectorAll(root: Node, selector: string): AwaitableIterable<Node>;
}
/**
* This class mimics the injected {@link CustomQuerySelectorRegistry}.
*/
class CustomQuerySelectorRegistry {
#selectors = new Map<string, CustomQuerySelector>();
register(name: string, handler: CustomQueryHandler): void {
if (!handler.queryOne && handler.queryAll) {
const querySelectorAll = handler.queryAll;
handler.queryOne = (node, selector) => {
for (const result of querySelectorAll(node, selector)) {
return result;
}
return null;
};
} else if (handler.queryOne && !handler.queryAll) {
const querySelector = handler.queryOne;
handler.queryAll = (node, selector) => {
const result = querySelector(node, selector);
return result ? [result] : [];
};
} else if (!handler.queryOne || !handler.queryAll) {
throw new Error('At least one query method must be defined.');
}
this.#selectors.set(name, {
querySelector: handler.queryOne,
querySelectorAll: handler.queryAll!,
});
}
unregister(name: string): void {
this.#selectors.delete(name);
}
get(name: string): CustomQuerySelector | undefined {
return this.#selectors.get(name);
}
clear() {
this.#selectors.clear();
}
}
/**
* @internal
*/
export const customQuerySelectors = new CustomQuerySelectorRegistry();

View File

@@ -0,0 +1,294 @@
/**
* @license
* Copyright 2023 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import type {AwaitableIterable} from '../common/types.js';
import {AsyncIterableUtil} from '../util/AsyncIterableUtil.js';
import {ariaQuerySelectorAll} from './ARIAQuerySelector.js';
import {customQuerySelectors} from './CustomQuerySelector.js';
import {textQuerySelectorAll} from './TextQuerySelector.js';
import {pierce, pierceAll} from './util.js';
import {xpathQuerySelectorAll} from './XPathQuerySelector.js';
const IDENT_TOKEN_START = /[-\w\P{ASCII}*]/u;
/**
* @internal
*/
export type CSSSelector = string;
/**
* @internal
*/
export interface PPseudoSelector {
name: string;
value: string;
}
/**
* @internal
*/
export const enum PCombinator {
Descendent = '>>>',
Child = '>>>>',
}
/**
* @internal
*/
export type CompoundPSelector = Array<CSSSelector | PPseudoSelector>;
/**
* @internal
*/
export type ComplexPSelector = Array<CompoundPSelector | PCombinator>;
/**
* @internal
*/
export type ComplexPSelectorList = ComplexPSelector[];
interface QueryableNode extends Node {
querySelectorAll: typeof Document.prototype.querySelectorAll;
}
const isQueryableNode = (node: Node): node is QueryableNode => {
return 'querySelectorAll' in node;
};
class PQueryEngine {
#complexSelector: ComplexPSelector;
#compoundSelector: CompoundPSelector = [];
#selector: CSSSelector | PPseudoSelector | undefined = undefined;
elements: AwaitableIterable<Node>;
constructor(element: Node, complexSelector: ComplexPSelector) {
this.elements = [element];
this.#complexSelector = complexSelector;
this.#next();
}
async run(): Promise<void> {
if (typeof this.#selector === 'string') {
switch (this.#selector.trimStart()) {
case ':scope':
// `:scope` has some special behavior depending on the node. It always
// represents the current node within a compound selector, but by
// itself, it depends on the node. For example, Document is
// represented by `<html>`, but any HTMLElement is not represented by
// itself (i.e. `null`). This can be troublesome if our combinators
// are used right after so we treat this selector specially.
this.#next();
break;
}
}
for (; this.#selector !== undefined; this.#next()) {
const selector = this.#selector;
if (typeof selector === 'string') {
// The regular expression tests if the selector is a type/universal
// selector. Any other case means we want to apply the selector onto
// the element itself (e.g. `element.class`, `element>div`,
// `element:hover`, etc.).
if (selector[0] && IDENT_TOKEN_START.test(selector[0])) {
this.elements = AsyncIterableUtil.flatMap(
this.elements,
async function* (element) {
if (isQueryableNode(element)) {
yield* element.querySelectorAll(selector);
}
},
);
} else {
this.elements = AsyncIterableUtil.flatMap(
this.elements,
async function* (element) {
if (!element.parentElement) {
if (!isQueryableNode(element)) {
return;
}
yield* element.querySelectorAll(selector);
return;
}
let index = 0;
for (const child of element.parentElement.children) {
++index;
if (child === element) {
break;
}
}
yield* element.parentElement.querySelectorAll(
`:scope>:nth-child(${index})${selector}`,
);
},
);
}
} else {
this.elements = AsyncIterableUtil.flatMap(
this.elements,
async function* (element) {
switch (selector.name) {
case 'text':
yield* textQuerySelectorAll(element, selector.value);
break;
case 'xpath':
yield* xpathQuerySelectorAll(element, selector.value);
break;
case 'aria':
yield* ariaQuerySelectorAll(element, selector.value);
break;
default:
const querySelector = customQuerySelectors.get(selector.name);
if (!querySelector) {
throw new Error(`Unknown selector type: ${selector.name}`);
}
yield* querySelector.querySelectorAll(element, selector.value);
}
},
);
}
}
}
#next() {
if (this.#compoundSelector.length !== 0) {
this.#selector = this.#compoundSelector.shift();
return;
}
if (this.#complexSelector.length === 0) {
this.#selector = undefined;
return;
}
const selector = this.#complexSelector.shift();
switch (selector) {
case PCombinator.Child: {
this.elements = AsyncIterableUtil.flatMap(this.elements, pierce);
this.#next();
break;
}
case PCombinator.Descendent: {
this.elements = AsyncIterableUtil.flatMap(this.elements, pierceAll);
this.#next();
break;
}
default:
this.#compoundSelector = selector as CompoundPSelector;
this.#next();
break;
}
}
}
class DepthCalculator {
#cache = new WeakMap<Node, number[]>();
calculate(node: Node | null, depth: number[] = []): number[] {
if (node === null) {
return depth;
}
if (node instanceof ShadowRoot) {
node = node.host;
}
const cachedDepth = this.#cache.get(node);
if (cachedDepth) {
return [...cachedDepth, ...depth];
}
let index = 0;
for (
let prevSibling = node.previousSibling;
prevSibling;
prevSibling = prevSibling.previousSibling
) {
++index;
}
const value = this.calculate(node.parentNode, [index]);
this.#cache.set(node, value);
return [...value, ...depth];
}
}
const compareDepths = (a: number[], b: number[]): -1 | 0 | 1 => {
if (a.length + b.length === 0) {
return 0;
}
const [i = -1, ...otherA] = a;
const [j = -1, ...otherB] = b;
if (i === j) {
return compareDepths(otherA, otherB);
}
return i < j ? -1 : 1;
};
const domSort = async function* (elements: AwaitableIterable<Node>) {
const results = new Set<Node>();
for await (const element of elements) {
results.add(element);
}
const calculator = new DepthCalculator();
yield* [...results.values()]
.map(result => {
return [result, calculator.calculate(result)] as const;
})
.sort(([, a], [, b]) => {
return compareDepths(a, b);
})
.map(([result]) => {
return result;
});
};
/**
* Queries the given node for all nodes matching the given text selector.
*
* @internal
*/
export const pQuerySelectorAll = function (
root: Node,
selector: string,
): AwaitableIterable<Node> {
const selectors = JSON.parse(selector) as ComplexPSelectorList;
// If there are any empty elements, then this implies the selector has
// contiguous combinators (e.g. `>>> >>>>`) or starts/ends with one which we
// treat as illegal, similar to existing behavior.
if (
selectors.some(parts => {
let i = 0;
return parts.some(parts => {
if (typeof parts === 'string') {
++i;
} else {
i = 0;
}
return i > 1;
});
})
) {
throw new Error('Multiple deep combinators found in sequence.');
}
return domSort(
AsyncIterableUtil.flatMap(selectors, selectorParts => {
const query = new PQueryEngine(root, selectorParts);
void query.run();
return query.elements;
}),
);
};
/**
* Queries the given node for all nodes matching the given text selector.
*
* @internal
*/
export const pQuerySelector = async function (
root: Node,
selector: string,
): Promise<Node | null> {
for await (const element of pQuerySelectorAll(root, selector)) {
return element;
}
return null;
};

View File

@@ -0,0 +1,65 @@
/**
* @license
* Copyright 2022 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
*/
export const pierceQuerySelector = (
root: Node,
selector: string,
): Element | null => {
let found: Node | null = null;
const search = (root: Node) => {
const iter = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
do {
const currentNode = iter.currentNode as Element;
if (currentNode.shadowRoot) {
search(currentNode.shadowRoot);
}
if (currentNode instanceof ShadowRoot) {
continue;
}
if (currentNode !== root && !found && currentNode.matches(selector)) {
found = currentNode;
}
} while (!found && iter.nextNode());
};
if (root instanceof Document) {
root = root.documentElement;
}
search(root);
return found;
};
/**
* @internal
*/
export const pierceQuerySelectorAll = (
element: Node,
selector: string,
): Element[] => {
const result: Element[] = [];
const collect = (root: Node) => {
const iter = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
do {
const currentNode = iter.currentNode as Element;
if (currentNode.shadowRoot) {
collect(currentNode.shadowRoot);
}
if (currentNode instanceof ShadowRoot) {
continue;
}
if (currentNode !== root && currentNode.matches(selector)) {
result.push(currentNode);
}
} while (iter.nextNode());
};
if (element instanceof Document) {
element = element.documentElement;
}
collect(element);
return result;
};

168
node_modules/puppeteer-core/src/injected/Poller.ts generated vendored Normal file
View File

@@ -0,0 +1,168 @@
/**
* @license
* Copyright 2022 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import {assert} from '../util/assert.js';
import {Deferred} from '../util/Deferred.js';
/**
* @internal
*/
export interface Poller<T> {
start(): Promise<void>;
stop(): Promise<void>;
result(): Promise<T>;
}
/**
* @internal
*/
export class MutationPoller<T> implements Poller<T> {
#fn: () => Promise<T>;
#root: Node;
#observer?: MutationObserver;
#deferred?: Deferred<T>;
constructor(fn: () => Promise<T>, root: Node) {
this.#fn = fn;
this.#root = root;
}
async start(): Promise<void> {
const deferred = (this.#deferred = Deferred.create<T>());
const result = await this.#fn();
if (result) {
deferred.resolve(result);
return;
}
this.#observer = new MutationObserver(async () => {
const result = await this.#fn();
if (!result) {
return;
}
deferred.resolve(result);
await this.stop();
});
this.#observer.observe(this.#root, {
childList: true,
subtree: true,
attributes: true,
});
}
async stop(): Promise<void> {
assert(this.#deferred, 'Polling never started.');
if (!this.#deferred.finished()) {
this.#deferred.reject(new Error('Polling stopped'));
}
if (this.#observer) {
this.#observer.disconnect();
this.#observer = undefined;
}
}
result(): Promise<T> {
assert(this.#deferred, 'Polling never started.');
return this.#deferred.valueOrThrow();
}
}
/**
* @internal
*/
export class RAFPoller<T> implements Poller<T> {
#fn: () => Promise<T>;
#deferred?: Deferred<T>;
constructor(fn: () => Promise<T>) {
this.#fn = fn;
}
async start(): Promise<void> {
const deferred = (this.#deferred = Deferred.create<T>());
const result = await this.#fn();
if (result) {
deferred.resolve(result);
return;
}
const poll = async () => {
if (deferred.finished()) {
return;
}
const result = await this.#fn();
if (!result) {
window.requestAnimationFrame(poll);
return;
}
deferred.resolve(result);
await this.stop();
};
window.requestAnimationFrame(poll);
}
async stop(): Promise<void> {
assert(this.#deferred, 'Polling never started.');
if (!this.#deferred.finished()) {
this.#deferred.reject(new Error('Polling stopped'));
}
}
result(): Promise<T> {
assert(this.#deferred, 'Polling never started.');
return this.#deferred.valueOrThrow();
}
}
/**
* @internal
*/
export class IntervalPoller<T> implements Poller<T> {
#fn: () => Promise<T>;
#ms: number;
#interval?: NodeJS.Timeout;
#deferred?: Deferred<T>;
constructor(fn: () => Promise<T>, ms: number) {
this.#fn = fn;
this.#ms = ms;
}
async start(): Promise<void> {
const deferred = (this.#deferred = Deferred.create<T>());
const result = await this.#fn();
if (result) {
deferred.resolve(result);
return;
}
this.#interval = setInterval(async () => {
const result = await this.#fn();
if (!result) {
return;
}
deferred.resolve(result);
await this.stop();
}, this.#ms);
}
async stop(): Promise<void> {
assert(this.#deferred, 'Polling never started.');
if (!this.#deferred.finished()) {
this.#deferred.reject(new Error('Polling stopped'));
}
if (this.#interval) {
clearInterval(this.#interval);
this.#interval = undefined;
}
}
result(): Promise<T> {
assert(this.#deferred, 'Polling never started.');
return this.#deferred.valueOrThrow();
}
}

5
node_modules/puppeteer-core/src/injected/README.md generated vendored Normal file
View File

@@ -0,0 +1,5 @@
# Injected
This folder contains code that is injected into every Puppeteer execution context. Each file is transpiled using esbuild into a script in `src/generated` which is then imported into server code.
See `tools/generate_sources.ts` for more information.

146
node_modules/puppeteer-core/src/injected/TextContent.ts generated vendored Normal file
View File

@@ -0,0 +1,146 @@
/**
* @license
* Copyright 2022 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
interface NonTrivialValueNode extends Node {
value: string;
}
const TRIVIAL_VALUE_INPUT_TYPES = new Set(['checkbox', 'image', 'radio']);
/**
* Determines if the node has a non-trivial value property.
*
* @internal
*/
const isNonTrivialValueNode = (node: Node): node is NonTrivialValueNode => {
if (node instanceof HTMLSelectElement) {
return true;
}
if (node instanceof HTMLTextAreaElement) {
return true;
}
if (
node instanceof HTMLInputElement &&
!TRIVIAL_VALUE_INPUT_TYPES.has(node.type)
) {
return true;
}
return false;
};
const UNSUITABLE_NODE_NAMES = new Set(['SCRIPT', 'STYLE']);
/**
* Determines whether a given node is suitable for text matching.
*
* @internal
*/
export const isSuitableNodeForTextMatching = (node: Node): boolean => {
return (
!UNSUITABLE_NODE_NAMES.has(node.nodeName) && !document.head?.contains(node)
);
};
/**
* @internal
*/
export interface TextContent {
// Contains the full text of the node.
full: string;
// Contains the text immediately beneath the node.
immediate: string[];
}
/**
* Maps {@link Node}s to their computed {@link TextContent}.
*/
const textContentCache = new WeakMap<Node, TextContent>();
const eraseFromCache = (node: Node | null) => {
while (node) {
textContentCache.delete(node);
if (node instanceof ShadowRoot) {
node = node.host;
} else {
node = node.parentNode;
}
}
};
/**
* Erases the cache when the tree has mutated text.
*/
const observedNodes = new WeakSet<Node>();
const textChangeObserver = new MutationObserver(mutations => {
for (const mutation of mutations) {
eraseFromCache(mutation.target);
}
});
/**
* Builds the text content of a node using some custom logic.
*
* @remarks
* The primary reason this function exists is due to {@link ShadowRoot}s not having
* text content.
*
* @internal
*/
export const createTextContent = (root: Node): TextContent => {
let value = textContentCache.get(root);
if (value) {
return value;
}
value = {full: '', immediate: []};
if (!isSuitableNodeForTextMatching(root)) {
return value;
}
let currentImmediate = '';
if (isNonTrivialValueNode(root)) {
value.full = root.value;
value.immediate.push(root.value);
root.addEventListener(
'input',
event => {
eraseFromCache(event.target as HTMLInputElement);
},
{once: true, capture: true},
);
} else {
for (let child = root.firstChild; child; child = child.nextSibling) {
if (child.nodeType === Node.TEXT_NODE) {
value.full += child.nodeValue ?? '';
currentImmediate += child.nodeValue ?? '';
continue;
}
if (currentImmediate) {
value.immediate.push(currentImmediate);
}
currentImmediate = '';
if (child.nodeType === Node.ELEMENT_NODE) {
value.full += createTextContent(child).full;
}
}
if (currentImmediate) {
value.immediate.push(currentImmediate);
}
if (root instanceof Element && root.shadowRoot) {
value.full += createTextContent(root.shadowRoot).full;
}
if (!observedNodes.has(root)) {
textChangeObserver.observe(root, {
childList: true,
characterData: true,
subtree: true,
});
observedNodes.add(root);
}
}
textContentCache.set(root, value);
return value;
};

View File

@@ -0,0 +1,46 @@
/**
* @license
* Copyright 2022 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import {
createTextContent,
isSuitableNodeForTextMatching,
} from './TextContent.js';
/**
* Queries the given node for all nodes matching the given text selector.
*
* @internal
*/
export const textQuerySelectorAll = function* (
root: Node,
selector: string,
): Generator<Element> {
let yielded = false;
for (const node of root.childNodes) {
if (node instanceof Element && isSuitableNodeForTextMatching(node)) {
let matches: Generator<Element, boolean>;
if (!node.shadowRoot) {
matches = textQuerySelectorAll(node, selector);
} else {
matches = textQuerySelectorAll(node.shadowRoot, selector);
}
for (const match of matches) {
yield match;
yielded = true;
}
}
}
if (yielded) {
return;
}
if (root instanceof Element && isSuitableNodeForTextMatching(root)) {
const textContent = createTextContent(root);
if (textContent.full.includes(selector)) {
yield root;
}
}
};

View File

@@ -0,0 +1,39 @@
/**
* @license
* Copyright 2022 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
*/
export const xpathQuerySelectorAll = function* (
root: Node,
selector: string,
maxResults = -1,
): Iterable<Node> {
const doc = root.ownerDocument || document;
const iterator = doc.evaluate(
selector,
root,
null,
XPathResult.ORDERED_NODE_ITERATOR_TYPE,
);
const items = [];
let item;
// Read all results upfront to avoid
// https://stackoverflow.com/questions/48235278/xpath-error-the-document-has-mutated-since-the-result-was-returned.
while ((item = iterator.iterateNext())) {
items.push(item);
if (maxResults && items.length === maxResults) {
break;
}
}
for (let i = 0; i < items.length; i++) {
item = items[i];
yield item as Node;
items[i] = null;
}
};

53
node_modules/puppeteer-core/src/injected/injected.ts generated vendored Normal file
View File

@@ -0,0 +1,53 @@
/**
* @license
* Copyright 2022 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import {Deferred} from '../util/Deferred.js';
import {createFunction} from '../util/Function.js';
import * as ARIAQuerySelector from './ARIAQuerySelector.js';
import * as CSSSelector from './CSSSelector.js';
import * as CustomQuerySelectors from './CustomQuerySelector.js';
import * as PierceQuerySelector from './PierceQuerySelector.js';
import {IntervalPoller, MutationPoller, RAFPoller} from './Poller.js';
import * as PQuerySelector from './PQuerySelector.js';
import {
createTextContent,
isSuitableNodeForTextMatching,
} from './TextContent.js';
import * as TextQuerySelector from './TextQuerySelector.js';
import * as util from './util.js';
import * as XPathQuerySelector from './XPathQuerySelector.js';
/**
* @internal
*/
const PuppeteerUtil = Object.freeze({
...ARIAQuerySelector,
...CustomQuerySelectors,
...PierceQuerySelector,
...PQuerySelector,
...TextQuerySelector,
...util,
...XPathQuerySelector,
...CSSSelector,
Deferred,
createFunction,
createTextContent,
IntervalPoller,
isSuitableNodeForTextMatching,
MutationPoller,
RAFPoller,
});
/**
* @internal
*/
export type PuppeteerInjectedUtil = typeof PuppeteerUtil;
/**
* @internal
*/
export default PuppeteerUtil;

72
node_modules/puppeteer-core/src/injected/util.ts generated vendored Normal file
View File

@@ -0,0 +1,72 @@
/**
* @license
* Copyright 2024 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
const HIDDEN_VISIBILITY_VALUES = ['hidden', 'collapse'];
/**
* @internal
*/
export const checkVisibility = (
node: Node | null,
visible?: boolean,
): Node | boolean => {
if (!node) {
return visible === false;
}
if (visible === undefined) {
return node;
}
const element = (
node.nodeType === Node.TEXT_NODE ? node.parentElement : node
) as Element;
const style = window.getComputedStyle(element);
const isVisible =
style &&
!HIDDEN_VISIBILITY_VALUES.includes(style.visibility) &&
!isBoundingBoxEmpty(element);
return visible === isVisible ? node : false;
};
function isBoundingBoxEmpty(element: Element): boolean {
const rect = element.getBoundingClientRect();
return rect.width === 0 || rect.height === 0;
}
const hasShadowRoot = (node: Node): node is Node & {shadowRoot: ShadowRoot} => {
return 'shadowRoot' in node && node.shadowRoot instanceof ShadowRoot;
};
/**
* @internal
*/
export function* pierce(root: Node): IterableIterator<Node | ShadowRoot> {
if (hasShadowRoot(root)) {
yield root.shadowRoot;
} else {
yield root;
}
}
/**
* @internal
*/
export function* pierceAll(root: Node): IterableIterator<Node | ShadowRoot> {
root = pierce(root).next().value;
yield root;
const walkers = [document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT)];
for (const walker of walkers) {
let node: Element | null;
while ((node = walker.nextNode() as Element | null)) {
if (!node.shadowRoot) {
continue;
}
yield node.shadowRoot;
walkers.push(
document.createTreeWalker(node.shadowRoot, NodeFilter.SHOW_ELEMENT),
);
}
}
}