diff --git a/lib/modules/datasource/api.ts b/lib/modules/datasource/api.ts index 082c3a7d7b6..1893ea6d19f 100644 --- a/lib/modules/datasource/api.ts +++ b/lib/modules/datasource/api.ts @@ -61,6 +61,7 @@ import { PuppetForgeDatasource } from './puppet-forge'; import { PypiDatasource } from './pypi'; import { PythonVersionDatasource } from './python-version'; import { RepologyDatasource } from './repology'; +import { RpmDatasource } from './rpm'; import { RubyVersionDatasource } from './ruby-version'; import { RubygemsDatasource } from './rubygems'; import { SbtPackageDatasource } from './sbt-package'; @@ -139,6 +140,7 @@ api.set(PuppetForgeDatasource.id, new PuppetForgeDatasource()); api.set(PypiDatasource.id, new PypiDatasource()); api.set(PythonVersionDatasource.id, new PythonVersionDatasource()); api.set(RepologyDatasource.id, new RepologyDatasource()); +api.set(RpmDatasource.id, new RpmDatasource()); api.set(RubyVersionDatasource.id, new RubyVersionDatasource()); api.set(RubygemsDatasource.id, new RubygemsDatasource()); api.set(SbtPackageDatasource.id, new SbtPackageDatasource()); diff --git a/lib/modules/datasource/rpm/index.spec.ts b/lib/modules/datasource/rpm/index.spec.ts new file mode 100644 index 00000000000..7b171b621fa --- /dev/null +++ b/lib/modules/datasource/rpm/index.spec.ts @@ -0,0 +1,451 @@ +import { gzipSync } from 'node:zlib'; +import { RpmDatasource } from '.'; +import * as httpMock from '~test/http-mock'; + +describe('modules/datasource/rpm/index', () => { + describe('getPrimaryGzipUrl', () => { + const registryUrl = 'https://example.com/repo/repodata/'; + const rpmDatasource = new RpmDatasource(); + + it('returns the correct primary.xml URL', async () => { + const repomdXml = ` + + + + +`; + + httpMock + .scope(registryUrl) + .get('/repomd.xml') + .reply(200, repomdXml, { 'Content-Type': 'application/gzip' }); + + const primaryXmlUrl = await rpmDatasource.getPrimaryGzipUrl(registryUrl); + + expect(primaryXmlUrl).toBe( + 'https://example.com/repo/repodata/somesha256-primary.xml.gz', + ); + }); + + it('throws an error if repomd.xml is missing', async () => { + httpMock.scope(registryUrl).get('/repomd.xml').reply(404, 'Not Found'); + + await expect( + rpmDatasource.getPrimaryGzipUrl(registryUrl), + ).rejects.toThrow(`Response code 404 (Not Found)`); + }); + + it('throws an error if http.getText fails', async () => { + httpMock + .scope(registryUrl) + .get('/repomd.xml') + .replyWithError('Network error'); + + await expect( + rpmDatasource.getPrimaryGzipUrl(registryUrl), + ).rejects.toThrow('Network error'); + }); + + it('throws an error if repomdXml is not in XML format', async () => { + const repomdXml = ` + + + + +`; + httpMock + .scope(registryUrl) + .get('/repomd.xml') + .reply(200, repomdXml, { 'Content-Type': 'application/xml' }); + await expect( + rpmDatasource.getPrimaryGzipUrl(registryUrl), + ).rejects.toThrow(`is not in XML format.`); + }); + + it('throws an error if no primary data is found', async () => { + const repomdXml = ` + + + + +`; + + httpMock + .scope(registryUrl) + .get('/repomd.xml') + .reply(200, repomdXml, { 'Content-Type': 'application/xml' }); + + await expect( + rpmDatasource.getPrimaryGzipUrl(registryUrl), + ).rejects.toThrow( + 'No primary data found in https://example.com/repo/repodata/repomd.xml', + ); + }); + + it('throws an error if no location element is found', async () => { + const repomdXml = ` + + + + +`; + + httpMock + .scope(registryUrl) + .get('/repomd.xml') + .reply(200, repomdXml, { 'Content-Type': 'application/xml' }); + + await expect( + rpmDatasource.getPrimaryGzipUrl(registryUrl), + ).rejects.toThrow( + 'No location element found in https://example.com/repo/repodata/repomd.xml', + ); + }); + + it('throws an error if location href is missing', async () => { + const repomdXml = ` + + + + +`; + + httpMock + .scope(registryUrl) + .get('/repomd.xml') + .reply(200, repomdXml, { 'Content-Type': 'application/xml' }); + + await expect( + rpmDatasource.getPrimaryGzipUrl(registryUrl), + ).rejects.toThrow( + `No href found in https://example.com/repo/repodata/repomd.xml`, + ); + }); + }); + + describe('getReleasesByPackageName', () => { + const packageName = 'example-package'; + const rpmDatasource = new RpmDatasource(); + const primaryXmlUrl = + 'https://example.com/repo/repodata/somesha256-primary.xml.gz'; + + it('returns the correct releases', async () => { + const primaryXml = ` + + + example-package + x86_64 + + + + example-package + x86_64 + + + + example-package + x86_64 + + + + example-package + x86_64 + + + +`; + // gzip the primaryXml content + const gzippedPrimaryXml = gzipSync(primaryXml); + httpMock + .scope(primaryXmlUrl.replace(/\/[^/]+$/, '')) + .get('/somesha256-primary.xml.gz') + .reply(200, gzippedPrimaryXml, { + 'Content-Type': 'application/gzip', + }); + const releases = await rpmDatasource.getReleasesByPackageName( + primaryXmlUrl, + packageName, + ); + expect(releases).toEqual({ + releases: [ + { + version: '1.0-2.azl3', + }, + { + version: '1.1-1.azl3', + }, + { + version: '1.1-2.azl3', + }, + { + version: '1.2', + }, + ], + }); + }); + + it('throws an error if somesha256-primary.xml.gz is not found', async () => { + httpMock + .scope(primaryXmlUrl.replace(/\/[^/]+$/, '')) + .get('/somesha256-primary.xml.gz') + .reply(404, 'Not Found'); + + await expect( + rpmDatasource.getReleasesByPackageName(primaryXmlUrl, packageName), + ).rejects.toThrow(`Response code 404 (Not Found)`); + }); + + it('throws an error if response.body is empty', async () => { + httpMock + .scope(primaryXmlUrl.replace(/\/[^/]+$/, '')) + .get('/somesha256-primary.xml.gz') + .reply(200, '', { 'Content-Type': 'application/gzip' }); + + await expect( + rpmDatasource.getReleasesByPackageName(primaryXmlUrl, packageName), + ).rejects.toThrowError( + 'Empty response body from getting ' + primaryXmlUrl + '.', + ); + }); + + it('returns null if no element package is found in primary.xml', async () => { + const primaryXml = ` + + + example-package + x86_64 + + + +`; + // gzip the primaryXml content + const gzippedprimaryXml = gzipSync(primaryXml); + httpMock + .scope(primaryXmlUrl.replace(/\/[^/]+$/, '')) + .get('/somesha256-primary.xml.gz') + .reply(200, gzippedprimaryXml, { + 'Content-Type': 'application/gzip', + }); + const result = await rpmDatasource.getReleasesByPackageName( + primaryXmlUrl, + packageName, + ); + expect(result).toBeNull(); + }); + + it('returns null if the specific packageName is not found in primary.xml', async () => { + const primaryXml = ` + + + wrong-package + x86_64 + + + +`; + // gzip the primaryXml content + const gzippedprimaryXml = gzipSync(primaryXml); + httpMock + .scope(primaryXmlUrl.replace(/\/[^/]+$/, '')) + .get('/somesha256-primary.xml.gz') + .reply(200, gzippedprimaryXml, { + 'Content-Type': 'application/gzip', + }); + expect( + await rpmDatasource.getReleasesByPackageName( + primaryXmlUrl, + packageName, + ), + ).toBeNull(); + }); + + it('returns an empty array if version is not found in a version element', async () => { + const primaryXml = ` + + + example-package + x86_64 + + + +`; + // gzip the primaryXml content + const gzippedprimaryXml = gzipSync(primaryXml); + httpMock + .scope(primaryXmlUrl.replace(/\/[^/]+$/, '')) + .get('/somesha256-primary.xml.gz') + .reply(200, gzippedprimaryXml, { + 'Content-Type': 'application/gzip', + }); + const releases = await rpmDatasource.getReleasesByPackageName( + primaryXmlUrl, + packageName, + ); + expect(releases).toBeNull(); + }); + + // this is most likely a bug in the RPM XML file, but we can still handle it gracefully + it('returns an array of releases without duplicate versionWithRel', async () => { + const primaryXmlUrl = + 'https://example.com/repo/repodata/somesha256-primary.xml.gz'; + const primaryXml = ` + + + example-package + x86_64 + + + + example-package + x86_64 + + + +`; + // gzip the primaryXml content + const gzippedprimaryXml = gzipSync(primaryXml); + httpMock + .scope(primaryXmlUrl.replace(/\/[^/]+$/, '')) + .get('/somesha256-primary.xml.gz') + .reply(200, gzippedprimaryXml, { + 'Content-Type': 'application/gzip', + }); + const releases = await rpmDatasource.getReleasesByPackageName( + primaryXmlUrl, + packageName, + ); + expect(releases).toEqual({ + releases: [ + { + version: '1.0-dulp.azl3', + }, + ], + }); + }); + + it('handles parser error event in getReleasesByPackageName', async () => { + const primaryXmlMalformed = ` +<%$#metadata xmlns="http://linux.duke.edu/metadata/common"> + + example-package + x86_64 + + +`; + // gzip the primaryXml content + const gzippedprimaryXml = gzipSync(primaryXmlMalformed); + httpMock + .scope(primaryXmlUrl.replace(/\/[^/]+$/, '')) + .get('/somesha256-primary.xml.gz') + .reply(200, gzippedprimaryXml, { + 'Content-Type': 'application/gzip', + }); + await expect( + rpmDatasource.getReleasesByPackageName(primaryXmlUrl, packageName), + ).rejects.toThrowError('Unencoded <'); + }); + }); + + describe('getReleases', () => { + const registryUrl = 'https://example.com/repo/repodata/'; + const rpmDatasource = new RpmDatasource(); + + it('returns null if registryUrl is not provided', async () => { + const releases = await rpmDatasource.getReleases({ + registryUrl: undefined, + packageName: 'example-package', + }); + expect(releases).toBeNull(); + }); + + it('returns null if primaryXmlUrl is empty', async () => { + vi.spyOn(rpmDatasource, 'getPrimaryGzipUrl').mockResolvedValue(null); + const releases = await rpmDatasource.getReleases({ + registryUrl: 'someurl', + packageName: 'example-package', + }); + expect(releases).toBeNull(); + }); + + it('returns null if packageName is not provided', async () => { + const releases = await rpmDatasource.getReleases({ + registryUrl, + packageName: '', + }); + expect(releases).toBeNull(); + }); + + it('returns the correct releases', async () => { + //mock the getPrimaryGzipUrl method to return the primaryXmlUrl + vi.spyOn(rpmDatasource, 'getPrimaryGzipUrl').mockResolvedValue( + 'https://example.com/repo/repodata/', + ); + vi.spyOn(rpmDatasource, 'getReleasesByPackageName').mockResolvedValue({ + releases: [ + { + version: '1.0-2.azl3', + }, + { + version: '1.1-1.azl3', + }, + { + version: '1.1-2.azl3', + }, + { + version: '1.2', + }, + ], + }); + + const releases = await rpmDatasource.getReleases({ + registryUrl, + packageName: 'example-package', + }); + expect(releases).toEqual({ + releases: [ + { + version: '1.0-2.azl3', + }, + { + version: '1.1-1.azl3', + }, + { + version: '1.1-2.azl3', + }, + { + version: '1.2', + }, + ], + }); + }); + + it('throws an error if getPrimaryGzipUrl fails', async () => { + vi.spyOn(rpmDatasource, 'getPrimaryGzipUrl').mockRejectedValue( + new Error('Something wrong'), + ); + + await expect( + rpmDatasource.getReleases({ + registryUrl, + packageName: 'example-package', + }), + ).rejects.toThrow('Something wrong'); + }); + + it('throws an error if getReleasesByPackageName fails', async () => { + vi.spyOn(rpmDatasource, 'getPrimaryGzipUrl').mockResolvedValue( + 'https://example.com/repo/repodata/', + ); + vi.spyOn(rpmDatasource, 'getReleasesByPackageName').mockRejectedValue( + new Error('Something wrong'), + ); + + await expect( + rpmDatasource.getReleases({ + registryUrl, + packageName: 'example-package', + }), + ).rejects.toThrow('Something wrong'); + }); + }); +}); diff --git a/lib/modules/datasource/rpm/index.ts b/lib/modules/datasource/rpm/index.ts new file mode 100644 index 00000000000..0ba1758f51b --- /dev/null +++ b/lib/modules/datasource/rpm/index.ts @@ -0,0 +1,220 @@ +import { Readable } from 'node:stream'; +import { gunzip } from 'node:zlib'; +import { promisify } from 'util'; +import sax from 'sax'; +import { XmlDocument } from 'xmldoc'; +import { logger } from '../../../logger'; +import { cache } from '../../../util/cache/package/decorator'; +import type { HttpResponse } from '../../../util/http'; +import { joinUrlParts } from '../../../util/url'; +import { Datasource } from '../datasource'; +import type { GetReleasesConfig, Release, ReleaseResult } from '../types'; + +const gunzipAsync = promisify(gunzip); + +export class RpmDatasource extends Datasource { + static readonly id = 'rpm'; + + // repomd.xml is a standard file name in RPM repositories which contains metadata about the repository + static readonly repomdXmlFileName = 'repomd.xml'; + + constructor() { + super(RpmDatasource.id); + } + + /** + * Users are able to specify custom RPM repositories as long as they follow the format. + * There is a URI http://linux.duke.edu/metadata/common in the -primary.xml. + * But according to this post, it's not something we can really look into or reference. + * @see{https://lists.rpm.org/pipermail/rpm-ecosystem/2015-October/000283.html} + */ + override readonly customRegistrySupport = true; + + /** + * Fetches the release information for a given package from the registry URL. + * + * @param registryUrl - the registryUrl should be the folder which contains repodata.xml and its corresponding file list -primary.xml.gz, e.g.: https://packages.microsoft.com/azurelinux/3.0/prod/cloud-native/x86_64/repodata/ + * @param packageName - the name of the package to fetch releases for. + * @returns The release result if the package is found, otherwise null. + */ + @cache({ + namespace: `datasource-${RpmDatasource.id}`, + key: ({ registryUrl, packageName }: GetReleasesConfig) => + `${registryUrl}:${packageName}`, + ttlMinutes: 1440, + }) + async getReleases({ + registryUrl, + packageName, + }: GetReleasesConfig): Promise { + if (!registryUrl || !packageName) { + return null; + } + try { + const primaryGzipUrl = await this.getPrimaryGzipUrl(registryUrl); + if (!primaryGzipUrl) { + return null; + } + return await this.getReleasesByPackageName(primaryGzipUrl, packageName); + } catch (err) { + this.handleGenericErrors(err); + } + } + + // Fetches the primary.xml.gz URL from the repomd.xml file. + @cache({ + namespace: `datasource-${RpmDatasource.id}`, + key: (registryUrl: string) => registryUrl, + ttlMinutes: 1440, + }) + async getPrimaryGzipUrl(registryUrl: string): Promise { + const repomdUrl = joinUrlParts( + registryUrl, + RpmDatasource.repomdXmlFileName, + ); + const response = await this.http.getText(repomdUrl.toString()); + + // check if repomd.xml is in XML format + if (!response.body.startsWith(' { + let response: HttpResponse; + let decompressedBuffer: Buffer; + try { + // primaryGzipUrl is a .gz file, need to extract it before parsing + response = await this.http.getBuffer(primaryGzipUrl); + if (response.body.length === 0) { + logger.debug(`Empty response body from getting ${primaryGzipUrl}.`); + throw new Error(`Empty response body from getting ${primaryGzipUrl}.`); + } + // decompress the gzipped file + decompressedBuffer = await gunzipAsync(response.body); + } catch (err) { + logger.debug( + `Failed to fetch or decompress ${primaryGzipUrl}: ${ + err instanceof Error ? err.message : err + }`, + ); + throw err; + } + + // Use sax streaming parser to handle large XML files efficiently + // This allows us to parse the XML file without loading the entire file into memory. + const releases: Record = {}; + let insidePackage = false; + let isTargetPackage = false; + let insideName = false; + + // Create a SAX parser in strict mode + const saxParser = sax.createStream(true, { + lowercase: true, // normalize tag names to lowercase + trim: true, + }); + + saxParser.on('opentag', (node: sax.Tag) => { + if (node.name === 'package' && node.attributes.type === 'rpm') { + insidePackage = true; + isTargetPackage = false; + } + if (insidePackage && node.name === 'name') { + insideName = true; + } + if (insidePackage && isTargetPackage && node.name === 'version') { + // rel is optional + if (node.attributes.rel === undefined) { + const version = `${node.attributes.ver}`; + releases[version] = { version }; + } else { + const version = `${node.attributes.ver}-${node.attributes.rel}`; + releases[version] = { version }; + } + } + }); + saxParser.on('text', (text: string) => { + if (insidePackage && insideName) { + if (text.trim() === packageName) { + isTargetPackage = true; + } + } + }); + saxParser.on('closetag', (tag: string) => { + if (tag === 'name' && insidePackage) { + insideName = false; + } + if (tag === 'package') { + insidePackage = false; + isTargetPackage = false; + } + }); + + await new Promise((resolve, reject) => { + let settled = false; + saxParser.on('error', (err: Error) => { + if (settled) { + return; + } + settled = true; + logger.debug(`SAX parsing error in ${primaryGzipUrl}: ${err.message}`); + setImmediate(() => saxParser.removeAllListeners()); + reject(err); + }); + saxParser.on('end', () => { + settled = true; + setImmediate(() => saxParser.removeAllListeners()); + resolve(); + }); + Readable.from(decompressedBuffer).pipe(saxParser); + }); + + if (Object.keys(releases).length === 0) { + logger.trace( + `No releases found for package ${packageName} in ${primaryGzipUrl}`, + ); + return null; + } + return { + releases: Object.values(releases).map((release) => ({ + version: release.version, + })), + }; + } +} diff --git a/lib/modules/datasource/rpm/readme.md b/lib/modules/datasource/rpm/readme.md new file mode 100644 index 00000000000..3ec04b43049 --- /dev/null +++ b/lib/modules/datasource/rpm/readme.md @@ -0,0 +1,94 @@ +This datasource `rpm` returns releases of the RPM packages. +It assumes the RPM repository is following the RPM standard and by default it has a repomd.xml in the directory provided by user in the `registryUrl`. +According to this Pulp project doc, , + +> repomd.xml is the metadata file that clients use to discover what repository metadata files exist in the repository. +> It should always be located at repodata/repomd.xml relative to the root of the repository. + +## Set URL when using an RPM repository + +To use an RPM repository with the datasource, you must set a `registryUrl` with the directory that contains the `repomd.xml` and corresponding `primary.xml`. + +**Example**: + +If we have + +- `http://example.com/repo/repodata/repomd.xml` +- `http://example.com/repo/repodata/-primary.xml` where `` is a dynamically generated SHA256 pattern. + +Then the `registryUrl` should set as `http://example.com/repo/repodata/` or `http://example.com/repo/repodata`. + +## Usage Example + +Say you're defining dnf/tdnf/yum packages in a `manifest.json` and you want Renovate to update them. + +Assuming your `manifest.json` looks like this. + +```manifest.json +{ + "package1": "1.0.0-1", + "package2": "1.1.0" +} +``` + +where the versioning format could be `-`, or just `` + +```renovate.json +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "customManagers": [ + { + "customType": "regex", + "fileMatch": [ + "path_to_manifest_json" + ], + "registryUrlTemplate": "http://example.com/repo/repodata/", + "datasourceTemplate": "rpm" + } + ] +} +``` + +In an RPM repository, the `-primary.xml` looks like this: + +``` +` + + + example-package + x86_64 + + + + example-package + x86_64 + + + + example-package + x86_64 + + + + example-package + x86_64 + + +... +... + +``` + +You can see that `ver` and `rel` (release/revision) is stored separately. +But the RPM datasource implementation will combine these together as `ver-rel`. +That's why in the `manifest.json` above, the version is defined as `1.0.0-1`, if `rel` is available. +Or just `1.1.0` if `rel` is not available. + +## Limitation and Consideration + +In real-world scenarios, the decompressed `primary.xml` file from an RPM repository can be extremely large. +To handle this efficiently, this implementation uses streaming XML parsing, which processes the file incrementally and avoids loading the entire XML into memory. + +Streaming XML parsing is a practical solution for large files in Node.js, but for extremely large or complex cases (e.g., files exceeding ~512MB), you may still encounter memory or performance issues. +For such scenarios, consider using more robust approaches such as native modules, optimized SAX parsers, or external tools. +Contributions and suggestions for further improving large file handling are welcome. diff --git a/lib/util/cache/package/types.ts b/lib/util/cache/package/types.ts index f67588ed1b3..52524adec2d 100644 --- a/lib/util/cache/package/types.ts +++ b/lib/util/cache/package/types.ts @@ -96,6 +96,7 @@ export type PackageCacheNamespace = | 'datasource-python-version' | 'datasource-releases' | 'datasource-repology' + | 'datasource-rpm' | 'datasource-ruby-version' | 'datasource-rubygems' | 'datasource-sbt-package' diff --git a/package.json b/package.json index f0a0b529734..e30f8c9fb80 100644 --- a/package.json +++ b/package.json @@ -245,6 +245,7 @@ "remark": "13.0.0", "remark-github": "10.1.0", "safe-stable-stringify": "2.5.0", + "sax": "1.4.1", "semver": "7.7.2", "semver-stable": "3.0.0", "semver-utils": "1.1.4", @@ -304,6 +305,7 @@ "@types/node": "22.15.30", "@types/parse-link-header": "2.0.3", "@types/punycode": "2.1.4", + "@types/sax": "1.2.7", "@types/semver": "7.7.0", "@types/semver-stable": "3.0.2", "@types/semver-utils": "1.1.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 17933abb5c8..695125a4bad 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -308,6 +308,9 @@ importers: safe-stable-stringify: specifier: 2.5.0 version: 2.5.0 + sax: + specifier: 1.4.1 + version: 1.4.1 semver: specifier: 7.7.2 version: 7.7.2 @@ -465,6 +468,9 @@ importers: '@types/punycode': specifier: 2.1.4 version: 2.1.4 + '@types/sax': + specifier: 1.2.7 + version: 1.2.7 '@types/semver': specifier: 7.7.0 version: 7.7.0 @@ -1077,18 +1083,22 @@ packages: resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/config-array@0.20.0': - resolution: {integrity: sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==} + '@eslint/config-array@0.20.1': + resolution: {integrity: sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/config-helpers@0.2.2': - resolution: {integrity: sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==} + '@eslint/config-helpers@0.2.3': + resolution: {integrity: sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/core@0.14.0': resolution: {integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/core@0.15.0': + resolution: {integrity: sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/eslintrc@3.3.1': resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1101,8 +1111,8 @@ packages: resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.3.1': - resolution: {integrity: sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==} + '@eslint/plugin-kit@0.3.2': + resolution: {integrity: sha512-4SaFZCNfJqvk/kenHpI8xvN42DMaoycy4PzKc5otHxRswww1kAt82OlBuwRVLofCACCTZEcla2Ydxv8scMXaTg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@gwhitney/detect-indent@7.0.1': @@ -2150,6 +2160,9 @@ packages: '@types/responselike@1.0.3': resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} + '@types/sax@1.2.7': + resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==} + '@types/semver-stable@3.0.2': resolution: {integrity: sha512-uNLK57+EY0r8VprVwHytHhlTb1tUVZiWgXkMBKoeu1/3LaFq+ZiaG29xAC3APAWG7xdedwGqeUY8N1y9YG1vjw==} @@ -2285,88 +2298,98 @@ packages: resolution: {integrity: sha512-qHV7pW7E85A0x6qyrFn+O+q1k1p3tQCsqIZ1KZ5ESLXY57aTvUd3/a4rdPTeXisvhXn2VQG0VSKUqs8KHF2zcA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@unrs/resolver-binding-darwin-arm64@1.7.13': - resolution: {integrity: sha512-LIKeCzNSkTWwGHjtiUIfvS96+7kpuyrKq2pzw/0XT2S8ykczj40Hh27oLTbXguCX8tGrCoaD2yXxzwqMMhAzhA==} + '@unrs/resolver-binding-android-arm-eabi@1.9.0': + resolution: {integrity: sha512-h1T2c2Di49ekF2TE8ZCoJkb+jwETKUIPDJ/nO3tJBKlLFPu+fyd93f0rGP/BvArKx2k2HlRM4kqkNarj3dvZlg==} + cpu: [arm] + os: [android] + + '@unrs/resolver-binding-android-arm64@1.9.0': + resolution: {integrity: sha512-sG1NHtgXtX8owEkJ11yn34vt0Xqzi3k9TJ8zppDmyG8GZV4kVWw44FHwKwHeEFl07uKPeC4ZoyuQaGh5ruJYPA==} + cpu: [arm64] + os: [android] + + '@unrs/resolver-binding-darwin-arm64@1.9.0': + resolution: {integrity: sha512-nJ9z47kfFnCxN1z/oYZS7HSNsFh43y2asePzTEZpEvK7kGyuShSl3RRXnm/1QaqFL+iP+BjMwuB+DYUymOkA5A==} cpu: [arm64] os: [darwin] - '@unrs/resolver-binding-darwin-x64@1.7.13': - resolution: {integrity: sha512-GB5G3qUNrdo2l6xaZehpz1ln4wCQ75tr51HZ8OQEcX6XkBIFVL9E4ikCZvCmRmUgKGR+zP5ogyFib7ZbIMWKWA==} + '@unrs/resolver-binding-darwin-x64@1.9.0': + resolution: {integrity: sha512-TK+UA1TTa0qS53rjWn7cVlEKVGz2B6JYe0C++TdQjvWYIyx83ruwh0wd4LRxYBM5HeuAzXcylA9BH2trARXJTw==} cpu: [x64] os: [darwin] - '@unrs/resolver-binding-freebsd-x64@1.7.13': - resolution: {integrity: sha512-rb8gzoBgqVhDkQiKaq+MrFPhNK3x8XkSFhgU55LfgOa5skv7KIdM3dELKzQVNZNlY49DuZmm0FsEfHK5xPKKiA==} + '@unrs/resolver-binding-freebsd-x64@1.9.0': + resolution: {integrity: sha512-6uZwzMRFcD7CcCd0vz3Hp+9qIL2jseE/bx3ZjaLwn8t714nYGwiE84WpaMCYjU+IQET8Vu/+BNAGtYD7BG/0yA==} cpu: [x64] os: [freebsd] - '@unrs/resolver-binding-linux-arm-gnueabihf@1.7.13': - resolution: {integrity: sha512-bqdzngbTGzhsqhTV3SWECyZUAyvtewKtrCW4E8QPcK6yHSaN0k1h9gKwNOBxFwIqkQRsAibpm18XDum8M5AiCw==} + '@unrs/resolver-binding-linux-arm-gnueabihf@1.9.0': + resolution: {integrity: sha512-bPUBksQfrgcfv2+mm+AZinaKq8LCFvt5PThYqRotqSuuZK1TVKkhbVMS/jvSRfYl7jr3AoZLYbDkItxgqMKRkg==} cpu: [arm] os: [linux] - '@unrs/resolver-binding-linux-arm-musleabihf@1.7.13': - resolution: {integrity: sha512-vkoL3DSS5tsUNLhNtBJWaqDJNNEQsMCr0o2N02sLCSpe5S8TQHz+klQT42Qgj4PqATMwnG3OF0QQ5BH0oAKIPg==} + '@unrs/resolver-binding-linux-arm-musleabihf@1.9.0': + resolution: {integrity: sha512-uT6E7UBIrTdCsFQ+y0tQd3g5oudmrS/hds5pbU3h4s2t/1vsGWbbSKhBSCD9mcqaqkBwoqlECpUrRJCmldl8PA==} cpu: [arm] os: [linux] - '@unrs/resolver-binding-linux-arm64-gnu@1.7.13': - resolution: {integrity: sha512-uNpLKxlDF+NF6aUztbAVhhFSF65zf/6QEfk5NifUgYFbpBObzvMnl2ydEsXV96spwPcmeNTpG9byvq+Twwd3HQ==} + '@unrs/resolver-binding-linux-arm64-gnu@1.9.0': + resolution: {integrity: sha512-vdqBh911wc5awE2bX2zx3eflbyv8U9xbE/jVKAm425eRoOVv/VseGZsqi3A3SykckSpF4wSROkbQPvbQFn8EsA==} cpu: [arm64] os: [linux] - '@unrs/resolver-binding-linux-arm64-musl@1.7.13': - resolution: {integrity: sha512-mEFL6q7vtxA6YJ9sLbxCnKOBynOvClVOcqwUErmaCxA94hgP11rlstouySxJCGeFAb8KfUX9mui82waYrqoBlQ==} + '@unrs/resolver-binding-linux-arm64-musl@1.9.0': + resolution: {integrity: sha512-/8JFZ/SnuDr1lLEVsxsuVwrsGquTvT51RZGvyDB/dOK3oYK2UqeXzgeyq6Otp8FZXQcEYqJwxb9v+gtdXn03eQ==} cpu: [arm64] os: [linux] - '@unrs/resolver-binding-linux-ppc64-gnu@1.7.13': - resolution: {integrity: sha512-MjJaNk8HK3rCOIPS6AQPJXlrDfG1LaePum+CZddHZygPqDNZyVrVdWTadT+U51vIx5QOdEE0oXcgTY+7VYsU1g==} + '@unrs/resolver-binding-linux-ppc64-gnu@1.9.0': + resolution: {integrity: sha512-FkJjybtrl+rajTw4loI3L6YqSOpeZfDls4SstL/5lsP2bka9TiHUjgMBjygeZEis1oC8LfJTS8FSgpKPaQx2tQ==} cpu: [ppc64] os: [linux] - '@unrs/resolver-binding-linux-riscv64-gnu@1.7.13': - resolution: {integrity: sha512-9gAuT1+ed2eIuOXHSu4SdJOe7SUEzPTpOTEuTjGePvMEoWHywY5pvlcY7xMn3d8rhKHpwMzEhl8F8Oy+rkudzA==} + '@unrs/resolver-binding-linux-riscv64-gnu@1.9.0': + resolution: {integrity: sha512-w/NZfHNeDusbqSZ8r/hp8iL4S39h4+vQMc9/vvzuIKMWKppyUGKm3IST0Qv0aOZ1rzIbl9SrDeIqK86ZpUK37w==} cpu: [riscv64] os: [linux] - '@unrs/resolver-binding-linux-riscv64-musl@1.7.13': - resolution: {integrity: sha512-CNrJythJN9jC8SIJGoawebYylzGNJuWAWTKxxxx5Fr3DGEXbex/We4U7N4u6/dQAK3cLVOuAE/9a4D2JH35JIA==} + '@unrs/resolver-binding-linux-riscv64-musl@1.9.0': + resolution: {integrity: sha512-bEPBosut8/8KQbUixPry8zg/fOzVOWyvwzOfz0C0Rw6dp+wIBseyiHKjkcSyZKv/98edrbMknBaMNJfA/UEdqw==} cpu: [riscv64] os: [linux] - '@unrs/resolver-binding-linux-s390x-gnu@1.7.13': - resolution: {integrity: sha512-J0MVXXPvM2Bv+f+gzOZHLHEmXUJNKwJqkfMDTwE763w/tD+OA7UlTMLQihrcYRXwW5jZ8nbM2cEWTeFsTiH2JQ==} + '@unrs/resolver-binding-linux-s390x-gnu@1.9.0': + resolution: {integrity: sha512-LDtMT7moE3gK753gG4pc31AAqGUC86j3AplaFusc717EUGF9ZFJ356sdQzzZzkBk1XzMdxFyZ4f/i35NKM/lFA==} cpu: [s390x] os: [linux] - '@unrs/resolver-binding-linux-x64-gnu@1.7.13': - resolution: {integrity: sha512-Ii2WhtIpeWUe6XG/YhPUX3JNL3PiyXe56PJzqAYDUyB0gctkk/nngpuPnNKlLMcN9FID0T39mIJPhA6YpRcGDQ==} + '@unrs/resolver-binding-linux-x64-gnu@1.9.0': + resolution: {integrity: sha512-WmFd5KINHIXj8o1mPaT8QRjA9HgSXhN1gl9Da4IZihARihEnOylu4co7i/yeaIpcfsI6sYs33cNZKyHYDh0lrA==} cpu: [x64] os: [linux] - '@unrs/resolver-binding-linux-x64-musl@1.7.13': - resolution: {integrity: sha512-8F5E9EhtGYkfEM1OhyVgq76+SnMF5NfZS4v5Rq9JlfuqPnqXWgUjg903hxnG54PQr4I3jmG5bEeT77pGAA3Vvg==} + '@unrs/resolver-binding-linux-x64-musl@1.9.0': + resolution: {integrity: sha512-CYuXbANW+WgzVRIl8/QvZmDaZxrqvOldOwlbUjIM4pQ46FJ0W5cinJ/Ghwa/Ng1ZPMJMk1VFdsD/XwmCGIXBWg==} cpu: [x64] os: [linux] - '@unrs/resolver-binding-wasm32-wasi@1.7.13': - resolution: {integrity: sha512-7RXGTyDtyR/5o1FlBcjEaQQmQ2rKvu5Jq0Uhvce3PsbreZ61M4LQ5Mey2OMomIq4opphAkfDdm/lkHhWJNKNrw==} + '@unrs/resolver-binding-wasm32-wasi@1.9.0': + resolution: {integrity: sha512-6Rp2WH0OoitMYR57Z6VE8Y6corX8C6QEMWLgOV6qXiJIeZ1F9WGXY/yQ8yDC4iTraotyLOeJ2Asea0urWj2fKQ==} engines: {node: '>=14.0.0'} cpu: [wasm32] - '@unrs/resolver-binding-win32-arm64-msvc@1.7.13': - resolution: {integrity: sha512-MomJVcaVZe3j+CvkcfIVEcQyOOzauKpJYGY8d6PoKXn1FalMVGHX9/c0kXCI0WCK+CRGMExAiQhD8jkhyUVKxg==} + '@unrs/resolver-binding-win32-arm64-msvc@1.9.0': + resolution: {integrity: sha512-rknkrTRuvujprrbPmGeHi8wYWxmNVlBoNW8+4XF2hXUnASOjmuC9FNF1tGbDiRQWn264q9U/oGtixyO3BT8adQ==} cpu: [arm64] os: [win32] - '@unrs/resolver-binding-win32-ia32-msvc@1.7.13': - resolution: {integrity: sha512-pnHfzbFj6e4gUARI1Yvz0TUhmFZae248O7JOMCSmSBN3R35RJiKyHmsMuIiPrUYWDzm5jUMPTxSs+b3Ipawusw==} + '@unrs/resolver-binding-win32-ia32-msvc@1.9.0': + resolution: {integrity: sha512-Ceymm+iBl+bgAICtgiHyMLz6hjxmLJKqBim8tDzpX61wpZOx2bPK6Gjuor7I2RiUynVjvvkoRIkrPyMwzBzF3A==} cpu: [ia32] os: [win32] - '@unrs/resolver-binding-win32-x64-msvc@1.7.13': - resolution: {integrity: sha512-tI0+FTntE3BD0UxhTP12F/iTtkeMK+qh72/2aSxPZnTlOcMR9CTJid8CdppbSjj9wenq7PNcqScLtpPENH3Lvg==} + '@unrs/resolver-binding-win32-x64-msvc@1.9.0': + resolution: {integrity: sha512-k59o9ZyeyS0hAlcaKFezYSH2agQeRFEB7KoQLXl3Nb3rgkqT1NY9Vwy+SqODiLmYnEjxWJVRE/yq2jFVqdIxZw==} cpu: [x64] os: [win32] @@ -2725,8 +2748,8 @@ packages: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} - caniuse-lite@1.0.30001722: - resolution: {integrity: sha512-DCQHBBZtiK6JVkAGw7drvAMK0Q0POD/xZvEmDp6baiMMP6QXXk9HpD6mNYBZWhOPG6LvIDb82ITqtWjhDckHCA==} + caniuse-lite@1.0.30001723: + resolution: {integrity: sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==} chai@5.2.0: resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} @@ -3123,8 +3146,8 @@ packages: engines: {node: '>=18'} hasBin: true - electron-to-chromium@1.5.166: - resolution: {integrity: sha512-QPWqHL0BglzPYyJJ1zSSmwFFL6MFXhbACOCcsCdUMCkzPdS9/OIBVxg516X/Ado2qwAq8k0nJJ7phQPCqiaFAw==} + electron-to-chromium@1.5.167: + resolution: {integrity: sha512-LxcRvnYO5ez2bMOFpbuuVuAI5QNeY1ncVytE/KXaL6ZNfzX1yPlAO0nSOyIHx2fVAuUprMqPs/TdVhUFZy7SIQ==} email-addresses@5.0.0: resolution: {integrity: sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==} @@ -3891,8 +3914,8 @@ packages: resolution: {integrity: sha512-YVt14UZCgsX1vZQ3gKjkWVdBdHQ6eu3MPU1TBgL1H5orXe2+jWD006WCPPtOuwlQm10NuzOW5WawiF1Q9veW8g==} engines: {node: '>=18.20'} - import-in-the-middle@1.14.0: - resolution: {integrity: sha512-g5zLT0HaztRJWysayWYiUq/7E5H825QIiecMD2pI5QO7Wzr847l6GDvPvmZaDIdrDtS2w7qRczywxiK6SL5vRw==} + import-in-the-middle@1.14.2: + resolution: {integrity: sha512-5tCuY9BV8ujfOpwtAGgsTx9CGUapcFMEEyByLv1B+v2+6DhAcw+Zr0nhQT7uwaZ7DiourxFEscghOR8e1aPLQw==} import-meta-resolve@4.1.0: resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} @@ -5248,8 +5271,8 @@ packages: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} - postcss@8.5.4: - resolution: {integrity: sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==} + postcss@8.5.5: + resolution: {integrity: sha512-d/jtm+rdNT8tpXuHY5MMtcbJFBkhXE6593XVR9UoGCH8jSFGci7jGvMGH5RYd5PBJW+00NZQt6gf7CbagJCrhg==} engines: {node: ^10 || ^12 || >=14} prebuild-install@7.1.3: @@ -5958,8 +5981,8 @@ packages: peerDependencies: typescript: '>=4.8.4' - ts-essentials@10.1.0: - resolution: {integrity: sha512-LirrVzbhIpFQ9BdGfqLnM9r7aP9rnyfeoxbP5ZEkdr531IaY21+KdebRSsbvqu28VDJtcDDn+AlGn95t0c52zQ==} + ts-essentials@10.1.1: + resolution: {integrity: sha512-4aTB7KLHKmUvkjNj8V+EdnmuVTiECzn3K+zIbRthumvHu+j44x3w63xpfs0JL3NGIzGXqoQ7AV591xHO+XrOTw==} peerDependencies: typescript: '>=4.5.0' peerDependenciesMeta: @@ -6145,8 +6168,8 @@ packages: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} - unrs-resolver@1.7.13: - resolution: {integrity: sha512-QUjCYKAgrdJpf3wA73zWjOrO7ra19lfnwQ8HRkNOLah5AVDqOS38UunnyhzsSL8AE+2/AGnAHxlr8cGshCP35A==} + unrs-resolver@1.9.0: + resolution: {integrity: sha512-wqaRu4UnzBD2ABTC1kLfBjAqIDZ5YUTr/MLGa7By47JV1bJDSW7jq/ZSLigB7enLe7ubNaJhtnBXgrc/50cEhg==} upath@2.0.1: resolution: {integrity: sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==} @@ -7470,7 +7493,7 @@ snapshots: '@eslint-community/regexpp@4.12.1': {} - '@eslint/config-array@0.20.0': + '@eslint/config-array@0.20.1': dependencies: '@eslint/object-schema': 2.1.6 debug: 4.4.1 @@ -7478,12 +7501,16 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/config-helpers@0.2.2': {} + '@eslint/config-helpers@0.2.3': {} '@eslint/core@0.14.0': dependencies: '@types/json-schema': 7.0.15 + '@eslint/core@0.15.0': + dependencies: + '@types/json-schema': 7.0.15 + '@eslint/eslintrc@3.3.1': dependencies: ajv: 6.12.6 @@ -7502,9 +7529,9 @@ snapshots: '@eslint/object-schema@2.1.6': {} - '@eslint/plugin-kit@0.3.1': + '@eslint/plugin-kit@0.3.2': dependencies: - '@eslint/core': 0.14.0 + '@eslint/core': 0.15.0 levn: 0.4.1 '@gwhitney/detect-indent@7.0.1': {} @@ -7762,7 +7789,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/api-logs': 0.202.0 - import-in-the-middle: 1.14.0 + import-in-the-middle: 1.14.2 require-in-the-middle: 7.5.2 transitivePeerDependencies: - supports-color @@ -8722,6 +8749,10 @@ snapshots: dependencies: '@types/node': 22.15.30 + '@types/sax@1.2.7': + dependencies: + '@types/node': 22.15.30 + '@types/semver-stable@3.0.2': {} '@types/semver-utils@1.1.3': {} @@ -8787,8 +8818,8 @@ snapshots: '@typescript-eslint/project-service@8.33.1(typescript@5.8.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.34.0(typescript@5.8.3) - '@typescript-eslint/types': 8.34.0 + '@typescript-eslint/tsconfig-utils': 8.33.1(typescript@5.8.3) + '@typescript-eslint/types': 8.33.1 debug: 4.4.1 typescript: 5.8.3 transitivePeerDependencies: @@ -8900,57 +8931,63 @@ snapshots: '@typescript-eslint/types': 8.34.0 eslint-visitor-keys: 4.2.1 - '@unrs/resolver-binding-darwin-arm64@1.7.13': + '@unrs/resolver-binding-android-arm-eabi@1.9.0': + optional: true + + '@unrs/resolver-binding-android-arm64@1.9.0': + optional: true + + '@unrs/resolver-binding-darwin-arm64@1.9.0': optional: true - '@unrs/resolver-binding-darwin-x64@1.7.13': + '@unrs/resolver-binding-darwin-x64@1.9.0': optional: true - '@unrs/resolver-binding-freebsd-x64@1.7.13': + '@unrs/resolver-binding-freebsd-x64@1.9.0': optional: true - '@unrs/resolver-binding-linux-arm-gnueabihf@1.7.13': + '@unrs/resolver-binding-linux-arm-gnueabihf@1.9.0': optional: true - '@unrs/resolver-binding-linux-arm-musleabihf@1.7.13': + '@unrs/resolver-binding-linux-arm-musleabihf@1.9.0': optional: true - '@unrs/resolver-binding-linux-arm64-gnu@1.7.13': + '@unrs/resolver-binding-linux-arm64-gnu@1.9.0': optional: true - '@unrs/resolver-binding-linux-arm64-musl@1.7.13': + '@unrs/resolver-binding-linux-arm64-musl@1.9.0': optional: true - '@unrs/resolver-binding-linux-ppc64-gnu@1.7.13': + '@unrs/resolver-binding-linux-ppc64-gnu@1.9.0': optional: true - '@unrs/resolver-binding-linux-riscv64-gnu@1.7.13': + '@unrs/resolver-binding-linux-riscv64-gnu@1.9.0': optional: true - '@unrs/resolver-binding-linux-riscv64-musl@1.7.13': + '@unrs/resolver-binding-linux-riscv64-musl@1.9.0': optional: true - '@unrs/resolver-binding-linux-s390x-gnu@1.7.13': + '@unrs/resolver-binding-linux-s390x-gnu@1.9.0': optional: true - '@unrs/resolver-binding-linux-x64-gnu@1.7.13': + '@unrs/resolver-binding-linux-x64-gnu@1.9.0': optional: true - '@unrs/resolver-binding-linux-x64-musl@1.7.13': + '@unrs/resolver-binding-linux-x64-musl@1.9.0': optional: true - '@unrs/resolver-binding-wasm32-wasi@1.7.13': + '@unrs/resolver-binding-wasm32-wasi@1.9.0': dependencies: '@napi-rs/wasm-runtime': 0.2.11 optional: true - '@unrs/resolver-binding-win32-arm64-msvc@1.7.13': + '@unrs/resolver-binding-win32-arm64-msvc@1.9.0': optional: true - '@unrs/resolver-binding-win32-ia32-msvc@1.7.13': + '@unrs/resolver-binding-win32-ia32-msvc@1.9.0': optional: true - '@unrs/resolver-binding-win32-x64-msvc@1.7.13': + '@unrs/resolver-binding-win32-x64-msvc@1.9.0': optional: true '@vitest/coverage-v8@3.2.2(vitest@3.2.2(@types/debug@4.1.12)(@types/node@22.15.30)(tsx@4.19.4)(yaml@2.8.0))': @@ -9302,8 +9339,8 @@ snapshots: browserslist@4.25.0: dependencies: - caniuse-lite: 1.0.30001722 - electron-to-chromium: 1.5.166 + caniuse-lite: 1.0.30001723 + electron-to-chromium: 1.5.167 node-releases: 2.0.19 update-browserslist-db: 1.1.3(browserslist@4.25.0) @@ -9392,7 +9429,7 @@ snapshots: camelcase@5.3.1: {} - caniuse-lite@1.0.30001722: {} + caniuse-lite@1.0.30001723: {} chai@5.2.0: dependencies: @@ -9761,7 +9798,7 @@ snapshots: minimatch: 10.0.1 semver: 7.7.2 - electron-to-chromium@1.5.166: {} + electron-to-chromium@1.5.167: {} email-addresses@5.0.0: {} @@ -9947,12 +9984,12 @@ snapshots: strip-ansi: 6.0.1 text-table: 0.2.0 - eslint-import-context@0.1.8(unrs-resolver@1.7.13): + eslint-import-context@0.1.8(unrs-resolver@1.9.0): dependencies: get-tsconfig: 4.10.1 stable-hash-x: 0.1.1 optionalDependencies: - unrs-resolver: 1.7.13 + unrs-resolver: 1.9.0 eslint-import-resolver-node@0.3.9: dependencies: @@ -9966,12 +10003,12 @@ snapshots: dependencies: debug: 4.4.1 eslint: 9.28.0 - eslint-import-context: 0.1.8(unrs-resolver@1.7.13) + eslint-import-context: 0.1.8(unrs-resolver@1.9.0) get-tsconfig: 4.10.1 is-bun-module: 2.0.0 stable-hash-x: 0.1.1 tinyglobby: 0.2.14 - unrs-resolver: 1.7.13 + unrs-resolver: 1.9.0 optionalDependencies: eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.33.1(eslint@9.28.0)(typescript@5.8.3))(eslint-import-resolver-typescript@4.4.3)(eslint@9.28.0) eslint-plugin-import-x: 4.15.1(@typescript-eslint/utils@8.34.0(eslint@9.28.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.28.0) @@ -9995,12 +10032,12 @@ snapshots: comment-parser: 1.4.1 debug: 4.4.1 eslint: 9.28.0 - eslint-import-context: 0.1.8(unrs-resolver@1.7.13) + eslint-import-context: 0.1.8(unrs-resolver@1.9.0) is-glob: 4.0.3 minimatch: 10.0.1 semver: 7.7.2 stable-hash-x: 0.1.1 - unrs-resolver: 1.7.13 + unrs-resolver: 1.9.0 optionalDependencies: '@typescript-eslint/utils': 8.34.0(eslint@9.28.0)(typescript@5.8.3) eslint-import-resolver-node: 0.3.9 @@ -10054,12 +10091,12 @@ snapshots: dependencies: '@eslint-community/eslint-utils': 4.7.0(eslint@9.28.0) '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.20.0 - '@eslint/config-helpers': 0.2.2 + '@eslint/config-array': 0.20.1 + '@eslint/config-helpers': 0.2.3 '@eslint/core': 0.14.0 '@eslint/eslintrc': 3.3.1 '@eslint/js': 9.28.0 - '@eslint/plugin-kit': 0.3.1 + '@eslint/plugin-kit': 0.3.2 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 @@ -10694,7 +10731,7 @@ snapshots: transitivePeerDependencies: - supports-color - import-in-the-middle@1.14.0: + import-in-the-middle@1.14.2: dependencies: acorn: 8.15.0 acorn-import-attributes: 1.9.5(acorn@8.15.0) @@ -12140,7 +12177,7 @@ snapshots: possible-typed-array-names@1.1.0: {} - postcss@8.5.4: + postcss@8.5.5: dependencies: nanoid: 3.3.11 picocolors: 1.1.1 @@ -13006,7 +13043,7 @@ snapshots: dependencies: typescript: 5.8.3 - ts-essentials@10.1.0(typescript@5.8.3): + ts-essentials@10.1.1(typescript@5.8.3): optionalDependencies: typescript: 5.8.3 @@ -13188,27 +13225,29 @@ snapshots: universalify@2.0.1: {} - unrs-resolver@1.7.13: + unrs-resolver@1.9.0: dependencies: napi-postinstall: 0.2.4 optionalDependencies: - '@unrs/resolver-binding-darwin-arm64': 1.7.13 - '@unrs/resolver-binding-darwin-x64': 1.7.13 - '@unrs/resolver-binding-freebsd-x64': 1.7.13 - '@unrs/resolver-binding-linux-arm-gnueabihf': 1.7.13 - '@unrs/resolver-binding-linux-arm-musleabihf': 1.7.13 - '@unrs/resolver-binding-linux-arm64-gnu': 1.7.13 - '@unrs/resolver-binding-linux-arm64-musl': 1.7.13 - '@unrs/resolver-binding-linux-ppc64-gnu': 1.7.13 - '@unrs/resolver-binding-linux-riscv64-gnu': 1.7.13 - '@unrs/resolver-binding-linux-riscv64-musl': 1.7.13 - '@unrs/resolver-binding-linux-s390x-gnu': 1.7.13 - '@unrs/resolver-binding-linux-x64-gnu': 1.7.13 - '@unrs/resolver-binding-linux-x64-musl': 1.7.13 - '@unrs/resolver-binding-wasm32-wasi': 1.7.13 - '@unrs/resolver-binding-win32-arm64-msvc': 1.7.13 - '@unrs/resolver-binding-win32-ia32-msvc': 1.7.13 - '@unrs/resolver-binding-win32-x64-msvc': 1.7.13 + '@unrs/resolver-binding-android-arm-eabi': 1.9.0 + '@unrs/resolver-binding-android-arm64': 1.9.0 + '@unrs/resolver-binding-darwin-arm64': 1.9.0 + '@unrs/resolver-binding-darwin-x64': 1.9.0 + '@unrs/resolver-binding-freebsd-x64': 1.9.0 + '@unrs/resolver-binding-linux-arm-gnueabihf': 1.9.0 + '@unrs/resolver-binding-linux-arm-musleabihf': 1.9.0 + '@unrs/resolver-binding-linux-arm64-gnu': 1.9.0 + '@unrs/resolver-binding-linux-arm64-musl': 1.9.0 + '@unrs/resolver-binding-linux-ppc64-gnu': 1.9.0 + '@unrs/resolver-binding-linux-riscv64-gnu': 1.9.0 + '@unrs/resolver-binding-linux-riscv64-musl': 1.9.0 + '@unrs/resolver-binding-linux-s390x-gnu': 1.9.0 + '@unrs/resolver-binding-linux-x64-gnu': 1.9.0 + '@unrs/resolver-binding-linux-x64-musl': 1.9.0 + '@unrs/resolver-binding-wasm32-wasi': 1.9.0 + '@unrs/resolver-binding-win32-arm64-msvc': 1.9.0 + '@unrs/resolver-binding-win32-ia32-msvc': 1.9.0 + '@unrs/resolver-binding-win32-x64-msvc': 1.9.0 upath@2.0.1: {} @@ -13298,7 +13337,7 @@ snapshots: esbuild: 0.25.5 fdir: 6.4.6(picomatch@4.0.2) picomatch: 4.0.2 - postcss: 8.5.4 + postcss: 8.5.5 rollup: 4.43.0 tinyglobby: 0.2.14 optionalDependencies: @@ -13309,7 +13348,7 @@ snapshots: vitest-mock-extended@3.1.0(typescript@5.8.3)(vitest@3.2.2(@types/debug@4.1.12)(@types/node@22.15.30)(tsx@4.19.4)(yaml@2.8.0)): dependencies: - ts-essentials: 10.1.0(typescript@5.8.3) + ts-essentials: 10.1.1(typescript@5.8.3) typescript: 5.8.3 vitest: 3.2.2(@types/debug@4.1.12)(@types/node@22.15.30)(tsx@4.19.4)(yaml@2.8.0)