mirror of
https://github.com/web-infra-dev/midscene.git
synced 2025-12-29 16:09:35 +00:00
fix(keybord): resolve selectAll/Copy/Paste event (#384)
* fix(keybord): resolve selectAll/Copy/Paste event * chore: fix e2e test * chore: upgrade deps * chore: add keybord commands ref
This commit is contained in:
parent
f7c583cdf5
commit
8e69f8d151
@ -23,7 +23,7 @@
|
||||
"dependencies": {
|
||||
"@midscene/core": "workspace:*",
|
||||
"@midscene/web": "workspace:*",
|
||||
"puppeteer": "23.0.2",
|
||||
"puppeteer": "24.2.0",
|
||||
"http-server": "14.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@ -144,7 +144,7 @@
|
||||
"js-sha256": "0.11.0",
|
||||
"js-yaml": "4.1.0",
|
||||
"playwright": "1.44.1",
|
||||
"puppeteer": "23.0.2",
|
||||
"puppeteer": "24.2.0",
|
||||
"typescript": "~5.0.4",
|
||||
"vitest": "^1.6.0",
|
||||
"webdriverio": "9.0.6"
|
||||
|
||||
@ -79,7 +79,11 @@ export class Page implements AbstractPage {
|
||||
get keyboard() {
|
||||
return {
|
||||
type: (text: string) => this.keyboardType(text),
|
||||
press: (key: WebKeyInput) => this.keyboardPress(key),
|
||||
press: (
|
||||
action:
|
||||
| { key: WebKeyInput; command?: string }
|
||||
| { key: WebKeyInput; command?: string }[],
|
||||
) => this.keyboardPressAction(action),
|
||||
};
|
||||
}
|
||||
|
||||
@ -226,6 +230,38 @@ export class Page implements AbstractPage {
|
||||
]);
|
||||
}
|
||||
|
||||
private async keyboardPressAction(
|
||||
action:
|
||||
| { key: WebKeyInput; command?: string }
|
||||
| { key: WebKeyInput; command?: string }[],
|
||||
): Promise<void> {
|
||||
if (Array.isArray(action)) {
|
||||
for (const act of action) {
|
||||
await this.browser.performActions([
|
||||
{
|
||||
type: 'key',
|
||||
id: 'keyboard',
|
||||
actions: [
|
||||
{ type: 'keyDown', value: act.key },
|
||||
{ type: 'keyUp', value: act.key },
|
||||
],
|
||||
},
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
await this.browser.performActions([
|
||||
{
|
||||
type: 'key',
|
||||
id: 'keyboard',
|
||||
actions: [
|
||||
{ type: 'keyDown', value: action.key },
|
||||
{ type: 'keyUp', value: action.key },
|
||||
],
|
||||
},
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private async mouseClick(
|
||||
x: number,
|
||||
y: number,
|
||||
|
||||
@ -389,7 +389,9 @@ export default class ChromeExtensionProxyPage implements AbstractPage {
|
||||
|
||||
await sleep(100);
|
||||
|
||||
await this.keyboard.press('Backspace');
|
||||
await this.keyboard.press({
|
||||
key: 'Backspace',
|
||||
});
|
||||
}
|
||||
|
||||
mouse = {
|
||||
@ -465,16 +467,21 @@ export default class ChromeExtensionProxyPage implements AbstractPage {
|
||||
});
|
||||
await cdpKeyboard.type(text, { delay: 0 });
|
||||
},
|
||||
press: async (key: WebKeyInput | WebKeyInput[]) => {
|
||||
press: async (
|
||||
action:
|
||||
| { key: WebKeyInput; command?: string }
|
||||
| { key: WebKeyInput; command?: string }[],
|
||||
) => {
|
||||
const cdpKeyboard = new CdpKeyboard({
|
||||
send: this.sendCommandToDebugger.bind(this),
|
||||
});
|
||||
const keys = Array.isArray(key) ? key : [key];
|
||||
const keys = Array.isArray(action) ? action : [action];
|
||||
for (const k of keys) {
|
||||
await cdpKeyboard.down(k);
|
||||
const commands = k.command ? [k.command] : [];
|
||||
await cdpKeyboard.down(k.key, { commands });
|
||||
}
|
||||
for (const k of [...keys].reverse()) {
|
||||
await cdpKeyboard.up(k);
|
||||
await cdpKeyboard.up(k.key);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@ -37,6 +37,7 @@ import type { ElementInfo } from '@midscene/shared/extractor';
|
||||
import type { KeyInput } from 'puppeteer';
|
||||
import type { WebElementInfo } from '../web-element';
|
||||
import { TaskCache } from './task-cache';
|
||||
import { getKeyCommands } from './ui-utils';
|
||||
import type { WebUIContext } from './utils';
|
||||
|
||||
interface ExecutionResult<OutputType = any> {
|
||||
@ -296,8 +297,9 @@ export class PageTaskExecutor {
|
||||
thought: plan.thought,
|
||||
locate: plan.locate,
|
||||
executor: async (taskParam) => {
|
||||
assert(taskParam?.value, 'No key to press');
|
||||
await this.page.keyboard.press(taskParam.value as KeyInput);
|
||||
const keys = getKeyCommands(taskParam.value);
|
||||
|
||||
await this.page.keyboard.press(keys);
|
||||
},
|
||||
};
|
||||
tasks.push(taskActionKeyboardPress);
|
||||
@ -767,7 +769,7 @@ export class PageTaskExecutor {
|
||||
const cacheGroup = this.taskCache.getCacheGroupByPrompt(userPrompt);
|
||||
const isCompleted = false;
|
||||
let currentActionNumber = 0;
|
||||
const maxActionNumber = 20;
|
||||
const maxActionNumber = 40;
|
||||
|
||||
while (!isCompleted && currentActionNumber < maxActionNumber) {
|
||||
currentActionNumber++;
|
||||
|
||||
@ -13,6 +13,29 @@ export function typeStr(task: ExecutionTask) {
|
||||
return task.subType ? `${task.type} / ${task.subType || ''}` : task.type;
|
||||
}
|
||||
|
||||
export function getKeyCommands(
|
||||
value: string | string[],
|
||||
): Array<{ key: string; command?: string }> {
|
||||
// Ensure value is an array of keys
|
||||
const keys = Array.isArray(value) ? value : [value];
|
||||
|
||||
// Process each key to attach a corresponding command if needed, based on the presence of 'Meta' or 'Control' in the keys array.
|
||||
// ref: https://github.com/puppeteer/puppeteer/pull/9357/files#diff-32cf475237b000f980eb214a0a823e45a902bddb7d2426d677cae96397aa0ae4R94
|
||||
return keys.reduce((acc: Array<{ key: string; command?: string }>, k) => {
|
||||
const includeMeta = keys.includes('Meta') || keys.includes('Control');
|
||||
if (includeMeta && (k === 'a' || k === 'A')) {
|
||||
return acc.concat([{ key: k, command: 'SelectAll' }]);
|
||||
}
|
||||
if (includeMeta && (k === 'c' || k === 'C')) {
|
||||
return acc.concat([{ key: k, command: 'Copy' }]);
|
||||
}
|
||||
if (includeMeta && (k === 'v' || k === 'V')) {
|
||||
return acc.concat([{ key: k, command: 'Paste' }]);
|
||||
}
|
||||
return acc.concat([{ key: k }]);
|
||||
}, []);
|
||||
}
|
||||
|
||||
export function paramStr(task: ExecutionTask) {
|
||||
let value: string | undefined | object;
|
||||
if (task.type === 'Planning') {
|
||||
|
||||
@ -21,7 +21,11 @@ export interface MouseAction {
|
||||
|
||||
export interface KeyboardAction {
|
||||
type: (text: string) => Promise<void>;
|
||||
press: (key: WebKeyInput) => Promise<void>;
|
||||
press: (
|
||||
action:
|
||||
| { key: WebKeyInput; command?: string }
|
||||
| { key: WebKeyInput; command?: string }[],
|
||||
) => Promise<void>;
|
||||
}
|
||||
|
||||
export abstract class AbstractPage {
|
||||
@ -52,7 +56,11 @@ export abstract class AbstractPage {
|
||||
get keyboard(): KeyboardAction {
|
||||
return {
|
||||
type: async (text: string) => {},
|
||||
press: async (key: WebKeyInput) => {},
|
||||
press: async (
|
||||
action:
|
||||
| { key: WebKeyInput; command?: string }
|
||||
| { key: WebKeyInput; command?: string }[],
|
||||
) => {},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -139,14 +139,19 @@ export class Page<
|
||||
return {
|
||||
type: async (text: string) =>
|
||||
this.underlyingPage.keyboard.type(text, { delay: 80 }),
|
||||
press: async (key: WebKeyInput | WebKeyInput[]) => {
|
||||
const keys = Array.isArray(key) ? key : [key];
|
||||
for (const key of keys) {
|
||||
await this.underlyingPage.keyboard.down(key);
|
||||
}
|
||||
|
||||
for (const key of [...keys].reverse()) {
|
||||
await this.underlyingPage.keyboard.up(key);
|
||||
press: async (
|
||||
action:
|
||||
| { key: WebKeyInput; command?: string }
|
||||
| { key: WebKeyInput; command?: string }[],
|
||||
) => {
|
||||
const keys = Array.isArray(action) ? action : [action];
|
||||
for (const k of keys) {
|
||||
const commands = k.command ? [k.command] : [];
|
||||
await this.underlyingPage.keyboard.down(k.key, { commands });
|
||||
}
|
||||
for (const k of [...keys].reverse()) {
|
||||
await this.underlyingPage.keyboard.up(k.key);
|
||||
}
|
||||
},
|
||||
down: async (key: WebKeyInput) => {
|
||||
@ -184,7 +189,7 @@ export class Page<
|
||||
await this.underlyingPage.keyboard.up('Control');
|
||||
}
|
||||
await sleep(100);
|
||||
await this.keyboard.press('Backspace');
|
||||
await this.keyboard.press([{ key: 'Backspace' }]);
|
||||
}
|
||||
|
||||
private async moveToPoint(point?: Point): Promise<void> {
|
||||
|
||||
@ -6,23 +6,20 @@ import { sleep } from '@midscene/core/utils';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
vi.setConfig({
|
||||
testTimeout: 40 * 60 * 1000,
|
||||
testTimeout: 300 * 1000,
|
||||
});
|
||||
|
||||
describe.skipIf(!process.env.BRIDGE_MODE)('drag event', () => {
|
||||
it('agent in cli side, current tab', async () => {
|
||||
const agent = new AgentOverChromeBridge({
|
||||
cacheId: 'star-midscene-github',
|
||||
});
|
||||
await agent.connectCurrentTab({
|
||||
trackingActiveTab: true,
|
||||
// cacheId: 'finish-form-and-submit',
|
||||
});
|
||||
await agent.connectCurrentTab();
|
||||
|
||||
await agent.aiAction(
|
||||
'Search midscene github and complete the star like or cancel',
|
||||
);
|
||||
await sleep(2000);
|
||||
|
||||
await sleep(3000);
|
||||
await agent.aiAction('输入 "Happy Birthday,只需要输入即可"');
|
||||
await agent.aiQuery('输入框内容:Array<string>');
|
||||
|
||||
await agent.destroy();
|
||||
});
|
||||
|
||||
@ -13,36 +13,36 @@ test('ai todo', async ({ ai, aiQuery }) => {
|
||||
}
|
||||
|
||||
await ai('Enter "Happy Birthday" in the task box');
|
||||
// await ai('Enter "Learn JS today"in the task box, then press Enter to create');
|
||||
await ai('Enter "Learn JS today"in the task box, then press Enter to create');
|
||||
|
||||
// await ai(
|
||||
// 'Enter "Learn Rust tomorrow" in the task box, then press Enter to create',
|
||||
// );
|
||||
// await ai(
|
||||
// 'Enter "Learning AI the day after tomorrow" in the task box, then press Enter to create',
|
||||
// );
|
||||
await ai(
|
||||
'Enter "Learn Rust tomorrow" in the task box, then press Enter to create',
|
||||
);
|
||||
await ai(
|
||||
'Enter "Learning AI the day after tomorrow" in the task box, then press Enter to create',
|
||||
);
|
||||
|
||||
// const allTaskList = await aiQuery<string[]>('string[], tasks in the list');
|
||||
// console.log('allTaskList', allTaskList);
|
||||
// // expect(allTaskList.length).toBe(3);
|
||||
// expect(allTaskList).toContain('Learn JS today');
|
||||
// expect(allTaskList).toContain('Learn Rust tomorrow');
|
||||
// expect(allTaskList).toContain('Learning AI the day after tomorrow');
|
||||
const allTaskList = await aiQuery<string[]>('string[], tasks in the list');
|
||||
console.log('allTaskList', allTaskList);
|
||||
// expect(allTaskList.length).toBe(3);
|
||||
expect(allTaskList).toContain('Learn JS today');
|
||||
expect(allTaskList).toContain('Learn Rust tomorrow');
|
||||
expect(allTaskList).toContain('Learning AI the day after tomorrow');
|
||||
|
||||
// await ai('Move your mouse over the second item in the task list');
|
||||
// await ai('Click the delete button to the right of the second task');
|
||||
// await ai('Click the checkbox next to the second task');
|
||||
// await ai('Click the "completed" Status button below the task list');
|
||||
await ai('Move your mouse over the second item in the task list');
|
||||
await ai('Click the delete button to the right of the second task');
|
||||
await ai('Click the checkbox next to the second task');
|
||||
await ai('Click the "completed" Status button below the task list');
|
||||
|
||||
// const taskList = await aiQuery<string[]>(
|
||||
// 'string[], Extract all task names from the list',
|
||||
// );
|
||||
// expect(taskList.length).toBe(1);
|
||||
// expect(taskList[0]).toBe('Learning AI the day after tomorrow');
|
||||
const taskList = await aiQuery<string[]>(
|
||||
'string[], Extract all task names from the list',
|
||||
);
|
||||
expect(taskList.length).toBe(1);
|
||||
expect(taskList[0]).toBe('Learning AI the day after tomorrow');
|
||||
|
||||
// const placeholder = await ai(
|
||||
// 'string, return the placeholder text in the input box',
|
||||
// { type: 'query' },
|
||||
// );
|
||||
// expect(placeholder).toBe('What needs to be done?');
|
||||
const placeholder = await ai(
|
||||
'string, return the placeholder text in the input box',
|
||||
{ type: 'query' },
|
||||
);
|
||||
expect(placeholder).toBe('What needs to be done?');
|
||||
});
|
||||
|
||||
@ -15,6 +15,13 @@ describe('puppeteer integration', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('input and clear text', async () => {
|
||||
const { originPage, reset } = await launchPage('https://www.google.com/');
|
||||
resetFn = reset;
|
||||
const agent = new PuppeteerAgent(originPage);
|
||||
await agent.aiAction('Enter "happy birthday" and select Delete all');
|
||||
});
|
||||
|
||||
it('Sauce Demo, agent with yaml script', async () => {
|
||||
const { originPage, reset } = await launchPage('https://www.bing.com/');
|
||||
resetFn = reset;
|
||||
@ -22,17 +29,17 @@ describe('puppeteer integration', () => {
|
||||
await sleep(3000);
|
||||
const { result } = await agent.runYaml(
|
||||
`
|
||||
tasks:
|
||||
- name: search weather
|
||||
flow:
|
||||
- ai: input 'weather today' in input box, click search button
|
||||
- sleep: 3000
|
||||
tasks:
|
||||
- name: search weather
|
||||
flow:
|
||||
- ai: input 'weather today' in input box, click search button
|
||||
- sleep: 3000
|
||||
|
||||
- name: query weather
|
||||
flow:
|
||||
- aiQuery: "the result shows the weather info, {description: string}"
|
||||
name: weather
|
||||
`,
|
||||
- name: query weather
|
||||
flow:
|
||||
- aiQuery: "the result shows the weather info, {description: string}"
|
||||
name: weather
|
||||
`,
|
||||
);
|
||||
|
||||
expect(result.weather.description).toBeDefined();
|
||||
@ -46,11 +53,11 @@ tasks:
|
||||
try {
|
||||
await agent.runYaml(
|
||||
`
|
||||
tasks:
|
||||
- name: search weather
|
||||
flow:
|
||||
- aiAssert: the result shows food delivery service
|
||||
`,
|
||||
tasks:
|
||||
- name: search weather
|
||||
flow:
|
||||
- aiAssert: the result shows food delivery service
|
||||
`,
|
||||
);
|
||||
} catch (e: any) {
|
||||
errorMsg = e.message;
|
||||
@ -66,22 +73,22 @@ tasks:
|
||||
const agent = new PuppeteerAgent(originPage);
|
||||
const { result } = await agent.runYaml(
|
||||
`
|
||||
tasks:
|
||||
- name: search weather
|
||||
flow:
|
||||
- ai: input 'weather today' in input box, click search button
|
||||
- sleep: 3000
|
||||
tasks:
|
||||
- name: search weather
|
||||
flow:
|
||||
- ai: input 'weather today' in input box, click search button
|
||||
- sleep: 3000
|
||||
|
||||
- name: query weather
|
||||
flow:
|
||||
- aiQuery: "the result shows the weather info, {description: string}"
|
||||
name: weather
|
||||
- name: query weather
|
||||
flow:
|
||||
- aiQuery: "the result shows the weather info, {description: string}"
|
||||
name: weather
|
||||
|
||||
- name: error
|
||||
continueOnError: true
|
||||
flow:
|
||||
- aiAssert: the result shows food delivery service
|
||||
`,
|
||||
- name: error
|
||||
continueOnError: true
|
||||
flow:
|
||||
- aiAssert: the result shows food delivery service
|
||||
`,
|
||||
);
|
||||
|
||||
expect(result.weather.description).toBeDefined();
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { getKeyCommands } from '@/common/ui-utils';
|
||||
import { getCurrentExecutionFile } from '@/common/utils';
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
|
||||
@ -28,3 +29,43 @@ describe('TaskCache', () => {
|
||||
expect(currentExecutionFile).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getKeyCommands', () => {
|
||||
it('should return a single key without command when no meta or control key is provided', () => {
|
||||
const result = getKeyCommands('a');
|
||||
expect(result).toEqual([{ key: 'a' }]);
|
||||
});
|
||||
|
||||
it('should work with array input without meta key', () => {
|
||||
const result = getKeyCommands(['b', 'd']);
|
||||
expect(result).toEqual([{ key: 'b' }, { key: 'd' }]);
|
||||
});
|
||||
|
||||
it('should attach "SelectAll" command when "Meta" is present with key "a"', () => {
|
||||
const result = getKeyCommands(['Meta', 'a', 'b']);
|
||||
expect(result).toEqual([
|
||||
{ key: 'Meta' },
|
||||
{ key: 'a', command: 'SelectAll' },
|
||||
{ key: 'b' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should attach "Copy" command when "Control" is present with key "c"', () => {
|
||||
const result = getKeyCommands(['Control', 'c', 'x']);
|
||||
expect(result).toEqual([
|
||||
{ key: 'Control' },
|
||||
{ key: 'c', command: 'Copy' },
|
||||
{ key: 'x' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should attach proper commands for uppercase letters when "Meta" is present', () => {
|
||||
const result = getKeyCommands(['Meta', 'A', 'C', 'V']);
|
||||
expect(result).toEqual([
|
||||
{ key: 'Meta' },
|
||||
{ key: 'A', command: 'SelectAll' },
|
||||
{ key: 'C', command: 'Copy' },
|
||||
{ key: 'V', command: 'Paste' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@ -15,8 +15,9 @@ dotenv.config({
|
||||
const aiTestType = process.env.AI_TEST_TYPE;
|
||||
const unitTests = ['tests/unit-test/**/*.test.ts'];
|
||||
const aiWebTests = [
|
||||
'tests/ai/web/**/*.test.ts',
|
||||
'tests/ai/bridge/**/*.test.ts',
|
||||
'tests/ai/web/puppeteer/agent.test.ts',
|
||||
// 'tests/ai/web/**/*.test.ts',
|
||||
// 'tests/ai/bridge/**/*.test.ts',
|
||||
];
|
||||
const aiNativeTests = ['tests/ai/native/**/*.test.ts'];
|
||||
// const aiNativeTests = ['tests/ai/native/appium/dongchedi.test.ts'];
|
||||
|
||||
212
pnpm-lock.yaml
generated
212
pnpm-lock.yaml
generated
@ -91,8 +91,8 @@ importers:
|
||||
specifier: 14.1.1
|
||||
version: 14.1.1
|
||||
puppeteer:
|
||||
specifier: 23.0.2
|
||||
version: 23.0.2(typescript@5.0.4)
|
||||
specifier: 24.2.0
|
||||
version: 24.2.0(typescript@5.0.4)
|
||||
devDependencies:
|
||||
'@modern-js/module-tools':
|
||||
specifier: 2.60.6
|
||||
@ -412,8 +412,8 @@ importers:
|
||||
specifier: 1.44.1
|
||||
version: 1.44.1
|
||||
puppeteer:
|
||||
specifier: 23.0.2
|
||||
version: 23.0.2(typescript@5.0.4)
|
||||
specifier: 24.2.0
|
||||
version: 24.2.0(typescript@5.0.4)
|
||||
typescript:
|
||||
specifier: ~5.0.4
|
||||
version: 5.0.4
|
||||
@ -3098,13 +3098,13 @@ packages:
|
||||
'@promptbook/utils@0.69.5':
|
||||
resolution: {integrity: sha512-xm5Ti/Hp3o4xHrsK9Yy3MS6KbDxYbq485hDsFvxqaNA7equHLPdo8H8faTitTeb14QCDfLW4iwCxdVYu5sn6YQ==}
|
||||
|
||||
'@puppeteer/browsers@2.3.0':
|
||||
resolution: {integrity: sha512-ioXoq9gPxkss4MYhD+SFaU9p1IHFUX0ILAWFPyjGaBdjLsYAlZw6j1iLA0N/m12uVHLFDfSYNF7EQccjinIMDA==}
|
||||
'@puppeteer/browsers@2.4.0':
|
||||
resolution: {integrity: sha512-x8J1csfIygOwf6D6qUAZ0ASk3z63zPb7wkNeHRerCMh82qWKUrOgkuP005AJC8lDL6/evtXETGEJVcwykKT4/g==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
'@puppeteer/browsers@2.4.0':
|
||||
resolution: {integrity: sha512-x8J1csfIygOwf6D6qUAZ0ASk3z63zPb7wkNeHRerCMh82qWKUrOgkuP005AJC8lDL6/evtXETGEJVcwykKT4/g==}
|
||||
'@puppeteer/browsers@2.7.1':
|
||||
resolution: {integrity: sha512-MK7rtm8JjaxPN7Mf1JdZIZKPD2Z+W7osvrC1vjpvfOX1K0awDIHYbNi89f7eotp7eMUn2shWnt03HwVbriXtKQ==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
@ -4361,12 +4361,23 @@ packages:
|
||||
bare-fs@2.3.5:
|
||||
resolution: {integrity: sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw==}
|
||||
|
||||
bare-fs@4.0.1:
|
||||
resolution: {integrity: sha512-ilQs4fm/l9eMfWY2dY0WCIUplSUp7U0CT1vrqMg1MUdeZl4fypu5UP0XcDBK5WBQPJAKP1b7XEodISmekH/CEg==}
|
||||
engines: {bare: '>=1.7.0'}
|
||||
|
||||
bare-os@2.4.4:
|
||||
resolution: {integrity: sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ==}
|
||||
|
||||
bare-os@3.4.0:
|
||||
resolution: {integrity: sha512-9Ous7UlnKbe3fMi7Y+qh0DwAup6A1JkYgPnjvMDNOlmnxNRQvQ/7Nst+OnUQKzk0iAT0m9BisbDVp9gCv8+ETA==}
|
||||
engines: {bare: '>=1.6.0'}
|
||||
|
||||
bare-path@2.1.3:
|
||||
resolution: {integrity: sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==}
|
||||
|
||||
bare-path@3.0.0:
|
||||
resolution: {integrity: sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==}
|
||||
|
||||
bare-stream@2.3.2:
|
||||
resolution: {integrity: sha512-EFZHSIBkDgSHIwj2l2QZfP4U5OcD4xFAOwhSb/vlr9PIqyGJGvB/nfClJbcnh3EY4jtPE4zsb5ztae96bVF79A==}
|
||||
|
||||
@ -4634,8 +4645,8 @@ packages:
|
||||
resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==}
|
||||
engines: {node: '>=6.0'}
|
||||
|
||||
chromium-bidi@0.6.4:
|
||||
resolution: {integrity: sha512-8zoq6ogmhQQkAKZVKO2ObFTl4uOkqoX1PlKQX3hZQ5E9cbUotcAb7h4pTNVAGGv8Z36PF3CtdOriEp/Rz82JqQ==}
|
||||
chromium-bidi@1.2.0:
|
||||
resolution: {integrity: sha512-XtdJ1GSN6S3l7tO7F77GhNsw0K367p0IsLYf2yZawCVAKKC3lUvDhPdMVrB2FNhmhfW43QGYbEX3Wg6q0maGwQ==}
|
||||
peerDependencies:
|
||||
devtools-protocol: '*'
|
||||
|
||||
@ -5216,12 +5227,12 @@ packages:
|
||||
devlop@1.1.0:
|
||||
resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
|
||||
|
||||
devtools-protocol@0.0.1312386:
|
||||
resolution: {integrity: sha512-DPnhUXvmvKT2dFA/j7B+riVLUt9Q6RKJlcppojL5CoRywJJKLDYnRlw0gTFKfgDPHP5E04UoB71SxoJlVZy8FA==}
|
||||
|
||||
devtools-protocol@0.0.1380148:
|
||||
resolution: {integrity: sha512-1CJABgqLxbYxVI+uJY/UDUHJtJ0KZTSjNYJYKqd9FRoXT33WDakDHNxRapMEgzeJ/C3rcs01+avshMnPmKQbvA==}
|
||||
|
||||
devtools-protocol@0.0.1402036:
|
||||
resolution: {integrity: sha512-JwAYQgEvm3yD45CHB+RmF5kMbWtXBaOGwuxa87sZogHcLCv8c/IqnThaoQ1y60d7pXWjSKWQphPEc+1rAScVdg==}
|
||||
|
||||
didyoumean@1.2.2:
|
||||
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
|
||||
|
||||
@ -7689,6 +7700,10 @@ packages:
|
||||
resolution: {integrity: sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==}
|
||||
engines: {node: '>= 14'}
|
||||
|
||||
pac-proxy-agent@7.1.0:
|
||||
resolution: {integrity: sha512-Z5FnLVVZSnX7WjBg0mhDtydeRZ1xMcATZThjySQUHqr+0ksP8kqaw23fNKkaaN/Z8gwLUs/W7xdl0I75eP2Xyw==}
|
||||
engines: {node: '>= 14'}
|
||||
|
||||
pac-resolver@7.0.1:
|
||||
resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==}
|
||||
engines: {node: '>= 14'}
|
||||
@ -8260,6 +8275,10 @@ packages:
|
||||
resolution: {integrity: sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==}
|
||||
engines: {node: '>= 14'}
|
||||
|
||||
proxy-agent@6.5.0:
|
||||
resolution: {integrity: sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==}
|
||||
engines: {node: '>= 14'}
|
||||
|
||||
proxy-from-env@1.1.0:
|
||||
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
||||
|
||||
@ -8289,12 +8308,12 @@ packages:
|
||||
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
puppeteer-core@23.0.2:
|
||||
resolution: {integrity: sha512-MvOHn+g1TYkAR2oVd/bf/YWXKqFTJmkhyyurYgxkrjh8rBOL1ZH5VyOsLJi0bLO7/yoipAmk1gFZEx9HUJnaoA==}
|
||||
puppeteer-core@24.2.0:
|
||||
resolution: {integrity: sha512-e4A4/xqWdd4kcE6QVHYhJ+Qlx/+XpgjP4d8OwBx0DJoY/nkIRhSgYmKQnv7+XSs1ofBstalt+XPGrkaz4FoXOQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
puppeteer@23.0.2:
|
||||
resolution: {integrity: sha512-I/l1P8s8brcLG+oW9AwF8hUaOSGGJcGKMflXRgULUH0S3ABptlLI9ZKjqWDo8ipY6v789ZKd+bNKtcCwpTh5Ww==}
|
||||
puppeteer@24.2.0:
|
||||
resolution: {integrity: sha512-z8vv7zPEgrilIbOo3WNvM+2mXMnyM9f4z6zdrB88Fzeuo43Oupmjrzk3EpuvuCtyK0A7Lsllfx7Z+4BvEEGJcQ==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
@ -9311,6 +9330,10 @@ packages:
|
||||
resolution: {integrity: sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==}
|
||||
engines: {node: '>= 14'}
|
||||
|
||||
socks-proxy-agent@8.0.5:
|
||||
resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==}
|
||||
engines: {node: '>= 14'}
|
||||
|
||||
socks@2.8.3:
|
||||
resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==}
|
||||
engines: {node: '>= 10.0.0', npm: '>= 3.0.0'}
|
||||
@ -9599,6 +9622,9 @@ packages:
|
||||
tar-fs@3.0.6:
|
||||
resolution: {integrity: sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==}
|
||||
|
||||
tar-fs@3.0.8:
|
||||
resolution: {integrity: sha512-ZoROL70jptorGAlgAYiLoBLItEKw/fUxg9BSYK/dF/GAGYFJOJJJMvjPAKDJraCXFwadD456FCuvLWgfhMsPwg==}
|
||||
|
||||
tar-stream@2.2.0:
|
||||
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
|
||||
engines: {node: '>=6'}
|
||||
@ -9855,6 +9881,9 @@ packages:
|
||||
resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
typed-query-selector@2.12.0:
|
||||
resolution: {integrity: sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==}
|
||||
|
||||
typescript@5.0.4:
|
||||
resolution: {integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==}
|
||||
engines: {node: '>=12.20'}
|
||||
@ -10769,7 +10798,7 @@ snapshots:
|
||||
'@babel/core': 7.26.0
|
||||
'@babel/helper-compilation-targets': 7.25.9
|
||||
'@babel/helper-plugin-utils': 7.25.9
|
||||
debug: 4.3.7(supports-color@5.5.0)
|
||||
debug: 4.4.0
|
||||
lodash.debounce: 4.0.8
|
||||
resolve: 1.22.8
|
||||
transitivePeerDependencies:
|
||||
@ -13281,8 +13310,8 @@ snapshots:
|
||||
source-map: 0.7.4
|
||||
webpack: 5.95.0
|
||||
webpack-sources: 3.2.3
|
||||
zod: 3.23.8
|
||||
zod-validation-error: 1.2.0(zod@3.23.8)
|
||||
zod: 3.24.1
|
||||
zod-validation-error: 1.2.0(zod@3.24.1)
|
||||
transitivePeerDependencies:
|
||||
- '@swc/core'
|
||||
- '@types/express'
|
||||
@ -14352,19 +14381,6 @@ snapshots:
|
||||
dependencies:
|
||||
spacetrim: 0.11.59
|
||||
|
||||
'@puppeteer/browsers@2.3.0':
|
||||
dependencies:
|
||||
debug: 4.3.7(supports-color@5.5.0)
|
||||
extract-zip: 2.0.1
|
||||
progress: 2.0.3
|
||||
proxy-agent: 6.4.0
|
||||
semver: 7.6.3
|
||||
tar-fs: 3.0.6
|
||||
unbzip2-stream: 1.4.3
|
||||
yargs: 17.7.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@puppeteer/browsers@2.4.0':
|
||||
dependencies:
|
||||
debug: 4.3.7(supports-color@5.5.0)
|
||||
@ -14378,6 +14394,18 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@puppeteer/browsers@2.7.1':
|
||||
dependencies:
|
||||
debug: 4.4.0
|
||||
extract-zip: 2.0.1
|
||||
progress: 2.0.3
|
||||
proxy-agent: 6.5.0
|
||||
semver: 7.7.1
|
||||
tar-fs: 3.0.8
|
||||
yargs: 17.7.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@rc-component/async-validator@5.0.4':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.0
|
||||
@ -14642,8 +14670,8 @@ snapshots:
|
||||
util: 0.12.5
|
||||
watchpack: 2.4.2
|
||||
webpack-sources: 3.2.3
|
||||
zod: 3.23.8
|
||||
zod-validation-error: 1.2.0(zod@3.23.8)
|
||||
zod: 3.24.1
|
||||
zod-validation-error: 1.2.0(zod@3.24.1)
|
||||
transitivePeerDependencies:
|
||||
- '@types/webpack'
|
||||
- sockjs-client
|
||||
@ -15538,7 +15566,7 @@ snapshots:
|
||||
|
||||
agent-base@6.0.2:
|
||||
dependencies:
|
||||
debug: 4.3.7(supports-color@5.5.0)
|
||||
debug: 4.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@ -15548,8 +15576,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
agent-base@7.1.3:
|
||||
optional: true
|
||||
agent-base@7.1.3: {}
|
||||
|
||||
agentkeepalive@4.5.0:
|
||||
dependencies:
|
||||
@ -15894,14 +15921,29 @@ snapshots:
|
||||
bare-stream: 2.3.2
|
||||
optional: true
|
||||
|
||||
bare-fs@4.0.1:
|
||||
dependencies:
|
||||
bare-events: 2.5.0
|
||||
bare-path: 3.0.0
|
||||
bare-stream: 2.3.2
|
||||
optional: true
|
||||
|
||||
bare-os@2.4.4:
|
||||
optional: true
|
||||
|
||||
bare-os@3.4.0:
|
||||
optional: true
|
||||
|
||||
bare-path@2.1.3:
|
||||
dependencies:
|
||||
bare-os: 2.4.4
|
||||
optional: true
|
||||
|
||||
bare-path@3.0.0:
|
||||
dependencies:
|
||||
bare-os: 3.4.0
|
||||
optional: true
|
||||
|
||||
bare-stream@2.3.2:
|
||||
dependencies:
|
||||
streamx: 2.20.1
|
||||
@ -16219,12 +16261,11 @@ snapshots:
|
||||
|
||||
chrome-trace-event@1.0.4: {}
|
||||
|
||||
chromium-bidi@0.6.4(devtools-protocol@0.0.1312386):
|
||||
chromium-bidi@1.2.0(devtools-protocol@0.0.1402036):
|
||||
dependencies:
|
||||
devtools-protocol: 0.0.1312386
|
||||
devtools-protocol: 0.0.1402036
|
||||
mitt: 3.0.1
|
||||
urlpattern-polyfill: 10.0.0
|
||||
zod: 3.23.8
|
||||
zod: 3.24.1
|
||||
|
||||
ci-info@3.9.0: {}
|
||||
|
||||
@ -16860,10 +16901,10 @@ snapshots:
|
||||
dependencies:
|
||||
dequal: 2.0.3
|
||||
|
||||
devtools-protocol@0.0.1312386: {}
|
||||
|
||||
devtools-protocol@0.0.1380148: {}
|
||||
|
||||
devtools-protocol@0.0.1402036: {}
|
||||
|
||||
didyoumean@1.2.2: {}
|
||||
|
||||
diff-sequences@29.6.3: {}
|
||||
@ -17499,7 +17540,7 @@ snapshots:
|
||||
|
||||
extract-zip@2.0.1:
|
||||
dependencies:
|
||||
debug: 4.3.7(supports-color@5.5.0)
|
||||
debug: 4.4.0
|
||||
get-stream: 5.2.0
|
||||
yauzl: 2.10.0
|
||||
optionalDependencies:
|
||||
@ -17817,7 +17858,7 @@ snapshots:
|
||||
dependencies:
|
||||
basic-ftp: 5.0.5
|
||||
data-uri-to-buffer: 6.0.2
|
||||
debug: 4.3.7(supports-color@5.5.0)
|
||||
debug: 4.4.0
|
||||
fs-extra: 11.2.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@ -18276,7 +18317,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@tootallnate/once': 2.0.0
|
||||
agent-base: 6.0.2
|
||||
debug: 4.3.7(supports-color@5.5.0)
|
||||
debug: 4.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@ -18330,7 +18371,7 @@ snapshots:
|
||||
https-proxy-agent@5.0.1:
|
||||
dependencies:
|
||||
agent-base: 6.0.2
|
||||
debug: 4.3.7(supports-color@5.5.0)
|
||||
debug: 4.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@ -18347,7 +18388,6 @@ snapshots:
|
||||
debug: 4.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
optional: true
|
||||
|
||||
human-id@1.0.2: {}
|
||||
|
||||
@ -19646,7 +19686,7 @@ snapshots:
|
||||
micromark@3.2.0:
|
||||
dependencies:
|
||||
'@types/debug': 4.1.12
|
||||
debug: 4.3.7(supports-color@5.5.0)
|
||||
debug: 4.4.0
|
||||
decode-named-character-reference: 1.0.2
|
||||
micromark-core-commonmark: 1.1.0
|
||||
micromark-factory-space: 1.1.0
|
||||
@ -20118,7 +20158,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@tootallnate/quickjs-emscripten': 0.23.0
|
||||
agent-base: 7.1.1
|
||||
debug: 4.3.7(supports-color@5.5.0)
|
||||
debug: 4.4.0
|
||||
get-uri: 6.0.3
|
||||
http-proxy-agent: 7.0.2
|
||||
https-proxy-agent: 7.0.5
|
||||
@ -20127,6 +20167,19 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
pac-proxy-agent@7.1.0:
|
||||
dependencies:
|
||||
'@tootallnate/quickjs-emscripten': 0.23.0
|
||||
agent-base: 7.1.3
|
||||
debug: 4.4.0
|
||||
get-uri: 6.0.3
|
||||
http-proxy-agent: 7.0.2
|
||||
https-proxy-agent: 7.0.6
|
||||
pac-resolver: 7.0.1
|
||||
socks-proxy-agent: 8.0.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
pac-resolver@7.0.1:
|
||||
dependencies:
|
||||
degenerator: 5.0.1
|
||||
@ -20696,6 +20749,19 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
proxy-agent@6.5.0:
|
||||
dependencies:
|
||||
agent-base: 7.1.3
|
||||
debug: 4.4.0
|
||||
http-proxy-agent: 7.0.2
|
||||
https-proxy-agent: 7.0.6
|
||||
lru-cache: 7.18.3
|
||||
pac-proxy-agent: 7.1.0
|
||||
proxy-from-env: 1.1.0
|
||||
socks-proxy-agent: 8.0.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
proxy-from-env@1.1.0: {}
|
||||
|
||||
pseudomap@1.0.2: {}
|
||||
@ -20724,25 +20790,27 @@ snapshots:
|
||||
|
||||
punycode@2.3.1: {}
|
||||
|
||||
puppeteer-core@23.0.2:
|
||||
puppeteer-core@24.2.0:
|
||||
dependencies:
|
||||
'@puppeteer/browsers': 2.3.0
|
||||
chromium-bidi: 0.6.4(devtools-protocol@0.0.1312386)
|
||||
debug: 4.3.7(supports-color@5.5.0)
|
||||
devtools-protocol: 0.0.1312386
|
||||
'@puppeteer/browsers': 2.7.1
|
||||
chromium-bidi: 1.2.0(devtools-protocol@0.0.1402036)
|
||||
debug: 4.4.0
|
||||
devtools-protocol: 0.0.1402036
|
||||
typed-query-selector: 2.12.0
|
||||
ws: 8.18.0
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
|
||||
puppeteer@23.0.2(typescript@5.0.4):
|
||||
puppeteer@24.2.0(typescript@5.0.4):
|
||||
dependencies:
|
||||
'@puppeteer/browsers': 2.3.0
|
||||
chromium-bidi: 0.6.4(devtools-protocol@0.0.1312386)
|
||||
'@puppeteer/browsers': 2.7.1
|
||||
chromium-bidi: 1.2.0(devtools-protocol@0.0.1402036)
|
||||
cosmiconfig: 9.0.0(typescript@5.0.4)
|
||||
devtools-protocol: 0.0.1312386
|
||||
puppeteer-core: 23.0.2
|
||||
devtools-protocol: 0.0.1402036
|
||||
puppeteer-core: 24.2.0
|
||||
typed-query-selector: 2.12.0
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- supports-color
|
||||
@ -21994,6 +22062,14 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
socks-proxy-agent@8.0.5:
|
||||
dependencies:
|
||||
agent-base: 7.1.3
|
||||
debug: 4.4.0
|
||||
socks: 2.8.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
socks@2.8.3:
|
||||
dependencies:
|
||||
ip-address: 9.0.5
|
||||
@ -22313,6 +22389,14 @@ snapshots:
|
||||
bare-fs: 2.3.5
|
||||
bare-path: 2.1.3
|
||||
|
||||
tar-fs@3.0.8:
|
||||
dependencies:
|
||||
pump: 3.0.2
|
||||
tar-stream: 3.1.7
|
||||
optionalDependencies:
|
||||
bare-fs: 4.0.1
|
||||
bare-path: 3.0.0
|
||||
|
||||
tar-stream@2.2.0:
|
||||
dependencies:
|
||||
bl: 4.1.0
|
||||
@ -22574,6 +22658,8 @@ snapshots:
|
||||
is-typed-array: 1.1.13
|
||||
possible-typed-array-names: 1.0.0
|
||||
|
||||
typed-query-selector@2.12.0: {}
|
||||
|
||||
typescript@5.0.4: {}
|
||||
|
||||
typescript@5.6.3: {}
|
||||
@ -23294,10 +23380,6 @@ snapshots:
|
||||
dependencies:
|
||||
zod: 3.23.8
|
||||
|
||||
zod-validation-error@1.2.0(zod@3.23.8):
|
||||
dependencies:
|
||||
zod: 3.23.8
|
||||
|
||||
zod-validation-error@1.2.0(zod@3.24.1):
|
||||
dependencies:
|
||||
zod: 3.24.1
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user