mirror of
https://github.com/orangecoding/fredy.git
synced 2026-06-16 12:31:07 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ebc57702dc | ||
|
|
3aa30bc1e2 | ||
|
|
f97fb48e51 | ||
|
|
4b15894603 | ||
|
|
31a14a0352 | ||
|
|
eecbe91dbd | ||
|
|
9dd3947cb7 | ||
|
|
c151f4f76e |
16
README.md
16
README.md
@@ -9,10 +9,18 @@
|
|||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||

|
<p align="center">
|
||||||
[](https://github.com/orangecoding/fredy/actions/workflows/docker.yml)
|
<a href="https://fredy.orange-coding.net/" target="_blank">Website</a> |
|
||||||

|
<a href="https://demo-fredy.orange-coding.net/" target="_blank">Demo</a>
|
||||||

|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="https://github.com/orangecoding/fredy/actions/workflows/test.yml/badge.svg" alt="Tests" />
|
||||||
|
<img src="https://github.com/orangecoding/fredy/actions/workflows/docker.yml/badge.svg" alt="Docker" />
|
||||||
|
<img src="https://github.com/orangecoding/fredy/actions/workflows/check_source.yml/badge.svg" alt="Source" />
|
||||||
|
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fghcr-badge.elias.eu.org%2Fapi%2Forangecoding%2Ffredy%2Ffredy&query=%24.downloadCount&label=Docker%20Pulls" alt="Docker Pulls" />
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Fredy 🏡 – Your Self-Hosted Real Estate Finder for Germany
|
# Fredy 🏡 – Your Self-Hosted Real Estate Finder for Germany
|
||||||
|
|||||||
18
docker-test.sh
Normal file
18
docker-test.sh
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Stop and remove old container if it exists
|
||||||
|
if [ "$(docker ps -aq -f name=fredy)" ]; then
|
||||||
|
docker stop fredy || true
|
||||||
|
docker rm fredy || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build image from local Dockerfile
|
||||||
|
docker build -t fredy:local .
|
||||||
|
|
||||||
|
# Run container with volumes and port mapping
|
||||||
|
docker run -d --name fredy \
|
||||||
|
-v fredy_conf:/conf \
|
||||||
|
-v fredy_db:/db \
|
||||||
|
-p 9998:9998 \
|
||||||
|
fredy:local
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import restana from 'restana';
|
import restana from 'restana';
|
||||||
import * as listingStorage from '../../services/storage/listingsStorage.js';
|
import * as listingStorage from '../../services/storage/listingsStorage.js';
|
||||||
import { isAdmin as isAdminFn } from '../security.js';
|
import { isAdmin as isAdminFn } from '../security.js';
|
||||||
|
import logger from '../../services/logger.js';
|
||||||
|
|
||||||
const service = restana();
|
const service = restana();
|
||||||
|
|
||||||
const listingsRouter = service.newRouter();
|
const listingsRouter = service.newRouter();
|
||||||
@@ -8,7 +10,7 @@ const listingsRouter = service.newRouter();
|
|||||||
listingsRouter.get('/table', async (req, res) => {
|
listingsRouter.get('/table', async (req, res) => {
|
||||||
const { page, pageSize = 50, filter, sortfield = null, sortdir = 'asc' } = req.query || {};
|
const { page, pageSize = 50, filter, sortfield = null, sortdir = 'asc' } = req.query || {};
|
||||||
|
|
||||||
const result = listingStorage.queryListings({
|
res.body = listingStorage.queryListings({
|
||||||
page: page ? parseInt(page, 10) : 1,
|
page: page ? parseInt(page, 10) : 1,
|
||||||
pageSize: pageSize ? parseInt(pageSize, 10) : 50,
|
pageSize: pageSize ? parseInt(pageSize, 10) : 50,
|
||||||
filter: filter || undefined,
|
filter: filter || undefined,
|
||||||
@@ -17,7 +19,31 @@ listingsRouter.get('/table', async (req, res) => {
|
|||||||
userId: req.session.currentUser,
|
userId: req.session.currentUser,
|
||||||
isAdmin: isAdminFn(req),
|
isAdmin: isAdminFn(req),
|
||||||
});
|
});
|
||||||
res.body = result;
|
|
||||||
res.send();
|
res.send();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
listingsRouter.delete('/job', async (req, res) => {
|
||||||
|
const { jobId } = req.body;
|
||||||
|
try {
|
||||||
|
listingStorage.deleteListingsByJobId(jobId);
|
||||||
|
} catch (error) {
|
||||||
|
res.send(new Error(error));
|
||||||
|
logger.error(error);
|
||||||
|
}
|
||||||
|
res.send();
|
||||||
|
});
|
||||||
|
|
||||||
|
listingsRouter.delete('/', async (req, res) => {
|
||||||
|
const { ids } = req.body;
|
||||||
|
try {
|
||||||
|
if (Array.isArray(ids) && ids.length > 0) {
|
||||||
|
listingStorage.deleteListingsById(ids);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
res.send(new Error(error));
|
||||||
|
logger.error(error);
|
||||||
|
}
|
||||||
|
res.send();
|
||||||
|
});
|
||||||
|
|
||||||
export { listingsRouter };
|
export { listingsRouter };
|
||||||
|
|||||||
@@ -251,3 +251,26 @@ export const queryListings = ({
|
|||||||
|
|
||||||
return { totalNumber, page: safePage, result: rows };
|
return { totalNumber, page: safePage, result: rows };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all listings for a given job id.
|
||||||
|
*
|
||||||
|
* @param {string} jobId - The job identifier whose listings should be removed.
|
||||||
|
* @returns {any} The result from SqliteConnection.execute (may contain changes count).
|
||||||
|
*/
|
||||||
|
export const deleteListingsByJobId = (jobId) => {
|
||||||
|
if (!jobId) return;
|
||||||
|
return SqliteConnection.execute(`DELETE FROM listings WHERE job_id = @jobId`, { jobId });
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete listings by a list of listing IDs.
|
||||||
|
*
|
||||||
|
* @param {string[]} ids - Array of listing IDs to delete.
|
||||||
|
* @returns {any} The result from SqliteConnection.execute.
|
||||||
|
*/
|
||||||
|
export const deleteListingsById = (ids) => {
|
||||||
|
if (!Array.isArray(ids) || ids.length === 0) return;
|
||||||
|
const placeholders = ids.map(() => '?').join(',');
|
||||||
|
return SqliteConnection.execute(`DELETE FROM listings WHERE id IN (${placeholders})`, ids);
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "fredy",
|
"name": "fredy",
|
||||||
"version": "14.0.0",
|
"version": "14.0.1",
|
||||||
"description": "[F]ind [R]eal [E]states [d]amn eas[y].",
|
"description": "[F]ind [R]eal [E]states [d]amn eas[y].",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepare": "husky",
|
"prepare": "husky",
|
||||||
@@ -76,7 +76,7 @@
|
|||||||
"node-mailjet": "6.0.9",
|
"node-mailjet": "6.0.9",
|
||||||
"p-throttle": "^8.0.0",
|
"p-throttle": "^8.0.0",
|
||||||
"package-up": "^5.0.0",
|
"package-up": "^5.0.0",
|
||||||
"puppeteer": "^24.22.3",
|
"puppeteer": "^24.23.0",
|
||||||
"puppeteer-extra": "^3.3.6",
|
"puppeteer-extra": "^3.3.6",
|
||||||
"puppeteer-extra-plugin-stealth": "^2.11.2",
|
"puppeteer-extra-plugin-stealth": "^2.11.2",
|
||||||
"query-string": "9.3.1",
|
"query-string": "9.3.1",
|
||||||
@@ -88,7 +88,7 @@
|
|||||||
"semver": "^7.7.2",
|
"semver": "^7.7.2",
|
||||||
"serve-static": "2.2.0",
|
"serve-static": "2.2.0",
|
||||||
"slack": "11.0.2",
|
"slack": "11.0.2",
|
||||||
"vite": "7.1.7",
|
"vite": "7.1.9",
|
||||||
"x-var": "^3.0.1",
|
"x-var": "^3.0.1",
|
||||||
"zustand": "^5.0.8"
|
"zustand": "^5.0.8"
|
||||||
},
|
},
|
||||||
@@ -106,7 +106,7 @@
|
|||||||
"husky": "9.1.7",
|
"husky": "9.1.7",
|
||||||
"less": "4.4.1",
|
"less": "4.4.1",
|
||||||
"lint-staged": "16.2.3",
|
"lint-staged": "16.2.3",
|
||||||
"mocha": "11.7.2",
|
"mocha": "11.7.4",
|
||||||
"nodemon": "^3.1.10",
|
"nodemon": "^3.1.10",
|
||||||
"prettier": "3.6.2"
|
"prettier": "3.6.2"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
height: 1.7rem;
|
||||||
|
|
||||||
&__version {
|
&__version {
|
||||||
padding-left: .5rem;
|
padding-left: .5rem;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { Button, Empty, Table, Switch } from '@douyinfe/semi-ui';
|
import { Button, Empty, Table, Switch, Popover } from '@douyinfe/semi-ui';
|
||||||
import { IconDelete, IconEdit, IconHistogram } from '@douyinfe/semi-icons';
|
import { IconDelete, IconDescend2, IconEdit, IconHistogram } from '@douyinfe/semi-icons';
|
||||||
import { IllustrationNoResult, IllustrationNoResultDark } from '@douyinfe/semi-illustrations';
|
import { IllustrationNoResult, IllustrationNoResultDark } from '@douyinfe/semi-illustrations';
|
||||||
|
|
||||||
import './JobTable.less';
|
import './JobTable.less';
|
||||||
@@ -14,7 +14,16 @@ const empty = (
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default function JobTable({ jobs = {}, onJobRemoval, onJobStatusChanged, onJobEdit, onJobInsight } = {}) {
|
const getPopoverContent = (text) => <article className="jobPopoverContent">{text}</article>;
|
||||||
|
|
||||||
|
export default function JobTable({
|
||||||
|
jobs = {},
|
||||||
|
onJobRemoval,
|
||||||
|
onJobStatusChanged,
|
||||||
|
onJobEdit,
|
||||||
|
onJobInsight,
|
||||||
|
onListingRemoval,
|
||||||
|
} = {}) {
|
||||||
return (
|
return (
|
||||||
<Table
|
<Table
|
||||||
pagination={false}
|
pagination={false}
|
||||||
@@ -58,9 +67,18 @@ export default function JobTable({ jobs = {}, onJobRemoval, onJobStatusChanged,
|
|||||||
render: (_, job) => {
|
render: (_, job) => {
|
||||||
return (
|
return (
|
||||||
<div className="interactions">
|
<div className="interactions">
|
||||||
<Button type="primary" icon={<IconHistogram />} onClick={() => onJobInsight(job.id)} />
|
<Popover content={getPopoverContent('Job Insights')}>
|
||||||
<Button type="secondary" icon={<IconEdit />} onClick={() => onJobEdit(job.id)} />
|
<Button type="primary" icon={<IconHistogram />} onClick={() => onJobInsight(job.id)} />
|
||||||
<Button type="danger" icon={<IconDelete />} onClick={() => onJobRemoval(job.id)} />
|
</Popover>
|
||||||
|
<Popover content={getPopoverContent('Edit a Job')}>
|
||||||
|
<Button type="secondary" icon={<IconEdit />} onClick={() => onJobEdit(job.id)} />
|
||||||
|
</Popover>
|
||||||
|
<Popover content={getPopoverContent('Delete all found Listings of this Job')}>
|
||||||
|
<Button type="danger" icon={<IconDescend2 />} onClick={() => onListingRemoval(job.id)} />
|
||||||
|
</Popover>
|
||||||
|
<Popover content={getPopoverContent('Delete Job')}>
|
||||||
|
<Button type="danger" icon={<IconDelete />} onClick={() => onJobRemoval(job.id)} />
|
||||||
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,6 +5,11 @@
|
|||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.jobPopoverContent {
|
||||||
|
padding: 1rem;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.interactions {
|
.interactions {
|
||||||
flex-direction: initial;
|
flex-direction: initial;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useState, useEffect, useMemo } from 'react';
|
import React, { useState, useEffect, useMemo } from 'react';
|
||||||
import { Table, Popover, Input, Descriptions, Tag, Image, Empty } from '@douyinfe/semi-ui';
|
import { Table, Popover, Input, Descriptions, Tag, Image, Empty, Button, Card, Toast } from '@douyinfe/semi-ui';
|
||||||
import { useActions, useSelector } from '../../services/state/store.js';
|
import { useActions, useSelector } from '../../services/state/store.js';
|
||||||
import { IconClose, IconSearch, IconTick } from '@douyinfe/semi-icons';
|
import { IconClose, IconDelete, IconSearch, IconTick } from '@douyinfe/semi-icons';
|
||||||
import * as timeService from '../../services/time/timeService.js';
|
import * as timeService from '../../services/time/timeService.js';
|
||||||
import debounce from 'lodash/debounce';
|
import debounce from 'lodash/debounce';
|
||||||
import no_image from '../../assets/no_image.jpg';
|
import no_image from '../../assets/no_image.jpg';
|
||||||
@@ -9,6 +9,7 @@ import no_image from '../../assets/no_image.jpg';
|
|||||||
import './ListingsTable.less';
|
import './ListingsTable.less';
|
||||||
import { format } from '../../services/time/timeService.js';
|
import { format } from '../../services/time/timeService.js';
|
||||||
import { IllustrationNoResult, IllustrationNoResultDark } from '@douyinfe/semi-illustrations';
|
import { IllustrationNoResult, IllustrationNoResultDark } from '@douyinfe/semi-illustrations';
|
||||||
|
import { xhrDelete } from '../../services/xhr.js';
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
@@ -81,6 +82,7 @@ const columns = [
|
|||||||
title: 'Title',
|
title: 'Title',
|
||||||
dataIndex: 'title',
|
dataIndex: 'title',
|
||||||
sorter: true,
|
sorter: true,
|
||||||
|
ellipsis: true,
|
||||||
render: (text, row) => {
|
render: (text, row) => {
|
||||||
return (
|
return (
|
||||||
<a href={row.url} target="_blank" rel="noopener noreferrer">
|
<a href={row.url} target="_blank" rel="noopener noreferrer">
|
||||||
@@ -106,12 +108,13 @@ export default function ListingsTable() {
|
|||||||
const pageSize = 10;
|
const pageSize = 10;
|
||||||
const [sortData, setSortData] = useState({});
|
const [sortData, setSortData] = useState({});
|
||||||
const [filter, setFilter] = useState(null);
|
const [filter, setFilter] = useState(null);
|
||||||
|
const [selectedKeys, setSelectedKeys] = useState([]);
|
||||||
|
|
||||||
const handlePageChange = (_page) => {
|
const handlePageChange = (_page) => {
|
||||||
setPage(_page);
|
setPage(_page);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
const loadTable = () => {
|
||||||
let sortfield = null;
|
let sortfield = null;
|
||||||
let sortdir = null;
|
let sortdir = null;
|
||||||
|
|
||||||
@@ -120,10 +123,20 @@ export default function ListingsTable() {
|
|||||||
sortdir = sortData.direction;
|
sortdir = sortData.direction;
|
||||||
}
|
}
|
||||||
actions.listingsTable.getListingsTable({ page, pageSize, sortfield, sortdir, filter });
|
actions.listingsTable.getListingsTable({ page, pageSize, sortfield, sortdir, filter });
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadTable();
|
||||||
}, [page, sortData, filter]);
|
}, [page, sortData, filter]);
|
||||||
|
|
||||||
const handleFilterChange = useMemo(() => debounce((value) => setFilter(value), 500), []);
|
const handleFilterChange = useMemo(() => debounce((value) => setFilter(value), 500), []);
|
||||||
|
|
||||||
|
const rowSelection = {
|
||||||
|
onChange: (selectedRowKeys) => {
|
||||||
|
setSelectedKeys(selectedRowKeys);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const expandRowRender = (record) => {
|
const expandRowRender = (record) => {
|
||||||
return (
|
return (
|
||||||
<div className="listingsTable__expanded">
|
<div className="listingsTable__expanded">
|
||||||
@@ -156,6 +169,18 @@ export default function ListingsTable() {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onRemoveSelectedListings = async () => {
|
||||||
|
if (selectedKeys != null && selectedKeys.length > 0) {
|
||||||
|
try {
|
||||||
|
await xhrDelete('/api/listings/', { ids: selectedKeys });
|
||||||
|
Toast.success('Listing(s) successfully removed');
|
||||||
|
loadTable();
|
||||||
|
} catch (error) {
|
||||||
|
Toast.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Input
|
<Input
|
||||||
@@ -165,12 +190,20 @@ export default function ListingsTable() {
|
|||||||
placeholder="Search"
|
placeholder="Search"
|
||||||
onChange={handleFilterChange}
|
onChange={handleFilterChange}
|
||||||
/>
|
/>
|
||||||
|
{selectedKeys != null && selectedKeys.length > 0 && (
|
||||||
|
<Card className="listingsTable__toolbar">
|
||||||
|
<Button type="danger" icon={<IconDelete />} onClick={() => onRemoveSelectedListings()}>
|
||||||
|
Remove selected Listings
|
||||||
|
</Button>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
<Table
|
<Table
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
empty={empty}
|
empty={empty}
|
||||||
hideExpandedColumn={false}
|
hideExpandedColumn={false}
|
||||||
sticky={{ top: 5 }}
|
sticky={{ top: 5 }}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
rowSelection={rowSelection}
|
||||||
expandedRowRender={expandRowRender}
|
expandedRowRender={expandRowRender}
|
||||||
dataSource={tableData?.result || []}
|
dataSource={tableData?.result || []}
|
||||||
onChange={(changeSet) => {
|
onChange={(changeSet) => {
|
||||||
|
|||||||
@@ -7,4 +7,8 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__toolbar {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,17 @@ export default function Jobs() {
|
|||||||
const onJobRemoval = async (jobId) => {
|
const onJobRemoval = async (jobId) => {
|
||||||
try {
|
try {
|
||||||
await xhrDelete('/api/jobs', { jobId });
|
await xhrDelete('/api/jobs', { jobId });
|
||||||
Toast.success('Job successfully remove');
|
Toast.success('Job successfully removed');
|
||||||
|
await actions.jobs.getJobs();
|
||||||
|
} catch (error) {
|
||||||
|
Toast.error(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onListingRemoval = async (jobId) => {
|
||||||
|
try {
|
||||||
|
await xhrDelete('/api/listings/job', { jobId });
|
||||||
|
Toast.success('Listings successfully removed');
|
||||||
await actions.jobs.getJobs();
|
await actions.jobs.getJobs();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Toast.error(error);
|
Toast.error(error);
|
||||||
@@ -49,6 +59,7 @@ export default function Jobs() {
|
|||||||
<JobTable
|
<JobTable
|
||||||
jobs={jobs || []}
|
jobs={jobs || []}
|
||||||
onJobRemoval={onJobRemoval}
|
onJobRemoval={onJobRemoval}
|
||||||
|
onListingRemoval={onListingRemoval}
|
||||||
onJobStatusChanged={onJobStatusChanged}
|
onJobStatusChanged={onJobStatusChanged}
|
||||||
onJobInsight={(jobId) => navigate(`/jobs/insights/${jobId}`)}
|
onJobInsight={(jobId) => navigate(`/jobs/insights/${jobId}`)}
|
||||||
onJobEdit={(jobId) => navigate(`/jobs/edit/${jobId}`)}
|
onJobEdit={(jobId) => navigate(`/jobs/edit/${jobId}`)}
|
||||||
|
|||||||
62
yarn.lock
62
yarn.lock
@@ -2863,10 +2863,10 @@ devlop@^1.0.0, devlop@^1.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
dequal "^2.0.0"
|
dequal "^2.0.0"
|
||||||
|
|
||||||
devtools-protocol@0.0.1495869:
|
devtools-protocol@0.0.1508733:
|
||||||
version "0.0.1495869"
|
version "0.0.1508733"
|
||||||
resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1495869.tgz#f68daef77a48d5dcbcdd55dbfa3265a51989c91b"
|
resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1508733.tgz#047deb3531470efda2c7bf43c10b3ae9e4b3d51b"
|
||||||
integrity sha512-i+bkd9UYFis40RcnkW7XrOprCujXRAHg62IVh/Ah3G8MmNXpCGt1m0dTFhSdx/AVs8XEMbdOGRwdkR1Bcta8AA==
|
integrity sha512-QJ1R5gtck6nDcdM+nlsaJXcelPEI7ZxSMw1ujHpO1c4+9l+Nue5qlebi9xO1Z2MGr92bFOQTW7/rrheh5hHxDg==
|
||||||
|
|
||||||
diff@^7.0.0:
|
diff@^7.0.0:
|
||||||
version "7.0.0"
|
version "7.0.0"
|
||||||
@@ -4274,6 +4274,11 @@ is-number@^7.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
|
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
|
||||||
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
|
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
|
||||||
|
|
||||||
|
is-path-inside@^3.0.3:
|
||||||
|
version "3.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
|
||||||
|
integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
|
||||||
|
|
||||||
is-plain-obj@^2.1.0:
|
is-plain-obj@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287"
|
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287"
|
||||||
@@ -5370,10 +5375,10 @@ mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3:
|
|||||||
resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
|
resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
|
||||||
integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
|
integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
|
||||||
|
|
||||||
mocha@11.7.2:
|
mocha@11.7.4:
|
||||||
version "11.7.2"
|
version "11.7.4"
|
||||||
resolved "https://registry.yarnpkg.com/mocha/-/mocha-11.7.2.tgz#3c0079fe5cc2f8ea86d99124debcc42bb1ab22b5"
|
resolved "https://registry.yarnpkg.com/mocha/-/mocha-11.7.4.tgz#f161b17aeccb0762484b33bdb3f7ab9410ba5c82"
|
||||||
integrity sha512-lkqVJPmqqG/w5jmmFtiRvtA2jkDyNVUcefFJKb2uyX4dekk8Okgqop3cgbFiaIvj8uCRJVTP5x9dfxGyXm2jvQ==
|
integrity sha512-1jYAaY8x0kAZ0XszLWu14pzsf4KV740Gld4HXkhNTXwcHx4AUEDkPzgEHg9CM5dVcW+zv036tjpsEbLraPJj4w==
|
||||||
dependencies:
|
dependencies:
|
||||||
browser-stdout "^1.3.1"
|
browser-stdout "^1.3.1"
|
||||||
chokidar "^4.0.1"
|
chokidar "^4.0.1"
|
||||||
@@ -5383,6 +5388,7 @@ mocha@11.7.2:
|
|||||||
find-up "^5.0.0"
|
find-up "^5.0.0"
|
||||||
glob "^10.4.5"
|
glob "^10.4.5"
|
||||||
he "^1.2.0"
|
he "^1.2.0"
|
||||||
|
is-path-inside "^3.0.3"
|
||||||
js-yaml "^4.1.0"
|
js-yaml "^4.1.0"
|
||||||
log-symbols "^4.1.0"
|
log-symbols "^4.1.0"
|
||||||
minimatch "^9.0.5"
|
minimatch "^9.0.5"
|
||||||
@@ -5962,17 +5968,17 @@ punycode@^2.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
|
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
|
||||||
integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
|
integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
|
||||||
|
|
||||||
puppeteer-core@24.22.3:
|
puppeteer-core@24.23.0:
|
||||||
version "24.22.3"
|
version "24.23.0"
|
||||||
resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-24.22.3.tgz#63285a37da6e2c44069c0b31f2171f8ab81bbe23"
|
resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-24.23.0.tgz#1f84abafa480358652ae8df340af984438173a14"
|
||||||
integrity sha512-M/Jhg4PWRANSbL/C9im//Yb55wsWBS5wdp+h59iwM+EPicVQQCNs56iC5aEAO7avfDPRfxs4MM16wHjOYHNJEw==
|
integrity sha512-yl25C59gb14sOdIiSnJ08XiPP+O2RjuyZmEG+RjYmCXO7au0jcLf7fRiyii96dXGUBW7Zwei/mVKfxMx/POeFw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@puppeteer/browsers" "2.10.10"
|
"@puppeteer/browsers" "2.10.10"
|
||||||
chromium-bidi "9.1.0"
|
chromium-bidi "9.1.0"
|
||||||
debug "^4.4.3"
|
debug "^4.4.3"
|
||||||
devtools-protocol "0.0.1495869"
|
devtools-protocol "0.0.1508733"
|
||||||
typed-query-selector "^2.12.0"
|
typed-query-selector "^2.12.0"
|
||||||
webdriver-bidi-protocol "0.2.11"
|
webdriver-bidi-protocol "0.3.6"
|
||||||
ws "^8.18.3"
|
ws "^8.18.3"
|
||||||
|
|
||||||
puppeteer-extra-plugin-stealth@^2.11.2:
|
puppeteer-extra-plugin-stealth@^2.11.2:
|
||||||
@@ -6022,16 +6028,16 @@ puppeteer-extra@^3.3.6:
|
|||||||
debug "^4.1.1"
|
debug "^4.1.1"
|
||||||
deepmerge "^4.2.2"
|
deepmerge "^4.2.2"
|
||||||
|
|
||||||
puppeteer@^24.22.3:
|
puppeteer@^24.23.0:
|
||||||
version "24.22.3"
|
version "24.23.0"
|
||||||
resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-24.22.3.tgz#07dcfabdb4e924b014cb7b96bcc92f43086e637e"
|
resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-24.23.0.tgz#fa3c1bffc1b40c3d7a59b9463d444ff4be69f5c7"
|
||||||
integrity sha512-mnhXzIqSYSJ1SMv1RYH07YMzWP81xCmmQj91Q8iQMZqnf97eVzeHgsGL6kpywiGCi+nQafta/+NkwM4URMy/XQ==
|
integrity sha512-BVR1Lg8sJGKXY79JARdIssFWK2F6e1j+RyuJP66w4CUmpaXjENicmA3nNpUXA8lcTdDjAndtP+oNdni3T/qQqA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@puppeteer/browsers" "2.10.10"
|
"@puppeteer/browsers" "2.10.10"
|
||||||
chromium-bidi "9.1.0"
|
chromium-bidi "9.1.0"
|
||||||
cosmiconfig "^9.0.0"
|
cosmiconfig "^9.0.0"
|
||||||
devtools-protocol "0.0.1495869"
|
devtools-protocol "0.0.1508733"
|
||||||
puppeteer-core "24.22.3"
|
puppeteer-core "24.23.0"
|
||||||
typed-query-selector "^2.12.0"
|
typed-query-selector "^2.12.0"
|
||||||
|
|
||||||
qs@^6.14.0:
|
qs@^6.14.0:
|
||||||
@@ -7408,10 +7414,10 @@ vfile@^6.0.0:
|
|||||||
"@types/unist" "^3.0.0"
|
"@types/unist" "^3.0.0"
|
||||||
vfile-message "^4.0.0"
|
vfile-message "^4.0.0"
|
||||||
|
|
||||||
vite@7.1.7:
|
vite@7.1.9:
|
||||||
version "7.1.7"
|
version "7.1.9"
|
||||||
resolved "https://registry.yarnpkg.com/vite/-/vite-7.1.7.tgz#ed3f9f06e21d6574fe1ad425f6b0912d027ffc13"
|
resolved "https://registry.yarnpkg.com/vite/-/vite-7.1.9.tgz#ba844410e5d0c0f2a4eaf17a52af60ebea322cbf"
|
||||||
integrity sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==
|
integrity sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg==
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild "^0.25.0"
|
esbuild "^0.25.0"
|
||||||
fdir "^6.5.0"
|
fdir "^6.5.0"
|
||||||
@@ -7427,10 +7433,10 @@ web-streams-polyfill@^3.0.3:
|
|||||||
resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b"
|
resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b"
|
||||||
integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==
|
integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==
|
||||||
|
|
||||||
webdriver-bidi-protocol@0.2.11:
|
webdriver-bidi-protocol@0.3.6:
|
||||||
version "0.2.11"
|
version "0.3.6"
|
||||||
resolved "https://registry.yarnpkg.com/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.2.11.tgz#dba18d9b0a33aed33fab272dbd6e42411ac753cc"
|
resolved "https://registry.yarnpkg.com/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.3.6.tgz#55ad4ff9697532e3e04fb0446bb6dd4c158b3ad5"
|
||||||
integrity sha512-Y9E1/oi4XMxcR8AT0ZC4OvYntl34SPgwjmELH+owjBr0korAX4jKgZULBWILGCVGdVCQ0dodTToIETozhG8zvA==
|
integrity sha512-mlGndEOA9yK9YAbvtxaPTqdi/kaCWYYfwrZvGzcmkr/3lWM+tQj53BxtpVd6qbC6+E5OnHXgCcAhre6AkXzxjA==
|
||||||
|
|
||||||
whatwg-encoding@^3.1.1:
|
whatwg-encoding@^3.1.1:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
|
|||||||
Reference in New Issue
Block a user