Compare commits
54 Commits
migrate-do
...
@verdaccio
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0501eea78 | ||
|
|
d22ef4e61e | ||
|
|
96d2f0f24d | ||
|
|
0b66de1274 | ||
|
|
897ca09828 | ||
|
|
85bf8dc6d8 | ||
|
|
7fa42b5a4e | ||
|
|
4ec28f20b3 | ||
|
|
003be2a561 | ||
|
|
bd3d50b7c0 | ||
|
|
93348f0208 | ||
|
|
c120b32d07 | ||
|
|
47ee18c3dd | ||
|
|
0938e965cb | ||
|
|
72c3cbb5bf | ||
|
|
626ae6aae9 | ||
|
|
184bd51418 | ||
|
|
837f00dc56 | ||
|
|
52c1d22c91 | ||
|
|
09503bcd51 | ||
|
|
1e8e6e4212 | ||
|
|
c7285cf933 | ||
|
|
0f2da1897c | ||
|
|
06132a2ceb | ||
|
|
688eb15e77 | ||
|
|
2fef671547 | ||
|
|
d945bfe99d | ||
|
|
610ed5cd04 | ||
|
|
85d8ea16d6 | ||
|
|
08ebbce001 | ||
|
|
7513899660 | ||
|
|
534fafd741 | ||
|
|
9509b63345 | ||
|
|
4d96585991 | ||
|
|
0be45da363 | ||
|
|
7751a55a34 | ||
|
|
acb8a99f55 | ||
|
|
b776e1f897 | ||
|
|
d33266b5a6 | ||
|
|
b19ddca81f | ||
|
|
f86da50d91 | ||
|
|
7e9fa2a5b3 | ||
|
|
6add153fde | ||
|
|
0052cc5b97 | ||
|
|
5b64bb9e3e | ||
|
|
97df95af40 | ||
|
|
6c0ba0e3a4 | ||
|
|
3d113dd38d | ||
|
|
c6dc42e92b | ||
|
|
ca0844a45a | ||
|
|
3ef4a49bba | ||
|
|
387d9f01ea | ||
|
|
dedc20b24c | ||
|
|
96b6c404a9 |
5
.changeset/afraid-cars-sneeze.md
Normal file
5
.changeset/afraid-cars-sneeze.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@verdaccio/loaders': minor
|
||||
---
|
||||
|
||||
feat(loader): support scoped plugins in plugin folder
|
||||
5
.changeset/afraid-cheetahs-rule.md
Normal file
5
.changeset/afraid-cheetahs-rule.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@verdaccio/ui-components': patch
|
||||
---
|
||||
|
||||
fix(ui): check token expiry on refresh/timer
|
||||
18
.changeset/brave-ears-drive.md
Normal file
18
.changeset/brave-ears-drive.md
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
'@verdaccio/local-storage': patch
|
||||
'@verdaccio/server': patch
|
||||
'@verdaccio/server-fastify': patch
|
||||
'@verdaccio/test-helper': patch
|
||||
'@verdaccio/ui-components': patch
|
||||
'@verdaccio/tarball': patch
|
||||
'@verdaccio/types': patch
|
||||
'@verdaccio/middleware': patch
|
||||
'verdaccio': patch
|
||||
'@verdaccio/config': patch
|
||||
'@verdaccio/proxy': patch
|
||||
'@verdaccio/store': patch
|
||||
'@verdaccio/auth': patch
|
||||
'@verdaccio/api': patch
|
||||
---
|
||||
|
||||
chore(utils): replace @verdaccio/utils dependency with core
|
||||
8
.changeset/clean-rings-listen.md
Normal file
8
.changeset/clean-rings-listen.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
'@verdaccio/ui-theme': minor
|
||||
'@verdaccio/ui-components': minor
|
||||
'@verdaccio/middleware': patch
|
||||
'@verdaccio/ui-i18n': patch
|
||||
---
|
||||
|
||||
feat: web v1 login frontend (experimental)
|
||||
6
.changeset/few-beds-itch.md
Normal file
6
.changeset/few-beds-itch.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@verdaccio/store': patch
|
||||
'@verdaccio/types': patch
|
||||
---
|
||||
|
||||
chore(types): use "Person" instead of "Author"
|
||||
5
.changeset/four-buttons-remember.md
Normal file
5
.changeset/four-buttons-remember.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@verdaccio/web': patch
|
||||
---
|
||||
|
||||
chore(web): move utils to web package
|
||||
5
.changeset/hip-deers-join.md
Normal file
5
.changeset/hip-deers-join.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@verdaccio/core': patch
|
||||
---
|
||||
|
||||
chore(core): http status codes
|
||||
5
.changeset/hungry-dolls-destroy.md
Normal file
5
.changeset/hungry-dolls-destroy.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@verdaccio/local-storage': patch
|
||||
---
|
||||
|
||||
fix(local-storage): uncontrolled data used in path expression
|
||||
5
.changeset/loud-dingos-cough.md
Normal file
5
.changeset/loud-dingos-cough.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@verdaccio/e2e-cli-npm-common': patch
|
||||
---
|
||||
|
||||
fix(e2e): test.each was not working as expected
|
||||
6
.changeset/modern-llamas-know.md
Normal file
6
.changeset/modern-llamas-know.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@verdaccio/website': patch
|
||||
---
|
||||
|
||||
- Updated the docs to show how we can make AWS CodeArtifact work with Verdaccio
|
||||
- Added the example uplinks configuration
|
||||
6
.changeset/nasty-cooks-watch.md
Normal file
6
.changeset/nasty-cooks-watch.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@verdaccio/e2e-cli-npm11': patch
|
||||
'@verdaccio/e2e-cli-npm-common': patch
|
||||
---
|
||||
|
||||
feat(e2e): add npm11
|
||||
9
.changeset/poor-hats-smile.md
Normal file
9
.changeset/poor-hats-smile.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
'@verdaccio/types': patch
|
||||
'@verdaccio/middleware': patch
|
||||
'@verdaccio/core': patch
|
||||
'@verdaccio/config': patch
|
||||
'@verdaccio/api': patch
|
||||
---
|
||||
|
||||
feat: web v1 login backend (experimental)
|
||||
6
.changeset/poor-queens-shop.md
Normal file
6
.changeset/poor-queens-shop.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@verdaccio/signature': patch
|
||||
'@verdaccio/config': patch
|
||||
---
|
||||
|
||||
chore(signature): remove duplicate code
|
||||
@@ -61,61 +61,81 @@
|
||||
"@verdaccio/e2e-cli-pnpm10": "1.0.0",
|
||||
"docusaurus-plugin-downloads": "2.0.0",
|
||||
"@verdaccio/local-publish": "0.0.2",
|
||||
"@verdaccio/e2e-cli-npm10": "1.0.1"
|
||||
"@verdaccio/e2e-cli-npm10": "1.0.1",
|
||||
"@verdaccio/e2e-cli-npm11": "1.0.2-next-8.0"
|
||||
},
|
||||
"changesets": [
|
||||
"afraid-cars-sneeze",
|
||||
"afraid-cheetahs-rule",
|
||||
"angry-doors-tan",
|
||||
"beige-lions-type",
|
||||
"blue-paws-cheer",
|
||||
"brave-ears-drive",
|
||||
"breezy-geckos-search",
|
||||
"breezy-toys-judge",
|
||||
"bright-bobcats-ring",
|
||||
"brown-lions-talk",
|
||||
"brown-planets-approve",
|
||||
"calm-mangos-compare",
|
||||
"chatty-apricots-report",
|
||||
"clean-beds-wash",
|
||||
"clean-rings-listen",
|
||||
"clever-bees-happen",
|
||||
"cool-seals-watch",
|
||||
"curly-mirrors-smile",
|
||||
"curvy-rockets-camp",
|
||||
"cyan-snakes-kiss",
|
||||
"dirty-countries-play",
|
||||
"early-eyes-float",
|
||||
"early-trainers-grin",
|
||||
"eight-countries-think",
|
||||
"eighty-apes-think",
|
||||
"eleven-rocks-dream",
|
||||
"few-beds-itch",
|
||||
"few-ears-deny",
|
||||
"fifty-falcons-design",
|
||||
"forty-hounds-matter",
|
||||
"four-buttons-remember",
|
||||
"fresh-owls-hunt",
|
||||
"funny-fireants-tan",
|
||||
"gentle-stingrays-repeat",
|
||||
"gold-files-speak",
|
||||
"gold-squids-watch",
|
||||
"great-candles-hang",
|
||||
"green-eagles-boil",
|
||||
"healthy-ducks-drive",
|
||||
"healthy-zoos-lie",
|
||||
"hip-deers-join",
|
||||
"hip-eggs-serve",
|
||||
"hip-suns-jam",
|
||||
"hot-crews-live",
|
||||
"hungry-dolls-destroy",
|
||||
"itchy-glasses-end",
|
||||
"large-turkeys-change",
|
||||
"long-eyes-drum",
|
||||
"long-singers-drive",
|
||||
"loud-dingos-cough",
|
||||
"lucky-crabs-enjoy",
|
||||
"modern-llamas-know",
|
||||
"nasty-cooks-watch",
|
||||
"nasty-experts-bow",
|
||||
"nice-garlics-tie",
|
||||
"nine-countries-remember",
|
||||
"nine-onions-talk",
|
||||
"ninety-geese-do",
|
||||
"ninety-hotels-dance",
|
||||
"odd-fishes-cry",
|
||||
"old-clocks-destroy",
|
||||
"pink-jeans-lick",
|
||||
"pink-pants-try",
|
||||
"poor-hats-smile",
|
||||
"poor-queens-shop",
|
||||
"popular-trees-grin",
|
||||
"proud-houses-switch",
|
||||
"purple-planes-do",
|
||||
"quick-avocados-type",
|
||||
"quick-seas-deny",
|
||||
"real-balloons-travel",
|
||||
"real-seahorses-change",
|
||||
"red-dolls-repair",
|
||||
"rotten-melons-notice",
|
||||
@@ -123,20 +143,27 @@
|
||||
"rude-birds-design",
|
||||
"rude-socks-walk",
|
||||
"serious-apes-rule",
|
||||
"sharp-queens-rhyme",
|
||||
"shiny-buttons-laugh",
|
||||
"short-mails-smoke",
|
||||
"short-turkeys-boil",
|
||||
"silent-bags-listen",
|
||||
"silent-tips-admire",
|
||||
"silver-houses-remain",
|
||||
"silver-insects-train",
|
||||
"slow-cars-guess",
|
||||
"smooth-games-share",
|
||||
"strange-pants-chew",
|
||||
"stupid-camels-build",
|
||||
"sweet-crabs-deliver",
|
||||
"swift-knives-shop",
|
||||
"swift-mangos-grab",
|
||||
"swift-zebras-cheer",
|
||||
"ten-jeans-approve",
|
||||
"tender-buckets-smoke",
|
||||
"thick-dolphins-rule",
|
||||
"thirty-comics-trade",
|
||||
"tidy-socks-battle",
|
||||
"tricky-impalas-shake",
|
||||
"tricky-knives-end",
|
||||
"twelve-games-wonder",
|
||||
@@ -145,6 +172,8 @@
|
||||
"violet-boxes-float",
|
||||
"weak-cherries-serve",
|
||||
"wet-cats-behave",
|
||||
"wicked-points-relate",
|
||||
"wild-coins-lie",
|
||||
"yellow-flies-sniff"
|
||||
]
|
||||
}
|
||||
|
||||
5
.changeset/real-balloons-travel.md
Normal file
5
.changeset/real-balloons-travel.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@verdaccio/web': patch
|
||||
---
|
||||
|
||||
fix: config web login default
|
||||
5
.changeset/sharp-queens-rhyme.md
Normal file
5
.changeset/sharp-queens-rhyme.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@verdaccio/ui-components': patch
|
||||
---
|
||||
|
||||
chore(ui): avoid ts errors for CircularProgress
|
||||
6
.changeset/swift-knives-shop.md
Normal file
6
.changeset/swift-knives-shop.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@verdaccio/core': patch
|
||||
'@verdaccio/utils': patch
|
||||
---
|
||||
|
||||
chore(core): move more utils to core
|
||||
12
.changeset/swift-mangos-grab.md
Normal file
12
.changeset/swift-mangos-grab.md
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
'@verdaccio/server': patch
|
||||
'@verdaccio/core': patch
|
||||
'@verdaccio/loaders': patch
|
||||
'@verdaccio/config': patch
|
||||
'@verdaccio/store': patch
|
||||
'@verdaccio/auth': patch
|
||||
'@verdaccio/api': patch
|
||||
'@verdaccio/web': patch
|
||||
---
|
||||
|
||||
fix(config): server settings
|
||||
5
.changeset/tidy-socks-battle.md
Normal file
5
.changeset/tidy-socks-battle.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@verdaccio/core': patch
|
||||
---
|
||||
|
||||
fix(core): remove `lodash` from a dependency
|
||||
7
.changeset/wicked-points-relate.md
Normal file
7
.changeset/wicked-points-relate.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@verdaccio/auth': patch
|
||||
'@verdaccio/signature': patch
|
||||
'@verdaccio/types': patch
|
||||
---
|
||||
|
||||
fix: support JWT sign and verify options
|
||||
5
.changeset/wild-coins-lie.md
Normal file
5
.changeset/wild-coins-lie.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@verdaccio/tarball': patch
|
||||
---
|
||||
|
||||
chore(tarball): remove lodash, fix typing
|
||||
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -68,7 +68,7 @@ jobs:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
node_version: [18, 20, 21, 22, 23]
|
||||
node_version: [18, 20, 21, 22, 23, 24]
|
||||
name: ${{ matrix.os }} / Node ${{ matrix.node_version }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
|
||||
6
.github/workflows/codeql-analysis.yml
vendored
6
.github/workflows/codeql-analysis.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16
|
||||
uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
|
||||
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@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16
|
||||
uses: github/codeql-action/autobuild@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
|
||||
|
||||
# ℹ️ 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@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16
|
||||
uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
|
||||
|
||||
47
.github/workflows/docker-publish.yml
vendored
47
.github/workflows/docker-publish.yml
vendored
@@ -2,50 +2,49 @@ name: Docker publish to docker.io
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- .github/workflows/docker-publish.yml
|
||||
- .github/workflows/docker-publish.yml
|
||||
- 'packages/**'
|
||||
- 'docker-bin/**'
|
||||
- 'package.json'
|
||||
- 'pnpm-*.yaml'
|
||||
- 'Dockerfile'
|
||||
- '.dockerignore'
|
||||
branches:
|
||||
- 'master'
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'verdaccio/verdaccio'
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
||||
- uses: docker/setup-buildx-action@v1
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
driver-opts: network=host
|
||||
- uses: docker/login-action@v1
|
||||
name: Login Docker Hub
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Prepare docker image tags
|
||||
|
||||
- name: Prepare Docker image tags
|
||||
id: docker_meta
|
||||
uses: crazy-max/ghaction-docker-meta@v1
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
||||
with:
|
||||
images: ${{ github.repository }}
|
||||
tag-custom: nightly-master
|
||||
tag-custom-only: ${{ github.ref == 'refs/heads/master' }}
|
||||
tag-latest: false
|
||||
tag-semver: |
|
||||
{{version}}
|
||||
{{major}}
|
||||
{{major}}.{{minor}}
|
||||
tags: |
|
||||
type=raw,value=nightly-master
|
||||
type=raw,value=latest,enable=false
|
||||
labels: |
|
||||
org.opencontainers.image.source=${{ github.repositoryUrl }}
|
||||
org.opencontainers.image.created=${{ steps.docker_meta.outputs.created }}
|
||||
org.opencontainers.image.version=${{ steps.docker_meta.outputs.version }}
|
||||
|
||||
- name: Build & Push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
|
||||
9
.github/workflows/e2e-ci.yml
vendored
9
.github/workflows/e2e-ci.yml
vendored
@@ -43,9 +43,10 @@ jobs:
|
||||
npm7,
|
||||
npm8,
|
||||
npm9,
|
||||
npm10
|
||||
npm10,
|
||||
npm11,
|
||||
]
|
||||
node: [22]
|
||||
node: [22, 24]
|
||||
name: ${{ matrix.pkg }}/ ubuntu-latest / ${{ matrix.node }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -72,7 +73,7 @@ jobs:
|
||||
pnpm9,
|
||||
pnpm10,
|
||||
]
|
||||
node: [22]
|
||||
node: [2, 24]
|
||||
name: ${{ matrix.pkg }}/ ubuntu-latest / ${{ matrix.node }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -99,7 +100,7 @@ jobs:
|
||||
yarn3,
|
||||
yarn4
|
||||
]
|
||||
node: [22]
|
||||
node: [22, 24]
|
||||
name: ${{ matrix.pkg }}/ ubuntu-latest / ${{ matrix.node }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
2
.github/workflows/static-data.yml
vendored
2
.github/workflows/static-data.yml
vendored
@@ -36,7 +36,7 @@ jobs:
|
||||
- name: Get docker downloads
|
||||
run: pnpm --filter @verdaccio/local-scripts run pull:docker
|
||||
- name: Get npmjs monhtly downloads
|
||||
run: pnpm --filter @verdaccio/local-scripts run downloads:mounthly
|
||||
run: pnpm --filter @verdaccio/local-scripts run downloads:monthly
|
||||
- name: Get npmjs year downloads
|
||||
run: pnpm --filter @verdaccio/local-scripts run downloads:yearly
|
||||
- name: update contributors
|
||||
|
||||
1
.github/workflows/x-smok-test-docker.yml
vendored
1
.github/workflows/x-smok-test-docker.yml
vendored
@@ -16,6 +16,7 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM --platform=${BUILDPLATFORM:-linux/amd64} node:22-alpine AS builder
|
||||
FROM --platform=${BUILDPLATFORM:-linux/amd64} node:24-alpine AS builder
|
||||
|
||||
ENV NODE_ENV=development \
|
||||
VERDACCIO_BUILD_REGISTRY=https://registry.npmjs.org
|
||||
@@ -21,7 +21,7 @@ RUN npm -g i corepack && \
|
||||
# NODE_ENV=production pnpm install --frozen-lockfile --ignore-scripts
|
||||
# RUN pnpm install --prod --ignore-scripts
|
||||
|
||||
FROM node:22-alpine
|
||||
FROM node:24-alpine
|
||||
LABEL maintainer="https://github.com/verdaccio/verdaccio"
|
||||
|
||||
ENV VERDACCIO_APPDIR=/opt/verdaccio \
|
||||
|
||||
28
README.md
28
README.md
@@ -100,19 +100,21 @@ Our goal is to give you the confidence to use your preferred package manager wit
|
||||
|
||||
### Commands
|
||||
|
||||
| cmd | npm6 | npm7 | npm8 | npm9 | npm10 | pnpm8 | pnpm9 | pnpm10 | yarn1 | yarn2 | yarn3 | yarn4 |
|
||||
| --------- | ---- | ---- | ---- | ---- | ----- | ----- | ----- | ------ | ----- | ----- | ----- | ----- |
|
||||
| publish | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| unpublish | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
| info | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| audit | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
|
||||
| install | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| deprecate | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⛔ | ⛔ | ⛔ | ⛔ |
|
||||
| ping | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⛔ | ⛔ | ⛔ | ⛔ |
|
||||
| search | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⛔ | ⛔ | ⛔ | ⛔ |
|
||||
| star | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⛔ | ⛔ | ⛔ | ⛔ |
|
||||
| stars | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⛔ | ⛔ | ⛔ | ⛔ |
|
||||
| dist-tag | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
|
||||
| cmd | npm6 | npm7 | npm8 | npm9 | npm10 | npm11 | pnpm8 | pnpm9 | pnpm10 | yarn1 | yarn2 | yarn3 | yarn4 |
|
||||
| --------- | ---- | ---- | ---- | ---- | ----- | ----- | ----- | ----- | ------ | ----- | ----- | ----- | ----- |
|
||||
| publish | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| unpublish | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||||
| info | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| audit | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
|
||||
| install | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| deprecate | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⛔ | ⛔ | ⛔ | ⛔ |
|
||||
| ping | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⛔ | ⛔ | ⛔ | ⛔ |
|
||||
| search | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⛔ | ⛔ | ⛔ | ⛔ |
|
||||
| star | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⛔ | ⛔ | ⛔ | ⛔ |
|
||||
| stars | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⛔ | ⛔ | ⛔ | ⛔ |
|
||||
| dist-tag | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
|
||||
|
||||
<!-- n6 n7 n8 n9 n10 n11 p8 p9 p10 y1 y2 y3 y4 -->
|
||||
|
||||
> notes:
|
||||
>
|
||||
|
||||
@@ -4,16 +4,16 @@
|
||||
"version": "2.0.0-next-8.0",
|
||||
"main": "src/index.ts",
|
||||
"devDependencies": {
|
||||
"@verdaccio/config": "workspace:8.0.0-next-8.15",
|
||||
"@verdaccio/core": "workspace:8.0.0-next-8.15",
|
||||
"@verdaccio/types": "workspace:13.0.0-next-8.5",
|
||||
"debug": "4.4.0",
|
||||
"@verdaccio/config": "workspace:8.0.0-next-8.17",
|
||||
"@verdaccio/core": "workspace:8.0.0-next-8.17",
|
||||
"@verdaccio/types": "workspace:13.0.0-next-8.6",
|
||||
"debug": "4.4.1",
|
||||
"fs-extra": "11.2.0",
|
||||
"get-port": "5.1.1",
|
||||
"got": "11.8.6",
|
||||
"js-yaml": "4.1.0",
|
||||
"lodash": "4.17.21",
|
||||
"verdaccio": "workspace:8.0.0-next-8.15"
|
||||
"verdaccio": "workspace:8.0.0-next-8.17"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo no test",
|
||||
|
||||
9
e2e/cli/e2e-npm-commons/CHANGELOG.md
Normal file
9
e2e/cli/e2e-npm-commons/CHANGELOG.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# @verdaccio/e2e-cli-npm-common
|
||||
|
||||
## 1.0.1-next-8.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- d945bfe: fix(e2e): test.each was not working as expected
|
||||
- 534fafd: feat(e2e): add npm11
|
||||
- @verdaccio/test-cli-commons@2.0.0-next-8.0
|
||||
@@ -12,7 +12,7 @@ export function runAudit(npm) {
|
||||
await registry.init();
|
||||
});
|
||||
|
||||
test.each([['verdaccio-memory', '@verdaccio/cli']])(
|
||||
test.each([['verdaccio-memory'], ['@verdaccio/cli']])(
|
||||
'should audit a package %s',
|
||||
async (pkgName) => {
|
||||
const { tempFolder } = await prepareGenericEmptyProject(
|
||||
|
||||
@@ -17,26 +17,30 @@ export function runDistTag(npm) {
|
||||
await registry.init();
|
||||
});
|
||||
|
||||
test.each([['@foo/foo', 'foo']])('should list dist-tags for %s', async (pkgName) => {
|
||||
const { tempFolder } = await prepareGenericEmptyProject(
|
||||
pkgName,
|
||||
'1.0.0',
|
||||
registry.port,
|
||||
registry.getToken(),
|
||||
registry.getRegistryUrl()
|
||||
);
|
||||
await npmUtils.publish(npm, tempFolder, pkgName, registry);
|
||||
await npmUtils.bumbUp(npm, tempFolder, registry);
|
||||
await npmUtils.publish(npm, tempFolder, pkgName, registry, ['--tag', 'beta']);
|
||||
const resp2 = await npm(
|
||||
{ cwd: tempFolder },
|
||||
'dist-tag',
|
||||
'ls',
|
||||
'--json',
|
||||
...addRegistry(registry.getRegistryUrl())
|
||||
);
|
||||
expect(resp2.stdout).toEqual('beta: 1.1.0latest: 1.0.0');
|
||||
});
|
||||
test.each([['@foo/verdaccio-foo'], ['verdaccio-foo']])(
|
||||
'should list dist-tags for %s',
|
||||
async (pkgName) => {
|
||||
// Packages must not exist in npm registry to avoid conflicts with data from uplink
|
||||
const { tempFolder } = await prepareGenericEmptyProject(
|
||||
pkgName,
|
||||
'1.0.0',
|
||||
registry.port,
|
||||
registry.getToken(),
|
||||
registry.getRegistryUrl()
|
||||
);
|
||||
await npmUtils.publish(npm, tempFolder, pkgName, registry);
|
||||
await npmUtils.bumbUp(npm, tempFolder, registry);
|
||||
await npmUtils.publish(npm, tempFolder, pkgName, registry, ['--tag', 'beta']);
|
||||
const resp2 = await npm(
|
||||
{ cwd: tempFolder },
|
||||
'dist-tag',
|
||||
'ls',
|
||||
'--json',
|
||||
...addRegistry(registry.getRegistryUrl())
|
||||
);
|
||||
expect(resp2.stdout).toEqual('beta: 1.1.0latest: 1.0.0');
|
||||
}
|
||||
);
|
||||
|
||||
test.each([['@verdaccio/bar']])('should remove tag with dist-tags for %s', async (pkgName) => {
|
||||
const { tempFolder } = await prepareGenericEmptyProject(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "@verdaccio/e2e-cli-npm-common",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1-next-8.0",
|
||||
"dependencies": {
|
||||
"@verdaccio/test-cli-commons": "workspace:2.0.0-next-8.0"
|
||||
},
|
||||
|
||||
@@ -12,12 +12,18 @@ export function runPublish(npm) {
|
||||
await registry.init();
|
||||
});
|
||||
|
||||
test.each([['verdaccio-memory', 'verdaccio', '@verdaccio/foo', '@verdaccio/some-foo']])(
|
||||
test.each([['verdaccio-memory'], ['verdaccio'], ['@verdaccio/foo'], ['@verdaccio/some-foo']])(
|
||||
'should publish a package %s',
|
||||
async (pkgName) => {
|
||||
// As of npm v11, npm will fetch the packument from the npm registry before publishing (from the uplink),
|
||||
// and there are more checks in the client:
|
||||
// - "You cannot publish over the previously published versions"
|
||||
// - "You must specify a tag using --tag when publishing a prerelease version"
|
||||
// - "Cannot implicitly apply the 'latest' tag because previously published version x.y.z is higher than the new version a.b.c"
|
||||
// Therefore, we pick a version that is higher than any of the published versions for the test packages (and not a pre-release).
|
||||
const { tempFolder } = await prepareGenericEmptyProject(
|
||||
pkgName,
|
||||
'1.0.0-patch',
|
||||
'99.0.0',
|
||||
registry.port,
|
||||
registry.getToken(),
|
||||
registry.getRegistryUrl()
|
||||
@@ -31,7 +37,6 @@ export function runPublish(npm) {
|
||||
const parsedBody = JSON.parse(resp.stdout as string);
|
||||
expect(parsedBody.name).toEqual(pkgName);
|
||||
expect(parsedBody.files).toBeDefined();
|
||||
expect(parsedBody.files).toBeDefined();
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ export function runStar(npm) {
|
||||
test.each([['@verdaccio/foo']])('should star a package %s', async (pkgName) => {
|
||||
const { tempFolder } = await prepareGenericEmptyProject(
|
||||
pkgName,
|
||||
'1.0.0-patch',
|
||||
'1.0.0',
|
||||
registry.port,
|
||||
registry.getToken(),
|
||||
registry.getRegistryUrl()
|
||||
@@ -39,7 +39,7 @@ export function runStar(npm) {
|
||||
test.each([['@verdaccio/bar']])('should unstar a package %s', async (pkgName) => {
|
||||
const { tempFolder } = await prepareGenericEmptyProject(
|
||||
pkgName,
|
||||
'1.0.0-patch',
|
||||
'1.0.0',
|
||||
registry.port,
|
||||
registry.getToken(),
|
||||
registry.getRegistryUrl()
|
||||
@@ -67,7 +67,7 @@ export function runStar(npm) {
|
||||
const pkgName = '@verdaccio/stars';
|
||||
const { tempFolder } = await prepareGenericEmptyProject(
|
||||
pkgName,
|
||||
'1.0.0-patch',
|
||||
'1.0.0',
|
||||
registry.port,
|
||||
registry.getToken(),
|
||||
registry.getRegistryUrl()
|
||||
|
||||
@@ -17,12 +17,12 @@ export function runUnpublish(npm) {
|
||||
await registry.init();
|
||||
});
|
||||
|
||||
test.each([['@verdaccio/test1', 'super-package-do-not-exist-spam']])(
|
||||
test.each([['@verdaccio/test1'], ['super-package-do-not-exist-spam']])(
|
||||
'should unpublish a full package %s',
|
||||
async (pkgName) => {
|
||||
const { tempFolder } = await prepareGenericEmptyProject(
|
||||
pkgName,
|
||||
'1.0.0-beta',
|
||||
'1.0.0',
|
||||
registry.port,
|
||||
registry.getToken(),
|
||||
registry.getRegistryUrl()
|
||||
@@ -44,16 +44,16 @@ export function runUnpublish(npm) {
|
||||
'--json',
|
||||
...addRegistry(registry.getRegistryUrl())
|
||||
);
|
||||
expect(resp2.stdout).toEqual('- @verdaccio/test1');
|
||||
expect(resp2.stdout).toEqual(`- ${pkgName}`);
|
||||
}
|
||||
);
|
||||
|
||||
test.each([['@verdaccio/test1', 'super-package-do-not-exist-spam']])(
|
||||
test.each([['@verdaccio/test1'], ['super-package-do-not-exist-spam']])(
|
||||
'should unpublish a package %s version',
|
||||
async (pkgName) => {
|
||||
const { tempFolder } = await prepareGenericEmptyProject(
|
||||
pkgName,
|
||||
'1.0.0-beta',
|
||||
'1.0.0',
|
||||
registry.port,
|
||||
registry.getToken(),
|
||||
registry.getRegistryUrl()
|
||||
@@ -69,13 +69,13 @@ export function runUnpublish(npm) {
|
||||
const resp2 = await npm(
|
||||
{ cwd: tempFolder },
|
||||
'unpublish',
|
||||
`${pkgName}@1.0.0-beta`,
|
||||
`${pkgName}@1.0.0`,
|
||||
'--force',
|
||||
'--loglevel=info',
|
||||
'--json',
|
||||
...addRegistry(registry.getRegistryUrl())
|
||||
);
|
||||
expect(resp2.stdout).toEqual('- @verdaccio/test1@1.0.0-beta');
|
||||
expect(resp2.stdout).toEqual(`- ${pkgName}@1.0.0`);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# @verdaccio/e2e-cli-npm9
|
||||
|
||||
## 1.0.2-next-8.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [d945bfe]
|
||||
- Updated dependencies [534fafd]
|
||||
- @verdaccio/e2e-cli-npm-common@1.0.1-next-8.0
|
||||
- @verdaccio/test-cli-commons@2.0.0-next-8.0
|
||||
|
||||
## 1.0.2-next-8.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "@verdaccio/e2e-cli-npm10",
|
||||
"version": "1.0.2-next-8.0",
|
||||
"version": "1.0.2-next-8.1",
|
||||
"dependencies": {
|
||||
"@verdaccio/test-cli-commons": "workspace:2.0.0-next-8.0",
|
||||
"@verdaccio/e2e-cli-npm-common": "workspace:*",
|
||||
|
||||
3
e2e/cli/e2e-npm11/.babelrc
Normal file
3
e2e/cli/e2e-npm11/.babelrc
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "../../../.babelrc"
|
||||
}
|
||||
7
e2e/cli/e2e-npm11/.eslintrc
Normal file
7
e2e/cli/e2e-npm11/.eslintrc
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"rules": {
|
||||
"no-console": 0,
|
||||
"@typescript-eslint/no-var-requires": 0,
|
||||
"@typescript-eslint/explicit-member-accessibility": 0
|
||||
}
|
||||
}
|
||||
79
e2e/cli/e2e-npm11/CHANGELOG.md
Normal file
79
e2e/cli/e2e-npm11/CHANGELOG.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# @verdaccio/e2e-cli-npm9
|
||||
|
||||
## 1.0.2-next-8.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 534fafd: feat(e2e): add npm11
|
||||
- Updated dependencies [d945bfe]
|
||||
- Updated dependencies [534fafd]
|
||||
- @verdaccio/e2e-cli-npm-common@1.0.1-next-8.0
|
||||
- @verdaccio/test-cli-commons@2.0.0-next-8.0
|
||||
|
||||
## 1.0.2-next-8.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- d4fc827: fix(e2e): name for npm10 test
|
||||
- @verdaccio/test-cli-commons@2.0.0-next-8.0
|
||||
|
||||
## 1.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [351aeeaa8]
|
||||
- Updated dependencies [d167f92e1]
|
||||
- Updated dependencies [c383eb68c]
|
||||
- @verdaccio/test-cli-commons@1.1.0
|
||||
|
||||
## 1.0.1-6-next.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c383eb68]
|
||||
- @verdaccio/test-cli-commons@1.1.0-6-next.7
|
||||
|
||||
## 1.0.1-6-next.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [d167f92e]
|
||||
- @verdaccio/test-cli-commons@1.1.0-6-next.6
|
||||
|
||||
## 1.0.1-6-next.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @verdaccio/test-cli-commons@1.0.1-6-next.5
|
||||
|
||||
## 1.0.1-6-next.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @verdaccio/test-cli-commons@1.0.1-6-next.4
|
||||
|
||||
## 1.0.1-6-next.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 351aeeaa: fix(deps): @verdaccio/utils should be a prod dep of local-storage
|
||||
- Updated dependencies [351aeeaa]
|
||||
- @verdaccio/test-cli-commons@1.0.1-6-next.3
|
||||
|
||||
## 1.0.1-6-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @verdaccio/test-cli-commons@1.0.1-6-next.2
|
||||
|
||||
## 1.0.1-6-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @verdaccio/test-cli-commons@1.0.1-6-next.1
|
||||
|
||||
## 1.0.1-6-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @verdaccio/test-cli-commons@1.0.1-6-next.0
|
||||
9
e2e/cli/e2e-npm11/audit.spec.ts
Normal file
9
e2e/cli/e2e-npm11/audit.spec.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { describe } from 'vitest';
|
||||
|
||||
import { runAudit } from '@verdaccio/e2e-cli-npm-common';
|
||||
|
||||
import { npm } from './utils';
|
||||
|
||||
describe('audit a package', () => {
|
||||
runAudit(npm);
|
||||
});
|
||||
9
e2e/cli/e2e-npm11/deprecate.spec.ts
Normal file
9
e2e/cli/e2e-npm11/deprecate.spec.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { describe } from 'vitest';
|
||||
|
||||
import { runDeprecate } from '@verdaccio/e2e-cli-npm-common';
|
||||
|
||||
import { npm } from './utils';
|
||||
|
||||
describe('deprecate a package', () => {
|
||||
runDeprecate(npm);
|
||||
});
|
||||
9
e2e/cli/e2e-npm11/dist-tags.spec.ts
Normal file
9
e2e/cli/e2e-npm11/dist-tags.spec.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { describe } from 'vitest';
|
||||
|
||||
import { runDistTag } from '@verdaccio/e2e-cli-npm-common';
|
||||
|
||||
import { npm } from './utils';
|
||||
|
||||
describe('dist-tags a package', () => {
|
||||
runDistTag(npm);
|
||||
});
|
||||
9
e2e/cli/e2e-npm11/info.spec.ts
Normal file
9
e2e/cli/e2e-npm11/info.spec.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { describe } from 'vitest';
|
||||
|
||||
import { runInfo } from '@verdaccio/e2e-cli-npm-common';
|
||||
|
||||
import { npm } from './utils';
|
||||
|
||||
describe('info a package', () => {
|
||||
runInfo(npm);
|
||||
});
|
||||
9
e2e/cli/e2e-npm11/install.spec.ts
Normal file
9
e2e/cli/e2e-npm11/install.spec.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { describe } from 'vitest';
|
||||
|
||||
import { runInstall } from '@verdaccio/e2e-cli-npm-common';
|
||||
|
||||
import { npm } from './utils';
|
||||
|
||||
describe('install a project packages', () => {
|
||||
runInstall(npm);
|
||||
});
|
||||
13
e2e/cli/e2e-npm11/package.json
Normal file
13
e2e/cli/e2e-npm11/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "@verdaccio/e2e-cli-npm11",
|
||||
"version": "1.0.2-next-8.1",
|
||||
"dependencies": {
|
||||
"@verdaccio/test-cli-commons": "workspace:2.0.0-next-8.0",
|
||||
"@verdaccio/e2e-cli-npm-common": "workspace:*",
|
||||
"npm": "11.4.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "vitest run --testTimeout 50000"
|
||||
}
|
||||
}
|
||||
9
e2e/cli/e2e-npm11/ping.spec.ts
Normal file
9
e2e/cli/e2e-npm11/ping.spec.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { describe } from 'vitest';
|
||||
|
||||
import { runPing } from '@verdaccio/e2e-cli-npm-common';
|
||||
|
||||
import { npm } from './utils';
|
||||
|
||||
describe('ping registry', () => {
|
||||
runPing(npm);
|
||||
});
|
||||
9
e2e/cli/e2e-npm11/publish.spec.ts
Normal file
9
e2e/cli/e2e-npm11/publish.spec.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { describe } from 'vitest';
|
||||
|
||||
import { runPublish } from '@verdaccio/e2e-cli-npm-common';
|
||||
|
||||
import { npm } from './utils';
|
||||
|
||||
describe('publish a package', () => {
|
||||
runPublish(npm);
|
||||
});
|
||||
9
e2e/cli/e2e-npm11/search.spec.ts
Normal file
9
e2e/cli/e2e-npm11/search.spec.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { describe } from 'vitest';
|
||||
|
||||
import { runSearch } from '@verdaccio/e2e-cli-npm-common';
|
||||
|
||||
import { npm } from './utils';
|
||||
|
||||
describe('search a package', () => {
|
||||
runSearch(npm);
|
||||
});
|
||||
9
e2e/cli/e2e-npm11/star.spec.ts
Normal file
9
e2e/cli/e2e-npm11/star.spec.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { describe } from 'vitest';
|
||||
|
||||
import { runStar } from '@verdaccio/e2e-cli-npm-common';
|
||||
|
||||
import { npm } from './utils';
|
||||
|
||||
describe('star a package', () => {
|
||||
runStar(npm);
|
||||
});
|
||||
8
e2e/cli/e2e-npm11/tsconfig.json
Normal file
8
e2e/cli/e2e-npm11/tsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.reference.json",
|
||||
"references": [
|
||||
{
|
||||
"path": "../cli-commons"
|
||||
}
|
||||
]
|
||||
}
|
||||
9
e2e/cli/e2e-npm11/unpublish.spec.ts
Normal file
9
e2e/cli/e2e-npm11/unpublish.spec.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { describe } from 'vitest';
|
||||
|
||||
import { runUnpublish } from '@verdaccio/e2e-cli-npm-common';
|
||||
|
||||
import { npm } from './utils';
|
||||
|
||||
describe('unpublish a package', () => {
|
||||
runUnpublish(npm);
|
||||
});
|
||||
41
e2e/cli/e2e-npm11/utils.ts
Normal file
41
e2e/cli/e2e-npm11/utils.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { SpawnOptions } from 'child_process';
|
||||
import { join } from 'path';
|
||||
|
||||
import { exec } from '@verdaccio/test-cli-commons';
|
||||
import { addRegistry } from '@verdaccio/test-cli-commons';
|
||||
|
||||
export function getCommand() {
|
||||
return join(__dirname, './node_modules/.bin/npm');
|
||||
}
|
||||
|
||||
export function npm(options: SpawnOptions, ...args: string[]) {
|
||||
return exec(options, getCommand(), args);
|
||||
}
|
||||
|
||||
export async function bumbUp(tempFolder, registry) {
|
||||
await npm({ cwd: tempFolder }, 'version', 'minor', ...addRegistry(registry.getRegistryUrl()));
|
||||
}
|
||||
|
||||
export async function publish(tempFolder, pkgName, registry, arg: string[] = []) {
|
||||
const resp = await npm(
|
||||
{ cwd: tempFolder },
|
||||
'publish',
|
||||
...arg,
|
||||
'--json',
|
||||
...addRegistry(registry.getRegistryUrl())
|
||||
);
|
||||
const parsedBody = JSON.parse(resp.stdout as string);
|
||||
expect(parsedBody.name).toEqual(pkgName);
|
||||
}
|
||||
|
||||
export async function getInfoVersions(pkgName, registry) {
|
||||
const infoResp = await npm(
|
||||
{},
|
||||
'info',
|
||||
pkgName,
|
||||
'--json',
|
||||
...addRegistry(registry.getRegistryUrl())
|
||||
);
|
||||
const infoBody = JSON.parse(infoResp.stdout as string);
|
||||
return infoBody;
|
||||
}
|
||||
@@ -1,5 +1,14 @@
|
||||
# @verdaccio/e2e-cli-npm6
|
||||
|
||||
## 1.0.2-next-8.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [d945bfe]
|
||||
- Updated dependencies [534fafd]
|
||||
- @verdaccio/e2e-cli-npm-common@1.0.1-next-8.0
|
||||
- @verdaccio/test-cli-commons@2.0.0-next-8.0
|
||||
|
||||
## 1.0.2-next-8.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "@verdaccio/e2e-cli-npm6",
|
||||
"version": "1.0.2-next-8.0",
|
||||
"version": "1.0.2-next-8.1",
|
||||
"dependencies": {
|
||||
"@verdaccio/test-cli-commons": "workspace:2.0.0-next-8.0",
|
||||
"@verdaccio/e2e-cli-npm-common": "workspace:*",
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# @verdaccio/e2e-cli-npm7
|
||||
|
||||
## 1.0.2-next-8.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [d945bfe]
|
||||
- Updated dependencies [534fafd]
|
||||
- @verdaccio/e2e-cli-npm-common@1.0.1-next-8.0
|
||||
- @verdaccio/test-cli-commons@2.0.0-next-8.0
|
||||
|
||||
## 1.0.2-next-8.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "@verdaccio/e2e-cli-npm7",
|
||||
"version": "1.0.2-next-8.0",
|
||||
"version": "1.0.2-next-8.1",
|
||||
"dependencies": {
|
||||
"@verdaccio/test-cli-commons": "workspace:2.0.0-next-8.0",
|
||||
"@verdaccio/e2e-cli-npm-common": "workspace:*",
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# @verdaccio/e2e-cli-npm8
|
||||
|
||||
## 1.0.2-next-8.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [d945bfe]
|
||||
- Updated dependencies [534fafd]
|
||||
- @verdaccio/e2e-cli-npm-common@1.0.1-next-8.0
|
||||
- @verdaccio/test-cli-commons@2.0.0-next-8.0
|
||||
|
||||
## 1.0.2-next-8.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "@verdaccio/e2e-cli-npm8",
|
||||
"version": "1.0.2-next-8.0",
|
||||
"version": "1.0.2-next-8.1",
|
||||
"dependencies": {
|
||||
"@verdaccio/test-cli-commons": "workspace:2.0.0-next-8.0",
|
||||
"@verdaccio/e2e-cli-npm-common": "workspace:*",
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# @verdaccio/e2e-cli-npm9
|
||||
|
||||
## 1.0.2-next-8.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [d945bfe]
|
||||
- Updated dependencies [534fafd]
|
||||
- @verdaccio/e2e-cli-npm-common@1.0.1-next-8.0
|
||||
- @verdaccio/test-cli-commons@2.0.0-next-8.0
|
||||
|
||||
## 1.0.2-next-8.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "@verdaccio/e2e-cli-npm9",
|
||||
"version": "1.0.2-next-8.0",
|
||||
"version": "1.0.2-next-8.1",
|
||||
"dependencies": {
|
||||
"@verdaccio/test-cli-commons": "workspace:2.0.0-next-8.0",
|
||||
"@verdaccio/e2e-cli-npm-common": "workspace:*",
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
FROM tutum/nginx
|
||||
RUN rm /etc/nginx/sites-enabled/default
|
||||
ADD sites-enabled /etc/nginx/sites-enabled
|
||||
@@ -1,6 +1,17 @@
|
||||
version: '2'
|
||||
version: '3.9'
|
||||
|
||||
services:
|
||||
nginx:
|
||||
build: ./nginx
|
||||
container_name: reverse_proxy
|
||||
ports:
|
||||
- '80:80'
|
||||
depends_on:
|
||||
- verdaccio
|
||||
volumes:
|
||||
- logs:/var/log/nginx
|
||||
restart: unless-stopped
|
||||
|
||||
verdaccio:
|
||||
image: verdaccio/verdaccio:nightly-master
|
||||
container_name: verdaccio_root_path
|
||||
@@ -8,19 +19,8 @@ services:
|
||||
- '4873:4873'
|
||||
volumes:
|
||||
- verdaccio:/verdaccio
|
||||
|
||||
nginx:
|
||||
restart: always
|
||||
build: conf/nginx
|
||||
ports:
|
||||
- '80:80'
|
||||
volumes:
|
||||
- /www/public
|
||||
volumes_from:
|
||||
- verdaccio
|
||||
links:
|
||||
- verdaccio:verdaccio
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
verdaccio:
|
||||
driver: local
|
||||
logs:
|
||||
|
||||
8
e2e/docker/proxy-nginx/nginx/Dockerfile
Executable file
8
e2e/docker/proxy-nginx/nginx/Dockerfile
Executable file
@@ -0,0 +1,8 @@
|
||||
# Light, maintained NGINX build
|
||||
FROM nginx:alpine
|
||||
|
||||
# Remove the default vhost
|
||||
RUN rm /etc/nginx/conf.d/default.conf
|
||||
|
||||
# Copy our custom server block
|
||||
COPY verdaccio.conf /etc/nginx/conf.d/verdaccio.conf
|
||||
@@ -1,7 +1,7 @@
|
||||
server {
|
||||
listen 80 default_server;
|
||||
access_log /var/log/nginx/verdaccio.log;
|
||||
charset utf-8;
|
||||
access_log /var/log/nginx/verdaccio.log;
|
||||
charset utf-8;
|
||||
location / {
|
||||
proxy_pass http://verdaccio:4873/;
|
||||
proxy_set_header Host $host;
|
||||
@@ -11,4 +11,4 @@ server {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_redirect off;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,11 @@
|
||||
"name": "@verdaccio/e2e-ui",
|
||||
"version": "2.0.0",
|
||||
"devDependencies": {
|
||||
"verdaccio": "workspace:8.0.0-next-8.15",
|
||||
"@verdaccio/core": "workspace:8.0.0-next-8.15",
|
||||
"@verdaccio/config": "workspace:8.0.0-next-8.15",
|
||||
"@verdaccio/test-helper": "workspace:4.0.0-next-8.4",
|
||||
"debug": "4.4.0",
|
||||
"verdaccio": "workspace:8.0.0-next-8.17",
|
||||
"@verdaccio/core": "workspace:8.0.0-next-8.17",
|
||||
"@verdaccio/config": "workspace:8.0.0-next-8.17",
|
||||
"@verdaccio/test-helper": "workspace:4.0.0-next-8.5",
|
||||
"debug": "4.4.1",
|
||||
"cypress": "^13.6.0",
|
||||
"get-port": "5.1.1"
|
||||
},
|
||||
|
||||
45
package.json
45
package.json
@@ -15,34 +15,34 @@
|
||||
"url": "https://opencollective.com/verdaccio"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.26.4",
|
||||
"@babel/core": "7.26.9",
|
||||
"@babel/cli": "7.27.2",
|
||||
"@babel/core": "7.27.4",
|
||||
"@babel/eslint-parser": "7.25.9",
|
||||
"@babel/node": "7.26.0",
|
||||
"@babel/plugin-proposal-decorators": "7.25.9",
|
||||
"@babel/plugin-proposal-function-sent": "7.25.9",
|
||||
"@babel/plugin-proposal-throw-expressions": "7.25.9",
|
||||
"@babel/node": "7.27.1",
|
||||
"@babel/plugin-proposal-decorators": "7.27.1",
|
||||
"@babel/plugin-proposal-function-sent": "7.27.1",
|
||||
"@babel/plugin-proposal-throw-expressions": "7.27.1",
|
||||
"@babel/plugin-syntax-dynamic-import": "7.8.3",
|
||||
"@babel/plugin-syntax-import-meta": "7.10.4",
|
||||
"@babel/plugin-transform-async-to-generator": "7.25.9",
|
||||
"@babel/plugin-transform-class-properties": "7.25.9",
|
||||
"@babel/plugin-transform-classes": "7.25.9",
|
||||
"@babel/plugin-transform-export-namespace-from": "7.25.9",
|
||||
"@babel/plugin-transform-json-strings": "7.25.9",
|
||||
"@babel/plugin-transform-nullish-coalescing-operator": "7.26.6",
|
||||
"@babel/plugin-transform-numeric-separator": "7.25.9",
|
||||
"@babel/plugin-transform-object-rest-spread": "7.25.9",
|
||||
"@babel/plugin-transform-optional-chaining": "7.25.9",
|
||||
"@babel/plugin-transform-runtime": "7.26.9",
|
||||
"@babel/preset-env": "7.26.9",
|
||||
"@babel/preset-react": "7.26.3",
|
||||
"@babel/plugin-transform-async-to-generator": "7.27.1",
|
||||
"@babel/plugin-transform-class-properties": "7.27.1",
|
||||
"@babel/plugin-transform-classes": "7.27.1",
|
||||
"@babel/plugin-transform-export-namespace-from": "7.27.1",
|
||||
"@babel/plugin-transform-json-strings": "7.27.1",
|
||||
"@babel/plugin-transform-nullish-coalescing-operator": "7.27.1",
|
||||
"@babel/plugin-transform-numeric-separator": "7.27.1",
|
||||
"@babel/plugin-transform-object-rest-spread": "7.27.3",
|
||||
"@babel/plugin-transform-optional-chaining": "7.27.1",
|
||||
"@babel/plugin-transform-runtime": "7.27.4",
|
||||
"@babel/preset-env": "7.27.2",
|
||||
"@babel/preset-react": "7.27.1",
|
||||
"@babel/preset-typescript": "7.24.7",
|
||||
"@babel/register": "7.25.9",
|
||||
"@babel/runtime": "7.26.9",
|
||||
"@babel/register": "7.27.1",
|
||||
"@babel/runtime": "7.27.6",
|
||||
"@changesets/changelog-github": "0.5.1",
|
||||
"@changesets/cli": "2.27.12",
|
||||
"@changesets/get-dependents-graph": "2.1.3",
|
||||
"@crowdin/cli": "4.4.1",
|
||||
"@crowdin/cli": "4.7.1",
|
||||
"@dianmora/contributors": "5.0.0",
|
||||
"@emotion/react": "11.10.6",
|
||||
"@emotion/styled": "11.10.6",
|
||||
@@ -63,6 +63,7 @@
|
||||
"@types/lodash": "4.17.7",
|
||||
"@types/mime": "3.0.4",
|
||||
"@types/minimatch": "5.1.2",
|
||||
"@types/ms": "2.1.0",
|
||||
"@types/node": "20.14.12",
|
||||
"@types/node-fetch": "2.6.11",
|
||||
"@types/qs": "6.9.15",
|
||||
@@ -95,7 +96,7 @@
|
||||
"babel-plugin-emotion": "11.0.0",
|
||||
"concurrently": "8.2.2",
|
||||
"cross-env": "7.0.3",
|
||||
"debug": "4.4.0",
|
||||
"debug": "4.4.1",
|
||||
"detect-secrets": "1.0.6",
|
||||
"eslint": "8.57.1",
|
||||
"fs-extra": "11.2.0",
|
||||
|
||||
@@ -1,5 +1,42 @@
|
||||
# @verdaccio/api
|
||||
|
||||
## 8.1.0-next-8.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [96d2f0f]
|
||||
- @verdaccio/core@8.0.0-next-8.17
|
||||
- @verdaccio/auth@8.0.0-next-8.17
|
||||
- @verdaccio/config@8.0.0-next-8.17
|
||||
- @verdaccio/middleware@8.0.0-next-8.17
|
||||
- @verdaccio/store@8.0.0-next-8.17
|
||||
- @verdaccio/logger@8.0.0-next-8.17
|
||||
|
||||
## 8.1.0-next-8.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 72c3cbb: chore(utils): replace @verdaccio/utils dependency with core
|
||||
- 626ae6a: feat: web v1 login backend (experimental)
|
||||
- b19ddca: fix(config): server settings
|
||||
- 5f036c0: fix(api): cidr whitelist for tokens
|
||||
- Updated dependencies [72c3cbb]
|
||||
- Updated dependencies [626ae6a]
|
||||
- Updated dependencies [d33266b]
|
||||
- Updated dependencies [9509b63]
|
||||
- Updated dependencies [626ae6a]
|
||||
- Updated dependencies [2fef671]
|
||||
- Updated dependencies [acb8a99]
|
||||
- Updated dependencies [b19ddca]
|
||||
- Updated dependencies [fdf44a6]
|
||||
- Updated dependencies [3ef4a49]
|
||||
- @verdaccio/middleware@8.0.0-next-8.16
|
||||
- @verdaccio/config@8.0.0-next-8.16
|
||||
- @verdaccio/store@8.0.0-next-8.16
|
||||
- @verdaccio/auth@8.0.0-next-8.16
|
||||
- @verdaccio/core@8.0.0-next-8.16
|
||||
- @verdaccio/logger@8.0.0-next-8.16
|
||||
|
||||
## 8.1.0-next-8.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@verdaccio/api",
|
||||
"version": "8.1.0-next-8.15",
|
||||
"version": "8.1.0-next-8.17",
|
||||
"description": "Verdaccio Registry API",
|
||||
"main": "./build/index.js",
|
||||
"types": "build/index.d.ts",
|
||||
@@ -42,25 +42,24 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@verdaccio/auth": "workspace:8.0.0-next-8.15",
|
||||
"@verdaccio/config": "workspace:8.0.0-next-8.15",
|
||||
"@verdaccio/core": "workspace:8.0.0-next-8.15",
|
||||
"@verdaccio/logger": "workspace:8.0.0-next-8.15",
|
||||
"@verdaccio/middleware": "workspace:8.0.0-next-8.15",
|
||||
"@verdaccio/store": "workspace:8.0.0-next-8.15",
|
||||
"@verdaccio/utils": "workspace:8.1.0-next-8.15",
|
||||
"@verdaccio/auth": "workspace:8.0.0-next-8.17",
|
||||
"@verdaccio/config": "workspace:8.0.0-next-8.17",
|
||||
"@verdaccio/core": "workspace:8.0.0-next-8.17",
|
||||
"@verdaccio/logger": "workspace:8.0.0-next-8.17",
|
||||
"@verdaccio/middleware": "workspace:8.0.0-next-8.17",
|
||||
"@verdaccio/store": "workspace:8.0.0-next-8.17",
|
||||
"abortcontroller-polyfill": "1.7.8",
|
||||
"body-parser": "1.20.3",
|
||||
"cookies": "0.9.1",
|
||||
"debug": "4.4.0",
|
||||
"debug": "4.4.1",
|
||||
"express": "4.21.2",
|
||||
"lodash": "4.17.21",
|
||||
"mime": "2.6.0",
|
||||
"semver": "7.7.1"
|
||||
"semver": "7.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@verdaccio/test-helper": "workspace:4.0.0-next-8.4",
|
||||
"@verdaccio/types": "workspace:13.0.0-next-8.5",
|
||||
"@verdaccio/test-helper": "workspace:4.0.0-next-8.5",
|
||||
"@verdaccio/types": "workspace:13.0.0-next-8.6",
|
||||
"mockdate": "3.0.5",
|
||||
"supertest": "7.0.0"
|
||||
},
|
||||
|
||||
@@ -20,6 +20,7 @@ import publish from './publish';
|
||||
import search from './search';
|
||||
import stars from './stars';
|
||||
import user from './user';
|
||||
import login from './v1/login';
|
||||
import profile from './v1/profile';
|
||||
import v1Search from './v1/search';
|
||||
import token from './v1/token';
|
||||
@@ -71,6 +72,9 @@ export default function (config: Config, auth: Auth, storage: Storage, logger: L
|
||||
v1Search(app, auth, storage, logger);
|
||||
token(app, auth, storage, config, logger);
|
||||
pkg(app, auth, storage, logger);
|
||||
if (config.flags?.webLogin) {
|
||||
login(app, auth, storage, config, logger);
|
||||
}
|
||||
return app;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,13 +9,14 @@ import {
|
||||
API_MESSAGE,
|
||||
HEADERS,
|
||||
HTTP_STATUS,
|
||||
authUtils,
|
||||
cryptoUtils,
|
||||
errorUtils,
|
||||
validationUtils,
|
||||
} from '@verdaccio/core';
|
||||
import { USER_API_ENDPOINTS, rateLimit } from '@verdaccio/middleware';
|
||||
import { Logger } from '@verdaccio/types';
|
||||
import { Config, RemoteUser } from '@verdaccio/types';
|
||||
import { getAuthenticatedMessage, mask } from '@verdaccio/utils';
|
||||
|
||||
import { $NextFunctionVer, $RequestExtend } from '../types/custom';
|
||||
|
||||
@@ -39,7 +40,7 @@ export default function (route: Router, auth: Auth, config: Config, logger: Logg
|
||||
}
|
||||
|
||||
const username = req.params.org_couchdb_user.split(':')[1];
|
||||
const message = getAuthenticatedMessage(req.remote_user.name);
|
||||
const message = authUtils.getAuthenticatedMessage(req.remote_user.name);
|
||||
debug('user authenticated message %o', message);
|
||||
res.status(HTTP_STATUS.OK);
|
||||
next({
|
||||
@@ -107,7 +108,7 @@ export default function (route: Router, auth: Auth, config: Config, logger: Logg
|
||||
res.status(HTTP_STATUS.CREATED);
|
||||
res.set(HEADERS.CACHE_CONTROL, 'no-cache, no-store');
|
||||
|
||||
const message = getAuthenticatedMessage(req.remote_user.name);
|
||||
const message = authUtils.getAuthenticatedMessage(req.remote_user.name);
|
||||
debug('login: created user message %o', message);
|
||||
|
||||
return next({
|
||||
@@ -119,10 +120,8 @@ export default function (route: Router, auth: Auth, config: Config, logger: Logg
|
||||
} else {
|
||||
debug('adduser: %o', name);
|
||||
if (
|
||||
validationUtils.validatePassword(
|
||||
password,
|
||||
config?.serverSettings?.passwordValidationRegex
|
||||
) === false
|
||||
validationUtils.validatePassword(password, config?.server?.passwordValidationRegex) ===
|
||||
false
|
||||
) {
|
||||
debug('adduser: invalid password');
|
||||
// eslint-disable-next-line new-cap
|
||||
@@ -148,7 +147,7 @@ export default function (route: Router, auth: Auth, config: Config, logger: Logg
|
||||
? await getApiToken(auth, config, user as RemoteUser, password)
|
||||
: undefined;
|
||||
if (token) {
|
||||
debug('adduser: new token %o', mask(token as string, 4));
|
||||
debug('adduser: new token %o', cryptoUtils.mask(token as string, 4));
|
||||
}
|
||||
if (!token) {
|
||||
return next(errorUtils.getUnauthorized());
|
||||
|
||||
167
packages/api/src/v1/login.ts
Normal file
167
packages/api/src/v1/login.ts
Normal file
@@ -0,0 +1,167 @@
|
||||
import buildDebug from 'debug';
|
||||
import { Response, Router } from 'express';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
|
||||
import { Auth, getApiToken } from '@verdaccio/auth';
|
||||
import { createRemoteUser } from '@verdaccio/config';
|
||||
import { API_ERROR, HEADERS, HTTP_STATUS, authUtils, errorUtils } from '@verdaccio/core';
|
||||
import { LOGIN_API_ENDPOINTS, rateLimit } from '@verdaccio/middleware';
|
||||
import { Storage } from '@verdaccio/store';
|
||||
import { Config, Logger } from '@verdaccio/types';
|
||||
|
||||
import { $NextFunctionVer, $RequestExtend } from '../../types/custom';
|
||||
|
||||
const debug = buildDebug('verdaccio:api:login');
|
||||
|
||||
const WEB_LOGIN_SESSION_ID = 'web-login-sessionId';
|
||||
|
||||
// https://github.com/npm/npm-profile/blob/main/lib/index.js
|
||||
export default function (
|
||||
route: Router,
|
||||
auth: Auth,
|
||||
storage: Storage,
|
||||
config: Config,
|
||||
logger: Logger
|
||||
): void {
|
||||
route.post(
|
||||
LOGIN_API_ENDPOINTS.login,
|
||||
rateLimit(config?.userRateLimit),
|
||||
async function (req: $RequestExtend, res: Response): Promise<void> {
|
||||
// create new login session (without token)
|
||||
const sessionId = randomUUID();
|
||||
debug('creating login session %o', sessionId);
|
||||
|
||||
await storage.saveToken({
|
||||
user: sessionId,
|
||||
token: '',
|
||||
key: WEB_LOGIN_SESSION_ID,
|
||||
readonly: false,
|
||||
created: new Date().getTime(),
|
||||
});
|
||||
|
||||
res.status(HTTP_STATUS.OK);
|
||||
|
||||
// Assuming Web UI is running on the same address as API
|
||||
const protocol = req.protocol;
|
||||
const host = req.hostname;
|
||||
const port = req.socket.localPort;
|
||||
|
||||
const loginUrl = `${protocol}://${host}:${port}/-/web/login?next=${LOGIN_API_ENDPOINTS.login_cli}/${sessionId}`;
|
||||
const doneUrl = `${protocol}://${host}:${port}${LOGIN_API_ENDPOINTS.login_done}/${sessionId}`;
|
||||
debug('loginUrl: %o', loginUrl);
|
||||
debug('doneUrl: %o', doneUrl);
|
||||
|
||||
res.json({
|
||||
loginUrl,
|
||||
doneUrl,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
route.get(
|
||||
LOGIN_API_ENDPOINTS.login_done_session,
|
||||
rateLimit(config?.userRateLimit),
|
||||
async function (req: $RequestExtend, res: Response, next: $NextFunctionVer): Promise<void> {
|
||||
debug('polling login session %o', req.params.sessionId);
|
||||
|
||||
if (!req.params.sessionId) {
|
||||
return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.SESSION_ID_REQUIRED));
|
||||
}
|
||||
const sessionId = req.params.sessionId;
|
||||
if (sessionId.length !== 36) {
|
||||
return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.SESSION_ID_INVALID));
|
||||
}
|
||||
|
||||
try {
|
||||
const tokens = await storage.readTokens({ user: sessionId });
|
||||
|
||||
if (tokens && tokens.length === 1 && tokens[0].key === WEB_LOGIN_SESSION_ID) {
|
||||
if (tokens[0].token.length === 0) {
|
||||
debug('waiting for authentication');
|
||||
// Poll again after short delay
|
||||
// TODO: make this configurable (default 5 seconds)
|
||||
res.status(HTTP_STATUS.ACCEPTED);
|
||||
res.set(HEADERS.RETRY_AFTER, '5');
|
||||
res.json({});
|
||||
} else {
|
||||
// session token can only be used once
|
||||
await storage.deleteToken(sessionId, tokens[0].key);
|
||||
|
||||
// Check if token has expired
|
||||
// TODO: make this configurable (default 2 minutes)
|
||||
const tokenCreatedDate = new Date(tokens[0].created);
|
||||
const minutesAgo = new Date(Date.now() - 2 * 60 * 1000);
|
||||
if (tokenCreatedDate < minutesAgo) {
|
||||
debug('session token expired');
|
||||
return next(errorUtils.getUnauthorized(API_ERROR.SESSION_TOKEN_EXPIRED));
|
||||
}
|
||||
|
||||
debug('session token is valid, login successful');
|
||||
|
||||
res.status(HTTP_STATUS.OK);
|
||||
res.json({ token: tokens[0].token });
|
||||
}
|
||||
} else {
|
||||
return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.SESSION_ID_INVALID));
|
||||
}
|
||||
} catch (error: any) {
|
||||
logger.error({ error: error.msg }, 'token list has failed: @{error}');
|
||||
return next(errorUtils.getCode(HTTP_STATUS.INTERNAL_ERROR, error.message));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
route.post(
|
||||
LOGIN_API_ENDPOINTS.login_cli_session,
|
||||
rateLimit(config?.userRateLimit),
|
||||
async function (req: $RequestExtend, res: Response, next: $NextFunctionVer): Promise<void> {
|
||||
const { username, password } = req.body;
|
||||
debug('authenticating login session %o for user %o', req.params.sessionId, username);
|
||||
|
||||
if (!req.params.sessionId) {
|
||||
return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.SESSION_ID_REQUIRED));
|
||||
}
|
||||
const sessionId = req.params.sessionId;
|
||||
if (sessionId.length !== 36) {
|
||||
return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.SESSION_ID_INVALID));
|
||||
}
|
||||
|
||||
auth.authenticate(
|
||||
username,
|
||||
password,
|
||||
async function callbackAuthenticate(err, user): Promise<void> {
|
||||
if (err) {
|
||||
logger.trace(
|
||||
{ username, err },
|
||||
'authenticating for user @{username} failed. Error: @{err.message}'
|
||||
);
|
||||
return next(errorUtils.getCode(HTTP_STATUS.UNAUTHORIZED, err.message));
|
||||
}
|
||||
|
||||
const remoteUser = createRemoteUser(username, user?.groups || []);
|
||||
const token = await getApiToken(auth, config, remoteUser, password);
|
||||
|
||||
if (!token) {
|
||||
return next(errorUtils.getUnauthorized());
|
||||
}
|
||||
|
||||
// Replace login session with token (to be picked up by the "done" endpoint)
|
||||
await storage.deleteToken(sessionId, WEB_LOGIN_SESSION_ID);
|
||||
await storage.saveToken({
|
||||
user: sessionId,
|
||||
token,
|
||||
key: WEB_LOGIN_SESSION_ID,
|
||||
readonly: false,
|
||||
created: new Date().getTime(),
|
||||
});
|
||||
|
||||
const message = authUtils.getAuthenticatedMessage(remoteUser.name ?? '');
|
||||
|
||||
res.status(HTTP_STATUS.CREATED);
|
||||
res.set(HEADERS.CACHE_CONTROL, 'no-cache, no-store');
|
||||
res.json({ ok: message, token });
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -74,7 +74,7 @@ export default function (route: Router, auth: Auth, config: Config): void {
|
||||
if (
|
||||
validationUtils.validatePassword(
|
||||
password.new,
|
||||
config?.serverSettings?.passwordValidationRegex
|
||||
config?.server?.passwordValidationRegex
|
||||
) === false
|
||||
) {
|
||||
/* eslint new-cap:off */
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import { Response, Router } from 'express';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { getApiToken } from '@verdaccio/auth';
|
||||
import { Auth } from '@verdaccio/auth';
|
||||
import { HEADERS, HTTP_STATUS, SUPPORT_ERRORS, errorUtils } from '@verdaccio/core';
|
||||
import { rateLimit } from '@verdaccio/middleware';
|
||||
import { TOKEN_API_ENDPOINTS } from '@verdaccio/middleware';
|
||||
import { Auth, getApiToken } from '@verdaccio/auth';
|
||||
import { HEADERS, HTTP_STATUS, SUPPORT_ERRORS, cryptoUtils, errorUtils } from '@verdaccio/core';
|
||||
import { TOKEN_API_ENDPOINTS, rateLimit } from '@verdaccio/middleware';
|
||||
import { Storage } from '@verdaccio/store';
|
||||
import { Config, RemoteUser, Token } from '@verdaccio/types';
|
||||
import { Logger } from '@verdaccio/types';
|
||||
import { mask, stringToMD5 } from '@verdaccio/utils';
|
||||
import { Config, Logger, RemoteUser, Token } from '@verdaccio/types';
|
||||
|
||||
import { $NextFunctionVer, $RequestExtend } from '../../types/custom';
|
||||
|
||||
@@ -94,9 +90,9 @@ export default function (
|
||||
throw errorUtils.getInternalError();
|
||||
}
|
||||
|
||||
const key = stringToMD5(token);
|
||||
const key = cryptoUtils.stringToMD5(token);
|
||||
// TODO: use a utility here
|
||||
const maskedToken = mask(token, 5);
|
||||
const maskedToken = cryptoUtils.mask(token, 5);
|
||||
const created = new Date().getTime();
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,7 +5,14 @@ import supertest from 'supertest';
|
||||
import { expect } from 'vitest';
|
||||
|
||||
import { parseConfigFile } from '@verdaccio/config';
|
||||
import { HEADERS, HEADER_TYPE, HTTP_STATUS, TOKEN_BEARER } from '@verdaccio/core';
|
||||
import {
|
||||
HEADERS,
|
||||
HEADER_TYPE,
|
||||
HTTP_STATUS,
|
||||
TOKEN_BEARER,
|
||||
authUtils,
|
||||
cryptoUtils,
|
||||
} from '@verdaccio/core';
|
||||
import { setup } from '@verdaccio/logger';
|
||||
import { Storage } from '@verdaccio/store';
|
||||
import {
|
||||
@@ -13,17 +20,18 @@ import {
|
||||
initializeServer as initializeServerHelper,
|
||||
} from '@verdaccio/test-helper';
|
||||
import { Author, GenericBody, PackageUsers } from '@verdaccio/types';
|
||||
import { buildToken, generateRandomHexString } from '@verdaccio/utils';
|
||||
|
||||
import apiMiddleware from '../../src';
|
||||
|
||||
setup({});
|
||||
|
||||
export const buildToken = authUtils.buildToken;
|
||||
|
||||
export const getConf = (conf) => {
|
||||
const configPath = path.join(__dirname, 'config', conf);
|
||||
const config = parseConfigFile(configPath);
|
||||
// custom config to avoid conflict with other tests
|
||||
config.auth.htpasswd.file = `${config.auth.htpasswd.file}-${generateRandomHexString()}`;
|
||||
config.auth.htpasswd.file = `${config.auth.htpasswd.file}-${cryptoUtils.generateRandomHexString()}`;
|
||||
return config;
|
||||
};
|
||||
|
||||
|
||||
21
packages/api/test/integration/config/login.yaml
Normal file
21
packages/api/test/integration/config/login.yaml
Normal file
@@ -0,0 +1,21 @@
|
||||
storage: ./storage
|
||||
|
||||
auth:
|
||||
htpasswd:
|
||||
file: ./htpasswd
|
||||
|
||||
packages:
|
||||
'@*/*':
|
||||
access: $all
|
||||
publish: $authenticated
|
||||
unpublish: $authenticated
|
||||
'**':
|
||||
access: $all
|
||||
publish: $authenticated
|
||||
unpublish: $authenticated
|
||||
|
||||
log: { type: stdout, format: pretty, level: debug }
|
||||
|
||||
## enable web login for testing
|
||||
flags:
|
||||
webLogin: true
|
||||
105
packages/api/test/integration/login.spec.ts
Normal file
105
packages/api/test/integration/login.spec.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import supertest from 'supertest';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
|
||||
import { HEADERS, HEADER_TYPE, HTTP_STATUS } from '@verdaccio/core';
|
||||
|
||||
import { initializeServer } from './_helper';
|
||||
import { createUser } from './_helper';
|
||||
|
||||
describe('login', () => {
|
||||
test('should return login and done urls, set session id', async () => {
|
||||
const app = await initializeServer('login.yaml');
|
||||
|
||||
const response = await supertest(app)
|
||||
.post('/-/v1/login')
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.set(HEADER_TYPE.ACCEPT_ENCODING, HEADERS.JSON)
|
||||
.send(JSON.stringify({}));
|
||||
|
||||
expect(response.status).toBe(HTTP_STATUS.OK);
|
||||
expect(response.headers['content-type']).toBe(HEADERS.JSON_CHARSET);
|
||||
expect(response.body).toEqual({
|
||||
loginUrl: expect.stringContaining('/-/web/login?next=/-/v1/login_cli/'),
|
||||
doneUrl: expect.stringContaining('/-/v1/done/'),
|
||||
});
|
||||
|
||||
const sessionId = response.body.doneUrl.split('/-/v1/done/')[1];
|
||||
expect(sessionId.length).toBe(36);
|
||||
});
|
||||
|
||||
test('should authenticate user using session id', async () => {
|
||||
const username = 'test';
|
||||
const password = 'password';
|
||||
const app = await initializeServer('login.yaml');
|
||||
await createUser(app, username, password);
|
||||
|
||||
const response = await supertest(app)
|
||||
.post('/-/v1/login')
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.set(HEADER_TYPE.ACCEPT_ENCODING, HEADERS.JSON)
|
||||
.send(JSON.stringify({}));
|
||||
|
||||
expect(response.status).toBe(HTTP_STATUS.OK);
|
||||
|
||||
const sessionId = response.body.doneUrl.split('/-/v1/done/')[1];
|
||||
|
||||
const response2 = await supertest(app)
|
||||
.post(`/-/v1/login_cli/${sessionId}`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.set(HEADER_TYPE.ACCEPT_ENCODING, HEADERS.JSON)
|
||||
.send(
|
||||
JSON.stringify({
|
||||
username,
|
||||
password,
|
||||
})
|
||||
);
|
||||
|
||||
expect(response2.status).toBe(HTTP_STATUS.CREATED);
|
||||
expect(response2.headers['content-type']).toBe(HEADERS.JSON_CHARSET);
|
||||
expect(response2.body).toEqual({
|
||||
ok: `you are authenticated as '${username}'`,
|
||||
token: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
test('should return session token when polled', async () => {
|
||||
const username = 'test';
|
||||
const password = 'password';
|
||||
const app = await initializeServer('login.yaml');
|
||||
await createUser(app, username, password);
|
||||
|
||||
const response = await supertest(app)
|
||||
.post('/-/v1/login')
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.set(HEADER_TYPE.ACCEPT_ENCODING, HEADERS.JSON)
|
||||
.send(JSON.stringify({}));
|
||||
|
||||
expect(response.status).toBe(HTTP_STATUS.OK);
|
||||
|
||||
const sessionId = response.body.doneUrl.split('/-/v1/done/')[1];
|
||||
|
||||
const response2 = await supertest(app)
|
||||
.post(`/-/v1/login_cli/${sessionId}`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.set(HEADER_TYPE.ACCEPT_ENCODING, HEADERS.JSON)
|
||||
.send(
|
||||
JSON.stringify({
|
||||
username,
|
||||
password,
|
||||
})
|
||||
);
|
||||
|
||||
expect(response2.status).toBe(HTTP_STATUS.CREATED);
|
||||
|
||||
const response3 = await supertest(app)
|
||||
.get(`/-/v1/done/${sessionId}`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.set(HEADER_TYPE.ACCEPT_ENCODING, HEADERS.JSON);
|
||||
|
||||
expect(response3.status).toBe(HTTP_STATUS.OK);
|
||||
expect(response3.headers['content-type']).toBe(HEADERS.JSON_CHARSET);
|
||||
expect(response3.body).toEqual({
|
||||
token: expect.any(String),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -2,9 +2,8 @@ import supertest from 'supertest';
|
||||
import { describe, test } from 'vitest';
|
||||
|
||||
import { HEADERS, HEADER_TYPE, HTTP_STATUS, TOKEN_BEARER } from '@verdaccio/core';
|
||||
import { buildToken } from '@verdaccio/utils';
|
||||
|
||||
import { createUser, initializeServer } from './_helper';
|
||||
import { buildToken, createUser, initializeServer } from './_helper';
|
||||
|
||||
describe('profile ', () => {
|
||||
describe('get profile ', () => {
|
||||
|
||||
@@ -10,9 +10,14 @@ import {
|
||||
SUPPORT_ERRORS,
|
||||
TOKEN_BEARER,
|
||||
} from '@verdaccio/core';
|
||||
import { buildToken } from '@verdaccio/utils';
|
||||
|
||||
import { deleteTokenCLI, generateTokenCLI, getNewToken, initializeServer } from './_helper';
|
||||
import {
|
||||
buildToken,
|
||||
deleteTokenCLI,
|
||||
generateTokenCLI,
|
||||
getNewToken,
|
||||
initializeServer,
|
||||
} from './_helper';
|
||||
|
||||
describe('token', () => {
|
||||
describe('basics', () => {
|
||||
|
||||
@@ -2,9 +2,8 @@ import supertest from 'supertest';
|
||||
import { describe, expect, test, vi } from 'vitest';
|
||||
|
||||
import { API_ERROR, HEADERS, HEADER_TYPE, HTTP_STATUS, TOKEN_BEARER } from '@verdaccio/core';
|
||||
import { buildToken } from '@verdaccio/utils';
|
||||
|
||||
import { createUser, getPackage, initializeServer } from './_helper';
|
||||
import { buildToken, createUser, getPackage, initializeServer } from './_helper';
|
||||
|
||||
const FORBIDDEN_VUE = 'authorization required to access package vue';
|
||||
|
||||
|
||||
@@ -2,9 +2,8 @@ import supertest from 'supertest';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
|
||||
import { HEADERS, HTTP_STATUS, TOKEN_BEARER } from '@verdaccio/core';
|
||||
import { buildToken } from '@verdaccio/utils';
|
||||
|
||||
import { createUser, initializeServer } from './_helper';
|
||||
import { buildToken, createUser, initializeServer } from './_helper';
|
||||
|
||||
describe('whoami', () => {
|
||||
test('should return the logged username', async () => {
|
||||
|
||||
@@ -1,5 +1,38 @@
|
||||
# @verdaccio/auth
|
||||
|
||||
## 8.0.0-next-8.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [96d2f0f]
|
||||
- @verdaccio/core@8.0.0-next-8.17
|
||||
- @verdaccio/config@8.0.0-next-8.17
|
||||
- @verdaccio/loaders@8.0.0-next-8.7
|
||||
- verdaccio-htpasswd@13.0.0-next-8.17
|
||||
- @verdaccio/signature@8.0.0-next-8.9
|
||||
|
||||
## 8.0.0-next-8.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 72c3cbb: chore(utils): replace @verdaccio/utils dependency with core
|
||||
- b19ddca: fix(config): server settings
|
||||
- 3ef4a49: fix: support JWT sign and verify options
|
||||
- Updated dependencies [6add153]
|
||||
- Updated dependencies [72c3cbb]
|
||||
- Updated dependencies [9509b63]
|
||||
- Updated dependencies [76308ff]
|
||||
- Updated dependencies [626ae6a]
|
||||
- Updated dependencies [2fef671]
|
||||
- Updated dependencies [acb8a99]
|
||||
- Updated dependencies [b19ddca]
|
||||
- Updated dependencies [3ef4a49]
|
||||
- @verdaccio/loaders@8.0.0-next-8.7
|
||||
- @verdaccio/config@8.0.0-next-8.16
|
||||
- @verdaccio/core@8.0.0-next-8.16
|
||||
- @verdaccio/signature@8.0.0-next-8.8
|
||||
- verdaccio-htpasswd@13.0.0-next-8.16
|
||||
|
||||
## 8.0.0-next-8.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@verdaccio/auth",
|
||||
"version": "8.0.0-next-8.15",
|
||||
"version": "8.0.0-next-8.17",
|
||||
"description": "Verdaccio Authentication",
|
||||
"main": "./build/index.js",
|
||||
"types": "./build/index.d.ts",
|
||||
@@ -42,19 +42,18 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@verdaccio/config": "workspace:8.0.0-next-8.15",
|
||||
"@verdaccio/core": "workspace:8.0.0-next-8.15",
|
||||
"@verdaccio/loaders": "workspace:8.0.0-next-8.6",
|
||||
"@verdaccio/signature": "workspace:8.0.0-next-8.7",
|
||||
"@verdaccio/utils": "workspace:8.1.0-next-8.15",
|
||||
"debug": "4.4.0",
|
||||
"@verdaccio/config": "workspace:8.0.0-next-8.17",
|
||||
"@verdaccio/core": "workspace:8.0.0-next-8.17",
|
||||
"@verdaccio/loaders": "workspace:8.0.0-next-8.7",
|
||||
"@verdaccio/signature": "workspace:8.0.0-next-8.9",
|
||||
"debug": "4.4.1",
|
||||
"lodash": "4.17.21",
|
||||
"verdaccio-htpasswd": "workspace:13.0.0-next-8.15"
|
||||
"verdaccio-htpasswd": "workspace:13.0.0-next-8.17"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@verdaccio/middleware": "workspace:8.0.0-next-8.15",
|
||||
"@verdaccio/types": "workspace:13.0.0-next-8.5",
|
||||
"@verdaccio/logger": "workspace:8.0.0-next-8.15",
|
||||
"@verdaccio/middleware": "workspace:8.0.0-next-8.17",
|
||||
"@verdaccio/types": "workspace:13.0.0-next-8.6",
|
||||
"@verdaccio/logger": "workspace:8.0.0-next-8.17",
|
||||
"express": "4.21.2",
|
||||
"supertest": "7.0.0"
|
||||
},
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import buildDebug from 'debug';
|
||||
import _ from 'lodash';
|
||||
import _, { isFunction } from 'lodash';
|
||||
import { HTPasswd } from 'verdaccio-htpasswd';
|
||||
|
||||
import { createAnonymousRemoteUser, createRemoteUser } from '@verdaccio/config';
|
||||
import { TOKEN_VALID_LENGTH, createAnonymousRemoteUser, createRemoteUser } from '@verdaccio/config';
|
||||
import {
|
||||
API_ERROR,
|
||||
PLUGIN_CATEGORY,
|
||||
PLUGIN_PREFIX,
|
||||
SUPPORT_ERRORS,
|
||||
TOKEN_BASIC,
|
||||
TOKEN_BEARER,
|
||||
VerdaccioError,
|
||||
authUtils,
|
||||
errorUtils,
|
||||
pluginUtils,
|
||||
warningUtils,
|
||||
@@ -20,7 +22,6 @@ import {
|
||||
aesEncryptDeprecated,
|
||||
parseBasicPayload,
|
||||
signPayload,
|
||||
utils as signatureUtils,
|
||||
} from '@verdaccio/signature';
|
||||
import {
|
||||
AllowAccess,
|
||||
@@ -32,7 +33,6 @@ import {
|
||||
RemoteUser,
|
||||
Security,
|
||||
} from '@verdaccio/types';
|
||||
import { getMatchedPackagesSpec, isFunction, isNil } from '@verdaccio/utils';
|
||||
|
||||
import {
|
||||
$RequestExtend,
|
||||
@@ -126,7 +126,7 @@ class Auth implements IAuthMiddleware, TokenEncryption, pluginUtils.IBasicAuth {
|
||||
);
|
||||
},
|
||||
this.options.legacyMergeConfigs,
|
||||
this.config?.serverSettings?.pluginPrefix,
|
||||
this.config?.server?.pluginPrefix ?? PLUGIN_PREFIX,
|
||||
PLUGIN_CATEGORY.AUTHENTICATION
|
||||
);
|
||||
}
|
||||
@@ -142,14 +142,14 @@ class Auth implements IAuthMiddleware, TokenEncryption, pluginUtils.IBasicAuth {
|
||||
newPassword: string,
|
||||
cb: Callback
|
||||
): void {
|
||||
const validPlugins = _.filter(this.plugins, (plugin) => isFunction(plugin.changePassword));
|
||||
const validPlugins = _.filter(this.plugins, (plugin) => _.isFunction(plugin.changePassword));
|
||||
|
||||
if (_.isEmpty(validPlugins)) {
|
||||
return cb(errorUtils.getInternalError(SUPPORT_ERRORS.PLUGIN_MISSING_INTERFACE));
|
||||
}
|
||||
|
||||
for (const plugin of validPlugins) {
|
||||
if (isNil(plugin) || isFunction(plugin.changePassword) === false) {
|
||||
if (_.isNil(plugin) || _.isFunction(plugin.changePassword) === false) {
|
||||
debug('auth plugin does not implement changePassword, trying next one');
|
||||
continue;
|
||||
} else {
|
||||
@@ -278,7 +278,7 @@ class Auth implements IAuthMiddleware, TokenEncryption, pluginUtils.IBasicAuth {
|
||||
const pkg = Object.assign(
|
||||
{},
|
||||
pkgAllowAccess,
|
||||
getMatchedPackagesSpec(packageName, this.config.packages)
|
||||
authUtils.getMatchedPackagesSpec(packageName, this.config.packages)
|
||||
) as AllowAccess & PackageAccess;
|
||||
debug('allow access for %o', packageName);
|
||||
|
||||
@@ -312,7 +312,7 @@ class Auth implements IAuthMiddleware, TokenEncryption, pluginUtils.IBasicAuth {
|
||||
): void {
|
||||
const pkg = Object.assign(
|
||||
{ name: packageName, version: packageVersion },
|
||||
getMatchedPackagesSpec(packageName, this.config.packages)
|
||||
authUtils.getMatchedPackagesSpec(packageName, this.config.packages)
|
||||
);
|
||||
debug('allow unpublish for %o', packageName);
|
||||
|
||||
@@ -355,7 +355,7 @@ class Auth implements IAuthMiddleware, TokenEncryption, pluginUtils.IBasicAuth {
|
||||
const plugins = this.plugins.slice(0);
|
||||
const pkg = Object.assign(
|
||||
{ name: packageName, version: packageVersion },
|
||||
getMatchedPackagesSpec(packageName, this.config.packages)
|
||||
authUtils.getMatchedPackagesSpec(packageName, this.config.packages)
|
||||
);
|
||||
debug('allow publish for %o init | plugins: %o', packageName, plugins.length);
|
||||
|
||||
@@ -557,7 +557,7 @@ class Auth implements IAuthMiddleware, TokenEncryption, pluginUtils.IBasicAuth {
|
||||
|
||||
let credentials: RemoteUser | undefined;
|
||||
try {
|
||||
credentials = verifyJWTPayload(token, this.config.secret);
|
||||
credentials = verifyJWTPayload(token, this.config.secret, this.config.security);
|
||||
} catch (err: any) {
|
||||
// FIXME: intended behaviour, do we want it?
|
||||
}
|
||||
@@ -594,7 +594,7 @@ class Auth implements IAuthMiddleware, TokenEncryption, pluginUtils.IBasicAuth {
|
||||
* Encrypt a string.
|
||||
*/
|
||||
public aesEncrypt(value: string): string | void {
|
||||
if (this.secret.length === signatureUtils.TOKEN_VALID_LENGTH) {
|
||||
if (this.secret.length === TOKEN_VALID_LENGTH) {
|
||||
debug('signing with enhanced aes legacy');
|
||||
const token = aesEncrypt(value, this.secret);
|
||||
return token;
|
||||
|
||||
@@ -61,6 +61,6 @@ export function getMiddlewareCredentials(
|
||||
|
||||
debug('is jwt');
|
||||
if (_.isString(token) && scheme.toUpperCase() === TOKEN_BEARER.toUpperCase()) {
|
||||
return verifyJWTPayload(token, secretKey);
|
||||
return verifyJWTPayload(token, secretKey, security);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ export function getMiddlewareCredentials(
|
||||
|
||||
debug('is jwt');
|
||||
if (_.isString(token) && scheme.toUpperCase() === TOKEN_BEARER.toUpperCase()) {
|
||||
return verifyJWTPayload(token, secretKey);
|
||||
return verifyJWTPayload(token, secretKey, security);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,9 +131,9 @@ export async function getApiToken(
|
||||
|
||||
export const expireReasons: string[] = ['JsonWebTokenError', 'TokenExpiredError'];
|
||||
|
||||
export function verifyJWTPayload(token: string, secret: string): RemoteUser {
|
||||
export function verifyJWTPayload(token: string, secret: string, security: Security): RemoteUser {
|
||||
try {
|
||||
const payload: RemoteUser = verifyPayload(token, secret);
|
||||
const payload: RemoteUser = verifyPayload(token, secret, security?.api?.jwt?.verify);
|
||||
|
||||
return payload;
|
||||
} catch (error: any) {
|
||||
|
||||
@@ -9,16 +9,17 @@ import {
|
||||
parseConfigFile,
|
||||
} from '@verdaccio/config';
|
||||
import { getDefaultConfig } from '@verdaccio/config';
|
||||
import { TOKEN_BEARER } from '@verdaccio/core';
|
||||
import { TOKEN_BEARER, authUtils } from '@verdaccio/core';
|
||||
import { logger, setup } from '@verdaccio/logger';
|
||||
import { signPayload } from '@verdaccio/signature';
|
||||
import { Config, RemoteUser, Security } from '@verdaccio/types';
|
||||
import { buildToken, buildUserBuffer } from '@verdaccio/utils';
|
||||
|
||||
import { Auth, getApiToken, getMiddlewareCredentials, verifyJWTPayload } from '../src';
|
||||
|
||||
setup({});
|
||||
|
||||
const buildToken = authUtils.buildToken;
|
||||
|
||||
const parseConfigurationFile = (conf) => {
|
||||
const { name, ext } = path.parse(conf);
|
||||
const format = ext.startsWith('.') ? ext.substring(1) : 'yaml';
|
||||
@@ -37,6 +38,7 @@ describe('Auth utilities', () => {
|
||||
const conf = parseConfigFile(parseConfigurationSecurityFile(configFileName));
|
||||
// @ts-ignore
|
||||
const secConf = _.merge(getDefaultConfig(), conf);
|
||||
// @ts-expect-error
|
||||
secConf.secret = secret;
|
||||
const config: Config = new AppConfig(secConf);
|
||||
|
||||
@@ -101,7 +103,7 @@ describe('Auth utilities', () => {
|
||||
const user = 'test';
|
||||
const pass = 'test';
|
||||
// basic authentication need send user as base64
|
||||
const token = buildUserBuffer(user, pass).toString('base64');
|
||||
const token = authUtils.buildUserBuffer(user, pass).toString('base64');
|
||||
const config: Config = getConfig('security-legacy', secret);
|
||||
const security: Security = config.security;
|
||||
const credentials = getMiddlewareCredentials(security, secret, `Basic ${token}`);
|
||||
@@ -171,15 +173,17 @@ describe('Auth utilities', () => {
|
||||
|
||||
describe('verifyJWTPayload', () => {
|
||||
test('should fail on verify the token and return anonymous users', () => {
|
||||
expect(verifyJWTPayload('fakeToken', 'b2df428b9929d3ace7c598bbf4e496b2')).toEqual(
|
||||
createAnonymousRemoteUser()
|
||||
);
|
||||
const config: Config = getConfig('security-jwt', '12345');
|
||||
expect(
|
||||
verifyJWTPayload('fakeToken', 'b2df428b9929d3ace7c598bbf4e496b2', config.security)
|
||||
).toEqual(createAnonymousRemoteUser());
|
||||
});
|
||||
|
||||
test('should verify the token and return a remote user', async () => {
|
||||
const remoteUser = createRemoteUser('foo', []);
|
||||
const token = await signPayload(remoteUser, '12345');
|
||||
const verifiedToken = verifyJWTPayload(token, '12345');
|
||||
const config: Config = getConfig('security-jwt', '12345');
|
||||
const verifiedToken = verifyJWTPayload(token, '12345', config.security);
|
||||
expect(verifiedToken.groups).toEqual(remoteUser.groups);
|
||||
expect(verifiedToken.name).toEqual(remoteUser.name);
|
||||
});
|
||||
|
||||
@@ -7,14 +7,19 @@ import {
|
||||
ROLES,
|
||||
createAnonymousRemoteUser,
|
||||
createRemoteUser,
|
||||
getDefaultConfig,
|
||||
parseConfigFile,
|
||||
} from '@verdaccio/config';
|
||||
import { getDefaultConfig } from '@verdaccio/config';
|
||||
import { API_ERROR, CHARACTER_ENCODING, VerdaccioError, errorUtils } from '@verdaccio/core';
|
||||
import {
|
||||
API_ERROR,
|
||||
CHARACTER_ENCODING,
|
||||
VerdaccioError,
|
||||
authUtils,
|
||||
errorUtils,
|
||||
} from '@verdaccio/core';
|
||||
import { logger, setup } from '@verdaccio/logger';
|
||||
import { aesDecrypt, verifyPayload } from '@verdaccio/signature';
|
||||
import { Config, RemoteUser } from '@verdaccio/types';
|
||||
import { getAuthenticatedMessage } from '@verdaccio/utils';
|
||||
|
||||
import {
|
||||
ActionsAllowed,
|
||||
@@ -356,7 +361,7 @@ describe('Auth utilities', () => {
|
||||
|
||||
describe('getAuthenticatedMessage test', () => {
|
||||
test('should sign token with jwt enabled', () => {
|
||||
expect(getAuthenticatedMessage('test')).toBe("you are authenticated as 'test'");
|
||||
expect(authUtils.getAuthenticatedMessage('test')).toBe("you are authenticated as 'test'");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,11 +4,17 @@ import supertest from 'supertest';
|
||||
import { describe, expect, test, vi } from 'vitest';
|
||||
|
||||
import { Config as AppConfig, ROLES, createRemoteUser, getDefaultConfig } from '@verdaccio/config';
|
||||
import { HEADERS, HTTP_STATUS, SUPPORT_ERRORS, TOKEN_BEARER, errorUtils } from '@verdaccio/core';
|
||||
import {
|
||||
HEADERS,
|
||||
HTTP_STATUS,
|
||||
SUPPORT_ERRORS,
|
||||
TOKEN_BEARER,
|
||||
authUtils,
|
||||
errorUtils,
|
||||
} from '@verdaccio/core';
|
||||
import { logger, setup } from '@verdaccio/logger';
|
||||
import { errorReportingMiddleware, final, handleError } from '@verdaccio/middleware';
|
||||
import { Config } from '@verdaccio/types';
|
||||
import { buildToken } from '@verdaccio/utils';
|
||||
|
||||
import { $RequestExtend, Auth } from '../src';
|
||||
import {
|
||||
@@ -20,10 +26,12 @@ import {
|
||||
|
||||
setup({});
|
||||
|
||||
const buildToken = authUtils.buildToken;
|
||||
|
||||
// to avoid flaky test generate same ramdom key
|
||||
vi.mock('@verdaccio/utils', async (importOriginal) => {
|
||||
vi.mock('@verdaccio/core', async (importOriginal) => {
|
||||
return {
|
||||
...(await importOriginal<typeof import('@verdaccio/utils')>()),
|
||||
...(await importOriginal<typeof import('@verdaccio/core')>()),
|
||||
// used by enhanced legacy aes signature (minimum 32 characters)
|
||||
generateRandomSecretKey: () => 'GCYW/3IJzQI6GvPmy9sbMkFoiL7QLVw',
|
||||
// used by legacy aes signature
|
||||
@@ -572,19 +580,15 @@ describe('AuthTest', () => {
|
||||
app.use(express.json({ strict: false, limit: '10mb' }));
|
||||
|
||||
app.use(auth.apiJWTmiddleware());
|
||||
// @ts-expect-error
|
||||
app.use(errorReportingMiddleware(logger));
|
||||
app.get('/*', (req, res, next) => {
|
||||
if ((req as $RequestExtend).remote_user.error) {
|
||||
next(new Error((req as $RequestExtend).remote_user.error));
|
||||
} else {
|
||||
// @ts-expect-error
|
||||
next({ user: req?.remote_user });
|
||||
}
|
||||
});
|
||||
// @ts-expect-error
|
||||
app.use(handleError(logger));
|
||||
// @ts-expect-error
|
||||
app.use(final);
|
||||
return app;
|
||||
};
|
||||
@@ -706,7 +710,7 @@ describe('AuthTest', () => {
|
||||
await auth.init();
|
||||
const token = (await auth.jwtEncrypt(
|
||||
createRemoteUser('jwt_user', [ROLES.ALL]),
|
||||
config.security.api.jwt.sign
|
||||
config.security?.api?.jwt?.sign || {}
|
||||
)) as string;
|
||||
const app = await getServer(auth);
|
||||
const res = await supertest(app)
|
||||
|
||||
@@ -1,5 +1,30 @@
|
||||
# @verdaccio/cli
|
||||
|
||||
## 8.0.0-next-8.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [96d2f0f]
|
||||
- @verdaccio/core@8.0.0-next-8.17
|
||||
- @verdaccio/config@8.0.0-next-8.17
|
||||
- @verdaccio/node-api@8.0.0-next-8.17
|
||||
- @verdaccio/logger@8.0.0-next-8.17
|
||||
|
||||
## 8.0.0-next-8.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [72c3cbb]
|
||||
- Updated dependencies [9509b63]
|
||||
- Updated dependencies [626ae6a]
|
||||
- Updated dependencies [2fef671]
|
||||
- Updated dependencies [acb8a99]
|
||||
- Updated dependencies [b19ddca]
|
||||
- @verdaccio/config@8.0.0-next-8.16
|
||||
- @verdaccio/core@8.0.0-next-8.16
|
||||
- @verdaccio/node-api@8.0.0-next-8.16
|
||||
- @verdaccio/logger@8.0.0-next-8.16
|
||||
|
||||
## 8.0.0-next-8.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@verdaccio/cli",
|
||||
"version": "8.0.0-next-8.15",
|
||||
"version": "8.0.0-next-8.17",
|
||||
"author": {
|
||||
"name": "Juan Picado",
|
||||
"email": "juanpicado19@gmail.com"
|
||||
@@ -47,14 +47,14 @@
|
||||
"start": "ts-node src/index.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@verdaccio/config": "workspace:8.0.0-next-8.15",
|
||||
"@verdaccio/core": "workspace:8.0.0-next-8.15",
|
||||
"@verdaccio/logger": "workspace:8.0.0-next-8.15",
|
||||
"@verdaccio/node-api": "workspace:8.0.0-next-8.15",
|
||||
"@verdaccio/config": "workspace:8.0.0-next-8.17",
|
||||
"@verdaccio/core": "workspace:8.0.0-next-8.17",
|
||||
"@verdaccio/logger": "workspace:8.0.0-next-8.17",
|
||||
"@verdaccio/node-api": "workspace:8.0.0-next-8.17",
|
||||
"clipanion": "4.0.0-rc.4",
|
||||
"envinfo": "7.14.0",
|
||||
"kleur": "4.1.5",
|
||||
"semver": "7.7.1"
|
||||
"semver": "7.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ts-node": "10.9.2"
|
||||
|
||||
@@ -1,5 +1,26 @@
|
||||
# @verdaccio/config
|
||||
|
||||
## 8.0.0-next-8.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [96d2f0f]
|
||||
- @verdaccio/core@8.0.0-next-8.17
|
||||
|
||||
## 8.0.0-next-8.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 72c3cbb: chore(utils): replace @verdaccio/utils dependency with core
|
||||
- 626ae6a: feat: web v1 login backend (experimental)
|
||||
- 2fef671: chore(signature): remove duplicate code
|
||||
- b19ddca: fix(config): server settings
|
||||
- Updated dependencies [9509b63]
|
||||
- Updated dependencies [626ae6a]
|
||||
- Updated dependencies [acb8a99]
|
||||
- Updated dependencies [b19ddca]
|
||||
- @verdaccio/core@8.0.0-next-8.16
|
||||
|
||||
## 8.0.0-next-8.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@verdaccio/config",
|
||||
"version": "8.0.0-next-8.15",
|
||||
"version": "8.0.0-next-8.17",
|
||||
"description": "Verdaccio Configuration",
|
||||
"main": "./build/index.js",
|
||||
"types": "build/index.d.ts",
|
||||
@@ -42,9 +42,8 @@
|
||||
"build": "pnpm run build:js && pnpm run build:types"
|
||||
},
|
||||
"dependencies": {
|
||||
"@verdaccio/core": "workspace:8.0.0-next-8.15",
|
||||
"@verdaccio/utils": "workspace:8.1.0-next-8.15",
|
||||
"debug": "4.4.0",
|
||||
"@verdaccio/core": "workspace:8.0.0-next-8.17",
|
||||
"debug": "4.4.1",
|
||||
"js-yaml": "4.1.0",
|
||||
"lodash": "4.17.21",
|
||||
"minimatch": "7.4.6"
|
||||
|
||||
@@ -2,7 +2,7 @@ import assert from 'assert';
|
||||
import buildDebug from 'debug';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { APP_ERROR, validationUtils, warningUtils } from '@verdaccio/core';
|
||||
import { APP_ERROR, authUtils, cryptoUtils, validationUtils, warningUtils } from '@verdaccio/core';
|
||||
import { Codes } from '@verdaccio/core/build/warning-utils';
|
||||
import {
|
||||
Config as AppConfig,
|
||||
@@ -15,12 +15,11 @@ import {
|
||||
Security,
|
||||
ServerSettingsConf,
|
||||
} from '@verdaccio/types';
|
||||
import { generateRandomHexString, getMatchedPackagesSpec } from '@verdaccio/utils';
|
||||
|
||||
import { getUserAgent } from './agent';
|
||||
import { normalisePackageAccess } from './package-access';
|
||||
import { defaultSecurity } from './security';
|
||||
import serverSettings from './serverSettings';
|
||||
import defaultServerSettings from './serverSettings';
|
||||
import { generateRandomSecretKey } from './token';
|
||||
import { sanityCheckUplinksProps, uplinkSanityCheck } from './uplinks';
|
||||
|
||||
@@ -63,7 +62,7 @@ class Config implements AppConfig {
|
||||
|
||||
public plugins: string | void | null;
|
||||
public security: Security;
|
||||
public serverSettings: ServerSettingsConf;
|
||||
public server: ServerSettingsConf;
|
||||
private configOverrideOptions: { forceMigrateToSecureLegacySignature: boolean };
|
||||
// @ts-ignore
|
||||
public secret: string;
|
||||
@@ -102,10 +101,11 @@ class Config implements AppConfig {
|
||||
}),
|
||||
config.security
|
||||
);
|
||||
this.serverSettings = serverSettings;
|
||||
this.server = { ...defaultServerSettings, ...config.server };
|
||||
this.flags = {
|
||||
searchRemote: config.flags?.searchRemote ?? true,
|
||||
changePassword: config.flags?.changePassword ?? false,
|
||||
webLogin: config.flags?.webLogin ?? false,
|
||||
};
|
||||
this.user_agent = config.user_agent;
|
||||
|
||||
@@ -148,7 +148,7 @@ class Config implements AppConfig {
|
||||
// unique identifier of self server (or a cluster), used to avoid loops
|
||||
// @ts-ignore
|
||||
if (!this.server_id) {
|
||||
this.server_id = generateRandomHexString(6);
|
||||
this.server_id = cryptoUtils.generateRandomHexString(6);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,10 +162,13 @@ class Config implements AppConfig {
|
||||
|
||||
/**
|
||||
* Check for package spec
|
||||
* @param pkgName - package name
|
||||
* @returns package access
|
||||
* @deprecated use core.authUtils instead
|
||||
*/
|
||||
public getMatchedPackagesSpec(pkgName: string): PackageAccess | void {
|
||||
// TODO: remove this method and replace by library utils
|
||||
return getMatchedPackagesSpec(pkgName, this.packages);
|
||||
return authUtils.getMatchedPackagesSpec(pkgName, this.packages);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { randomBytes } from 'crypto';
|
||||
|
||||
// TODO: code duplicated at @verdaccio/signature
|
||||
export const TOKEN_VALID_LENGTH = 32;
|
||||
|
||||
/**
|
||||
* Secret key must have 32 characters.
|
||||
* // TODO: code duplicated at @verdaccio/signature
|
||||
*/
|
||||
export function generateRandomSecretKey(): string {
|
||||
return randomBytes(TOKEN_VALID_LENGTH).toString('base64').substring(0, TOKEN_VALID_LENGTH);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import assert from 'assert';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { authUtils } from '@verdaccio/core';
|
||||
import { PackageList, UpLinksConfList } from '@verdaccio/types';
|
||||
import { getMatchedPackagesSpec } from '@verdaccio/utils';
|
||||
|
||||
export const DEFAULT_REGISTRY = 'https://registry.npmjs.org';
|
||||
export const DEFAULT_UPLINK = 'npmjs';
|
||||
@@ -49,7 +49,7 @@ export function sanityCheckUplinksProps(configUpLinks: UpLinksConfList): UpLinks
|
||||
}
|
||||
|
||||
export function getProxiesForPackage(pkg: string, packages: PackageList): string[] {
|
||||
const matchedPkg = getMatchedPackagesSpec(pkg, packages);
|
||||
const matchedPkg = authUtils.getMatchedPackagesSpec(pkg, packages);
|
||||
return matchedPkg?.proxy || [];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
# @verdaccio/core
|
||||
|
||||
## 8.0.0-next-8.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 96d2f0f: fix(core): remove `lodash` from a dependency
|
||||
|
||||
## 8.0.0-next-8.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 9509b63: chore(core): http status codes
|
||||
- 626ae6a: feat: web v1 login backend (experimental)
|
||||
- acb8a99: chore(core): move more utils to core
|
||||
- b19ddca: fix(config): server settings
|
||||
|
||||
## 8.0.0-next-8.15
|
||||
|
||||
## 8.0.0-next-8.14
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@verdaccio/core",
|
||||
"version": "8.0.0-next-8.15",
|
||||
"version": "8.0.0-next-8.17",
|
||||
"description": "Verdaccio Core Components",
|
||||
"keywords": [
|
||||
"private",
|
||||
@@ -37,12 +37,12 @@
|
||||
"core-js": "3.40.0",
|
||||
"http-errors": "2.0.0",
|
||||
"http-status-codes": "2.3.0",
|
||||
"minimatch": "10.0.1",
|
||||
"process-warning": "1.0.0",
|
||||
"semver": "7.7.1"
|
||||
"semver": "7.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@verdaccio/types": "workspace:13.0.0-next-8.5",
|
||||
"lodash": "4.17.21",
|
||||
"@verdaccio/types": "workspace:13.0.0-next-8.6",
|
||||
"typedoc": "0.23.25",
|
||||
"typedoc-plugin-missing-exports": "latest"
|
||||
},
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user