0
Fork 0
mirror of https://github.com/verdaccio/verdaccio.git synced 2024-12-16 21:56:25 -05:00

vitest migration more packages (#4872)

* migrate storage package

* migrate middleware package

* migrate auth-memory plugin
This commit is contained in:
Juan Picado 2024-09-29 16:03:29 +02:00 committed by GitHub
parent 4afca90e21
commit 99a1af1c35
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 687 additions and 463 deletions

View file

@ -1,12 +0,0 @@
const config = require('../../jest/config');
module.exports = Object.assign({}, config, {
coverageThreshold: {
global: {
lines: 67,
functions: 70,
branches: 55,
statements: 67,
},
},
});

View file

@ -34,7 +34,7 @@
"build:types": "tsc --emitDeclarationOnly -p tsconfig.build.json",
"build:js": "babel src/ --out-dir build/ --copy-files --extensions \".ts,.tsx\" --source-maps",
"watch": "pnpm build:js -- --watch",
"test": "jest",
"test": "vitest run",
"test:snap": "jest --updateSnapshot",
"build": "pnpm run build:js && pnpm run build:types"
},
@ -57,6 +57,7 @@
"devDependencies": {
"@verdaccio/logger": "workspace:8.0.0-next-8.2",
"body-parser": "1.20.3",
"supertest": "7.0.0"
"supertest": "7.0.0",
"jsdom": "25.0.1"
}
}

View file

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`template custom body after 1`] = `
exports[`template > custom body after 1`] = `
"
<!DOCTYPE html>
<html lang="en-us">
@ -26,7 +26,7 @@ exports[`template custom body after 1`] = `
"
`;
exports[`template custom body before 1`] = `
exports[`template > custom body before 1`] = `
"
<!DOCTYPE html>
<html lang="en-us">
@ -52,7 +52,7 @@ exports[`template custom body before 1`] = `
"
`;
exports[`template custom render 1`] = `
exports[`template > custom render 1`] = `
"
<!DOCTYPE html>
<html lang="en-us">
@ -78,7 +78,7 @@ exports[`template custom render 1`] = `
"
`;
exports[`template custom title 1`] = `
exports[`template > custom title 1`] = `
"
<!DOCTYPE html>
<html lang="en-us">
@ -104,7 +104,7 @@ exports[`template custom title 1`] = `
"
`;
exports[`template meta scripts 1`] = `
exports[`template > meta scripts 1`] = `
"
<!DOCTYPE html>
<html lang="en-us">

View file

@ -1,4 +1,5 @@
import request from 'supertest';
import { test } from 'vitest';
import { HTTP_STATUS } from '@verdaccio/core';

View file

@ -1,4 +1,5 @@
import request from 'supertest';
import { expect, test } from 'vitest';
import { HTTP_STATUS } from '@verdaccio/core';

View file

@ -1,5 +1,6 @@
import express from 'express';
import request from 'supertest';
import { expect, test } from 'vitest';
import { HEADERS, HTTP_STATUS } from '@verdaccio/core';

View file

@ -1,5 +1,6 @@
import express from 'express';
import request from 'supertest';
import { test } from 'vitest';
import { HEADERS, HTTP_STATUS } from '@verdaccio/core';

View file

@ -1,5 +1,6 @@
import path from 'path';
import request from 'supertest';
import { test } from 'vitest';
import { HTTP_STATUS } from '@verdaccio/core';
import { logger, setup } from '@verdaccio/logger';

View file

@ -1,4 +1,5 @@
import request from 'supertest';
import { test } from 'vitest';
import { HTTP_STATUS } from '@verdaccio/core';

View file

@ -1,3 +1,5 @@
import { describe, expect, test } from 'vitest';
import { getManifestValue } from '../src/middlewares/web/utils/manifest';
const manifest = require('./partials/manifest/manifest.json');

View file

@ -1,5 +1,6 @@
import mime from 'mime';
import request from 'supertest';
import { test } from 'vitest';
import { HEADERS, HTTP_STATUS } from '@verdaccio/core';

View file

@ -1,4 +1,5 @@
import request from 'supertest';
import { describe, test } from 'vitest';
import { HTTP_STATUS } from '@verdaccio/core';

View file

@ -2,6 +2,7 @@ import express from 'express';
import { JSDOM } from 'jsdom';
import path from 'path';
import supertest from 'supertest';
import { describe, expect, test } from 'vitest';
import { HEADERS, HEADER_TYPE, HTTP_STATUS } from '@verdaccio/core';
import { setup } from '@verdaccio/logger';

View file

@ -1,4 +1,5 @@
import request from 'supertest';
import { expect, test } from 'vitest';
import { HEADERS, HTTP_STATUS } from '@verdaccio/core';

View file

@ -1,3 +1,5 @@
import { describe, expect, test } from 'vitest';
import template from '../src/middlewares/web/utils/template';
const manifest = require('./partials/manifest/manifest.json');

View file

