Compare commits

...

23 Commits

Author SHA1 Message Date
Marc Bernard
ab3bf4a5d4 chore: replace mime package with constants (#5471)
* chore: replace mime package with constants

* Revert types/mime

* Revert types/mime

* Update lock file
2025-11-14 07:59:14 +01:00
Marc Bernard
265c94af37 fix(proxy): proxy protocol check (#5474)
* fix(proxy): proxy protocol check

* Clarify docs
2025-11-14 07:49:57 +01:00
verdacciobot
430b3173b1 chore: updated static data 2025-11-13 00:22:45 +00:00
Marc Bernard
05c8e517c1 fix(ui): regression after MUI 7 upgrade (#5469) 2025-11-11 08:32:10 +01:00
Marc Bernard
b24f513b92 chore(lint): switch rules from jest to vitest (#5470)
* chore(lint): switch rules from jest to vitest

* Revert tsconfig

* Update active-directory
2025-11-10 23:00:04 +01:00
verdacciobot
6c0f9f9ba0 chore: updated static data 2025-11-10 01:14:21 +00:00
renovate[bot]
87b9c1e8da fix(deps): update dependency validator to v13.15.22 (#5467) 2025-11-09 15:45:26 +01:00
Marc Bernard
3aff890593 chore(deps): api, auth, cli, config, url (#5459) 2025-11-09 12:58:31 +01:00
renovate[bot]
b1ceb7b3ad chore(deps): update dependency cypress to v15.6.0 (#5465)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-08 17:52:52 +01:00
renovate[bot]
92a65eb6b4 chore(deps): update github/codeql-action digest to 51f7732 (#5454)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-08 12:46:12 +01:00
Juan Picado
9ecfd5a256 Update pink-mayflies-hide.md 2025-11-08 11:06:52 +01:00
Juan Picado
f3d696facb feat!: remove fastify server packages (#5463) 2025-11-08 10:11:23 +01:00
Marc Bernard
6d1a84aba1 chore(deps): node-api, fastify, ui-comp, web (#5460) 2025-11-07 21:44:20 +01:00
verdacciobot
6b9ac25158 chore: updated static data 2025-11-06 00:16:04 +00:00
verdacciobot
a94cc9c8e5 chore: updated static data 2025-11-03 00:17:11 +00:00
dependabot[bot]
f831a6508f chore(deps): bump github/codeql-action from 3.30.5 to 4.31.2 (#5451)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.30.5 to 4.31.2.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](3599b3baa1...0499de31b9)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.31.2
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-01 16:16:09 +01:00
verdacciobot
87d99ec1ed chore: updated static data 2025-10-30 00:16:50 +00:00
verdacciobot
1661abb5a8 chore: updated static data 2025-10-27 00:17:27 +00:00
Juan Picado
26a339518f chore: update material-ui and storybook 9 (#5444)
* chore: update material ui 7

* chore: update storybook 9
2025-10-26 12:47:29 +01:00
renovate[bot]
a97c85e975 fix(deps): update dependency pino to v9.14.0 (#5442)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-24 18:49:58 +02:00
renovate[bot]
1da9aefce3 fix(deps): update dependency core-js to v3.46.0 (#5441)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-24 18:31:04 +02:00
verdacciobot
c2ef25dfa1 chore: updated static data 2025-10-23 00:15:59 +00:00
verdacciobot
8dc3f3e1bc chore: updated static data 2025-10-20 00:16:57 +00:00
130 changed files with 5105 additions and 5622 deletions

View File

@@ -10,7 +10,6 @@
'verdaccio-memory': major
'@verdaccio/search-indexer': major
'@verdaccio/server': major
'@verdaccio/server-fastify': major
'@verdaccio/logger': major
'verdaccio-audit': major
'@verdaccio/ui-components': major

View File

@@ -1,7 +1,6 @@
---
'@verdaccio/local-storage': patch
'@verdaccio/server': patch
'@verdaccio/server-fastify': patch
'@verdaccio/test-helper': patch
'@verdaccio/ui-components': patch
'@verdaccio/tarball': patch

View File

@@ -5,7 +5,6 @@
'@verdaccio/local-scripts': patch
'@verdaccio/file-locking': patch
'@verdaccio/ui-theme': patch
'@verdaccio/server-fastify': patch
'@verdaccio/test-helper': patch
'@verdaccio/middleware': patch
'verdaccio': patch

View File

@@ -0,0 +1,9 @@
---
'@verdaccio/url': patch
'@verdaccio/config': patch
'@verdaccio/auth': patch
'@verdaccio/api': patch
'@verdaccio/cli': patch
---
chore(deps): api, auth, cli, config, url

View File

@@ -0,0 +1,6 @@
---
'@verdaccio/proxy': patch
'@verdaccio/website': patch
---
fix(proxy): proxy protocol check

View File

@@ -1,6 +1,5 @@
---
'@verdaccio/local-storage': patch
'@verdaccio/server-fastify': patch
'@verdaccio/middleware': patch
'@verdaccio/core': patch
'@verdaccio/config': patch

View File

@@ -0,0 +1,6 @@
---
'@verdaccio/middleware': patch
'@verdaccio/api': patch
---
chore: replace mime package with constants

View File

@@ -0,0 +1,13 @@
---
'@verdaccio/local-storage': patch
'@verdaccio/ui-theme': patch
'verdaccio-memory': patch
'@verdaccio/ui-components': patch
'@verdaccio/eslint-config': patch
'@verdaccio/middleware': patch
'@verdaccio/config': patch
'@verdaccio/store': patch
'@verdaccio/api': patch
---
chore(lint): switch rules from jest to vitest

View File

@@ -11,7 +11,6 @@
'verdaccio-memory': patch
'@verdaccio/search-indexer': patch
'@verdaccio/server': patch
'@verdaccio/server-fastify': patch
'@verdaccio/logger': patch
'@verdaccio/test-helper': patch
'@verdaccio/ui-components': patch

View File

@@ -0,0 +1,7 @@
---
'@verdaccio/ui-components': patch
'@verdaccio/node-api': patch
'@verdaccio/web': patch
---
chore(deps): node-api, fastify, ui-comp, web

View File

@@ -41,7 +41,6 @@
"@verdaccio/search": "7.0.0",
"@verdaccio/search-indexer": "7.0.0",
"@verdaccio/server": "7.0.0",
"@verdaccio/server-fastify": "7.0.0",
"@verdaccio/signature": "7.0.0",
"@verdaccio/cli-standalone": "7.0.0",
"@verdaccio/store": "7.0.0",

View File

@@ -0,0 +1,5 @@
---
'@verdaccio/ui-components': patch
---
fix(ui): regression after mui 7 upgrade

View File

@@ -12,7 +12,6 @@
'verdaccio-memory': patch
'@verdaccio/search-indexer': patch
'@verdaccio/server': patch
'@verdaccio/server-fastify': patch
'@verdaccio/logger': patch
'verdaccio-audit': patch
'@verdaccio/test-helper': patch

View File

@@ -4,7 +4,6 @@
'@verdaccio/ui-theme': patch
'@verdaccio/search-indexer': patch
'@verdaccio/server': patch
'@verdaccio/server-fastify': patch
'@verdaccio/test-helper': patch
'@verdaccio/middleware': patch
'verdaccio': patch

View File

@@ -19,3 +19,5 @@ yarn.js
packages/ui-components/storybook-static
dist.js
bundle.js
# generator templates
packages/tools/generator-verdaccio-plugin/generators/app/**

View File

@@ -1,3 +1,3 @@
module.exports = {
extends: ['@verdaccio/eslint-config'],
extends: ['@verdaccio/eslint-config', 'plugin:storybook/recommended'],
};

View File

@@ -37,7 +37,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.29.5
uses: github/codeql-action/init@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3.29.5
with:
config: |
paths-ignore:
@@ -50,7 +50,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.29.5
uses: github/codeql-action/autobuild@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3.29.5
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -64,4 +64,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.29.5
uses: github/codeql-action/analyze@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3.29.5

View File

@@ -136,7 +136,6 @@ Any interaction with the server should be done through the port `8000` eg: `npm
#### Useful commands
- `pnpm debug`: Run the server in debug mode `--inspect`. UI runs too but without hot reload. For automatic break use `pnpm debug:break`.
- `pnpm debug:fastify`: To contribute on the [fastify migration](https://github.com/verdaccio/verdaccio/discussions/2155) this is a temporary command for such purpose.
- `pnpm website`: Build the website, for more commands to run the _website_, run `cd website` and then `pnpm serve`, website will run on port `3000`.
- `pnpm docker`: Build the docker image. Requires `docker` command available in your system.

View File

@@ -8,7 +8,7 @@
"@verdaccio/config": "workspace:8.0.0-next-8.24",
"@verdaccio/test-helper": "workspace:4.0.0-next-8.8",
"debug": "4.4.3",
"cypress": "15.4.0",
"cypress": "15.6.0",
"get-port": "5.1.1"
},
"scripts": {

View File

@@ -147,7 +147,6 @@
"_debug:reload": "nodemon -d 3 packages/verdaccio/debug/bootstrap.js",
"start:ts": "ts-node packages/verdaccio/src/start.ts -- --listen 8000",
"debug": "node --trace-warnings --trace-uncaught --inspect packages/verdaccio/debug/bootstrap.js",
"debug:fastify": "cross-env VERDACCIO_SERVER=fastify node --trace-warnings --trace-uncaught --inspect packages/verdaccio/debug/bootstrap.js",
"debug:break": "node --trace-warnings --trace-uncaught --inspect-brk packages/verdaccio/debug/bootstrap.js",
"changeset": "changeset",
"changeset:check": "changeset status --since-master",

View File

@@ -48,20 +48,17 @@
"@verdaccio/logger": "workspace:8.0.0-next-8.24",
"@verdaccio/middleware": "workspace:8.0.0-next-8.24",
"@verdaccio/store": "workspace:8.0.0-next-8.24",
"abortcontroller-polyfill": "1.7.8",
"body-parser": "1.20.3",
"cookies": "0.9.1",
"debug": "4.4.3",
"express": "4.21.2",
"lodash": "4.17.21",
"mime": "2.6.0",
"semver": "7.7.3"
"lodash": "4.17.21"
},
"devDependencies": {
"@verdaccio/test-helper": "workspace:4.0.0-next-8.8",
"@verdaccio/types": "workspace:13.0.0-next-8.8",
"mockdate": "3.0.5",
"supertest": "7.1.4"
"nock": "13.5.6",
"supertest": "7.1.4",
"vitest": "3.2.4"
},
"funding": {
"type": "opencollective",

View File

@@ -1,9 +1,8 @@
import { Router } from 'express';
import _ from 'lodash';
import mime from 'mime';
import { Auth } from '@verdaccio/auth';
import { constants, errorUtils } from '@verdaccio/core';
import { HEADERS, constants, errorUtils } from '@verdaccio/core';
import { allow, media } from '@verdaccio/middleware';
import { DIST_TAGS_API_ENDPOINTS } from '@verdaccio/middleware';
import { Storage } from '@verdaccio/store';
@@ -42,14 +41,14 @@ export default function (route: Router, auth: Auth, storage: Storage, logger: Lo
route.put(
DIST_TAGS_API_ENDPOINTS.tagging,
can('publish'),
media(mime.getType('json')),
media(HEADERS.JSON),
addTagPackageVersionMiddleware
);
route.put(
DIST_TAGS_API_ENDPOINTS.tagging_package,
can('publish'),
media(mime.getType('json')),
media(HEADERS.JSON),
addTagPackageVersionMiddleware
);

View File

@@ -1,9 +1,8 @@
import buildDebug from 'debug';
import { Router } from 'express';
import mime from 'mime';
import { Auth } from '@verdaccio/auth';
import { API_MESSAGE, HTTP_STATUS } from '@verdaccio/core';
import { API_MESSAGE, HEADERS, HTTP_STATUS } from '@verdaccio/core';
import { allow, expectJson, media } from '@verdaccio/middleware';
// import star from './star';
import { PUBLISH_API_ENDPOINTS } from '@verdaccio/middleware';
@@ -122,7 +121,7 @@ export default function publish(
router.put(
PUBLISH_API_ENDPOINTS.add_package,
can('publish'),
media(mime.getType('json')),
media(HEADERS.JSON),
expectJson,
publishPackage(storage, logger, 'publish one version')
);
@@ -130,7 +129,7 @@ export default function publish(
router.put(
PUBLISH_API_ENDPOINTS.publish_package,
can('unpublish'),
media(mime.getType('json')),
media(HEADERS.JSON),
expectJson,
publishPackage(storage, logger, 'publish with revision')
);

View File

@@ -1,4 +1,3 @@
/* eslint-disable jest/no-commented-out-tests */
import nock from 'nock';
import { describe, expect, test } from 'vitest';

View File

@@ -5,8 +5,8 @@ import { HEADERS, HEADER_TYPE, HTTP_STATUS, TOKEN_BEARER } from '@verdaccio/core
import { buildToken, createUser, initializeServer } from './_helper';
describe('profile ', () => {
describe('get profile ', () => {
describe('profile', () => {
describe('get profile', () => {
test('should return Unauthorized if header token is missing', async () => {
const app = await initializeServer('profile.yaml');
return supertest(app)
@@ -26,7 +26,7 @@ describe('profile ', () => {
.expect(HTTP_STATUS.OK);
});
});
describe('post profile ', () => {
describe('post profile', () => {
test('should return Unauthorized if header token is missing', async () => {
const app = await initializeServer('profile.yaml');
return supertest(app)

View File

@@ -55,7 +55,8 @@
"@verdaccio/types": "workspace:13.0.0-next-8.8",
"@verdaccio/logger": "workspace:8.0.0-next-8.24",
"express": "4.21.2",
"supertest": "7.1.4"
"supertest": "7.1.4",
"vitest": "3.2.4"
},
"funding": {
"type": "opencollective",

View File

@@ -1,4 +1,4 @@
import { NextFunction, Request, Response } from 'express';
import type { NextFunction, Request, Response } from 'express';
import { VerdaccioError } from '@verdaccio/core';
import { AuthPackageAllow, JWTSignOptions, Logger, RemoteUser } from '@verdaccio/types';

View File

@@ -53,11 +53,12 @@
"@verdaccio/node-api": "workspace:8.0.0-next-8.24",
"clipanion": "4.0.0-rc.4",
"envinfo": "7.19.0",
"kleur": "4.1.5",
"semver": "7.7.3"
},
"devDependencies": {
"ts-node": "10.9.2"
"@verdaccio/types": "workspace:13.0.0-next-8.8",
"ts-node": "10.9.2",
"vitest": "3.2.4"
},
"funding": {
"type": "opencollective",

View File

@@ -45,11 +45,11 @@
"@verdaccio/core": "workspace:8.0.0-next-8.24",
"debug": "4.4.3",
"js-yaml": "4.1.0",
"lodash": "4.17.21",
"minimatch": "7.4.6"
"lodash": "4.17.21"
},
"devDependencies": {
"@types/minimatch": "5.1.2"
"@verdaccio/types": "workspace:13.0.0-next-8.8",
"vitest": "3.2.4"
},
"funding": {
"type": "opencollective",

View File

@@ -137,7 +137,6 @@ describe('checkSecretKey', () => {
const config = new Config(parseConfigFile(resolveConf('default')), {
forceMigrateToSecureLegacySignature: false,
});
// eslint-disable-next-line jest/no-standalone-expect
expect(() =>
// 64 characters secret long
config.checkSecretKey('b4982dbb0108531fafb552374d7e83724b6458a2b3ffa97ad0edb899bdaefc4a')
@@ -149,7 +148,6 @@ describe('checkSecretKey', () => {
forceMigrateToSecureLegacySignature: false,
});
config.security.api.migrateToSecureLegacySignature = true;
// eslint-disable-next-line jest/no-standalone-expect
expect(
config.checkSecretKey('b4982dbb0108531fafb552374d7e83724b6458a2b3ffa97ad0edb899bdaefc4a')
).toHaveLength(TOKEN_VALID_LENGTH);
@@ -161,7 +159,6 @@ describe('checkSecretKey', () => {
const config = new Config(parseConfigFile(resolveConf('default')));
config.security.api.migrateToSecureLegacySignature = false;
// 64 characters secret long
// eslint-disable-next-line jest/no-standalone-expect
expect(
config.checkSecretKey('b4982dbb0108531fafb552374d7e83724b6458a2b3ffa97ad0edb899bdaefc4a')
).toHaveLength(64);
@@ -172,17 +169,16 @@ describe('checkSecretKey', () => {
const config = new Config(parseConfigFile(resolveConf('default')));
config.security.api.migrateToSecureLegacySignature = true;
// 64 characters secret long
// eslint-disable-next-line jest/no-standalone-expect
expect(
config.checkSecretKey('b4982dbb0108531fafb552374d7e83724b6458a2b3ffa97ad0edb899bdaefc4a')
).toHaveLength(TOKEN_VALID_LENGTH);
});
test.todo('test emit warning with secret key');
test.todo('emit warning with secret key');
});
describe('getMatchedPackagesSpec', () => {
test('should match with react as defined in config file', () => {
test('should match with react as defined in config file - react', () => {
const configParsed = parseConfigFile(parseConfigurationFile('config-getMatchedPackagesSpec'));
const config = new Config(configParsed);
expect(config.getMatchedPackagesSpec('react')).toEqual({
@@ -193,7 +189,7 @@ describe('getMatchedPackagesSpec', () => {
});
});
test('should not match with react as defined in config file', () => {
test('should not match with react as defined in config file - somePackage', () => {
const configParsed = parseConfigFile(parseConfigurationFile('config-getMatchedPackagesSpec'));
const config = new Config(configParsed);
expect(config.getMatchedPackagesSpec('somePackage')).toEqual({

View File

@@ -12,7 +12,7 @@ describe('getConfigParsed', () => {
expect(config).toBeDefined();
});
test('parses config from a YAML file path', () => {
test('parses config from JSON', () => {
const yamlFile = path.join(partialsDir, 'config-getMatchedPackagesSpec.yaml');
const config = getConfigParsed(parseConfigFile(yamlFile));
expect(config).toBeDefined();

View File

@@ -35,11 +35,9 @@
"dependencies": {
"@verdaccio/core": "workspace:8.0.0-next-8.24",
"debug": "4.4.3",
"lodash": "4.17.21",
"validator": "13.15.15"
"validator": "13.15.22"
},
"devDependencies": {
"@verdaccio/types": "workspace:13.0.0-next-8.8",
"node-mocks-http": "1.14.1",
"vitest": "3.2.4"
},

View File

@@ -49,7 +49,7 @@
},
"devDependencies": {
"@verdaccio/types": "workspace:13.0.0-next-8.8",
"pino": "9.13.1",
"pino": "9.14.0",
"vitest": "3.2.4"
},
"funding": {

View File

@@ -50,7 +50,7 @@
"sonic-boom": "3.8.1"
},
"devDependencies": {
"pino": "9.13.1",
"pino": "9.14.0",
"vitest": "3.2.4"
},
"funding": {

View File

@@ -43,7 +43,7 @@
},
"dependencies": {
"@verdaccio/logger-commons": "workspace:8.0.0-next-8.24",
"pino": "9.13.1"
"pino": "9.14.0"
},
"devDependencies": {
"@verdaccio/types": "workspace:13.0.0-next-8.8"

View File

@@ -49,8 +49,7 @@
"express": "4.21.2",
"express-rate-limit": "5.5.1",
"lodash": "4.17.21",
"lru-cache": "7.18.3",
"mime": "2.6.0"
"lru-cache": "7.18.3"
},
"funding": {
"type": "opencollective",

View File

@@ -65,7 +65,7 @@ describe('packages requests', () => {
expect(res.status).toEqual(HTTP_STATUS.OK);
});
test('scoped package with encoded @ and path ', async () => {
test('scoped package with encoded @ and path', async () => {
const res = await request(app).get('/%40scope%2ffoo');
expect(res.body).toEqual({ package: '@scope/foo' });
expect(res.status).toEqual(HTTP_STATUS.OK);

View File

@@ -1,4 +1,3 @@
import mime from 'mime';
import request from 'supertest';
import { test } from 'vitest';
@@ -9,7 +8,7 @@ import { getApp } from './helper';
test('media is json', async () => {
const app = getApp([]);
app.get('/json', media(mime.getType('json')), (req, res) => {
app.get('/json', media(HEADERS.JSON), (req, res) => {
res.status(200).json();
});
@@ -22,7 +21,7 @@ test('media is json', async () => {
test('media is json with charset', async () => {
const app = getApp([]);
app.get('/json', media(mime.getType('json')), (req, res) => {
app.get('/json', media(HEADERS.JSON), (req, res) => {
res.status(200).json();
});
@@ -35,7 +34,7 @@ test('media is json with charset', async () => {
test('media is not json', async () => {
const app = getApp([]);
app.get('/json', media(mime.getType('json')), (req, res) => {
app.get('/json', media(HEADERS.JSON), (req, res) => {
res.status(HTTP_STATUS.OK).json({});
});
@@ -48,7 +47,7 @@ test('media is not json', async () => {
test('missing content-type', async () => {
const app = getApp([]);
app.get('/json', media(mime.getType('json')), (req, res) => {
app.get('/json', media(HEADERS.JSON), (req, res) => {
res.status(HTTP_STATUS.OK).json({});
});

View File

@@ -43,16 +43,13 @@
"license": "MIT",
"dependencies": {
"@verdaccio/config": "workspace:8.0.0-next-8.24",
"@verdaccio/core": "workspace:8.0.0-next-8.24",
"@verdaccio/logger": "workspace:8.0.0-next-8.24",
"@verdaccio/server": "workspace:8.0.0-next-8.24",
"@verdaccio/server-fastify": "workspace:8.0.0-next-8.24",
"debug": "4.4.3",
"lodash": "4.17.21"
},
"devDependencies": {
"@verdaccio/types": "workspace:13.0.0-next-8.8",
"selfsigned": "2.4.1",
"supertest": "7.1.4",
"vitest": "3.2.4"
},

View File

@@ -10,7 +10,6 @@ import url from 'node:url';
import { getConfigParsed, getListenAddress } from '@verdaccio/config';
import { logger, setup } from '@verdaccio/logger';
import expressServer from '@verdaccio/server';
import fastifyServer from '@verdaccio/server-fastify';
import { ConfigYaml, HttpsConfKeyCert, HttpsConfPfx } from '@verdaccio/types';
import { displayExperimentsInfoBox } from './experiments';
@@ -123,60 +122,48 @@ export async function initServer(
const addr = getListenAddress(port ?? config?.listen, logger);
displayExperimentsInfoBox(config.flags);
let app;
if (process.env.VERDACCIO_SERVER === 'fastify') {
app = await fastifyServer(config);
app.listen({ port: addr.port, host: addr.host }, (err) => {
if (err) {
reject(err);
} else {
resolve();
let app = await expressServer(config);
const serverFactory = createServerFactory(config, addr, app);
serverFactory
.listen(addr.port || addr.path, addr.host, (): void => {
// send a message for test
if (isFunction(process.send)) {
process.send({
verdaccio_started: true,
});
}
const addressServer = `${
addr.path
? url.format({
protocol: 'unix',
pathname: addr.path,
})
: url.format({
protocol: addr.proto,
hostname: addr.host,
port: addr.port,
pathname: '/',
})
}`;
logger.info({ addressServer }, 'http address: @{addressServer}');
logger.info({ version }, 'version: @{version}');
resolve();
})
.on('error', function (err): void {
reject(err);
process.exitCode = 1;
});
} else {
app = await expressServer(config);
const serverFactory = createServerFactory(config, addr, app);
serverFactory
.listen(addr.port || addr.path, addr.host, (): void => {
// send a message for test
if (isFunction(process.send)) {
process.send({
verdaccio_started: true,
});
}
const addressServer = `${
addr.path
? url.format({
protocol: 'unix',
pathname: addr.path,
})
: url.format({
protocol: addr.proto,
hostname: addr.host,
port: addr.port,
pathname: '/',
})
}`;
logger.info({ addressServer }, 'http address: @{addressServer}');
logger.info({ version }, 'version: @{version}');
resolve();
})
.on('error', function (err): void {
reject(err);
process.exitCode = 1;
});
function handleShutdownGracefully() {
logger.info('received shutdown signal - closing server gracefully...');
serverFactory.close(() => {
logger.info('server closed.');
process.exit(0);
});
}
function handleShutdownGracefully() {
logger.info('received shutdown signal - closing server gracefully...');
serverFactory.close(() => {
logger.info('server closed.');
process.exit(0);
});
}
for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP']) {
// Use once() so that receiving double signals exit the app.
process.once(signal, handleShutdownGracefully);
}
for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP']) {
// Use once() so that receiving double signals exit the app.
process.once(signal, handleShutdownGracefully);
}
});
}

View File

@@ -15,9 +15,6 @@
},
{
"path": "../server/express"
},
{
"path": "../server/fastify"
}
]
}

View File

@@ -1,14 +1,16 @@
import { vi } from 'vitest';
import { Logger } from '@verdaccio/types';
const logger: Logger = {
warn: jest.fn(),
error: jest.fn(),
fatal: jest.fn(),
info: jest.fn(),
debug: jest.fn(),
child: jest.fn(),
http: jest.fn(),
trace: jest.fn(),
warn: vi.fn(),
error: vi.fn(),
fatal: vi.fn(),
info: vi.fn(),
debug: vi.fn(),
child: vi.fn(),
http: vi.fn(),
trace: vi.fn(),
};
export default logger;

View File

@@ -1,9 +1,9 @@
import ActiveDirectory from 'activedirectory2';
import { vi } from 'vitest';
import { HTTP_STATUS } from '@verdaccio/core';
import ActiveDirectoryPlugin, { NotAuthMessage } from '../src/active-directory';
// eslint-disable-next-line jest/no-mocks-import
import logger from './__mocks__/Logger';
describe('Active Directory Plugin', () => {
@@ -34,12 +34,12 @@ describe('Active Directory Plugin', () => {
});
beforeEach(() => {
jest.resetModules();
vi.resetModules();
});
test('get error when connection fails', (done) => {
const errorMessage = 'Unknown error';
ActiveDirectory.prototype.authenticate = jest.fn((_1, _2, cb) => cb(errorMessage, undefined));
ActiveDirectory.prototype.authenticate = vi.fn((_1, _2, cb) => cb(errorMessage, undefined));
adPlugin.authenticate('', '', (error, authUser) => {
expect(ActiveDirectory.prototype.authenticate).toHaveBeenCalled();
@@ -52,7 +52,7 @@ describe('Active Directory Plugin', () => {
});
test('get error when not authenticated satisfactory', (done) => {
ActiveDirectory.prototype.authenticate = jest.fn((_1, _2, cb) => cb(null, false));
ActiveDirectory.prototype.authenticate = vi.fn((_1, _2, cb) => cb(null, false));
adPlugin.authenticate('', '', (error, authUser) => {
expect(ActiveDirectory.prototype.authenticate).toHaveBeenCalled();
@@ -68,7 +68,7 @@ describe('Active Directory Plugin', () => {
const user = 'user';
const password = 'password';
ActiveDirectory.prototype.authenticate = jest.fn((_1, _2, cb) => cb(null, true));
ActiveDirectory.prototype.authenticate = vi.fn((_1, _2, cb) => cb(null, true));
adPlugin.authenticate(user, password, (error, authUser) => {
expect(ActiveDirectory.prototype.authenticate).toHaveBeenCalled();
@@ -81,10 +81,10 @@ describe('Active Directory Plugin', () => {
test('get error when getting groups for user', (done) => {
const errorMessage = 'Unknown error retrieving groups';
ActiveDirectory.prototype.authenticate = jest.fn((_1, _2, cb) => cb(null, true));
ActiveDirectory.prototype.getGroupMembershipForUser = jest.fn((_, cb) =>
ActiveDirectory.prototype.authenticate = vi.fn((_1, _2, cb) => cb(null, true));
ActiveDirectory.prototype.getGroupMembershipForUser = vi.fn((_, cb) =>
cb(errorMessage as unknown as object, null)
) as jest.Mock;
);
adPluginSingleGroup.authenticate('', '', (error, authUser) => {
expect(ActiveDirectory.prototype.authenticate).toHaveBeenCalled();
@@ -101,10 +101,10 @@ describe('Active Directory Plugin', () => {
const user = 'user';
const password = 'password';
ActiveDirectory.prototype.authenticate = jest.fn((_1, _2, cb) => cb(null, true));
ActiveDirectory.prototype.getGroupMembershipForUser = jest.fn((_, cb) =>
ActiveDirectory.prototype.authenticate = vi.fn((_1, _2, cb) => cb(null, true));
ActiveDirectory.prototype.getGroupMembershipForUser = vi.fn((_, cb) =>
cb(null, [{ cn: 'notMatchGroup' }])
) as jest.Mock;
);
adPluginSingleGroup.authenticate(user, password, (error, authUser) => {
expect(ActiveDirectory.prototype.authenticate).toHaveBeenCalled();
@@ -124,10 +124,10 @@ describe('Active Directory Plugin', () => {
const user = 'user';
const password = 'password';
ActiveDirectory.prototype.authenticate = jest.fn((_1, _2, cb) => cb(null, true));
ActiveDirectory.prototype.getGroupMembershipForUser = jest.fn((_, cb) =>
ActiveDirectory.prototype.authenticate = vi.fn((_1, _2, cb) => cb(null, true));
ActiveDirectory.prototype.getGroupMembershipForUser = vi.fn((_, cb) =>
cb(null, [{ cn: groupName }])
) as jest.Mock;
);
adPluginSingleGroup.authenticate(user, password, (error, authUser) => {
expect(ActiveDirectory.prototype.authenticate).toHaveBeenCalled();
@@ -144,10 +144,10 @@ describe('Active Directory Plugin', () => {
const user = 'user';
const password = 'password';
ActiveDirectory.prototype.authenticate = jest.fn((_1, _2, cb) => cb(null, true));
ActiveDirectory.prototype.getGroupMembershipForUser = jest.fn((_, cb) =>
ActiveDirectory.prototype.authenticate = vi.fn((_1, _2, cb) => cb(null, true));
ActiveDirectory.prototype.getGroupMembershipForUser = vi.fn((_, cb) =>
cb(null, [{ cn: group2 }, { dn: group3 }])
) as jest.Mock;
);
adPluginMultiGroups.authenticate(user, password, (error, authUser) => {
expect(ActiveDirectory.prototype.authenticate).toHaveBeenCalled();

View File

@@ -1,4 +1,3 @@
/* eslint-disable jest/no-mocks-import */
import path from 'node:path';
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';

View File

@@ -28,6 +28,7 @@ const logger: Logger = {
warn: vi.fn(),
http: vi.fn(),
trace: vi.fn(),
fatal: vi.fn(),
};
vi.setConfig({ testTimeout: 20000 });

View File

@@ -1,4 +1,3 @@
/* eslint-disable jest/no-mocks-import */
import fs from 'node:fs';
import path from 'node:path';
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
@@ -16,6 +15,7 @@ const logger: Logger = {
warn: vi.fn(),
http: vi.fn(),
trace: vi.fn(),
fatal: vi.fn(),
};
describe('Local Database', () => {

View File

@@ -16,6 +16,7 @@ const logger: Logger = {
warn: vi.fn(),
http: vi.fn(),
trace: vi.fn(),
fatal: vi.fn(),
};
describe('Utitlies', () => {

View File

@@ -72,7 +72,7 @@ describe('MemoryHandler', () => {
expect(updated.name).toBe(pkgExample.name);
});
test('should fail on update a package', async () => {
test('should fail on update a package - mock implementation', async () => {
mockParsePackage.mockImplementationOnce(() => {
throw new Error('error on parse');
});
@@ -86,7 +86,7 @@ describe('MemoryHandler', () => {
);
});
test('should fail updateHandler update a package', async () => {
test('should fail on update a package - internal error', async () => {
const localMemory = new LocalMemory(memoryConfig, defaultConfig);
const pkgName = 'test6';
const handler = localMemory.getPackageStorage(pkgName);
@@ -99,7 +99,7 @@ describe('MemoryHandler', () => {
).rejects.toEqual(errorUtils.getInternalError('some error'));
});
test('should fail on update a package', async () => {
test('should fail on update a package - throw error', async () => {
const localMemory = new LocalMemory(memoryConfig, defaultConfig);
const pkgName = 'test7';
const handler = localMemory.getPackageStorage(pkgName);

View File

@@ -60,7 +60,7 @@
"react/jsx-curly-brace-presence": ["warn", { "props": "ignore", "children": "ignore" }],
"react/jsx-pascal-case": ["error"],
"react/jsx-props-no-multi-spaces": ["error"],
"react/jsx-sort-default-props": ["error"],
"react/sort-default-props": ["error"],
"react/jsx-sort-props": ["error"],
"react/no-string-refs": ["error"],
"react/no-danger-with-children": ["error"],
@@ -78,7 +78,6 @@
"react-hooks/exhaustive-deps": "warn",
"verdaccio/jsx-no-style": ["warn"],
"verdaccio/jsx-spread": ["warn"],
"jest/expect-expect": 0,
"quote-props":["error", "as-needed"],
"prefer-spread": 1,
"linebreak-style": 0,
@@ -94,7 +93,6 @@
"__DEBUG__": true
},
"env": {
"browser": true,
"jest/globals": true
"browser": true
}
}

View File

@@ -199,14 +199,14 @@
"username": "Utwo",
"id": 282668
},
{
"username": "BartDubois",
"id": 1180931
},
{
"username": "CrispyConductor",
"id": 2132722
},
{
"username": "BartDubois",
"id": 1180931
},
{
"username": "SnirBroshi",
"id": 26556598
@@ -280,20 +280,20 @@
"id": 38713281
},
{
"username": "kfatehi",
"id": 175305
},
{
"username": "gkalpak",
"id": 8604205
"username": "brenordr",
"id": 19731692
},
{
"username": "karfau",
"id": 135657
},
{
"username": "brenordr",
"id": 19731692
"username": "gkalpak",
"id": 8604205
},
{
"username": "kfatehi",
"id": 175305
},
{
"username": "frimuchkov",
@@ -343,6 +343,22 @@
"username": "jhonmike",
"id": 2499937
},
{
"username": "somethingSTRANGE",
"id": 6905832
},
{
"username": "plitex",
"id": 2946823
},
{
"username": "naveensrinivasan",
"id": 172697
},
{
"username": "nphyatt",
"id": 6487450
},
{
"username": "mysiar",
"id": 13708162
@@ -355,22 +371,6 @@
"username": "Marin-Kitagawa",
"id": 49131888
},
{
"username": "nphyatt",
"id": 6487450
},
{
"username": "naveensrinivasan",
"id": 172697
},
{
"username": "plitex",
"id": 2946823
},
{
"username": "somethingSTRANGE",
"id": 6905832
},
{
"username": "varijkapil13",
"id": 8291077
@@ -392,29 +392,29 @@
"id": 2234539
},
{
"username": "okv",
"id": 465522
"username": "danielo515",
"id": 2270425
},
{
"username": "SheetJSDev",
"id": 6070939
},
{
"username": "danielo515",
"id": 2270425
"username": "okv",
"id": 465522
},
{
"username": "s-h-a-d-o-w",
"id": 16936908
},
{
"username": "alan-agius4",
"id": 17563226
},
{
"username": "awshanks",
"id": 18176417
},
{
"username": "alan-agius4",
"id": 17563226
},
{
"username": "imsnif",
"id": 795598
@@ -451,22 +451,22 @@
"username": "josephg",
"id": 47413
},
{
"username": "MasterOdin",
"id": 1845314
},
{
"username": "marnel",
"id": 3189424
},
{
"username": "aledbf",
"id": 161571
},
{
"username": "kba",
"id": 273367
},
{
"username": "MasterOdin",
"id": 1845314
},
{
"username": "aledbf",
"id": 161571
},
{
"username": "Shreynik",
"id": 25787910
@@ -588,16 +588,16 @@
"id": 1174257
},
{
"username": "saurabh-gohil",
"id": 7500357
"username": "Skn0tt",
"id": 14912729
},
{
"username": "starizard",
"id": 4953349
},
{
"username": "Skn0tt",
"id": 14912729
"username": "saurabh-gohil",
"id": 7500357
},
{
"username": "saintmalik",
@@ -615,10 +615,6 @@
"username": "rostislav-simonik",
"id": 25525736
},
{
"username": "morlay",
"id": 1667873
},
{
"username": "fhp",
"id": 374671
@@ -659,6 +655,14 @@
"username": "Tiny-Fendy",
"id": 8954107
},
{
"username": "tomcoonen",
"id": 988013
},
{
"username": "hedocode",
"id": 22884999
},
{
"username": "morrain",
"id": 9381634
@@ -823,10 +827,6 @@
"username": "melodyVoid",
"id": 26846212
},
{
"username": "tomcoonen",
"id": 988013
},
{
"username": "grrowl",
"id": 907140
@@ -859,6 +859,10 @@
"username": "stek29",
"id": 11808223
},
{
"username": "vsugrob",
"id": 1497471
},
{
"username": "vitalybaev",
"id": 724423
@@ -919,10 +923,6 @@
"username": "divdavem",
"id": 1152706
},
{
"username": "hedocode",
"id": 22884999
},
{
"username": "bchenSyd",
"id": 8207081
@@ -1096,8 +1096,8 @@
"id": 47446
},
{
"username": "jchilton",
"id": 181054
"username": "ericmutta",
"id": 20465797
},
{
"username": "jondlm",
@@ -1176,8 +1176,8 @@
"id": 8955528
},
{
"username": "ericmutta",
"id": 20465797
"username": "morlay",
"id": 1667873
},
{
"username": "einfallstoll",
@@ -1255,6 +1255,10 @@
"username": "johannespfeiffer",
"id": 6780316
},
{
"username": "jchilton",
"id": 181054
},
{
"username": "notsag",
"id": 674589

View File

@@ -15,7 +15,9 @@ class CustomAgents {
this.proxy = proxy;
this.url = url;
this.agentOptions = agentOptions;
const { protocol } = this.getParsedUrl();
// Type of agent depends on the protocol of the server URL (no on the proxy)
// See https://www.npmjs.com/package/hpagent
const { protocol } = new URL(this.url);
this.agent = this.getAgent(protocol);
}
@@ -41,10 +43,6 @@ class CustomAgents {
: { http: new HttpAgent(this.agentOptions) };
}
}
private getParsedUrl() {
return this.proxy ? new URL(this.proxy) : new URL(this.url);
}
}
export default CustomAgents;

View File

@@ -632,20 +632,6 @@ class ProxyStorage implements IProxy {
}
}
// validate proxy protocol matches the proxy_key type
if (this.proxy) {
const proxyUrl = new URL(this.proxy);
const expectedProtocol = isHTTPS ? 'https:' : 'http:';
if (proxyUrl.protocol !== expectedProtocol) {
this.logger.error(
{ proxy: this.proxy, expectedProtocol },
`invalid protocol for ${proxy_key} - must be ${expectedProtocol}`
);
this.proxy = undefined;
return;
}
}
if (typeof this.proxy === 'string') {
debug('using proxy @{proxy} for @{url}', this.proxy, this.url.href);
this.logger.debug(

View File

@@ -28,60 +28,47 @@ describe('Check protocol of proxy', () => {
beforeEach(() => {
vi.clearAllMocks();
});
test('validate main config protocol - http', () => {
test('main config - http registry, http proxy', () => {
expect(
getProxyInstance(
'http://registry.domain.org',
{ http_proxy: 'http://registry.local.org' },
{}
).proxy
).toEqual('http://registry.local.org');
getProxyInstance('http://registry.domain.org', { http_proxy: 'http://proxy.local' }, {}).proxy
).toEqual('http://proxy.local');
});
test('main config invalid protocol - http', () => {
test('main config - http registry, https proxy', () => {
expect(
getProxyInstance(
'http://registry.domain.org',
{ http_proxy: 'https://registry.local.org' },
{}
).proxy
).toEqual(undefined);
expect(mockError).toHaveBeenCalledOnce();
getProxyInstance('http://registry.domain.org', { http_proxy: 'https://proxy.local' }, {})
.proxy
).toEqual('https://proxy.local');
});
test('main config invalid protocol - https', () => {
test('main config invalid config key - http registry', () => {
expect(
getProxyInstance(
'https://registry.domain.org',
{ https_proxy: 'http://registry.local.org' },
{}
).proxy
getProxyInstance('http://registry.domain.org', { https_proxy: 'anything' }, {}).proxy
).toEqual(undefined);
});
test('main config invalid config key - https registry', () => {
expect(
getProxyInstance('https://registry.domain.org', { http_proxy: 'anything' }, {}).proxy
).toEqual(undefined);
expect(mockError).toHaveBeenCalledOnce();
});
test('validate uplink config protocol - http', () => {
test('uplink config - http registry, http proxy', () => {
expect(
getProxyInstance(
'https://registry.domain.org',
{},
{ https_proxy: 'https://proxy.domain.org' }
).proxy
).toEqual('https://proxy.domain.org');
getProxyInstance('http://registry.domain.org', {}, { http_proxy: 'http://proxy.local' }).proxy
).toEqual('http://proxy.local');
});
test('uplink config invalid protocol - http', () => {
test('uplink config - http registry, https proxy', () => {
expect(
getProxyInstance('http://registry.domain.org', {}, { http_proxy: 'https://proxy.domain.org' })
getProxyInstance('http://registry.domain.org', {}, { http_proxy: 'https://proxy.local' })
.proxy
).toEqual(undefined);
expect(mockError).toHaveBeenCalledOnce();
).toEqual('https://proxy.local');
});
test('uplink config invalid protocol - https', () => {
test('uplink config invalid config key - http registry', () => {
expect(
getProxyInstance(
'https://registry.domain.org',
{},
{ https_proxy: 'http://proxy.domain.org' }
).proxy
getProxyInstance('http://registry.domain.org', {}, { https_proxy: 'anything' }).proxy
).toEqual(undefined);
});
test('uplink config invalid config key - https registry', () => {
expect(
getProxyInstance('https://registry.domain.org', {}, { http_proxy: 'anything' }).proxy
).toEqual(undefined);
expect(mockError).toHaveBeenCalledOnce();
});
});

View File

@@ -1,3 +0,0 @@
{
"extends": "../../../.babelrc"
}

View File

@@ -1,6 +0,0 @@
node_modules
coverage/
lib/
.nyc_output
tests-report/
build/

View File

@@ -1,5 +0,0 @@
{
"rules": {
"@typescript-eslint/no-use-before-define": "off"
}
}

View File

@@ -1 +0,0 @@
lib/

View File

@@ -1,6 +0,0 @@
/*
!/bin/**/*
!/build/**/*
!index.js
!LICENSE
!README.md

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2025 Verdaccio contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,88 +0,0 @@
# @verdaccio/server-fastify - Verdaccio Fastify Server
[![Verdaccio Home](https://img.shields.io/badge/Homepage-Verdaccio-405236?style=flat)](https://verdaccio.org)
[![MIT License](https://img.shields.io/github/license/verdaccio/verdaccio?label=License&color=405236)](https://github.com/verdaccio/verdaccio/blob/master/LICENSE)
[![Verdaccio Latest](https://img.shields.io/npm/v/verdaccio?label=Latest%20Version&color=405236)](https://github.com/verdaccio/verdaccio)
[![This Package Latest](https://img.shields.io/npm/v/@verdaccio/server-fastify?label=@verdaccio/server-fastify&color=405236)](https://npmjs.com/package/@verdaccio/server-fastify)
[![Documentation](https://img.shields.io/badge/Help-Verdaccio?style=flat&logo=Verdaccio&label=Verdaccio&color=cd4000)](https://verdaccio.org/docs)
[![Discord](https://img.shields.io/badge/Chat-Discord?style=flat&logo=Discord&label=Discord&color=cd4000)](https://discord.com/channels/388674437219745793)
[![Bluesky](https://img.shields.io/badge/Follow-Bluesky?style=flat&logo=Bluesky&label=Bluesky&color=cd4000)](https://bsky.app/profile/verdaccio.org)
[![Backers](https://img.shields.io/opencollective/backers/verdaccio?style=flat&logo=opencollective&label=Join%20Backers&color=cd4000)](https://opencollective.com/verdaccio/contribute)
[![Sponsors](https://img.shields.io/opencollective/sponsors/verdaccio?style=flat&logo=opencollective&label=Sponsor%20Us&color=cd4000)](https://opencollective.com/verdaccio/contribute)
[![Verdaccio Downloads](https://img.shields.io/npm/dm/verdaccio?style=flat&logo=npm&label=Npm%20Downloads&color=lightgrey)](https://www.npmjs.com/package/verdaccio)
[![Docker Pulls](https://img.shields.io/docker/pulls/verdaccio/verdaccio?style=flat&logo=docker&label=Docker%20Pulls&color=lightgrey)](https://hub.docker.com/r/verdaccio/verdaccio)
[![GitHub Stars](https://img.shields.io/github/stars/verdaccio?style=flat&logo=github&label=GitHub%20Stars%20%E2%AD%90&color=lightgrey)](https://github.com/verdaccio/verdaccio/stargazers)
A package intended to start a migration from Express to fastify. (WIP).
Run from root folder
```js
pnpm debug -- new
```
## Donations
Verdaccio is run by **volunteers**; nobody is working full-time on it. If you find this project to be useful and would like to support its development, consider making a donation - **your logo might end up in this readme.** 😉
**[Donate](https://opencollective.com/verdaccio)** 💵👍🏻 starting from _\$1/month_ or just one single contribution.
## Report a vulnerability
If you want to report a security vulnerability, please follow the steps which we have defined for you in our [security policy](https://github.com/verdaccio/verdaccio/security/policy).
## Open Collective Sponsors
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/verdaccio/contribute)]
[![sponsor](https://opencollective.com/verdaccio/sponsor/0/avatar.svg)](https://opencollective.com/verdaccio/sponsor/0/website)
[![sponsor](https://opencollective.com/verdaccio/sponsor/1/avatar.svg)](https://opencollective.com/verdaccio/sponsor/1/website)
[![sponsor](https://opencollective.com/verdaccio/sponsor/2/avatar.svg)](https://opencollective.com/verdaccio/sponsor/2/website)
[![sponsor](https://opencollective.com/verdaccio/sponsor/3/avatar.svg)](https://opencollective.com/verdaccio/sponsor/3/website)
[![sponsor](https://opencollective.com/verdaccio/sponsor/4/avatar.svg)](https://opencollective.com/verdaccio/sponsor/4/website)
[![sponsor](https://opencollective.com/verdaccio/sponsor/5/avatar.svg)](https://opencollective.com/verdaccio/sponsor/5/website)
[![sponsor](https://opencollective.com/verdaccio/sponsor/6/avatar.svg)](https://opencollective.com/verdaccio/sponsor/6/website)
[![sponsor](https://opencollective.com/verdaccio/sponsor/7/avatar.svg)](https://opencollective.com/verdaccio/sponsor/7/website)
[![sponsor](https://opencollective.com/verdaccio/sponsor/8/avatar.svg)](https://opencollective.com/verdaccio/sponsor/8/website)
[![sponsor](https://opencollective.com/verdaccio/sponsor/9/avatar.svg)](https://opencollective.com/verdaccio/sponsor/9/website)
## Open Collective Backers
Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/verdaccio/contribute)]
[![backers](https://opencollective.com/verdaccio/backers.svg?width=890)](https://opencollective.com/verdaccio/contributes)
## Special Thanks
Thanks to the following companies to help us to achieve our goals providing free open source licenses.
[![jetbrains](https://github.com/verdaccio/verdaccio/blob/master/assets/thanks/jetbrains/logo.jpg?raw=true)](https://www.jetbrains.com/)
[![crowdin](https://github.com/verdaccio/verdaccio/blob/master/assets/thanks/crowdin/logo.png?raw=true)](https://crowdin.com/)
## Contributors
This project exists thanks to all the people who contribute. [[Contribute](https://github.com/verdaccio/verdaccio/blob/master/CONTRIBUTING.md)].
[![contributors](https://opencollective.com/verdaccio/contributors.svg?width=890&button=true)](https://github.com/verdaccio/verdaccio/graphs/contributors)
## FAQ / Contact / Troubleshoot
If you have any issue you can try the following options. Do not hesitate to ask or check our issues database. Perhaps someone has asked already what you are looking for.
- [Blog](https://verdaccio.org/blog/)
- [Donations](https://opencollective.com/verdaccio)
- [Reporting an issue](https://github.com/verdaccio/verdaccio/blob/master/CONTRIBUTING.md#reporting-a-bug)
- [Running discussions](https://github.com/orgs/verdaccio/discussions)
- [Chat](https://discord.com/channels/388674437219745793)
- [Logos](https://verdaccio.org/docs/logo)
- [Docker Examples](https://github.com/verdaccio/verdaccio/tree/master/docker-examples)
- [FAQ](https://github.com/verdaccio/verdaccio/issues?utf8=%E2%9C%93&q=is%3Aissue%20label%3Aquestion%20)
## License
Verdaccio is [MIT licensed](https://github.com/verdaccio/verdaccio/blob/master/LICENSE)
The Verdaccio documentation and logos (excluding /thanks, e.g., .md, .png, .sketch files within the /assets folder) are
[Creative Commons licensed](https://creativecommons.org/licenses/by/4.0/).

View File

@@ -1,39 +0,0 @@
storage: ./storage
plugins: ./plugins
web:
title: Verdaccio
auth:
htpasswd:
file: ./htpasswd
uplinks:
npmjs:
url: https://registry.npmjs.org/
packages:
'@*/*':
access: $all
publish: $authenticated
unpublish: $authenticated
proxy: npmjs
'**':
access: $all
publish: $authenticated
unpublish: $authenticated
proxy: npmjs
server:
keepAliveTimeout: 60
middlewares:
audit:
enabled: true
log: { type: stdout, format: pretty, level: http }
flags:
token: false
search: false
i18n:
web: en-US

View File

@@ -1,32 +0,0 @@
import buildDebug from 'debug';
import path from 'node:path';
import { parseConfigFile } from '@verdaccio/config';
import { logger, setup } from '@verdaccio/logger';
import server from '../src/index';
const debug = buildDebug('verdaccio:fastify:debug');
/**
* This file is intended for fast development and debug, it should
* be removed eventually and the app start from @verdaccio/cli package.
*/
(async () => {
try {
const configFile = path.join(__dirname, './fastify-conf.yaml');
debug('configFile %s', configFile);
const configParsed = parseConfigFile(configFile);
setup(configParsed.log);
logger.info(`config location ${configFile}`);
debug('configParsed %s', configParsed);
process.title = 'fastify-verdaccio';
const ser = await server({ logger, config: configParsed });
await ser.listen(4873);
logger.info('fastify running on port 4873');
} catch (err: any) {
// eslint-disable-next-line no-console
console.error(err);
process.exit(1);
}
})();

View File

@@ -1,65 +0,0 @@
{
"name": "@verdaccio/server-fastify",
"version": "8.0.0-next-8.24",
"description": "Verdaccio Fastify Server",
"keywords": [
"private",
"package",
"repository",
"registry",
"enterprise",
"modules",
"proxy",
"server",
"verdaccio"
],
"main": "./build/index.js",
"types": "./build/index.d.ts",
"author": "Juan Picado <juanpicado19@gmail.com>",
"license": "MIT",
"homepage": "https://verdaccio.org",
"engines": {
"node": ">=18"
},
"repository": {
"type": "https",
"url": "https://github.com/verdaccio/verdaccio",
"directory": "packages/server/fastify"
},
"bugs": {
"url": "https://github.com/verdaccio/verdaccio/issues"
},
"publishConfig": {
"access": "public"
},
"dependencies": {
"@verdaccio/core": "workspace:8.0.0-next-8.24",
"@verdaccio/config": "workspace:8.0.0-next-8.24",
"@verdaccio/auth": "workspace:8.0.0-next-8.24",
"@verdaccio/logger": "workspace:8.0.0-next-8.24",
"@verdaccio/store": "workspace:8.0.0-next-8.24",
"@verdaccio/tarball": "workspace:13.0.0-next-8.24",
"core-js": "3.45.1",
"debug": "4.4.3",
"fastify": "4.25.2",
"fastify-plugin": "4.5.1",
"lodash": "4.17.21"
},
"devDependencies": {
"@verdaccio/types": "workspace:13.0.0-next-8.8",
"ts-node": "10.9.2"
},
"scripts": {
"clean": "rimraf ./build",
"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",
"watch": "pnpm build:js -- --watch",
"build": "pnpm run build:js && pnpm run build:types",
"start": "ts-node debug/index.ts"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/verdaccio"
}
}

View File

@@ -1,52 +0,0 @@
import buildDebug from 'debug';
import { FastifyInstance } from 'fastify';
import { MergeTags } from '@verdaccio/types';
const debug = buildDebug('verdaccio:fastify:dist-tags');
interface ParamsInterface {
packageName: string;
}
async function distTagsRoute(fastify: FastifyInstance) {
fastify.get<{ Params: ParamsInterface }>(
'/-/package/:packageName/dist-tags',
async (request, reply) => {
// @ts-ignore
const { packageName } = request.params;
debug('dist-tags: response %o', packageName);
const requestOptions = {
protocol: request.protocol,
headers: request.headers as any,
host: request.hostname,
remoteAddress: request.socket.remoteAddress,
};
const manifest = fastify.storage.getPackageByOptions({
name: packageName,
uplinksLook: true,
keepUpLinkData: true,
requestOptions,
});
reply.code(fastify.statusCode.OK).send(manifest[fastify.constants.DIST_TAGS]);
}
);
fastify.post<{ Params: ParamsInterface; Body: MergeTags }>(
'/-/package/:packageName/dist-tags',
async (request) => {
const { packageName } = request.params;
await fastify.storage.mergeTagsNext(packageName, request.body);
return { ok: fastify.constants.API_MESSAGE.TAG_UPDATED };
}
);
fastify.delete('/-/package/:packageName/dist-tags', async (request, reply) => {
// @ts-ignore
// const { packageName } = request.params;
reply.code(fastify.statusCode.NOT_FOUND);
});
}
export default distTagsRoute;

View File

@@ -1,68 +0,0 @@
import buildDebug from 'debug';
import { FastifyInstance } from 'fastify';
import { stringUtils } from '@verdaccio/core';
import { Storage } from '@verdaccio/store';
import { Package, Version } from '@verdaccio/types';
const debug = buildDebug('verdaccio:fastify:api:sidebar');
export type $SidebarPackage = Package & { latest: Version };
interface ParamsInterface {
name: string;
version: string;
}
async function manifestRoute(fastify: FastifyInstance) {
fastify.get<{ Params: ParamsInterface }>('/:name', async (request) => {
const { name } = request.params;
const storage = fastify.storage;
debug('pkg name %s ', name);
// @ts-ignore
const abbreviated =
stringUtils.getByQualityPriorityValue(request.headers['accept']) ===
Storage.ABBREVIATED_HEADER;
const data = await storage?.getPackageByOptions({
name,
// @ts-ignore
uplinksLook: true,
requestOptions: {
protocol: request.protocol,
headers: request.headers as any,
host: request.hostname,
},
abbreviated,
});
return data;
});
interface QueryInterface {
write: string;
}
fastify.get<{ Params: ParamsInterface; Querystring: QueryInterface }>(
'/:packageName/:version',
async (request) => {
const { name, version } = request.params;
const storage = fastify.storage;
const write = request.query.write === 'true';
debug('pkg name %s, with version / tag: %s ', name, version);
const requestOptions = {
protocol: request.protocol,
headers: request.headers as any,
host: request.hostname,
remoteAddress: request.socket.remoteAddress,
byPassCache: write,
};
const data = await storage?.getPackageByOptions({
name,
version,
uplinksLook: true,
requestOptions,
});
return data;
}
);
}
export default manifestRoute;

View File

@@ -1,15 +0,0 @@
/* eslint-disable no-console */
/* eslint-disable no-invalid-this */
import { FastifyInstance } from 'fastify';
import { logger } from '@verdaccio/logger';
async function pingRoute(fastify: FastifyInstance) {
fastify.get('/-/ping', async () => {
logger.http('ping');
return {};
});
}
export default pingRoute;

View File

@@ -1,30 +0,0 @@
// import buildDebug from 'debug';
// import { FastifyInstance } from 'fastify';
// import { Package, Version } from '@verdaccio/types';
// const debug = buildDebug('verdaccio:web:api:sidebar');
// export type $SidebarPackage = Package & { latest: Version };
// async function manifestRoute(fastify: FastifyInstance) {
// // TODO: review // :_rev?/:revision?
// fastify.put('/:packageName', async (request) => {
// // @ts-ignore
// const { packageName } = request.params;
// const storage = fastify.storage;
// debug('pkg name %s ', packageName);
// // const data = await storage?.getPackageNext({
// // name: packageName,
// // req: request.raw,
// // uplinksLook: true,
// // requestOptions: {
// // protocol: request.protocol,
// // headers: request.headers as any,
// // host: request.hostname,
// // },
// // });
// // return data;
// });
// }
// export default manifestRoute;

View File

@@ -1,37 +0,0 @@
/* eslint-disable no-console */
/* eslint-disable no-invalid-this */
import { FastifyInstance } from 'fastify';
import { searchUtils } from '@verdaccio/core';
import { logger } from '@verdaccio/logger';
interface QueryInterface {
url: string;
query: searchUtils.SearchQuery;
}
async function searchRoute(fastify: FastifyInstance) {
fastify.get<{ Querystring: QueryInterface }>('/-/v1/search', async (request, reply) => {
// TODO: apply security layer here like in
// packages/api/src/v1/search.ts
// TODO: add validations for query, some parameters are mandatory
// TODO: review which query fields are mandatory
const abort = new AbortController();
request.socket.on('aborted', () => {
abort.abort();
});
const { url, query } = request.query;
const storage = fastify.storage;
const data = await storage.search({
query,
url,
abort,
});
logger.http('search endpoint');
reply.code(200).send(data);
});
}
export default searchRoute;

View File

@@ -1,69 +0,0 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import buildDebug from 'debug';
import { FastifyInstance } from 'fastify';
import { HEADERS, HEADER_TYPE } from '@verdaccio/core';
const debug = buildDebug('verdaccio:fastify:tarball');
interface ParamsInterface {
package: string;
filename: string;
}
async function tarballRoute(fastify: FastifyInstance) {
fastify.get<{ Params: ParamsInterface }>('/:package/-/:filename', async (request, reply) => {
const { package: pkg, filename } = request.params;
debug('stream tarball for %s@%s', pkg, filename);
const abort = new AbortController();
const stream = (await fastify.storage.getTarball(pkg, filename, {
signal: abort.signal,
// enableRemote: true,
})) as any;
stream.on('content-length', (size: number) => {
reply.header(HEADER_TYPE.CONTENT_LENGTH, size);
});
// request.socket.on('abort', () => {
// debug('request aborted for %o', request.url);
// abort.abort();
// });
return stream;
});
interface ScopeParamsInterface {
filename: string;
scope: string;
name: string;
}
fastify.get<{ Params: ScopeParamsInterface }>(
'/:scope/:name/-/:filename',
async (request, reply) => {
const abort = new AbortController();
const { scope, name, filename } = request.params;
const scopedPackage = `${scope}/${name}`;
debug('stream scope tarball for %s@%s', scopedPackage, filename);
const stream = (await fastify.storage.getTarball(scopedPackage, filename, {
signal: abort.signal,
// enableRemote: true,
})) as any;
stream.on('content-length', (size: number) => {
reply.header(HEADER_TYPE.CONTENT_LENGTH, size);
});
// request.socket.on('abort', () => {
// debug('request aborted for %o', request.url);
// abort.abort();
// });
reply.header(HEADERS.CONTENT_TYPE, HEADERS.OCTET_STREAM);
return stream;
}
);
}
export default tarballRoute;

View File

@@ -1,151 +0,0 @@
/* eslint-disable no-console */
/* eslint-disable no-invalid-this */
import buildDebug from 'debug';
import { FastifyInstance } from 'fastify';
import _ from 'lodash';
import { getApiToken } from '@verdaccio/auth';
import { createRemoteUser } from '@verdaccio/config';
import { authUtils, validationUtils } from '@verdaccio/core';
import { logger } from '@verdaccio/logger';
import { RemoteUser } from '@verdaccio/types';
const debug = buildDebug('verdaccio:fastify:user');
async function userRoute(fastify: FastifyInstance) {
interface UserParamsInterface {
org_couchdb_user: string;
}
fastify.get<{ Params: UserParamsInterface }>('/:org_couchdb_user', async (request, reply) => {
// @ts-ignore
// TODO: compare org_couchdb_user with remote user name
const message = authUtils.getAuthenticatedMessage(request.userRemote.name);
logger.info('user authenticated message %o', message);
reply.code(fastify.statusCode.OK);
return { ok: message };
});
interface DeleteTokenParamsInterface {
token: string;
}
fastify.delete<{ Params: DeleteTokenParamsInterface }>(
'/token/:token',
async (request, reply) => {
debug('loging out');
const { token } = request.params;
const userRemote: RemoteUser = request.userRemote;
await fastify.auth.invalidateToken(token);
console.log('userRoute', userRemote);
reply.code(fastify.statusCode.OK);
return { ok: fastify.apiMessage.LOGGED_OUT };
}
);
interface UpdateUserParamsInterface {
username: string;
}
fastify.put<{
Body: { name: string; password: string };
Params: UpdateUserParamsInterface;
}>('/:username', async (request, reply) => {
const { name, password } = request.body;
const remoteName = request.userRemote.name;
if (_.isNil(remoteName) === false && _.isNil(name) === false && remoteName === name) {
// debug('login: no remote user detected');
fastify.auth.authenticate(
name,
password,
async function callbackAuthenticate(err, user): Promise<void> {
if (err) {
logger.trace(
{ name, err },
'authenticating for user @{username} failed. Error: @{err.message}'
);
reply
.code(fastify.statusCode.UNAUTHORIZED)
.send(
fastify.errorUtils.getCode(
fastify.statusCode.UNAUTHORIZED,
fastify.apiError.BAD_USERNAME_PASSWORD
)
);
}
const restoredRemoteUser: RemoteUser = createRemoteUser(name, user?.groups || []);
const token = await getApiToken(
fastify.auth,
fastify.configInstance,
restoredRemoteUser,
password
);
debug('login: new token');
if (!token) {
return reply.send(fastify.errorUtils.getUnauthorized());
} else {
reply.code(fastify.statusCode.CREATED);
const message = authUtils.getAuthenticatedMessage(remoteName);
debug('login: created user message %o', message);
reply.send({
ok: message,
token,
});
}
}
);
} else {
if (
validationUtils.validatePassword(
password as string,
fastify.configInstance?.server?.passwordValidationRegex
) === false
) {
debug('adduser: invalid password');
reply.code(fastify.statusCode.BAD_REQUEST).send(
fastify.errorUtils.getCode(
fastify.statusCode.BAD_REQUEST,
// eslint-disable-next-line new-cap
fastify.apiError.PASSWORD_SHORT
)
);
return;
}
fastify.auth.add_user(name, password, async function (err, user): Promise<void> {
if (err) {
if (
err.status >= fastify.statusCode.BAD_REQUEST &&
err.status < fastify.statusCode.INTERNAL_ERROR
) {
debug('adduser: error on create user');
// With npm registering is the same as logging in,
// and npm accepts only an 409 error.
// So, changing status code here.
const addUserError =
fastify.errorUtils.getCode(err.status, err.message) ||
fastify.errorUtils.getConflict(err.message);
reply.send(addUserError);
return;
}
}
const token =
name && password
? await getApiToken(fastify.auth, fastify.configInstance, user as RemoteUser, password)
: undefined;
debug('adduser: new token %o', token);
if (!token) {
return reply.send(fastify.errorUtils.getUnauthorized());
}
debug('adduser: user has been created');
reply.code(fastify.statusCode.CREATED).send({
ok: `user '${name}' created`,
token,
});
});
}
});
}
export default userRoute;

View File

@@ -1,15 +0,0 @@
import buildDebug from 'debug';
import { FastifyInstance } from 'fastify';
const debug = buildDebug('verdaccio:fastify:whoami');
async function whoamiRoute(fastify: FastifyInstance) {
fastify.get('/-/whoami', async (request, reply) => {
const username: string | void = request.userRemote.name;
debug('whoami: response %o', username);
reply.code(fastify.statusCode.OK);
return { username };
});
}
export default whoamiRoute;

View File

@@ -1 +0,0 @@
export { default } from './server';

View File

@@ -1,24 +0,0 @@
import { FastifyInstance } from 'fastify';
import fp from 'fastify-plugin';
import { Auth } from '@verdaccio/auth';
import { logger } from '@verdaccio/logger';
import { Config as IConfig } from '@verdaccio/types';
export default fp(
async function (fastify: FastifyInstance, opts: { config: IConfig; filters?: unknown }) {
const { config } = opts;
const auth = new Auth(config, logger);
await auth.init();
fastify.decorate('auth', auth);
},
{
fastify: '>=4.x',
}
);
declare module 'fastify' {
interface FastifyInstance {
auth: Auth;
}
}

View File

@@ -1,22 +0,0 @@
import { FastifyInstance } from 'fastify';
import fp from 'fastify-plugin';
import { Config as AppConfig } from '@verdaccio/config';
import { ConfigYaml, Config as IConfig } from '@verdaccio/types';
export default fp(
async function (fastify: FastifyInstance, opts: { config: ConfigYaml }) {
const { config } = opts;
const configInstance: IConfig = new AppConfig(Object.assign({}, config) as any);
fastify.decorate('configInstance', configInstance);
},
{
fastify: '>=4.x',
}
);
declare module 'fastify' {
interface FastifyInstance {
configInstance: IConfig;
}
}

View File

@@ -1,47 +0,0 @@
import fp from 'fastify-plugin';
import {
API_ERROR,
API_MESSAGE,
HTTP_STATUS,
constants,
errorUtils,
pluginUtils,
searchUtils,
streamUtils,
validationUtils,
warningUtils,
} from '@verdaccio/core';
export default fp(
async function (fastify) {
fastify.decorate('errorUtils', errorUtils);
fastify.decorate('searchUtils', searchUtils);
fastify.decorate('streamUtils', streamUtils);
fastify.decorate('validationUtils', validationUtils);
fastify.decorate('pluginUtils', pluginUtils);
fastify.decorate('warningUtils', warningUtils);
fastify.decorate('apiError', API_ERROR);
fastify.decorate('constants', constants);
fastify.decorate('apiMessage', API_MESSAGE);
fastify.decorate('statusCode', HTTP_STATUS);
},
{
fastify: '>=4.x',
}
);
declare module 'fastify' {
interface FastifyInstance {
apiError: typeof API_ERROR;
apiMessage: typeof API_MESSAGE;
statusCode: typeof HTTP_STATUS;
errorUtils: typeof errorUtils;
warningUtils: typeof warningUtils;
searchUtils: typeof searchUtils;
streamUtils: typeof streamUtils;
pluginUtils: typeof pluginUtils;
validationUtils: typeof validationUtils;
constants: typeof constants;
}
}

View File

@@ -1,25 +0,0 @@
import { FastifyInstance } from 'fastify';
import fp from 'fastify-plugin';
import { logger } from '@verdaccio/logger';
import { Storage } from '@verdaccio/store';
import { Config as IConfig } from '@verdaccio/types';
export default fp(
async function (fastify: FastifyInstance, opts: { config: IConfig; filters?: unknown }) {
const { config } = opts;
const storage: Storage = new Storage(config, logger);
// @ts-ignore
await storage.init(config, []);
fastify.decorate('storage', storage);
},
{
fastify: '>=4.x',
}
);
declare module 'fastify' {
interface FastifyInstance {
storage: Storage;
}
}

View File

@@ -1,115 +0,0 @@
import buildDebug from 'debug';
import { FastifyInstance } from 'fastify';
import _ from 'lodash';
import { validationUtils } from '@verdaccio/core';
import { JWTSignOptions, RemoteUser } from '@verdaccio/types';
const debug = buildDebug('verdaccio:fastify:web:login');
const loginBodySchema = {
body: {
type: 'object',
required: ['username', 'password'],
additionalProperties: false,
properties: {
username: { type: 'string' },
password: { type: 'string' },
},
},
};
const resetPasswordSchema = {
body: {
type: 'object',
required: ['password'],
additionalProperties: false,
properties: {
password: { type: 'string' },
},
},
};
async function loginRoute(fastify: FastifyInstance) {
fastify.post(
'/login',
{
schema: loginBodySchema,
},
async (request, reply) => {
// @ts-expect-error
const { username, password } = request.body;
debug('authenticate %o', username);
fastify.auth.authenticate(
username,
password,
async function callbackAuthenticate(err, user): Promise<void> {
if (err) {
const errorCode = err.message
? fastify.statusCode.UNAUTHORIZED
: fastify.statusCode.INTERNAL_ERROR;
reply.send(fastify.errorUtils.getCode(errorCode, err.message));
} else {
const jWTSignOptions: JWTSignOptions = fastify.configInstance.security.web.sign;
debug('jwtSignOptions: %o', jWTSignOptions);
const token = await fastify.auth.jwtEncrypt(user as RemoteUser, jWTSignOptions);
reply.code(fastify.statusCode.OK).send({ token, username });
}
}
);
}
);
fastify.put(
'/reset_password',
{
schema: resetPasswordSchema,
},
async (request, reply) => {
if (_.isNil(request.userRemote.name)) {
reply.send(
fastify.errorUtils.getCode(
fastify.statusCode.UNAUTHORIZED,
fastify.errorUtils.API_ERROR.MUST_BE_LOGGED
)
);
}
// @ts-ignore
const { password } = request.body;
const { name } = request.userRemote;
if (
validationUtils.validatePassword(
password.new,
fastify.configInstance?.server?.passwordValidationRegex
) === false
) {
reply.send(
fastify.errorUtils.getCode(
fastify.statusCode.BAD_REQUEST,
fastify.errorUtils.APP_ERROR.PASSWORD_VALIDATION
)
);
return;
}
fastify.auth.changePassword(
name as string,
password.old,
password.new,
(err, isUpdated): void => {
if (_.isNil(err) && isUpdated) {
reply.code(fastify.statusCode.OK);
} else {
reply.send(
fastify.errorUtils.getInternalError(
fastify.errorUtils.API_ERROR.INTERNAL_SERVER_ERROR
)
);
}
}
);
}
);
// });
}
export default loginRoute;

View File

@@ -1,60 +0,0 @@
import buildDebug from 'debug';
import { FastifyInstance } from 'fastify';
import { Manifest } from '@verdaccio/types';
const debug = buildDebug('verdaccio:fastify:web:readme');
export const NOT_README_FOUND = 'ERROR: No README data found!';
async function readmeRoute(fastify: FastifyInstance) {
fastify.get('/package/readme/:packageName', async (request, reply) => {
// @ts-ignore
const { version, packageName } = request.params;
debug('readme name %s version: %s', packageName, version);
const manifest = (await fastify.storage?.getPackageByOptions({
name: packageName,
// remove on refactor getPackageByOptions
// @ts-ignore
req: request.raw,
version,
uplinksLook: true,
requestOptions: {
protocol: request.protocol,
headers: request.headers as any,
host: request.hostname,
},
})) as Manifest;
try {
const parsedReadme = manifest.readme;
reply.code(fastify.statusCode.OK).send(parsedReadme);
} catch {
reply.code(fastify.statusCode.OK).send(NOT_README_FOUND);
}
});
fastify.get('/package/readme/:scope/:packageName', async (request, reply) => {
// @ts-ignore
const { version, packageName } = request.params;
debug('readme name %s version: %s', packageName, version);
const manifest = (await fastify.storage?.getPackageByOptions({
name: packageName,
// remove on refactor getPackageByOptions
// @ts-ignore
req: request.raw,
version,
uplinksLook: true,
requestOptions: {
protocol: request.protocol,
headers: request.headers as any,
host: request.hostname,
},
})) as Manifest;
try {
reply.code(fastify.statusCode.OK).send(manifest.readme);
} catch {
reply.code(fastify.statusCode.OK).send(NOT_README_FOUND);
}
});
}
export default readmeRoute;

View File

@@ -1,82 +0,0 @@
import buildDebug from 'debug';
import { FastifyInstance } from 'fastify';
import { Manifest, Version } from '@verdaccio/types';
const debug = buildDebug('verdaccio:fastify:web:sidebar');
export type $SidebarPackage = Manifest & { latest: Version };
const stringType = { type: 'string' };
const packageNameSchema = { packageName: stringType };
const paramsSchema = {
scope: stringType,
packageName: stringType,
};
async function sidebarRoute(fastify: FastifyInstance) {
fastify.get(
'/sidebar/:scope/:packageName',
{
schema: {
params: paramsSchema,
},
},
async (request, reply) => {
// @ts-ignore
const { packageName, scope } = request.params;
debug('pkg name %s, scope %s ', packageName, scope);
reply.code(fastify.statusCode.NOT_FOUND);
}
);
fastify.get(
'/sidebar/:packageName',
{
schema: {
params: packageNameSchema,
},
},
async (request, reply) => {
// @ts-ignore
const { packageName, scope } = request.params;
debug('pkg name %s, scope %s ', packageName, scope);
reply.code(fastify.statusCode.NOT_FOUND);
}
);
}
// function getSidebar(fastify: FastifyInstance, request: any, packageName, callback) {
// // fastify.storage.getPackage({
// // name: packageName,
// // uplinksLook: true,
// // keepUpLinkData: true,
// // req: request.raw,
// // callback: function (err: Error, info: $SidebarPackage): void {
// // debug('sidebar pkg info %o', info);
// // if (_.isNil(err)) {
// // const { v } = request.query;
// // let sideBarInfo = _.clone(info);
// // sideBarInfo.versions = convertDistRemoteToLocalTarballUrls(
// // info,
// // { protocol: request.protocol, headers: request.headers as any, host: request.hostname },
// // fastify.configInstance.url_prefix
// // ).versions;
// // if (typeof v === 'string' && isVersionValid(info, v)) {
// // sideBarInfo.latest = sideBarInfo.versions[v];
// // sideBarInfo.latest.author = formatAuthor(sideBarInfo.latest.author);
// // } else {
// // sideBarInfo.latest = sideBarInfo.versions[info[DIST_TAGS].latest];
// // sideBarInfo.latest.author = formatAuthor(sideBarInfo.latest.author);
// // }
// // sideBarInfo = deleteProperties(['readme', '_attachments', '_rev', 'name'], sideBarInfo);
// // const authorAvatar = fastify.configInstance.web
// // ? addGravatarSupport(sideBarInfo, fastify.configInstance.web.gravatar)
// // : addGravatarSupport(sideBarInfo);
// // callback(null, authorAvatar);
// // } else {
// // callback(fastify.statusCode.NOT_FOUND).send(err);
// // }
// // },
// // });
// reply.code(fastify.statusCode.NOT_FOUND);
// }
export default sidebarRoute;

View File

@@ -1,74 +0,0 @@
import buildDebug from 'debug';
import fastify from 'fastify';
import { Config as AppConfig, createAnonymousRemoteUser } from '@verdaccio/config';
import { logger } from '@verdaccio/logger';
import { ConfigYaml, Config as IConfig, RemoteUser } from '@verdaccio/types';
import distTags from './endpoints/dist-tags';
import manifest from './endpoints/manifest';
import ping from './endpoints/ping';
import search from './endpoints/search';
import tarball from './endpoints/tarball';
import user from './endpoints/user';
import whoami from './endpoints/whoami';
import authPlugin from './plugins/auth';
import configPlugin from './plugins/config';
import coreUtils from './plugins/coreUtils';
import storagePlugin from './plugins/storage';
import login from './routes/web/api/login';
import readme from './routes/web/api/readme';
import sidebar from './routes/web/api/sidebar';
const debug = buildDebug('verdaccio:fastify');
enum PREFIX {
WEB = '/-/verdaccio',
USER = '/-/user',
}
async function startServer(config: ConfigYaml): Promise<any> {
// eslint-disable-next-line prettier/prettier
const configInstance: IConfig = new AppConfig({ ...config } as any);
debug('start fastify server');
// TODO: custom logger type and logger accepted by fastify does not match
const fastifyInstance = fastify({ logger: logger as any });
fastifyInstance.addHook('onRequest', (request, reply, done) => {
request.userRemote = createAnonymousRemoteUser();
done();
});
fastifyInstance.register(coreUtils);
fastifyInstance.register(configPlugin, { config });
fastifyInstance.register(storagePlugin, { config: configInstance });
fastifyInstance.register(authPlugin, { config: configInstance });
// api
fastifyInstance.register((instance, opts, done) => {
instance.register(ping);
instance.register(user, { prefix: PREFIX.USER });
instance.register(search);
instance.register(whoami);
instance.register(manifest);
instance.register(tarball);
instance.register(distTags);
instance.register(readme, { prefix: PREFIX.WEB });
instance.register(sidebar, { prefix: PREFIX.WEB });
instance.register(login, { prefix: PREFIX.WEB });
done();
});
// web
fastifyInstance.register((instance, opts, done) => {
instance.register(ping, { prefix: '/web' });
done();
});
return fastifyInstance;
}
declare module 'fastify' {
interface FastifyRequest {
userRemote: RemoteUser;
}
}
export default startServer;

View File

@@ -1,9 +0,0 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./build"
},
"include": ["src/**/*"],
"exclude": ["src/**/*.test.ts"]
}

View File

@@ -1,29 +0,0 @@
{
"extends": "../../../tsconfig.reference.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./build"
},
"include": ["src/**/*"],
"exclude": ["src/**/*.test.ts"],
"references": [
{
"path": "../../store"
},
{
"path": "../../config"
},
{
"path": "../../auth"
},
{
"path": "../../logger/logger"
},
{
"path": "../../utils"
},
{
"path": "../../core/core"
}
]
}

View File

@@ -1996,7 +1996,7 @@ describe('storage', () => {
).rejects.toThrow(errorUtils.getServiceUnavailable(API_ERROR.SERVER_TIME_OUT));
});
test('should fetch abbreviated version of manifest ', async () => {
test('should fetch abbreviated version of manifest', async () => {
const fooManifest = generateLocalPackageMetadata('foo', '1.0.0');
nock(domain).get('/foo').reply(201, fooManifest);
const config = new Config(

View File

@@ -3,11 +3,11 @@
{
"id": 558752,
"login": "juanpicado",
"contributions": 5479,
"contributions": 5483,
"repositories": [
{
"name": "verdaccio",
"contributions": 3080
"contributions": 3084
},
{
"name": "verdaccio-cookbook",
@@ -232,11 +232,11 @@
{
"id": 59966492,
"login": "mbtools",
"contributions": 204,
"contributions": 208,
"repositories": [
{
"name": "verdaccio",
"contributions": 204
"contributions": 208
}
]
},
@@ -927,8 +927,8 @@
]
},
{
"id": 1180931,
"login": "BartDubois",
"id": 2132722,
"login": "CrispyConductor",
"contributions": 4,
"repositories": [
{
@@ -938,8 +938,8 @@
]
},
{
"id": 2132722,
"login": "CrispyConductor",
"id": 1180931,
"login": "BartDubois",
"contributions": 4,
"repositories": [
{
@@ -1183,19 +1183,8 @@
]
},
{
"id": 175305,
"login": "kfatehi",
"contributions": 3,
"repositories": [
{
"name": "verdaccio",
"contributions": 3
}
]
},
{
"id": 8604205,
"login": "gkalpak",
"id": 19731692,
"login": "brenordr",
"contributions": 3,
"repositories": [
{
@@ -1216,8 +1205,19 @@
]
},
{
"id": 19731692,
"login": "brenordr",
"id": 8604205,
"login": "gkalpak",
"contributions": 3,
"repositories": [
{
"name": "verdaccio",
"contributions": 3
}
]
},
{
"id": 175305,
"login": "kfatehi",
"contributions": 3,
"repositories": [
{
@@ -1402,6 +1402,50 @@
}
]
},
{
"id": 6905832,
"login": "somethingSTRANGE",
"contributions": 2,
"repositories": [
{
"name": "verdaccio",
"contributions": 2
}
]
},
{
"id": 2946823,
"login": "plitex",
"contributions": 2,
"repositories": [
{
"name": "verdaccio",
"contributions": 2
}
]
},
{
"id": 172697,
"login": "naveensrinivasan",
"contributions": 2,
"repositories": [
{
"name": "verdaccio",
"contributions": 2
}
]
},
{
"id": 6487450,
"login": "nphyatt",
"contributions": 2,
"repositories": [
{
"name": "verdaccio",
"contributions": 2
}
]
},
{
"id": 13708162,
"login": "mysiar",
@@ -1435,50 +1479,6 @@
}
]
},
{
"id": 6487450,
"login": "nphyatt",
"contributions": 2,
"repositories": [
{
"name": "verdaccio",
"contributions": 2
}
]
},
{
"id": 172697,
"login": "naveensrinivasan",
"contributions": 2,
"repositories": [
{
"name": "verdaccio",
"contributions": 2
}
]
},
{
"id": 2946823,
"login": "plitex",
"contributions": 2,
"repositories": [
{
"name": "verdaccio",
"contributions": 2
}
]
},
{
"id": 6905832,
"login": "somethingSTRANGE",
"contributions": 2,
"repositories": [
{
"name": "verdaccio",
"contributions": 2
}
]
},
{
"id": 8291077,
"login": "varijkapil13",
@@ -1535,8 +1535,8 @@
]
},
{
"id": 465522,
"login": "okv",
"id": 2270425,
"login": "danielo515",
"contributions": 2,
"repositories": [
{
@@ -1557,8 +1557,8 @@
]
},
{
"id": 2270425,
"login": "danielo515",
"id": 465522,
"login": "okv",
"contributions": 2,
"repositories": [
{
@@ -1579,8 +1579,8 @@
]
},
{
"id": 17563226,
"login": "alan-agius4",
"id": 18176417,
"login": "awshanks",
"contributions": 2,
"repositories": [
{
@@ -1590,8 +1590,8 @@
]
},
{
"id": 18176417,
"login": "awshanks",
"id": 17563226,
"login": "alan-agius4",
"contributions": 2,
"repositories": [
{
@@ -1699,17 +1699,6 @@
}
]
},
{
"id": 1845314,
"login": "MasterOdin",
"contributions": 2,
"repositories": [
{
"name": "verdaccio",
"contributions": 2
}
]
},
{
"id": 3189424,
"login": "marnel",
@@ -1722,8 +1711,8 @@
]
},
{
"id": 161571,
"login": "aledbf",
"id": 273367,
"login": "kba",
"contributions": 2,
"repositories": [
{
@@ -1733,8 +1722,19 @@
]
},
{
"id": 273367,
"login": "kba",
"id": 1845314,
"login": "MasterOdin",
"contributions": 2,
"repositories": [
{
"name": "verdaccio",
"contributions": 2
}
]
},
{
"id": 161571,
"login": "aledbf",
"contributions": 2,
"repositories": [
{
@@ -2126,8 +2126,8 @@
]
},
{
"id": 7500357,
"login": "saurabh-gohil",
"id": 14912729,
"login": "Skn0tt",
"contributions": 1,
"repositories": [
{
@@ -2148,8 +2148,8 @@
]
},
{
"id": 14912729,
"login": "Skn0tt",
"id": 7500357,
"login": "saurabh-gohil",
"contributions": 1,
"repositories": [
{
@@ -2202,17 +2202,6 @@
}
]
},
{
"id": 1667873,
"login": "morlay",
"contributions": 1,
"repositories": [
{
"name": "verdaccio",
"contributions": 1
}
]
},
{
"id": 374671,
"login": "fhp",
@@ -2323,6 +2312,28 @@
}
]
},
{
"id": 988013,
"login": "tomcoonen",
"contributions": 1,
"repositories": [
{
"name": "verdaccio",
"contributions": 1
}
]
},
{
"id": 22884999,
"login": "hedocode",
"contributions": 1,
"repositories": [
{
"name": "verdaccio",
"contributions": 1
}
]
},
{
"id": 9381634,
"login": "morrain",
@@ -2774,17 +2785,6 @@
}
]
},
{
"id": 988013,
"login": "tomcoonen",
"contributions": 1,
"repositories": [
{
"name": "verdaccio",
"contributions": 1
}
]
},
{
"id": 907140,
"login": "grrowl",
@@ -2873,6 +2873,17 @@
}
]
},
{
"id": 1497471,
"login": "vsugrob",
"contributions": 1,
"repositories": [
{
"name": "verdaccio",
"contributions": 1
}
]
},
{
"id": 724423,
"login": "vitalybaev",
@@ -3038,17 +3049,6 @@
}
]
},
{
"id": 22884999,
"login": "hedocode",
"contributions": 1,
"repositories": [
{
"name": "verdaccio",
"contributions": 1
}
]
},
{
"id": 8207081,
"login": "bchenSyd",
@@ -3523,8 +3523,8 @@
]
},
{
"id": 181054,
"login": "jchilton",
"id": 20465797,
"login": "ericmutta",
"contributions": 1,
"repositories": [
{
@@ -3743,8 +3743,8 @@
]
},
{
"id": 20465797,
"login": "ericmutta",
"id": 1667873,
"login": "morlay",
"contributions": 1,
"repositories": [
{
@@ -3962,6 +3962,17 @@
}
]
},
{
"id": 181054,
"login": "jchilton",
"contributions": 1,
"repositories": [
{
"name": "verdaccio",
"contributions": 1
}
]
},
{
"id": 674589,
"login": "notsag",
@@ -5268,7 +5279,7 @@
"full_name": "verdaccio/verdaccio",
"html_url": "https://github.com/verdaccio/verdaccio",
"description": "A lightweight Node.js private proxy registry",
"stargazers_count": 17217,
"stargazers_count": 17261,
"archived": false
},
{

View File

@@ -12,18 +12,19 @@
"email": "juanpicado19@gmail.com"
},
"dependencies": {
"@vitest/eslint-plugin": "1.4.2",
"eslint-config-google": "0.14.0",
"eslint-config-prettier": "8.10.0",
"eslint-config-prettier": "10.1.8",
"eslint-plugin-babel": "5.3.1",
"eslint-plugin-cypress": "2.15.2",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-jest": "27.9.0",
"eslint-plugin-cypress": "3.6.0",
"eslint-plugin-import": "2.32.0",
"eslint-plugin-jsx-a11y": "6.10.2",
"eslint-plugin-prettier": "5.2.1",
"eslint-plugin-react": "7.37.2",
"eslint-plugin-react-hooks": "4.6.2",
"eslint-plugin-simple-import-sort": "10.0.0",
"eslint-plugin-verdaccio": "10.0.0"
"eslint-plugin-prettier": "5.5.4",
"eslint-plugin-react": "7.37.5",
"eslint-plugin-react-hooks": "7.0.1",
"eslint-plugin-simple-import-sort": "12.1.1",
"eslint-plugin-verdaccio": "10.0.0",
"eslint-plugin-vitest-globals": "1.5.0"
},
"repository": {
"type": "https",

View File

@@ -3,7 +3,7 @@ module.exports = {
'./rules/base.js',
'./rules/prettier.js',
'./rules/react.js',
'./rules/jest.js',
'./rules/vitest.js',
'./rules/cypress.js',
],
env: {

View File

@@ -1,20 +0,0 @@
module.exports = {
extends: ['plugin:jest/recommended'],
plugins: ['jest'],
env: {
jest: true,
},
rules: {
'jest/no-export': 0,
'jest/no-test-callback': 0,
'jest/expect-expect': 0,
'jest/no-try-expect': 0,
'jest/no-done-callback': 'off',
'jest/no-conditional-expect': 'off',
'jest/valid-title': 'off',
// rules to fix
'jest/no-identical-title': ['warn'],
'jest/no-disabled-tests': ['warn'],
'jest/no-commented-out-tests': ['warn'],
},
};

View File

@@ -12,4 +12,9 @@ module.exports = {
rules: {
'react/prop-types': 0,
},
settings: {
react: {
version: 'detect',
},
},
};

View File

@@ -0,0 +1,18 @@
module.exports = {
// TODO: replace with plugin:@vitest/recommended after update to ESLint 9
extends: ['plugin:@vitest/legacy-recommended', 'plugin:vitest-globals/recommended'],
plugins: ['@vitest'],
env: {
'vitest-globals/env': true,
},
rules: {
'@vitest/expect-expect': 'off',
// rules to fix
'@vitest/no-identical-title': ['warn'],
'@vitest/no-disabled-tests': ['warn'],
'@vitest/no-commented-out-tests': ['warn'],
// TODO: Decide whether to use globals or not
// '@vitest/prefer-importing-vitest-globals': ['warn'],
// '@vitest/no-importing-vitest-globals': ['warn'],
},
};

View File

@@ -46,5 +46,11 @@
"2025-09-07T00:00:00Z": { "pullCount": 220259, "ipCount": 10404 },
"2025-09-14T00:00:00Z": { "pullCount": 45731, "ipCount": 6217 },
"2025-10-05T00:00:00Z": { "pullCount": 191633, "ipCount": 10206 },
"2025-10-12T00:00:00Z": { "pullCount": 185150, "ipCount": 9591 }
"2025-10-12T00:00:00Z": { "pullCount": 185150, "ipCount": 9591 },
"2025-09-21T00:00:00Z": { "pullCount": 201693, "ipCount": 10370 },
"2025-09-28T00:00:00Z": { "pullCount": 196628, "ipCount": 10324 },
"2025-10-19T00:00:00Z": { "pullCount": 199835, "ipCount": 10382 },
"2025-10-26T00:00:00Z": { "pullCount": 193385, "ipCount": 10716 },
"2025-11-02T00:00:00Z": { "pullCount": 187825, "ipCount": 10589 },
"2025-11-09T00:00:00Z": { "pullCount": 179384, "ipCount": 10062 }
}

View File

@@ -39,16 +39,6 @@
{ "downloads": 41869, "start": "2019-02-01", "end": "2019-02-28", "package": "verdaccio" },
{ "downloads": 38958, "start": "2019-03-01", "end": "2019-03-31", "package": "verdaccio" },
{ "downloads": 123377, "start": "2019-04-01", "end": "2019-04-30", "package": "verdaccio" },
{ "downloads": 127805, "start": "2019-05-01", "end": "2019-05-31", "package": "verdaccio" },
{ "downloads": 132708, "start": "2019-06-01", "end": "2019-06-30", "package": "verdaccio" },
{ "downloads": 185707, "start": "2019-07-01", "end": "2019-07-31", "package": "verdaccio" },
{ "downloads": 148043, "start": "2019-08-01", "end": "2019-08-31", "package": "verdaccio" },
{ "downloads": 136353, "start": "2019-09-01", "end": "2019-09-30", "package": "verdaccio" },
{ "downloads": 147244, "start": "2019-10-01", "end": "2019-10-31", "package": "verdaccio" },
{ "downloads": 60357, "start": "2019-11-01", "end": "2019-11-30", "package": "verdaccio" },
{ "downloads": 52255, "start": "2019-12-01", "end": "2019-12-31", "package": "verdaccio" },
{ "downloads": 62815, "start": "2020-01-01", "end": "2020-01-31", "package": "verdaccio" },
{ "downloads": 59476, "start": "2020-02-01", "end": "2020-02-29", "package": "verdaccio" },
{ "downloads": 73003, "start": "2020-03-01", "end": "2020-03-31", "package": "verdaccio" },
{ "downloads": 77269, "start": "2020-04-01", "end": "2020-04-30", "package": "verdaccio" },
{ "downloads": 92001, "start": "2020-05-01", "end": "2020-05-31", "package": "verdaccio" },
@@ -116,5 +106,6 @@
{ "downloads": 1422019, "start": "2025-07-01", "end": "2025-07-31", "package": "verdaccio" },
{ "downloads": 1262694, "start": "2025-08-01", "end": "2025-08-31", "package": "verdaccio" },
{ "downloads": 1466753, "start": "2025-09-01", "end": "2025-09-30", "package": "verdaccio" },
{ "downloads": 682248, "start": "2025-10-01", "end": "2025-10-17", "package": "verdaccio" }
{ "downloads": 1558233, "start": "2025-10-01", "end": "2025-10-31", "package": "verdaccio" },
{ "downloads": 517552, "start": "2025-11-01", "end": "2025-11-14", "package": "verdaccio" }
]

File diff suppressed because it is too large Load Diff

View File

@@ -8,5 +8,5 @@
"2022": 4723577,
"2023": 6079864,
"2024": 9095014,
"2025": 12266766
"2025": 13660303
}

View File

@@ -78,7 +78,6 @@
"react-hooks/exhaustive-deps": "warn",
"verdaccio/jsx-no-style": ["warn"],
"verdaccio/jsx-spread": ["warn"],
"jest/expect-expect": 0,
"quote-props":["error", "as-needed"],
"prefer-spread": 1,
"linebreak-style": 0,
@@ -94,7 +93,6 @@
"__DEBUG__": true
},
"env": {
"browser": true,
"jest/globals": true
"browser": true
}
}

View File

@@ -5,11 +5,9 @@ const config: StorybookConfig = {
stories: ['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
getAbsolutePath('@storybook/addon-actions'),
getAbsolutePath('@storybook/addon-links'),
getAbsolutePath('@storybook/addon-essentials'),
getAbsolutePath('@storybook/addon-interactions'),
getAbsolutePath('@storybook/addon-webpack5-compiler-babel'),
getAbsolutePath('@storybook/addon-docs'),
],
framework: {

View File

@@ -1,4 +1,4 @@
import type { Preview, StoryFn } from '@storybook/react';
import type { Preview, StoryFn } from '@storybook/react-webpack5';
import { initialize, mswLoader } from 'msw-storybook-addon';
import React from 'react';
import { Provider } from 'react-redux';

View File

@@ -23,8 +23,8 @@
"@emotion/react": "11.11.4",
"@emotion/styled": "11.11.5",
"@microlink/react-json-view": "1.26.2",
"@mui/icons-material": "5.17.1",
"@mui/material": "5.17.1",
"@mui/icons-material": "7.3.4",
"@mui/material": "7.3.4",
"@rematch/core": "2.2.0",
"@rematch/loading": "2.1.2",
"@verdaccio/ui-i18n": "workspace:*",
@@ -50,24 +50,19 @@
"react-virtualized": "9.22.6",
"redux": "4.2.1",
"semver": "7.7.3",
"validator": "13.15.15"
"validator": "13.15.22"
},
"devDependencies": {
"@babel/core": "7.28.4",
"@emotion/babel-plugin": "11.13.5",
"@storybook/addon-actions": "8.6.14",
"@storybook/addon-essentials": "8.6.14",
"@storybook/addon-interactions": "8.6.14",
"@storybook/addon-links": "8.6.14",
"@storybook/addon-links": "9.1.15",
"@storybook/addon-webpack5-compiler-babel": "3.0.6",
"@storybook/react": "8.6.14",
"@storybook/react-webpack5": "8.6.14",
"@storybook/react-webpack5": "9.1.15",
"@testing-library/dom": "10.4.1",
"@testing-library/jest-dom": "6.8.0",
"@testing-library/react": "16.3.0",
"@types/hast": "2.3.10",
"@types/node": "24.10.0",
"@types/react-router": "5.1.20",
"@types/unist": "2.0.11",
"@verdaccio/types": "workspace:13.0.0-next-8.8",
"@vitejs/plugin-react": "5.0.4",
"babel-loader": "9.2.1",
@@ -77,9 +72,11 @@
"msw": "2.11.3",
"msw-storybook-addon": "2.0.6",
"mutationobserver-shim": "0.3.7",
"storybook": "8.6.14",
"storybook": "9.1.15",
"vitest": "3.2.4",
"whatwg-fetch": "3.6.20"
"whatwg-fetch": "3.6.20",
"eslint-plugin-storybook": "9.1.15",
"@storybook/addon-docs": "9.1.15"
},
"msw": {
"workerDirectory": [

View File

@@ -1,4 +1,4 @@
import type { Meta, StoryObj } from '@storybook/react';
import type { Meta, StoryObj } from '@storybook/react-webpack5';
import { HttpResponse, http } from 'msw';
import React from 'react';
import { MemoryRouter } from 'react-router';

View File

@@ -1,4 +1,4 @@
import type { Meta, StoryObj } from '@storybook/react';
import type { Meta, StoryObj } from '@storybook/react-webpack5';
import { clone, merge } from 'lodash';
import React from 'react';

Some files were not shown because too many files have changed in this diff Show More