Compare commits

...

11 Commits

Author SHA1 Message Date
Claude
d34627b3f1 [AI] Improve release-note-generator to auto-create draft PRs
Rework the CLI tool so contributors no longer need to know the PR number
upfront. The new flow:

1. Asks for username, category, and summary first
2. Checks for an existing PR on the current branch
3. If no PR exists and `gh` CLI is available, offers to create a draft PR
   automatically (pushes the branch and runs `gh pr create --draft`)
4. Falls back to manual PR number entry

This eliminates the clunky workflow of having to create a draft PR
separately just to get the PR number for the release notes filename.

https://claude.ai/code/session_014ncE7UnSdms47J3Xwg6DYG
2026-03-07 21:27:58 +00:00
mibragimov
edce092ae8 fix(csv-import): trim whitespace from amount strings before parsing (#7149)
* fix(csv-import): trim whitespace from amount strings before parsing

looselyParseAmount relies on a regex anchored at $ to detect decimal
markers. Trailing whitespace (e.g. from Excel-saved CSVs) shifts the
pattern match so a thousands separator is misidentified as a decimal
point, producing wildly wrong values.

Adding trim() at the top of the function eliminates trailing/leading
whitespace before any regex logic runs.

Fixes actualbudget/actual#7121

* chore: add release notes for #7149
2026-03-07 20:41:47 +00:00
dependabot[bot]
77411394f6 Bump dompurify from 3.3.0 to 3.3.2 (#7143)
Bumps [dompurify](https://github.com/cure53/DOMPurify) from 3.3.0 to 3.3.2.
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/3.3.0...3.3.2)

---
updated-dependencies:
- dependency-name: dompurify
  dependency-version: 3.3.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
2026-03-07 20:24:41 +00:00
dependabot[bot]
235d94478f Bump express-rate-limit from 8.2.1 to 8.2.2 (#7140)
* Bump express-rate-limit from 8.2.1 to 8.2.2

Bumps [express-rate-limit](https://github.com/express-rate-limit/express-rate-limit) from 8.2.1 to 8.2.2.
- [Release notes](https://github.com/express-rate-limit/express-rate-limit/releases)
- [Commits](https://github.com/express-rate-limit/express-rate-limit/compare/v8.2.1...v8.2.2)

---
updated-dependencies:
- dependency-name: express-rate-limit
  dependency-version: 8.2.2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

* [AI] Update express-rate-limit to 8.3.0 to fix GHSA-46wh-pxpv-q5gq vulnerability

Co-authored-by: Matiss Janis Aboltins <MatissJanis@users.noreply.github.com>

* Add release notes for PR #7140

* [AI] Update release notes to reflect version 8.3.0

Co-authored-by: Matiss Janis Aboltins <MatissJanis@users.noreply.github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Matiss Janis Aboltins <MatissJanis@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-07 19:48:56 +00:00
Matiss Janis Aboltins
7e0edd43ec Sort theme catalog items alphabetically by name (#7144)
* [AI] Sort custom theme catalog options alphabetically in the UI

Sort catalog themes by name using localeCompare before rendering,
without modifying the underlying JSON data file.

https://claude.ai/code/session_01Y5SGaVYqsVWVsvXV8ZFXj3

* Add release notes for PR #7144

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-06 23:01:07 +00:00
Matiss Janis Aboltins
fdf5c8d0a9 [AI] Move window typings import to globals.ts (#7142)
* [AI] Remove parent path import of window.ts from desktop-client tsconfig

Replace the `../../packages/loot-core/typings/window.ts` include in
desktop-client's tsconfig.json with a proper package import. This adds
a `./typings/*` export to loot-core's package.json and creates a
globals.ts file in desktop-client that imports the window types via
the package name.

https://claude.ai/code/session_01GrgAzjWd3XvqwBTfXLerxc

* [autofix.ci] apply automated fixes

* Add release notes for PR #7142

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-06 23:00:50 +00:00
Matt Fiddaman
a8ec84ceac stop font size fluctuations showing in summary cards (#7092)
* stop summary cards from showing until font size settled

* note
2026-03-06 23:00:41 +00:00
Michael Clark
b727124603 Fix docker images (#7146)
* fix docker images

* release notes
2026-03-06 22:55:21 +00:00
dependabot[bot]
8bb7f207f2 Bump svgo from 3.3.2 to 3.3.3 (#7130)
Bumps [svgo](https://github.com/svg/svgo) from 3.3.2 to 3.3.3.
- [Release notes](https://github.com/svg/svgo/releases)
- [Commits](https://github.com/svg/svgo/compare/v3.3.2...v3.3.3)

---
updated-dependencies:
- dependency-name: svgo
  dependency-version: 3.3.3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-06 21:56:01 +00:00
dependabot[bot]
6e0c15eb12 Bump immutable from 5.1.3 to 5.1.5 (#7129)
Bumps [immutable](https://github.com/immutable-js/immutable-js) from 5.1.3 to 5.1.5.
- [Release notes](https://github.com/immutable-js/immutable-js/releases)
- [Changelog](https://github.com/immutable-js/immutable-js/blob/main/CHANGELOG.md)
- [Commits](https://github.com/immutable-js/immutable-js/compare/v5.1.3...v5.1.5)

---
updated-dependencies:
- dependency-name: immutable
  dependency-version: 5.1.5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
2026-03-06 21:51:14 +00:00
Michael Clark
4e2cec2c7a 🧪 Improving docker image test resiliency (#7141)
* improving test resiliency

* release notes
2026-03-06 21:02:31 +00:00
20 changed files with 255 additions and 75 deletions

View File

@@ -87,8 +87,8 @@ jobs:
- name: Test that the docker image boots
run: |
docker run --detach --network=host actualbudget/actual-server-testing
sleep 5
curl --fail -sS -LI -w '%{http_code}\n' --retry 10 --retry-delay 1 --retry-connrefused localhost:5006
sleep 10
curl --fail -sS -LI -w '%{http_code}\n' --retry 20 --retry-delay 1 --retry-connrefused localhost:5006
# This will use the cache from the earlier build step and not rebuild the image
# https://docs.docker.com/build/ci/github-actions/test-before-push/

View File

@@ -1,35 +1,32 @@
import { exec } from 'node:child_process';
import { existsSync, writeFile } from 'node:fs';
import { existsSync, writeFileSync } from 'node:fs';
import { exit } from 'node:process';
import prompts from 'prompts';
async function run() {
const username = await execAsync(
"gh api user --jq '.login'",
'To avoid having to enter your username, consider installing the official GitHub CLI (https://github.com/cli/cli) and logging in with `gh auth login`.',
);
const activePr = await getActivePr(username);
if (activePr) {
const hasGhCli = await checkGhCli();
const username = hasGhCli ? await execAsync("gh api user --jq '.login'") : '';
if (!username) {
console.log(
`Found potentially matching PR ${activePr.number}: ${activePr.title}`,
'Tip: Install the GitHub CLI (https://github.com/cli/cli) and run `gh auth login` to enable auto-detection of your username and auto-creation of draft PRs.',
);
}
const initialPrNumber = activePr?.number ?? (await getNextPrNumber());
const result = await prompts([
const activePr = await getActivePr(username);
if (activePr) {
console.log(`Found existing PR #${activePr.number}: ${activePr.title}`);
}
// Ask for category and summary first (before PR number)
const initial = await prompts([
{
name: 'githubUsername',
message: 'Comma-separated GitHub username(s)',
type: 'text',
initial: username,
},
{
name: 'pullRequestNumber',
message: 'PR Number',
type: 'number',
initial: initialPrNumber,
},
{
name: 'releaseNoteType',
message: 'Release Note Type',
@@ -50,28 +47,65 @@ async function run() {
]);
if (
!result.githubUsername ||
!result.oneLineSummary ||
!result.releaseNoteType ||
!result.pullRequestNumber
!initial.githubUsername ||
!initial.oneLineSummary ||
initial.releaseNoteType === undefined
) {
console.log('All questions must be answered. Exiting');
exit(1);
}
// Determine PR number: use existing PR, offer to create draft, or ask manually
let prNumber: number;
if (activePr) {
prNumber = activePr.number;
} else if (hasGhCli) {
const { action } = await prompts({
name: 'action',
message: 'No existing PR found. How would you like to get the PR number?',
type: 'select',
choices: [
{
title: '🚀 Create a draft PR automatically',
value: 'create-draft',
description:
'Creates a draft PR using the GitHub CLI and uses its number',
},
{
title: '✏️ Enter PR number manually',
value: 'manual',
description: 'Enter a PR number you already know',
},
],
});
if (!action) {
console.log('Exiting');
exit(1);
}
if (action === 'create-draft') {
prNumber = await createDraftPr(initial.oneLineSummary);
} else {
prNumber = await askForPrNumber();
}
} else {
prNumber = await askForPrNumber();
}
const fileContents = getFileContents(
result.releaseNoteType,
result.githubUsername,
result.oneLineSummary,
initial.releaseNoteType,
initial.githubUsername,
initial.oneLineSummary,
);
const prNumber = result.pullRequestNumber;
const filepath = `./upcoming-release-notes/${prNumber}.md`;
if (existsSync(filepath)) {
const { confirm } = await prompts({
name: 'confirm',
type: 'confirm',
message: `This will overwrite the existing release note ${filepath} Are you sure?`,
message: `This will overwrite the existing release note ${filepath}. Are you sure?`,
});
if (!confirm) {
console.log('Exiting');
@@ -79,14 +113,79 @@ async function run() {
}
}
writeFile(filepath, fileContents, err => {
if (err) {
console.error('Failed to write release note file:', err);
writeFileSync(filepath, fileContents);
console.log(`Release note generated successfully: ${filepath}`);
}
async function checkGhCli(): Promise<boolean> {
const result = await execAsync('gh --version');
return result !== '';
}
async function createDraftPr(title: string): Promise<number> {
// Ensure current branch is pushed to remote
const branchName = await execAsync('git rev-parse --abbrev-ref HEAD');
if (!branchName || branchName === 'master' || branchName === 'main') {
console.error(
'Cannot create a draft PR from the main/master branch. Please switch to a feature branch first.',
);
exit(1);
}
console.log(`Pushing branch "${branchName}" to remote...`);
const pushResult = await execAsync(
`git push -u origin ${branchName} 2>&1`,
'Failed to push branch to remote. Please push manually first.',
);
if (pushResult === '') {
// execAsync returns '' on error
exit(1);
}
console.log('Creating draft PR...');
const prUrl = await execAsync(
`gh pr create --draft --title "${title.replace(/"/g, '\\"')}" --body "" 2>&1`,
'Failed to create draft PR. Please create one manually via GitHub.',
);
if (!prUrl) {
exit(1);
}
// Extract PR number from URL (e.g., https://github.com/owner/repo/pull/1234)
const prNumberMatch = prUrl.match(/\/pull\/(\d+)/);
if (!prNumberMatch) {
// gh pr create might return just a number or other format
const numberMatch = prUrl.match(/(\d+)/);
if (!numberMatch) {
console.error('Could not parse PR number from output:', prUrl);
exit(1);
} else {
console.log(`Release note generated successfully: ${filepath}`);
}
const prNumber = parseInt(numberMatch[1], 10);
console.log(`Draft PR #${prNumber} created: ${prUrl.trim()}`);
return prNumber;
}
const prNumber = parseInt(prNumberMatch[1], 10);
console.log(`Draft PR #${prNumber} created: ${prUrl.trim()}`);
return prNumber;
}
async function askForPrNumber(): Promise<number> {
const nextPrNumber = await getNextPrNumber();
const { pullRequestNumber } = await prompts({
name: 'pullRequestNumber',
message: 'PR Number',
type: 'number',
initial: nextPrNumber,
});
if (!pullRequestNumber) {
console.log('PR number is required. Exiting');
exit(1);
}
return pullRequestNumber;
}
// makes an attempt to find an existing open PR from <username>:<branch>
@@ -169,7 +268,9 @@ async function execAsync(cmd: string, errorLog?: string): Promise<string> {
return new Promise<string>(res => {
exec(cmd, (error, stdout) => {
if (error) {
console.log(errorLog);
if (errorLog) {
console.log(errorLog);
}
res('');
} else {
res(stdout.trim());

View File

@@ -49,6 +49,7 @@ export function FormulaResult({
containerRef,
}: FormulaResultProps) {
const [fontSize, setFontSize] = useState<number>(initialFontSize);
const [hasSized, setHasSized] = useState(false);
const refDiv = useRef<HTMLDivElement>(null);
const previousFontSizeRef = useRef<number>(initialFontSize);
const format = useFormat();
@@ -89,7 +90,10 @@ export function FormulaResult({
height, // Ensure the text fits vertically by using the height as the limiting factor
);
setFontSize(calculatedFontSize);
if (calculatedFontSize > 0) {
setFontSize(calculatedFontSize);
setHasSized(true);
}
// Only call fontSizeChanged if the font size actually changed
if (
@@ -143,6 +147,7 @@ export function FormulaResult({
useEffect(() => {
if (fontSizeMode === 'static') {
setFontSize(staticFontSize);
setHasSized(true);
}
}, [fontSizeMode, staticFontSize]);
@@ -153,6 +158,8 @@ export function FormulaResult({
? theme.errorText
: theme.pageText;
const showContent = hasSized || fontSizeMode === 'static';
return (
<View style={{ flex: 1 }}>
{loading && <LoadingIndicator />}
@@ -175,9 +182,13 @@ export function FormulaResult({
color,
}}
>
<span aria-hidden="true">
<PrivacyFilter>{displayValue}</PrivacyFilter>
</span>
{!showContent ? (
<LoadingIndicator />
) : (
<span aria-hidden="true">
<PrivacyFilter>{displayValue}</PrivacyFilter>
</span>
)}
</View>
)}
</View>

View File

@@ -38,6 +38,7 @@ export function SummaryNumber({
}: SummaryNumberProps) {
const { t } = useTranslation();
const [fontSize, setFontSize] = useState<number>(initialFontSize);
const [hasSized, setHasSized] = useState(false);
const refDiv = useRef<HTMLDivElement>(null);
const format = useFormat();
const isNumericValue = Number.isFinite(value);
@@ -61,7 +62,10 @@ export function SummaryNumber({
height, // Ensure the text fits vertically by using the height as the limiting factor
);
setFontSize(calculatedFontSize);
if (calculatedFontSize > 0) {
setFontSize(calculatedFontSize);
setHasSized(true);
}
if (calculatedFontSize !== initialFontSize && fontSizeChanged) {
fontSizeChanged(calculatedFontSize);
@@ -107,9 +111,13 @@ export function SummaryNumber({
: theme.reportsNumberPositive,
}}
>
<FinancialText aria-hidden="true">
<PrivacyFilter>{displayAmount}</PrivacyFilter>
</FinancialText>
{!hasSized ? (
<LoadingIndicator />
) : (
<FinancialText aria-hidden="true">
<PrivacyFilter>{displayAmount}</PrivacyFilter>
</FinancialText>
)}
</View>
)}
</>

View File

@@ -246,7 +246,9 @@ export function ThemeInstaller({
return null;
}
const catalogItems = catalog ?? [];
const catalogItems = [...(catalog ?? [])].sort((a, b) =>
a.name.localeCompare(b.name),
);
const itemsPerRow = getItemsPerRow(width);
const rows: CatalogTheme[][] = [];
for (let i = 0; i < catalogItems.length; i += itemsPerRow) {

View File

@@ -0,0 +1 @@
import 'loot-core/typings/window';

View File

@@ -19,13 +19,7 @@
{ "path": "../loot-core" },
{ "path": "../component-library" }
],
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.js",
// TODO: remove loot-core dependency
"../../packages/loot-core/typings/window.ts"
],
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.js"],
"exclude": [
"node_modules",
"build",

View File

@@ -49,6 +49,7 @@
"./shared/*": "./src/shared/*.ts",
"./types/models": "./src/types/models/index.ts",
"./types/*": "./src/types/*.ts",
"./typings/*": "./typings/*.ts",
"./lib-dist/electron/bundle.desktop.js": "./lib-dist/electron/bundle.desktop.js"
},
"scripts": {

View File

@@ -72,6 +72,15 @@ describe('utility functions', () => {
expect(looselyParseAmount('(1 500.99)')).toBe(-1500.99);
});
test('looseParseAmount handles trailing whitespace', () => {
expect(looselyParseAmount('1055 ')).toBe(1055);
expect(looselyParseAmount('$1,055 ')).toBe(1055);
expect(looselyParseAmount('$1,055.00 ')).toBe(1055);
expect(looselyParseAmount(' $1,055 ')).toBe(1055);
expect(looselyParseAmount('3.45 ')).toBe(3.45);
expect(looselyParseAmount(' 3.45 ')).toBe(3.45);
});
test('number formatting works with comma-dot format', () => {
setNumberFormat({ format: 'comma-dot', hideFraction: false });
let formatter = getNumberFormat().formatter;

View File

@@ -550,6 +550,8 @@ export function looselyParseAmount(amount: string) {
return v.replace(/[^0-9-]/g, '');
}
amount = amount.trim();
if (amount.startsWith('(') && amount.endsWith(')')) {
// Remove Unicode minus inside parentheses before converting to ASCII minus
amount = amount.replace(/\u2212/g, '');

View File

@@ -38,7 +38,7 @@
"date-fns": "^4.1.0",
"debug": "^4.4.3",
"express": "^5.2.1",
"express-rate-limit": "^8.2.1",
"express-rate-limit": "^8.3.0",
"express-winston": "^4.2.0",
"ipaddr.js": "^2.3.0",
"jws": "^4.0.1",

View File

@@ -28,7 +28,9 @@ export async function run(direction: 'up' | 'down' = 'up'): Promise<void> {
> = {};
for (const f of files
.filter(f => f.endsWith('.js') || f.endsWith('.ts'))
.filter(
f => (f.endsWith('.js') || f.endsWith('.ts')) && !f.endsWith('.d.ts'),
)
.sort()) {
migrationsModules[f] = await import(
pathToFileURL(path.join(migrationsDir, f)).href

View File

@@ -0,0 +1,6 @@
---
category: Bugfixes
authors: [matt-fidd]
---
Stop font size fluctuations showing in summary cards

View File

@@ -0,0 +1,6 @@
---
category: Maintenance
authors: [dependabot[bot]]
---
Bump `express-rate-limit` dependency version from 8.2.1 to 8.3.0 for improvements.

View File

@@ -0,0 +1,6 @@
---
category: Maintenance
authors: [MikesGlitch]
---
Adding more retries to the Docker test in the pipeline

View File

@@ -0,0 +1,6 @@
---
category: Maintenance
authors: [MatissJanis]
---
Refactor TypeScript typings by moving window import to globals.ts for cleaner configuration.

View File

@@ -0,0 +1,6 @@
---
category: Enhancements
authors: [MatissJanis]
---
Sort theme catalog items alphabetically by name for improved user interface organization.

View File

@@ -0,0 +1,6 @@
---
category: Bugfix
authors: [MikesGlitch]
---
Fix migrations retrieval when running the docker images

View File

@@ -0,0 +1,6 @@
---
category: Bugfixes
authors: [mibragimov]
---
Fix CSV import incorrectly parsing transaction amounts that contain trailing whitespace (e.g. amounts from Excel-saved CSV files).

View File

@@ -118,7 +118,7 @@ __metadata:
date-fns: "npm:^4.1.0"
debug: "npm:^4.4.3"
express: "npm:^5.2.1"
express-rate-limit: "npm:^8.2.1"
express-rate-limit: "npm:^8.3.0"
express-winston: "npm:^4.2.0"
http-proxy-middleware: "npm:^3.0.5"
ipaddr.js: "npm:^2.3.0"
@@ -8852,13 +8852,6 @@ __metadata:
languageName: node
linkType: hard
"@trysound/sax@npm:0.2.0":
version: 0.2.0
resolution: "@trysound/sax@npm:0.2.0"
checksum: 10/7379713eca480ac0d9b6c7b063e06b00a7eac57092354556c81027066eb65b61ea141a69d0cc2e15d32e05b2834d4c9c2184793a5e36bbf5daf05ee5676af18c
languageName: node
linkType: hard
"@tsconfig/node10@npm:^1.0.7":
version: 1.0.11
resolution: "@tsconfig/node10@npm:1.0.11"
@@ -14699,14 +14692,14 @@ __metadata:
linkType: hard
"dompurify@npm:^3.2.5":
version: 3.3.0
resolution: "dompurify@npm:3.3.0"
version: 3.3.2
resolution: "dompurify@npm:3.3.2"
dependencies:
"@types/trusted-types": "npm:^2.0.7"
dependenciesMeta:
"@types/trusted-types":
optional: true
checksum: 10/d8782b10a0454344476936c91038d06c9450b3e3ada2ceb8f722525e6b54e64d847939b9f35bf385facd4139f0a2eaf7f5553efce351f8e9295620570875f002
checksum: 10/3ca02559677ce6d9583a500f21ffbb6b9e88f1af99f69fa0d0d9442cddbac98810588c869f8b435addb5115492d6e49870024bca322169b941bafedb99c7f281
languageName: node
linkType: hard
@@ -15917,14 +15910,14 @@ __metadata:
languageName: node
linkType: hard
"express-rate-limit@npm:^8.2.1":
version: 8.2.1
resolution: "express-rate-limit@npm:8.2.1"
"express-rate-limit@npm:^8.3.0":
version: 8.3.0
resolution: "express-rate-limit@npm:8.3.0"
dependencies:
ip-address: "npm:10.0.1"
ip-address: "npm:10.1.0"
peerDependencies:
express: ">= 4.11"
checksum: 10/7cbf70df2e88e590e463d2d8f93380775b2ea181d97f2c50c2ff9f2c666c247f83109a852b21d9c99ccc5762119101f281f54a27252a2f1a0a918be6d71f955b
checksum: 10/e896a66fecc10639e65873186fdfb71f19d6af650220eb7ea5450725215c3eed8dc6ddcfa1e68a9db8c9facc3326fbc281512ad3ccd8f107f42a2466ce12c18c
languageName: node
linkType: hard
@@ -18010,9 +18003,9 @@ __metadata:
linkType: hard
"immutable@npm:^5.0.2":
version: 5.1.3
resolution: "immutable@npm:5.1.3"
checksum: 10/6d29b29036143e7ea1e7f7be581c71bca873ea77c175d33c6c839bf4017265a58c41ec269e3ffcd7b483797fc7fa9c928b4ed3d6edfeeb1b5711d84f60d04090
version: 5.1.5
resolution: "immutable@npm:5.1.5"
checksum: 10/7aec2740239772ec8e92e793c991bd809203a97694f4ff3a18e50e28f9a6b02393ad033d87b458037bdf8c0ea37d4446d640e825f6171df3405cf6cf300ce028
languageName: node
linkType: hard
@@ -18145,7 +18138,14 @@ __metadata:
languageName: node
linkType: hard
"ip-address@npm:10.0.1, ip-address@npm:^10.0.1":
"ip-address@npm:10.1.0":
version: 10.1.0
resolution: "ip-address@npm:10.1.0"
checksum: 10/a6979629d1ad9c1fb424bc25182203fad739b40225aebc55ec6243bbff5035faf7b9ed6efab3a097de6e713acbbfde944baacfa73e11852bb43989c45a68d79e
languageName: node
linkType: hard
"ip-address@npm:^10.0.1":
version: 10.0.1
resolution: "ip-address@npm:10.0.1"
checksum: 10/09731acda32cd8e14c46830c137e7e5940f47b36d63ffb87c737331270287d631cf25aa95570907a67d3f919fdb25f4470c404eda21e62f22e0a55927f4dd0fb
@@ -25613,6 +25613,13 @@ __metadata:
languageName: node
linkType: hard
"sax@npm:^1.5.0":
version: 1.5.0
resolution: "sax@npm:1.5.0"
checksum: 10/9012ff37dda7a7ac5da45db2143b04036103e8bef8d586c3023afd5df6caf0ebd7f38017eee344ad2e2247eded7d38e9c42cf291d8dd91781352900ac0fd2d9f
languageName: node
linkType: hard
"saxes@npm:^6.0.0":
version: 6.0.0
resolution: "saxes@npm:6.0.0"
@@ -26938,19 +26945,19 @@ __metadata:
linkType: hard
"svgo@npm:^3.0.2, svgo@npm:^3.2.0":
version: 3.3.2
resolution: "svgo@npm:3.3.2"
version: 3.3.3
resolution: "svgo@npm:3.3.3"
dependencies:
"@trysound/sax": "npm:0.2.0"
commander: "npm:^7.2.0"
css-select: "npm:^5.1.0"
css-tree: "npm:^2.3.1"
css-what: "npm:^6.1.0"
csso: "npm:^5.0.5"
picocolors: "npm:^1.0.0"
sax: "npm:^1.5.0"
bin:
svgo: ./bin/svgo
checksum: 10/82fdea9b938884d808506104228e4d3af0050d643d5b46ff7abc903ff47a91bbf6561373394868aaf07a28f006c4057b8fbf14bbd666298abdd7cc590d4f7700
checksum: 10/f3c1b4d05d1704483e53515d5995af5f06a2718df85e3a8320f57bb256b8dc926b84c87a1a9b98e9d3ca1224314cc0676a803bdd03163508292f2d45c7077096
languageName: node
linkType: hard