@ -1,3 +1,5 @@
import { describe, expect, test } from 'vitest';
import { validatePrimaryColor } from '../src/middlewares/web/utils/web-utils';
describe('Utilities', () => {

View file

@ -1,4 +1,5 @@
import request from 'supertest';
import { describe, test } from 'vitest';
import { HTTP_STATUS } from '@verdaccio/core';

View file

@ -49,7 +49,6 @@
},
"devDependencies": {
"@verdaccio/types": "workspace:13.0.0-next-8.1",
"jest": "29.7.0",
"selfsigned": "2.4.1",
"supertest": "7.0.0"
},

View file

@ -1,3 +0,0 @@
const config = require('../../../jest/config');
module.exports = Object.assign({}, config, {});

View file

@ -45,7 +45,7 @@
"build:js": "babel src/ --out-dir build/ --copy-files --extensions \".ts,.tsx\" --source-maps",
"build": "pnpm run build:js && pnpm run build:types",
"watch": "pnpm build:js -- --watch",
"test": "jest"
"test": "vitest run"
},
"funding": {
"type": "opencollective",

View file

@ -1,3 +1,5 @@
import { beforeEach, describe, expect, test, vi } from 'vitest';
import { Config, getDefaultConfig } from '@verdaccio/config';
import { pluginUtils } from '@verdaccio/core';
@ -7,14 +9,14 @@ import { Users, VerdaccioMemoryConfig } from '../src/types';
describe('Memory', function () {
let auth: pluginUtils.Auth<VerdaccioMemoryConfig>;
const logger = {
child: jest.fn(() => {}),
http: jest.fn(() => {}),
trace: jest.fn(() => {}),
warn: jest.fn(() => {}),
info: jest.fn(() => {}),
debug: jest.fn(() => {}),
error: jest.fn(() => {}),
fatal: jest.fn(() => {}),
child: vi.fn(() => {}),
http: vi.fn(() => {}),
trace: vi.fn(() => {}),
warn: vi.fn(() => {}),
info: vi.fn(() => {}),
debug: vi.fn(() => {}),
error: vi.fn(() => {}),
fatal: vi.fn(() => {}),
};
const config = new Config(getDefaultConfig());
@ -36,44 +38,52 @@ describe('Memory', function () {
});
describe('#adduser', function () {
test('adds users', function (done) {
auth.adduser?.('test', 'secret', function (err, user) {
expect(err).toBeNull();
expect(user).toEqual(true);
done();
});
});
test('login existing users', function (done) {
auth.adduser?.('test', 'secret', function (err, user) {
expect(err).toBeNull();
expect(user).toEqual(true);
test('adds users', function () {
return new Promise((done) => {
auth.adduser?.('test', 'secret', function (err, user) {
expect(err).toBeNull();
expect(user).toBe(true);
done();
expect(user).toEqual(true);
done(true);
});
});
});
test('max users reached', function (done) {
const auth = new Memory({ users } as VerdaccioMemoryConfig, {
// @ts-expect-error
config: { ...config, max_users: -1 },
logger,
test('login existing users', function () {
return new Promise((done) => {
auth.adduser?.('test', 'secret', function (err, user) {
expect(err).toBeNull();
expect(user).toEqual(true);
auth.adduser?.('test', 'secret', function (err, user) {
expect(err).toBeNull();
expect(user).toBe(true);
done(true);
});
});
});
auth.adduser?.('fooooooooo', 'secret', function (err) {
expect(err).not.toBeNull();
expect(err?.message).toMatch(/maximum amount of users reached/);
done();
});
test('max users reached', function () {
return new Promise((done) => {
const auth = new Memory({ users } as VerdaccioMemoryConfig, {
// @ts-expect-error
config: { ...config, max_users: -1 },
logger,
});
auth.adduser?.('fooooooooo', 'secret', function (err) {
expect(err).not.toBeNull();
expect(err?.message).toMatch(/maximum amount of users reached/);
done(true);
});
});
});
});
describe('#allow_access', function () {
beforeEach(function (done) {
auth.adduser?.('test', 'secret', function () {
done();
beforeEach(function () {
return new Promise((done) => {
auth.adduser?.('test', 'secret', function () {
done();
});
});
});
@ -93,45 +103,63 @@ describe('Memory', function () {
);
};
test('should be allowed to access as $all to the package', function (done) {
accessBy(['$all'], done);
});
test('should be allowed to access as $anonymous to the package', function (done) {
accessBy(['$anonymous'], done);
});
test('should be allowed to access as $authenticated to the package', function (done) {
accessBy(['$authenticated'], done);
});
test('should be allowed to access as test to the package', function (done) {
accessBy(['test'], done);
});
test('should not to be allowed to access any package', function (done) {
// @ts-expect-error
auth.allow_access?.({}, { access: [], publish: [], proxy: [] }, function (err) {
expect(err).not.toBeNull();
expect(err?.message).toMatch(/not allowed to access package/);
done();
test('should be allowed to access as $all to the package', function () {
return new Promise((done) => {
accessBy(['$all'], done);
});
});
test('should not to be allowed to access the anyOtherUser package', function (done) {
// @ts-expect-error
auth.allow_access?.({}, { access: ['anyOtherUser'], publish: [], proxy: [] }, function (err) {
expect(err).not.toBeNull();
expect(err?.message).toMatch(/not allowed to access package/);
done();
test('should be allowed to access as $anonymous to the package', function () {
return new Promise((done) => {
accessBy(['$anonymous'], done);
});
});
test('should be allowed to access as $authenticated to the package', function () {
return new Promise((done) => {
accessBy(['$authenticated'], done);
});
});
test('should be allowed to access as test to the package', function () {
return new Promise((done) => {
accessBy(['test'], done);
});
});
test('should not to be allowed to access any package', function () {
return new Promise((done) => {
// @ts-expect-error
auth.allow_access?.({}, { access: [], publish: [], proxy: [] }, function (err) {
expect(err).not.toBeNull();
expect(err?.message).toMatch(/not allowed to access package/);
done(true);
});
});
});
test('should not to be allowed to access the anyOtherUser package', function () {
return new Promise((done) => {
// @ts-expect-error
auth.allow_access?.(
{},
{ access: ['anyOtherUser'], publish: [], proxy: [] },
function (err) {
expect(err).not.toBeNull();
expect(err?.message).toMatch(/not allowed to access package/);
done();
}
);
});
});
});
describe('#allow_publish', function () {
beforeEach(function (done) {
auth.adduser?.('test', 'secret', function () {
done();
beforeEach(function () {
return new Promise((done) => {
auth.adduser?.('test', 'secret', function () {
done();
});
});
});
@ -151,37 +179,53 @@ describe('Memory', function () {
);
};
test('should be allowed to access as $all to the package', function (done) {
accessBy(['$all'], done);
});
test('should be allowed to access as $anonymous to the package', function (done) {
accessBy(['$anonymous'], done);
});
test('should be allowed to access as $authenticated to the package', function (done) {
accessBy(['$authenticated'], done);
});
test('should be allowed to access as test to the package', function (done) {
accessBy(['test'], done);
});
test('should not to be allowed to access any package', function (done) {
// @ts-expect-error
auth.allow_publish?.({}, { publish: [], proxy: [], access: [] }, function (err) {
expect(err).not.toBeNull();
expect(err?.message).toMatch(/not allowed to publish package/);
done();
test('should be allowed to access as $all to the package', function () {
return new Promise((done) => {
accessBy(['$all'], done);
});
});
test('should not to be allowed to access the anyOtherUser package', function (done) {
// @ts-expect-error
auth.allow_publish({}, { publish: ['anyOtherUser'], proxy: [], access: [] }, function (err) {
expect(err).not.toBeNull();
expect(err?.message).toMatch(/not allowed to publish package/);
done();
test('should be allowed to access as $anonymous to the package', function () {
return new Promise((done) => {
accessBy(['$anonymous'], done);
});
});
test('should be allowed to access as $authenticated to the package', function () {
return new Promise((done) => {
accessBy(['$authenticated'], done);
});
});
test('should be allowed to access as test to the package', function () {
return new Promise((done) => {
accessBy(['test'], done);
});
});
test('should not to be allowed to access any package', function () {
return new Promise((done) => {
// @ts-expect-error
auth.allow_publish?.({}, { publish: [], proxy: [], access: [] }, function (err) {
expect(err).not.toBeNull();
expect(err?.message).toMatch(/not allowed to publish package/);
done(true);
});
});
});
test('should not to be allowed to access the anyOtherUser package', function () {
return new Promise((done) => {
// @ts-expect-error
auth.allow_publish(
{},
{ publish: ['anyOtherUser'], proxy: [], access: [] },
function (err) {
expect(err).not.toBeNull();
expect(err?.message).toMatch(/not allowed to publish package/);
done(true);
}
);
});
});
});
@ -189,70 +233,84 @@ describe('Memory', function () {
describe('#changePassword', function () {
let auth;
beforeEach(function (done) {
auth = new Memory(
{ users: {} },
{
config,
logger,
}
);
auth.adduser('test', 'secret', function () {
done();
beforeEach(function () {
return new Promise((done) => {
auth = new Memory(
{ users: {} },
{
config,
logger,
}
);
auth.adduser('test', 'secret', function () {
done();
});
});
});
test('should change password', function (done) {
auth.changePassword('test', 'secret', 'newSecret', function (err, ok) {
expect(err).toBeNull();
expect(ok).toBe(true);
done();
test('should change password', function () {
return new Promise((done) => {
auth.changePassword('test', 'secret', 'newSecret', function (err, ok) {
expect(err).toBeNull();
expect(ok).toBe(true);
done(true);
});
});
});
test('should fail change password with user not found', function (done) {
auth.changePassword('NOTFOUND', 'secret', 'newSecret', function (err) {
expect(err).not.toBeNull();
expect(err.message).toMatch(/user not found/);
done();
test('should fail change password with user not found', function () {
return new Promise((done) => {
auth.changePassword('NOTFOUND', 'secret', 'newSecret', function (err) {
expect(err).not.toBeNull();
expect(err.message).toMatch(/user not found/);
done(true);
});
});
});
});
describe('#authenticate', function () {
beforeEach(function (done) {
auth = new Memory(
{ users: {} },
{
config,
logger,
}
);
auth.adduser?.('test', 'secret', function () {
done();
beforeEach(function () {
return new Promise((done) => {
auth = new Memory(
{ users: {} },
{
config,
logger,
}
);
auth.adduser?.('test', 'secret', function () {
done();
});
});
});
test('validates existing users', function (done) {
auth.authenticate('test', 'secret', function (err, groups) {
expect(err).toBeNull();
expect(groups).toBeDefined();
done();
test('validates existing users', function () {
return new Promise((done) => {
auth.authenticate('test', 'secret', function (err, groups) {
expect(err).toBeNull();
expect(groups).toBeDefined();
done(true);
});
});
});
test('fails if wrong password', function (done) {
auth.authenticate('test', 'no-secret', function (err) {
expect(err).not.toBeNull();
done();
test('fails if wrong password', function () {
return new Promise((done) => {
auth.authenticate('test', 'no-secret', function (err) {
expect(err).not.toBeNull();
done(true);
});
});
});
test('fails if user does not exist', function (done) {
auth.authenticate('john', 'secret', function (err, groups) {
expect(err).toBeNull();
expect(groups).toBeFalsy();
done();
test('fails if user does not exist', function () {
return new Promise((done) => {
auth.authenticate('john', 'secret', function (err, groups) {
expect(err).toBeNull();
expect(groups).toBeFalsy();
done(true);
});
});
});
});

View file

@ -1,12 +0,0 @@
const config = require('../../jest/config');
module.exports = Object.assign({}, config, {
coverageThreshold: {
global: {
// FIXME: increase to 90
branches: 62,
functions: 84,
lines: 74,
},
},
});

View file

@ -30,7 +30,7 @@
},
"scripts": {
"clean": "rimraf ./build",
"test": "jest",
"test": "vitest run",
"type-check": "tsc --noEmit -p tsconfig.build.json",
"build:types": "tsc --emitDeclarationOnly -p tsconfig.build.json",
"build:js": "babel src/ --out-dir build/ --copy-files --extensions \".ts,.tsx\" --source-maps",

View file

@ -1,4 +1,5 @@
import nock from 'nock';
import { describe, expect, test } from 'vitest';
import { Config, getDefaultConfig } from '@verdaccio/config';
import { fileUtils, searchUtils } from '@verdaccio/core';

View file

@ -1,3 +1,5 @@
import { describe, expect, test } from 'vitest';
import { Manifest } from '@verdaccio/types';
import { generatePackageMetadata } from '../../api/node_modules/@verdaccio/test-helper/build';

View file

@ -1,3 +1,5 @@
import { describe, expect, test } from 'vitest';
import { DIST_TAGS } from '@verdaccio/core';
import { Manifest } from '@verdaccio/types';

View file

@ -5,6 +5,7 @@ import nock from 'nock';
import * as httpMocks from 'node-mocks-http';
import os from 'os';
import path from 'path';
import { beforeEach, describe, expect, test, vi } from 'vitest';
import { Config, getDefaultConfig } from '@verdaccio/config';
import {
@ -125,7 +126,7 @@ describe('storage', () => {
beforeEach(() => {
nock.cleanAll();
nock.abortPendingRequests();
jest.clearAllMocks();
vi.clearAllMocks();
});
describe('updateManifest', () => {
@ -890,289 +891,301 @@ describe('storage', () => {
});
describe('getTarball', () => {
test('should get a package from local storage', (done) => {
const pkgName = 'foo';
const config = new Config(
configExample({
...getDefaultConfig(),
storage: generateRandomStorage(),
})
);
const storage = new Storage(config, logger);
storage.init(config).then(() => {
const ac = new AbortController();
const bodyNewManifest = generatePackageMetadata(pkgName, '1.0.0');
storage
.updateManifest(bodyNewManifest, {
signal: ac.signal,
name: pkgName,
uplinksLook: false,
requestOptions: defaultRequestOptions,
})
.then(() => {
const abort = new AbortController();
storage
.getTarball(pkgName, `${pkgName}-1.0.0.tgz`, {
signal: abort.signal,
})
.then((stream) => {
stream.on('data', (dat) => {
expect(dat).toBeDefined();
expect(dat.length).toEqual(512);
});
stream.on('end', () => {
done();
});
stream.on('error', () => {
done('this should not happen');
});
});
});
});
});
test('should not found a package anywhere', (done) => {
const config = new Config(
configExample({
...getDefaultConfig(),
storage: generateRandomStorage(),
})
);
const storage = new Storage(config, logger);
storage.init(config).then(() => {
const abort = new AbortController();
storage
.getTarball('some-tarball', 'some-tarball-1.0.0.tgz', {
signal: abort.signal,
})
.then((stream) => {
stream.on('error', (err) => {
expect(err).toEqual(errorUtils.getNotFound(API_ERROR.NO_PACKAGE));
done();
});
});
});
});
test('should create a package if tarball is requested and does not exist locally', (done) => {
const pkgName = 'upstream';
const upstreamManifest = generateRemotePackageMetadata(
pkgName,
'1.0.0',
'https://registry.something.org'
);
nock('https://registry.verdaccio.org').get(`/${pkgName}`).reply(201, upstreamManifest);
nock('https://registry.something.org')
.get(`/${pkgName}/-/${pkgName}-1.0.0.tgz`)
// types does not match here with documentation
// @ts-expect-error
.replyWithFile(201, path.join(__dirname, 'fixtures/tarball.tgz'), {
[HEADER_TYPE.CONTENT_LENGTH]: 277,
});
const config = new Config(
configExample(
{
test('should get a package from local storage', () => {
return new Promise((done) => {
const pkgName = 'foo';
const config = new Config(
configExample({
...getDefaultConfig(),
storage: generateRandomStorage(),
},
'./fixtures/config/getTarball-getupstream.yaml',
__dirname
)
);
const storage = new Storage(config, logger);
storage.init(config).then(() => {
const abort = new AbortController();
storage
.getTarball(pkgName, `${pkgName}-1.0.0.tgz`, {
signal: abort.signal,
})
.then((stream) => {
stream.on('data', (dat) => {
expect(dat).toBeDefined();
);
const storage = new Storage(config, logger);
storage.init(config).then(() => {
const ac = new AbortController();
const bodyNewManifest = generatePackageMetadata(pkgName, '1.0.0');
storage
.updateManifest(bodyNewManifest, {
signal: ac.signal,
name: pkgName,
uplinksLook: false,
requestOptions: defaultRequestOptions,
})
.then(() => {
const abort = new AbortController();
storage
.getTarball(pkgName, `${pkgName}-1.0.0.tgz`, {
signal: abort.signal,
})
.then((stream) => {
stream.on('data', (dat) => {
expect(dat).toBeDefined();
expect(dat.length).toEqual(512);
});
stream.on('end', () => {
done(true);
});
stream.on('error', () => {
done('this should not happen');
});
});
});
stream.on('end', () => {
done();
});
stream.on('error', () => {
done('this should not happen');
});
});
});
});
});
test('should serve fetch tarball from upstream without dist info local', (done) => {
const pkgName = 'upstream';
const upstreamManifest = addNewVersion(
generateRemotePackageMetadata(pkgName, '1.0.0') as Manifest,
'1.0.1'
);
nock('https://registry.verdaccio.org').get(`/${pkgName}`).reply(201, upstreamManifest);
nock('http://localhost:5555')
.get(`/${pkgName}/-/${pkgName}-1.0.1.tgz`)
// types does not match here with documentation
// @ts-expect-error
.replyWithFile(201, path.join(__dirname, 'fixtures/tarball.tgz'), {
[HEADER_TYPE.CONTENT_LENGTH]: 277,
});
const config = new Config(
configExample(
{
test('should not found a package anywhere', () => {
return new Promise((done) => {
const config = new Config(
configExample({
...getDefaultConfig(),
storage: generateRandomStorage(),
},
'./fixtures/config/getTarball-getupstream.yaml',
__dirname
)
);
const storage = new Storage(config, logger);
storage.init(config).then(() => {
const ac = new AbortController();
const bodyNewManifest = generatePackageMetadata(pkgName, '1.0.0');
storage
.updateManifest(bodyNewManifest, {
signal: ac.signal,
name: pkgName,
uplinksLook: true,
revision: '1',
requestOptions: {
host: 'localhost',
protocol: 'http',
headers: {},
},
})
.then(() => {
const abort = new AbortController();
storage
.getTarball(pkgName, `${pkgName}-1.0.1.tgz`, {
signal: abort.signal,
})
.then((stream) => {
stream.on('data', (dat) => {
expect(dat).toBeDefined();
});
stream.on('end', () => {
done();
});
stream.on('error', () => {
done('this should not happen');
});
);
const storage = new Storage(config, logger);
storage.init(config).then(() => {
const abort = new AbortController();
storage
.getTarball('some-tarball', 'some-tarball-1.0.0.tgz', {
signal: abort.signal,
})
.then((stream) => {
stream.on('error', (err) => {
expect(err).toEqual(errorUtils.getNotFound(API_ERROR.NO_PACKAGE));
done();
});
});
});
});
});
});
test('should serve fetch tarball from upstream without with info local', (done) => {
const pkgName = 'upstream';
const upstreamManifest = addNewVersion(
addNewVersion(generateRemotePackageMetadata(pkgName, '1.0.0') as Manifest, '1.0.1'),
'1.0.2'
);
nock('https://registry.verdaccio.org')
.get(`/${pkgName}`)
.times(10)
.reply(201, upstreamManifest);
nock('http://localhost:5555')
.get(`/${pkgName}/-/${pkgName}-1.0.0.tgz`)
// types does not match here with documentation
// @ts-expect-error
.replyWithFile(201, path.join(__dirname, 'fixtures/tarball.tgz'), {
[HEADER_TYPE.CONTENT_LENGTH]: 277,
});
const storagePath = generateRandomStorage();
const config = new Config(
configExample(
{
storage: storagePath,
},
'./fixtures/config/getTarball-getupstream.yaml',
__dirname
)
);
const storage = new Storage(config, logger);
storage.init(config).then(() => {
const req = httpMocks.createRequest({
method: 'GET',
connection: { remoteAddress: fakeHost },
headers: {
host: fakeHost,
[HEADERS.FORWARDED_PROTO]: 'http',
},
url: '/',
});
return storage
.getPackageByOptions({
name: pkgName,
uplinksLook: true,
requestOptions: {
headers: req.headers as any,
protocol: req.protocol,
host: req.get('host') as string,
},
})
.then(() => {
const abort = new AbortController();
storage
.getTarball(pkgName, `${pkgName}-1.0.0.tgz`, {
signal: abort.signal,
})
.then((stream) => {
stream.on('data', (dat) => {
expect(dat).toBeDefined();
});
stream.on('end', () => {
done();
});
stream.once('error', () => {
done('this should not happen');
});
});
test('should create a package if tarball is requested and does not exist locally', () => {
return new Promise((done) => {
const pkgName = 'upstream';
const upstreamManifest = generateRemotePackageMetadata(
pkgName,
'1.0.0',
'https://registry.something.org'
);
nock('https://registry.verdaccio.org').get(`/${pkgName}`).reply(201, upstreamManifest);
nock('https://registry.something.org')
.get(`/${pkgName}/-/${pkgName}-1.0.0.tgz`)
// types does not match here with documentation
// @ts-expect-error
.replyWithFile(201, path.join(__dirname, 'fixtures/tarball.tgz'), {
[HEADER_TYPE.CONTENT_LENGTH]: 277,
});
const config = new Config(
configExample(
{
storage: generateRandomStorage(),
},
'./fixtures/config/getTarball-getupstream.yaml',
__dirname
)
);
const storage = new Storage(config, logger);
storage.init(config).then(() => {
const abort = new AbortController();
storage
.getTarball(pkgName, `${pkgName}-1.0.0.tgz`, {
signal: abort.signal,
})
.then((stream) => {
stream.on('data', (dat) => {
expect(dat).toBeDefined();
});
stream.on('end', () => {
done(true);
});
stream.on('error', () => {
done('this should not happen');
});
});
});
});
});
test('should serve local cache', (done) => {
const pkgName = 'upstream';
const config = new Config(
configExample(
{
storage: generateRandomStorage(),
},
'./fixtures/config/getTarball-getupstream.yaml',
__dirname
)
);
const storage = new Storage(config, logger);
storage.init(config).then(() => {
const ac = new AbortController();
const bodyNewManifest = generatePackageMetadata(pkgName, '1.0.0');
storage
.updateManifest(bodyNewManifest, {
signal: ac.signal,
name: pkgName,
uplinksLook: true,
revision: '1',
requestOptions: {
host: 'localhost',
protocol: 'http',
headers: {},
},
})
.then(() => {
const abort = new AbortController();
storage
.getTarball(pkgName, `${pkgName}-1.0.0.tgz`, {
signal: abort.signal,
})
.then((stream) => {
stream.on('data', (dat) => {
expect(dat).toBeDefined();
});
stream.on('end', () => {
done();
});
stream.on('error', () => {
done('this should not happen');
});
});
test('should serve fetch tarball from upstream without dist info local', () => {
return new Promise((done) => {
const pkgName = 'upstream';
const upstreamManifest = addNewVersion(
generateRemotePackageMetadata(pkgName, '1.0.0') as Manifest,
'1.0.1'
);
nock('https://registry.verdaccio.org').get(`/${pkgName}`).reply(201, upstreamManifest);
nock('http://localhost:5555')
.get(`/${pkgName}/-/${pkgName}-1.0.1.tgz`)
// types does not match here with documentation
// @ts-expect-error
.replyWithFile(201, path.join(__dirname, 'fixtures/tarball.tgz'), {
[HEADER_TYPE.CONTENT_LENGTH]: 277,
});
const config = new Config(
configExample(
{
storage: generateRandomStorage(),
},
'./fixtures/config/getTarball-getupstream.yaml',
__dirname
)
);
const storage = new Storage(config, logger);
storage.init(config).then(() => {
const ac = new AbortController();
const bodyNewManifest = generatePackageMetadata(pkgName, '1.0.0');
storage
.updateManifest(bodyNewManifest, {
signal: ac.signal,
name: pkgName,
uplinksLook: true,
revision: '1',
requestOptions: {
host: 'localhost',
protocol: 'http',
headers: {},
},
})
.then(() => {
const abort = new AbortController();
storage
.getTarball(pkgName, `${pkgName}-1.0.1.tgz`, {
signal: abort.signal,
})
.then((stream) => {
stream.on('data', (dat) => {
expect(dat).toBeDefined();
});
stream.on('end', () => {
done(true);
});
stream.on('error', () => {
done('this should not happen');
});
});
});
});
});
});
test('should serve fetch tarball from upstream without with info local', () => {
return new Promise((done) => {
const pkgName = 'upstream';
const upstreamManifest = addNewVersion(
addNewVersion(generateRemotePackageMetadata(pkgName, '1.0.0') as Manifest, '1.0.1'),
'1.0.2'
);
nock('https://registry.verdaccio.org')
.get(`/${pkgName}`)
.times(10)
.reply(201, upstreamManifest);
nock('http://localhost:5555')
.get(`/${pkgName}/-/${pkgName}-1.0.0.tgz`)
// types does not match here with documentation
// @ts-expect-error
.replyWithFile(201, path.join(__dirname, 'fixtures/tarball.tgz'), {
[HEADER_TYPE.CONTENT_LENGTH]: 277,
});
const storagePath = generateRandomStorage();
const config = new Config(
configExample(
{
storage: storagePath,
},
'./fixtures/config/getTarball-getupstream.yaml',
__dirname
)
);
const storage = new Storage(config, logger);
storage.init(config).then(() => {
const req = httpMocks.createRequest({
method: 'GET',
connection: { remoteAddress: fakeHost },
headers: {
host: fakeHost,
[HEADERS.FORWARDED_PROTO]: 'http',
},
url: '/',
});
return storage
.getPackageByOptions({
name: pkgName,
uplinksLook: true,
requestOptions: {
headers: req.headers as any,
protocol: req.protocol,
host: req.get('host') as string,
},
})
.then(() => {
const abort = new AbortController();
storage
.getTarball(pkgName, `${pkgName}-1.0.0.tgz`, {
signal: abort.signal,
})
.then((stream) => {
stream.on('data', (dat) => {
expect(dat).toBeDefined();
});
stream.on('end', () => {
done(true);
});
stream.once('error', () => {
done('this should not happen');
});
});
});
});
});
});
test('should serve local cache', () => {
return new Promise((done) => {
const pkgName = 'upstream';
const config = new Config(
configExample(
{
storage: generateRandomStorage(),
},
'./fixtures/config/getTarball-getupstream.yaml',
__dirname
)
);
const storage = new Storage(config, logger);
storage.init(config).then(() => {
const ac = new AbortController();
const bodyNewManifest = generatePackageMetadata(pkgName, '1.0.0');
storage
.updateManifest(bodyNewManifest, {
signal: ac.signal,
name: pkgName,
uplinksLook: true,
revision: '1',
requestOptions: {
host: 'localhost',
protocol: 'http',
headers: {},
},
})
.then(() => {
const abort = new AbortController();
storage
.getTarball(pkgName, `${pkgName}-1.0.0.tgz`, {
signal: abort.signal,
})
.then((stream) => {
stream.on('data', (dat) => {
expect(dat).toBeDefined();
});
stream.on('end', () => {
done(true);
});
stream.on('error', () => {
done('this should not happen');
});
});
});
});
});
});
});

View file

@ -1,4 +1,5 @@
import assert from 'assert';
import { describe, expect, test } from 'vitest';
import {
getVersion,
@ -118,7 +119,7 @@ describe('versions-utils', () => {
});
describe('removeLowerVersions', () => {
it('should remove lower semantic versions', () => {
test('should remove lower semantic versions', () => {
const inputArray = [
{ package: { name: 'object1', version: '1.0.0' } },
{ package: { name: 'object1', version: '2.0.0' } }, // Duplicate name 'object1'
@ -140,7 +141,7 @@ describe('versions-utils', () => {
expect(result).toEqual(expectedOutput);
});
it('should remove lower semantic versions 2', () => {
test('should remove lower semantic versions 2', () => {
const inputArray = [
{ package: { name: 'object1', version: '1.0.0' } },
{ package: { name: 'object1', version: '2.0.0' } }, // Duplicate name 'object1'

View file

@ -934,6 +934,9 @@ importers:
body-parser:
specifier: 1.20.3
version: 1.20.3(supports-color@6.1.0)
jsdom:
specifier: 25.0.1
version: 25.0.1
supertest:
specifier: 7.0.0
version: 7.0.0
@ -968,9 +971,6 @@ importers:
'@verdaccio/types':
specifier: workspace:13.0.0-next-8.1
version: link:../core/types
jest:
specifier: 29.7.0
version: 29.7.0(@types/node@20.14.12)(ts-node@10.9.2)
selfsigned:
specifier: 2.4.1
version: 2.4.1
@ -12200,6 +12200,15 @@ packages:
transitivePeerDependencies:
- supports-color
/agent-base@7.1.1:
resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==}
engines: {node: '>= 14'}
dependencies:
debug: 4.3.7(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
dev: true
/agentkeepalive@4.5.0:
resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==}
engines: {node: '>= 8.0.0'}
@ -15204,6 +15213,13 @@ packages:
cssom: 0.3.8
dev: true
/cssstyle@4.1.0:
resolution: {integrity: sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==}
engines: {node: '>=18'}
dependencies:
rrweb-cssom: 0.7.1
dev: true
/csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
@ -15575,6 +15591,14 @@ packages:
whatwg-url: 11.0.0
dev: true
/data-urls@5.0.0:
resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
engines: {node: '>=18'}
dependencies:
whatwg-mimetype: 4.0.0
whatwg-url: 14.0.0
dev: true
/data-view-buffer@1.0.1:
resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==}
engines: {node: '>= 0.4'}
@ -19264,6 +19288,13 @@ packages:
whatwg-encoding: 2.0.0
dev: true
/html-encoding-sniffer@4.0.0:
resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
engines: {node: '>=18'}
dependencies:
whatwg-encoding: 3.1.1
dev: true
/html-entities@1.4.0:
resolution: {integrity: sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==}
@ -19436,6 +19467,16 @@ packages:
transitivePeerDependencies:
- supports-color
/http-proxy-agent@7.0.2:
resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
engines: {node: '>= 14'}
dependencies:
agent-base: 7.1.1
debug: 4.3.7(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
dev: true
/http-proxy-middleware@0.19.1(debug@4.3.7)(supports-color@6.1.0):
resolution: {integrity: sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==}
engines: {node: '>=4.0.0'}
@ -19522,6 +19563,16 @@ packages:
transitivePeerDependencies:
- supports-color
/https-proxy-agent@7.0.5:
resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==}
engines: {node: '>= 14'}
dependencies:
agent-base: 7.1.1
debug: 4.3.7(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
dev: true
/human-id@1.0.2:
resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==}
dev: true
@ -21132,6 +21183,42 @@ packages:
- utf-8-validate
dev: true
/jsdom@25.0.1:
resolution: {integrity: sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==}
engines: {node: '>=18'}
peerDependencies:
canvas: ^2.11.2
peerDependenciesMeta:
canvas:
optional: true
dependencies:
cssstyle: 4.1.0
data-urls: 5.0.0
decimal.js: 10.4.3
form-data: 4.0.0
html-encoding-sniffer: 4.0.0
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.5
is-potential-custom-element-name: 1.0.1
nwsapi: 2.2.13
parse5: 7.1.2
rrweb-cssom: 0.7.1
saxes: 6.0.0
symbol-tree: 3.2.4
tough-cookie: 5.0.0
w3c-xmlserializer: 5.0.0
webidl-conversions: 7.0.0
whatwg-encoding: 3.1.1
whatwg-mimetype: 4.0.0
whatwg-url: 14.0.0
ws: 8.18.0
xml-name-validator: 5.0.0
transitivePeerDependencies:
- bufferutil
- supports-color
- utf-8-validate
dev: true
/jsesc@0.5.0:
resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==}
hasBin: true
@ -23579,6 +23666,10 @@ packages:
engines: {node: '>=0.10.0'}
dev: false
/nwsapi@2.2.13:
resolution: {integrity: sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ==}
dev: true
/nwsapi@2.2.7:
resolution: {integrity: sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==}
dev: true
@ -25845,6 +25936,11 @@ packages:
resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==}
engines: {node: '>=6'}
/punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
dev: true
/pupa@2.1.1:
resolution: {integrity: sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==}
engines: {node: '>=8'}
@ -27208,6 +27304,10 @@ packages:
fsevents: 2.3.3
dev: true
/rrweb-cssom@0.7.1:
resolution: {integrity: sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==}
dev: true
/rsvp@4.8.5:
resolution: {integrity: sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==}
engines: {node: 6.* || >= 7.*}
@ -28634,7 +28734,7 @@ packages:
peerDependencies:
postcss: ^8.2.15
dependencies:
browserslist: 4.22.2
browserslist: 4.23.2
postcss: 8.4.31
postcss-selector-parser: 6.0.13
@ -28644,7 +28744,7 @@ packages:
peerDependencies:
postcss: ^8.2.15
dependencies:
browserslist: 4.22.2
browserslist: 4.23.2
postcss: 8.4.40
postcss-selector-parser: 6.0.13
@ -29230,6 +29330,17 @@ packages:
engines: {node: '>=14.0.0'}
dev: true
/tldts-core@6.1.48:
resolution: {integrity: sha512-3gD9iKn/n2UuFH1uilBviK9gvTNT6iYwdqrj1Vr5mh8FuelvpRNaYVH4pNYqUgOGU4aAdL9X35eLuuj0gRsx+A==}
dev: true
/tldts@6.1.48:
resolution: {integrity: sha512-SPbnh1zaSzi/OsmHb1vrPNnYuwJbdWjwo5TbBYYMlTtH3/1DSb41t8bcSxkwDmmbG2q6VLPVvQc7Yf23T+1EEw==}
hasBin: true
dependencies:
tldts-core: 6.1.48
dev: true
/tmp@0.0.33:
resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==}
engines: {node: '>=0.6.0'}
@ -29316,6 +29427,13 @@ packages:
url-parse: 1.5.10
dev: true
/tough-cookie@5.0.0:
resolution: {integrity: sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==}
engines: {node: '>=16'}
dependencies:
tldts: 6.1.48
dev: true
/tr46@0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
@ -29326,6 +29444,13 @@ packages:
punycode: 2.3.0
dev: true
/tr46@5.0.0:
resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==}
engines: {node: '>=18'}
dependencies:
punycode: 2.3.1
dev: true
/tree-kill@1.2.2:
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
hasBin: true
@ -30575,6 +30700,13 @@ packages:
xml-name-validator: 4.0.0
dev: true
/w3c-xmlserializer@5.0.0:
resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
engines: {node: '>=18'}
dependencies:
xml-name-validator: 5.0.0
dev: true
/wait-on@5.3.0(debug@4.3.7):
resolution: {integrity: sha512-DwrHrnTK+/0QFaB9a8Ol5Lna3k7WvUR4jzSKmz0YaPBpuN2sACyiPVKVfj6ejnjcajAcvn3wlbTyMIn9AZouOg==}
engines: {node: '>=8.9.0'}
@ -30697,7 +30829,7 @@ packages:
opener: 1.5.2
picocolors: 1.0.0
sirv: 2.0.4
ws: 7.5.9
ws: 7.5.10
transitivePeerDependencies:
- bufferutil
- utf-8-validate
@ -31330,6 +31462,13 @@ packages:
iconv-lite: 0.6.3
dev: true
/whatwg-encoding@3.1.1:
resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
engines: {node: '>=18'}
dependencies:
iconv-lite: 0.6.3
dev: true
/whatwg-fetch@3.6.20:
resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==}
dev: true
@ -31339,6 +31478,11 @@ packages:
engines: {node: '>=12'}
dev: true
/whatwg-mimetype@4.0.0:
resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
engines: {node: '>=18'}
dev: true
/whatwg-url@11.0.0:
resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==}
engines: {node: '>=12'}
@ -31347,6 +31491,14 @@ packages:
webidl-conversions: 7.0.0
dev: true
/whatwg-url@14.0.0:
resolution: {integrity: sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==}
engines: {node: '>=18'}
dependencies:
tr46: 5.0.0
webidl-conversions: 7.0.0
dev: true
/whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
dependencies:
@ -31585,19 +31737,6 @@ packages:
optional: true
utf-8-validate:
optional: true
dev: true
/ws@7.5.9:
resolution: {integrity: sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==}
engines: {node: '>=8.3.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: ^5.0.2
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
/ws@8.14.2:
resolution: {integrity: sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==}
@ -31611,6 +31750,19 @@ packages:
utf-8-validate:
optional: true
/ws@8.18.0:
resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: '>=5.0.2'
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
dev: true
/x-default-browser@0.4.0:
resolution: {integrity: sha512-7LKo7RtWfoFN/rHx1UELv/2zHGMx8MkZKDq1xENmOCTkfIqZJ0zZ26NEJX8czhnPXVcqS0ARjjfJB+eJ0/5Cvw==}
hasBin: true
@ -31634,6 +31786,11 @@ packages:
engines: {node: '>=12'}
dev: true
/xml-name-validator@5.0.0:
resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
engines: {node: '>=18'}
dev: true
/xml@1.0.1:
resolution: {integrity: sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==}
dev: true