Compare commits

..

33 Commits

Author SHA1 Message Date
Juan Picado
d22673c61d chore: update versions (6-next) (#3409)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-10-15 09:02:41 +02:00
Juan Picado
2f835ac09e chore: migrate to cypress for ui e2e (#3434) 2022-10-15 07:43:42 +02:00
verdacciobot
852324c321 chore: updated static data 2022-10-13 00:18:16 +00:00
Juan Picado
54606976c3 chore(website): improve api docs part 2 (#3431)
* chore: remove unused package

* Update pnpm-lock.yaml

* chore: improve types in general plugins
2022-10-11 23:06:55 +02:00
renovate[bot]
484ecae3d2 chore(deps): update github/codeql-action digest to 8075783 (#3426)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-10 17:59:44 +02:00
dependabot[bot]
235d350dc1 chore(deps): bump github/codeql-action from 2.1.26 to 2.1.27 (#3430)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.1.26 to 2.1.27.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](e0e5ded33c...807578363a)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-10 17:59:31 +02:00
dependabot[bot]
af90e35cd1 chore(deps): bump actions/checkout from 3.0.2 to 3.1.0 (#3429)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3.0.2 to 3.1.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](2541b1294d...93ea575cb5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-10 17:59:20 +02:00
Juan Picado
791988a9aa chore(website): fix broken link 2022-10-10 08:31:38 +02:00
Juan Picado
64cfedb805 chore(website): add api section (#3424)
* chore: add api section

* chore: fix build

* Update website.yml

* chore: improve types plugin

clean up others stuff

* Update crowdin.yaml

* clean up
2022-10-10 08:00:09 +02:00
verdacciobot
bf36b9477c chore: updated static data 2022-10-10 00:18:45 +00:00
verdacciobot
737684e592 chore: updated static data 2022-10-06 00:19:47 +00:00
dependabot[bot]
e6fc774d44 chore(deps): bump github/codeql-action from 2.1.25 to 2.1.26 (#3419)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.1.25 to 2.1.26.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](86f3159a69...e0e5ded33c)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-03 13:50:22 +02:00
dependabot[bot]
1adb22553a chore(deps): bump actions/github-script from 6.2.0 to 6.3.1 (#3420)
Bumps [actions/github-script](https://github.com/actions/github-script) from 6.2.0 to 6.3.1.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](c713e510db...7dff1a8764)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-03 13:50:12 +02:00
dependabot[bot]
93dd256b21 chore(deps): bump actions/setup-node from 3.4.1 to 3.5.0 (#3421)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 3.4.1 to 3.5.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](2fddd8803e...969bd26639)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-03 13:49:58 +02:00
dependabot[bot]
404ab31e06 chore(deps): bump actions/cache from 3.0.8 to 3.0.10 (#3422)
Bumps [actions/cache](https://github.com/actions/cache) from 3.0.8 to 3.0.10.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](fd5de65bc8...56461b9eb0)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-03 13:49:41 +02:00
Andrii Bodnar
8897bdeb5d docs: Update Crowdin logo (#3418) 2022-10-03 10:48:51 +02:00
verdacciobot
717a36dd94 chore: updated static data 2022-10-03 00:18:28 +00:00
Juan Picado
25e0d4cb67 Update docusaurus.config.js 2022-10-02 22:12:30 +02:00
Juan Picado
7ab5f0167e chore(website): migrate GA 4 2022-10-02 09:18:45 +02:00
Juan Picado
07488a9ddb chore: fix website version deployment 2022-10-02 08:25:13 +02:00
Juan Picado
96d7295e55 chore: crowdin mapping (#3414)
* chore: crowdin mapping

* Update docusaurus.config.js

* Update docusaurus.config.js
2022-10-01 20:18:43 +02:00
verdacciobot
af7d493b80 chore: updated static data 2022-10-01 16:01:37 +00:00
Juan Picado
9d93a9a6b2 chore: update ci workflow 2022-10-01 17:59:23 +02:00
verdacciobot
4d44a91d89 chore: updated contributors list 2022-10-01 15:55:11 +00:00
Juan Picado
52be1eccb0 Update translations.yml 2022-10-01 17:49:12 +02:00
Juan Picado
00942e6d20 Update translations.yml 2022-10-01 17:46:55 +02:00
Juan Picado
0041d9407d chore: add versioning website (#3320)
* chore: add versioning website

* Update docusaurus.config.js

* chore: updated versions

* Update docusaurus.config.js
2022-10-01 17:21:26 +02:00
Juan Picado
3dc8591e26 chore: website small improvements (#3412)
* chore: website small improvements

* fix website build

* Update docusaurus.config.js
2022-10-01 14:08:40 +02:00
Juan Picado
ce013d2fcc refactor: reimplement star command (#3410) 2022-10-01 00:14:20 +02:00
Marc Bernard
6ad13de884 feat: display publication time on sidebar (#3408)
- Show the publication time next to the version on the sidebar
- Bonus: display the full date and time on hover
2022-09-30 19:53:46 +02:00
renovate[bot]
0783eeec1e fix(deps): update all core dependencies (master) (#3406)
* fix(deps): update all core dependencies

* chore: fix tests

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Juan Picado <juanpicado19@gmail.com>
2022-09-29 18:37:39 +02:00
verdacciobot
d4e6a6e2bf chore: updated contributors list 2022-09-29 00:21:55 +00:00
verdacciobot
0ebac198ce chore: updated contributors list 2022-09-28 07:30:12 +00:00
325 changed files with 9759 additions and 4270 deletions

View File

@@ -3,7 +3,6 @@
'@verdaccio/auth': major
'@verdaccio/cli': major
'@verdaccio/config': major
'@verdaccio/commons-api': major
'@verdaccio/core': major
'@verdaccio/local-storage': major
'@verdaccio/server-fastify': major
@@ -19,7 +18,6 @@
'@verdaccio/server': major
'@verdaccio/store': major
'@verdaccio/eslint-config': major
'@verdaccio/dev-types': major
'@verdaccio/utils': major
'verdaccio': major
'@verdaccio/web': major

View File

@@ -27,7 +27,6 @@
'@verdaccio/server': major
'@verdaccio/cli-standalone': major
'@verdaccio/store': major
'@verdaccio/dev-types': major
'@verdaccio/utils': major
'verdaccio': major
'@verdaccio/web': major

View File

@@ -3,7 +3,6 @@
'@verdaccio/auth': major
'@verdaccio/cli': major
'@verdaccio/config': major
'@verdaccio/commons-api': major
'@verdaccio/file-locking': major
'verdaccio-htpasswd': major
'@verdaccio/local-storage': major
@@ -25,7 +24,6 @@
'@verdaccio/proxy': major
'@verdaccio/server': major
'@verdaccio/store': major
'@verdaccio/dev-types': major
'@verdaccio/utils': major
'verdaccio': major
'@verdaccio/web': major

View File

@@ -3,7 +3,6 @@
'@verdaccio/auth': major
'@verdaccio/cli': major
'@verdaccio/config': major
'@verdaccio/commons-api': major
'@verdaccio/file-locking': major
'verdaccio-htpasswd': major
'@verdaccio/local-storage': major
@@ -19,7 +18,6 @@
'@verdaccio/proxy': major
'@verdaccio/server': major
'@verdaccio/store': major
'@verdaccio/dev-types': major
'@verdaccio/utils': major
'verdaccio': major
'@verdaccio/web': major

View File

@@ -6,7 +6,6 @@
'@verdaccio/auth': patch
'@verdaccio/cli': patch
'@verdaccio/config': patch
'@verdaccio/commons-api': patch
'@verdaccio/file-locking': patch
'verdaccio-htpasswd': patch
'@verdaccio/readme': patch
@@ -26,7 +25,6 @@
'verdaccio-memory': patch
'@verdaccio/proxy': patch
'@verdaccio/store': patch
'@verdaccio/dev-types': patch
'@verdaccio/utils': patch
'verdaccio': patch
'@verdaccio/web': patch

View File

@@ -3,7 +3,6 @@
'@verdaccio/auth': minor
'@verdaccio/cli': minor
'@verdaccio/config': minor
'@verdaccio/commons-api': minor
'@verdaccio/file-locking': minor
'verdaccio-htpasswd': minor
'@verdaccio/local-storage': minor
@@ -26,7 +25,6 @@
'@verdaccio/proxy': minor
'@verdaccio/server': minor
'@verdaccio/store': minor
'@verdaccio/dev-types': minor
'@verdaccio/utils': minor
'verdaccio': minor
'@verdaccio/web': minor

View File

@@ -3,7 +3,6 @@
'@verdaccio/auth': minor
'@verdaccio/cli': minor
'@verdaccio/config': minor
'@verdaccio/commons-api': minor
'@verdaccio/file-locking': minor
'verdaccio-htpasswd': minor
'@verdaccio/local-storage': minor
@@ -19,7 +18,6 @@
'@verdaccio/proxy': minor
'@verdaccio/server': minor
'@verdaccio/store': minor
'@verdaccio/dev-types': minor
'@verdaccio/utils': minor
'verdaccio': minor
'@verdaccio/web': minor

View File

@@ -54,7 +54,8 @@
"@verdaccio/local-publish": "0.0.1",
"@verdaccio/e2e-cli-npm9": "1.0.1-6-next.5",
"@verdaccio/e2e-ui": "2.0.0-6-next.3",
"customprefix-auth": "0.0.1"
"customprefix-auth": "0.0.1",
"@verdaccio/crowdin-translations": "1.0.0"
},
"changesets": [
"afraid-mice-obey",
@@ -114,6 +115,7 @@
"proud-jobs-hope",
"red-chefs-float",
"red-yaks-sell",
"rich-badgers-begin",
"rich-ghosts-rule",
"shaggy-carrots-unite",
"shaggy-parrots-smash",
@@ -138,6 +140,7 @@
"tiny-seals-join",
"tricky-taxis-watch",
"two-dolls-check",
"unlucky-hairs-wonder",
"wild-jokes-beam",
"witty-ties-speak"
]

View File

@@ -3,7 +3,6 @@
'@verdaccio/auth': patch
'@verdaccio/cli': patch
'@verdaccio/config': patch
'@verdaccio/commons-api': patch
'@verdaccio/file-locking': patch
'verdaccio-htpasswd': patch
'@verdaccio/local-storage': patch
@@ -19,7 +18,6 @@
'@verdaccio/proxy': patch
'@verdaccio/server': patch
'@verdaccio/store': patch
'@verdaccio/dev-types': patch
'@verdaccio/utils': patch
'verdaccio': patch
'@verdaccio/web': patch

View File

@@ -0,0 +1,9 @@
---
'@verdaccio/api': minor
'@verdaccio/url': minor
'@verdaccio/store': minor
'@verdaccio/test-helper': minor
'verdaccio': minor
---
refactor: npm star command support reimplemented

View File

@@ -1,7 +1,6 @@
---
'@verdaccio/types': major
'@verdaccio/ui-theme': major
'@verdaccio/dev-types': major
'@verdaccio/web': major
'@verdaccio/e2e-ui': major
---

View File

@@ -3,7 +3,6 @@
'@verdaccio/auth': patch
'@verdaccio/cli': patch
'@verdaccio/config': patch
'@verdaccio/commons-api': patch
'@verdaccio/file-locking': patch
'verdaccio-htpasswd': patch
'@verdaccio/local-storage': patch
@@ -18,7 +17,6 @@
'@verdaccio/proxy': patch
'@verdaccio/server': patch
'@verdaccio/store': patch
'@verdaccio/dev-types': patch
'@verdaccio/utils': patch
'verdaccio': patch
---

View File

@@ -0,0 +1,5 @@
---
'@verdaccio/ui-theme': patch
---
feat: Display publication time on sidebar

View File

@@ -18,8 +18,8 @@ jobs:
name: Prepare build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3
- uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93 # tag=v3
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
- uses: actions/setup-node@969bd2663942d722d85b6a8626225850c2f7be4b # tag=v3
with:
node-version: 16.x
- name: install pnpm
@@ -33,7 +33,7 @@ jobs:
- name: install dependencies
run: pnpm install
- name: Cache .pnpm-store
uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77 # tag=v3
uses: actions/cache@56461b9eb0f8438fd15c7a9968e3c9ebb18ceff1 # tag=v3
with:
path: ~/.pnpm-store
key: pnpm-${{ hashFiles('pnpm-lock.yaml') }}
@@ -65,8 +65,8 @@ jobs:
name: Benchmark autocannon
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3
- uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93 # tag=v3
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
- uses: actions/setup-node@969bd2663942d722d85b6a8626225850c2f7be4b # tag=v3
with:
node-version: 16.x
- uses: actions/download-artifact@fb598a63ae348fa914e94cd0ff38f362e927b741 # tag=v3
@@ -77,7 +77,7 @@ jobs:
- name: install pnpm
# require fixed version
run: sudo npm i pnpm@latest-6 -g
- uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77 # tag=v3
- uses: actions/cache@56461b9eb0f8438fd15c7a9968e3c9ebb18ceff1 # tag=v3
with:
path: ~/.pnpm-store
key: pnpm-${{ hashFiles('pnpm-lock.yaml') }}
@@ -126,8 +126,8 @@ jobs:
name: Benchmark hyperfine
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3
- uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93 # tag=v3
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
- uses: actions/setup-node@969bd2663942d722d85b6a8626225850c2f7be4b # tag=v3
with:
node-version: 16.x
- uses: actions/download-artifact@fb598a63ae348fa914e94cd0ff38f362e927b741 # tag=v3
@@ -138,7 +138,7 @@ jobs:
- name: install pnpm
# require fixed version
run: sudo npm i pnpm@latest-6 -g
- uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77 # tag=v3
- uses: actions/cache@56461b9eb0f8438fd15c7a9968e3c9ebb18ceff1 # tag=v3
with:
path: ~/.pnpm-store
key: pnpm-${{ hashFiles('pnpm-lock.yaml') }}

View File

@@ -20,12 +20,12 @@ jobs:
if: github.ref == 'refs/heads/master' && github.repository == 'verdaccio/verdaccio'
steps:
- name: checkout code repository
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
with:
fetch-depth: 0
- name: setup node.js
uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93 # tag=v3
uses: actions/setup-node@969bd2663942d722d85b6a8626225850c2f7be4b # tag=v3
with:
node-version: 14
registry-url: 'https://registry.npmjs.org'
@@ -47,8 +47,7 @@ jobs:
env:
NODE_AUTH_TOKEN: ${{ secrets.REGISTRY_AUTH_TOKEN }}
- name: crowdin download
env:
CROWDIN_VERDACCIO_PROJECT_ID: ${{ secrets.CROWDIN_VERDACCIO_PROJECT_ID }}
env:
CROWDIN_VERDACCIO_API_KEY: ${{ secrets.CROWDIN_VERDACCIO_API_KEY }}
CONTEXT: production
run: pnpm crowdin:download

View File

@@ -27,9 +27,9 @@ jobs:
env:
NODE_ENV: production
steps:
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
- name: Node
uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93 # tag=v3
uses: actions/setup-node@969bd2663942d722d85b6a8626225850c2f7be4b # tag=v3
with:
node-version-file: '.nvmrc'
- name: Install pnpm
@@ -41,7 +41,7 @@ jobs:
- name: Install
run: pnpm recursive install --frozen-lockfile --registry http://localhost:4873
- name: Cache .pnpm-store
uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77 # tag=v3
uses: actions/cache@56461b9eb0f8438fd15c7a9968e3c9ebb18ceff1 # tag=v3
with:
path: ~/.pnpm-store
key: pnpm-${{ hashFiles('pnpm-lock.yaml') }}
@@ -52,14 +52,14 @@ jobs:
name: Lint
needs: prepare
steps:
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
- name: Node
uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93 # tag=v3
uses: actions/setup-node@969bd2663942d722d85b6a8626225850c2f7be4b # tag=v3
with:
node-version-file: '.nvmrc'
- name: Install pnpm
run: npm i pnpm@6.32.15 -g
- uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77 # tag=v3
- uses: actions/cache@56461b9eb0f8438fd15c7a9968e3c9ebb18ceff1 # tag=v3
with:
path: ~/.pnpm-store
key: pnpm-${{ hashFiles('pnpm-lock.yaml') }}
@@ -72,14 +72,14 @@ jobs:
name: Format
needs: prepare
steps:
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
- name: Use Node
uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93 # tag=v3
uses: actions/setup-node@969bd2663942d722d85b6a8626225850c2f7be4b # tag=v3
with:
node-version-file: '.nvmrc'
- name: Install pnpm
run: npm i pnpm@6.32.15 -g
- uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77 # tag=v3
- uses: actions/cache@56461b9eb0f8438fd15c7a9968e3c9ebb18ceff1 # tag=v3
with:
path: ~/.pnpm-store
key: pnpm-${{ hashFiles('pnpm-lock.yaml') }}
@@ -97,14 +97,14 @@ jobs:
name: ${{ matrix.os }} / Node ${{ matrix.node_version }}
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
- name: Use Node ${{ matrix.node_version }}
uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93 # tag=v3
uses: actions/setup-node@969bd2663942d722d85b6a8626225850c2f7be4b # tag=v3
with:
node-version: ${{ matrix.node_version }}
- name: Install pnpm
run: npm i pnpm@6.32.15 -g
- uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77 # tag=v3
- uses: actions/cache@56461b9eb0f8438fd15c7a9968e3c9ebb18ceff1 # tag=v3
with:
path: ~/.pnpm-store
key: pnpm-${{ hashFiles('pnpm-lock.yaml') }}
@@ -119,13 +119,13 @@ jobs:
runs-on: ubuntu-latest
name: UI Test E2E
steps:
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3
- uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93 # tag=v3
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
- uses: actions/setup-node@969bd2663942d722d85b6a8626225850c2f7be4b # tag=v3
with:
node-version-file: '.nvmrc'
- name: Install pnpm
run: npm i pnpm@6.32.15 -g
- uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77 # tag=v3
- uses: actions/cache@56461b9eb0f8438fd15c7a9968e3c9ebb18ceff1 # tag=v3
with:
path: ~/.pnpm-store
key: pnpm-${{ hashFiles('pnpm-lock.yaml') }}
@@ -143,13 +143,13 @@ jobs:
name: synchronize translations
if: (github.event_name == 'push' && github.ref == 'refs/heads/master') || github.event_name == 'workflow_dispatch'
steps:
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3
- uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93 # tag=v3
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
- uses: actions/setup-node@969bd2663942d722d85b6a8626225850c2f7be4b # tag=v3
with:
node-version-file: '.nvmrc'
- name: Install pnpm
run: npm i pnpm@6.32.15 -g
- uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77 # tag=v3
- uses: actions/cache@56461b9eb0f8438fd15c7a9968e3c9ebb18ceff1 # tag=v3
with:
path: ~/.pnpm-store
key: pnpm-${{ hashFiles('pnpm-lock.yaml') }}
@@ -162,7 +162,6 @@ jobs:
run: pnpm write-translations --filter ...@verdaccio/website
- name: sync
env:
CROWDIN_VERDACCIO_PROJECT_ID: ${{ secrets.CROWDIN_VERDACCIO_PROJECT_ID }}
CROWDIN_VERDACCIO_API_KEY: ${{ secrets.CROWDIN_VERDACCIO_API_KEY }}
CONTEXT: production
run: pnpm crowdin:sync

View File

@@ -21,7 +21,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
@@ -34,7 +34,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@86f3159a697a097a813ad9bfa0002412d97690a4 # tag=v2
uses: github/codeql-action/init@807578363a7869ca324a79039e6db9c843e0e100 # tag=v2
# Override language selection by uncommenting this and choosing your languages
# with:
@@ -42,7 +42,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@86f3159a697a097a813ad9bfa0002412d97690a4 # tag=v2
uses: github/codeql-action/autobuild@807578363a7869ca324a79039e6db9c843e0e100 # tag=v2
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -56,4 +56,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@86f3159a697a097a813ad9bfa0002412d97690a4 # tag=v2
uses: github/codeql-action/analyze@807578363a7869ca324a79039e6db9c843e0e100 # tag=v2

View File

@@ -19,7 +19,7 @@ jobs:
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
- uses: docker/setup-qemu-action@27d0a4f181a40b142cce983c5393082c365d1480 # tag=v1
- uses: docker/setup-buildx-action@v1
with:

View File

@@ -15,9 +15,9 @@ jobs:
env:
NODE_ENV: production
steps:
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
- name: Use Node
uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93 # tag=v3
uses: actions/setup-node@969bd2663942d722d85b6a8626225850c2f7be4b # tag=v3
with:
node-version-file: '.nvmrc'
- name: Install pnpm
@@ -29,7 +29,7 @@ jobs:
- name: Install
run: pnpm recursive install --frozen-lockfile --reporter=silence --ignore-scripts --registry http://localhost:4873
- name: Cache .pnpm-store
uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77 # tag=v3
uses: actions/cache@56461b9eb0f8438fd15c7a9968e3c9ebb18ceff1 # tag=v3
with:
path: ~/.pnpm-store
key: pnpm-${{ hashFiles('pnpm-lock.yaml') }}-${{ github.run_id }}-${{ github.sha }}
@@ -39,14 +39,14 @@ jobs:
needs: [prepare]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
- name: Use Node 16
uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93 # tag=v3
uses: actions/setup-node@969bd2663942d722d85b6a8626225850c2f7be4b # tag=v3
with:
node-version-file: '.nvmrc'
- name: Install pnpm
run: npm i pnpm@6.32.15 -g
- uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77 # tag=v3
- uses: actions/cache@56461b9eb0f8438fd15c7a9968e3c9ebb18ceff1 # tag=v3
with:
path: ~/.pnpm-store
key: pnpm-${{ hashFiles('pnpm-lock.yaml') }}-${{ github.run_id }}-${{ github.sha }}
@@ -55,7 +55,7 @@ jobs:
- name: build
run: pnpm build
- name: Cache packages
uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77 # tag=v3
uses: actions/cache@56461b9eb0f8438fd15c7a9968e3c9ebb18ceff1 # tag=v3
id: cache-packages
with:
path: ./packages/
@@ -63,7 +63,7 @@ jobs:
restore-keys: |
packages-
# - name: Cache test
# uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77 # tag=v3
# uses: actions/cache@56461b9eb0f8438fd15c7a9968e3c9ebb18ceff1 # tag=v3
# id: cache-test
# with:
# path: ./e2e/
@@ -79,23 +79,23 @@ jobs:
name: ${{ matrix.pkg }} / ${{ matrix.os }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3
- uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93 # tag=v3
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
- uses: actions/setup-node@969bd2663942d722d85b6a8626225850c2f7be4b # tag=v3
with:
node-version-file: '.nvmrc'
- name: Install pnpm
run: npm i pnpm@6.32.15 -g
- uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77 # tag=v3
- uses: actions/cache@56461b9eb0f8438fd15c7a9968e3c9ebb18ceff1 # tag=v3
with:
path: ~/.pnpm-store
key: pnpm-${{ hashFiles('pnpm-lock.yaml') }}-${{ github.run_id }}-${{ github.sha }}
- name: Install
run: pnpm recursive install --offline --frozen-lockfile --reporter=silence --ignore-scripts --registry http://localhost:4873
- uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77 # tag=v3
- uses: actions/cache@56461b9eb0f8438fd15c7a9968e3c9ebb18ceff1 # tag=v3
with:
path: ./packages/
key: pkg-${{ hashFiles('pnpm-lock.yaml') }}-${{ github.run_id }}-${{ github.sha }}
# - uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77 # tag=v3
# - uses: actions/cache@56461b9eb0f8438fd15c7a9968e3c9ebb18ceff1 # tag=v3
# with:
# path: ./e2e/
# key: test-${{ hashFiles('pnpm-lock.yaml') }}-${{ github.run_id }}-${{ github.sha }}

View File

@@ -1,5 +1,5 @@
---
name: contributors
name: static data
on:
workflow_dispatch:
@@ -15,13 +15,13 @@ jobs:
name: Run script
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
with:
persist-credentials: false
fetch-depth: 0
- uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93 # tag=v3
- uses: actions/setup-node@969bd2663942d722d85b6a8626225850c2f7be4b # tag=v3
with:
node-version: 17.x
node-version: 18.x
- name: install pnpm
run: sudo npm i pnpm@latest-6 -g
- name: set store
@@ -32,17 +32,23 @@ jobs:
run: pnpm config set registry https://registry.verdaccio.org
- name: install dependencies
run: pnpm install
- name: Build Translations percentage
run: pnpm build --filter "@verdaccio/crowdin-translations"
- name: update contributors
run: pnpm run contributors
env:
TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: update translations
run: pnpm run translations
env:
TOKEN: ${{ secrets.CROWDIN_VERDACCIO_API_KEY }}
- name: format
run: pnpm format
- name: Commit & Push changes
uses: actions-js/push@a52398fac807b0c1e5f1492c969b477c8560a0ba # tag=v1.3
with:
github_token: ${{ secrets.TOKEN_VERDACCIOBOT_GITHUB }}
message: "chore: updated contributors list"
message: "chore: updated static data"
branch: master
author_email: verdaccio.npm@gmail.com
author_name: verdacciobot

View File

@@ -14,15 +14,15 @@ jobs:
env:
NODE_OPTIONS: --max_old_space_size=4096
steps:
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
- name: Use Node 16
uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93 # tag=v3
uses: actions/setup-node@969bd2663942d722d85b6a8626225850c2f7be4b # tag=v3
with:
node-version: 16
- name: Cache pnpm modules
uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77 # tag=v3
uses: actions/cache@56461b9eb0f8438fd15c7a9968e3c9ebb18ceff1 # tag=v3
env:
cache-name: cache-pnpm-modules
with:
@@ -37,10 +37,12 @@ jobs:
run_install: |
- recursive: true
args: [--frozen-lockfile]
- name: Build Plugins
run: pnpm build --filter "docusaurus-plugin-contributors"
- name: Build
run: pnpm build
- name: Build Translations percentage
run: pnpm build --filter "@verdaccio/crowdin-translations"
- name: Cache Docusaurus Build
uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77 # tag=v3
uses: actions/cache@56461b9eb0f8438fd15c7a9968e3c9ebb18ceff1 # tag=v3
with:
path: website/node_modules/.cache/webpack
key: cache/webpack-${{github.ref}}-${{ hashFiles('**/pnpm-lock.yaml') }}
@@ -52,8 +54,8 @@ jobs:
- name: Build Production
if: (github.event_name == 'push' && github.ref == 'refs/heads/master') || github.event_name == 'workflow_dispatch'
env:
CROWDIN_VERDACCIO_PROJECT_ID: ${{ secrets.CROWDIN_VERDACCIO_PROJECT_ID }}
CROWDIN_VERDACCIO_API_KEY: ${{ secrets.CROWDIN_VERDACCIO_API_KEY }}
SENTRY_KEY: ${{ secrets.SENTRY_KEY }}
CONTEXT: production
run: pnpm netlify:build:production --filter ...@verdaccio/website
@@ -98,7 +100,7 @@ jobs:
- name: Format lighthouse score
id: format_lighthouse_score
uses: actions/github-script@c713e510dbd7d213d92d41b7a7805a986f4c5c66 # tag=v6
uses: actions/github-script@7dff1a87643417cf3b95bb10b29f4c4bc60d8ebd # tag=v6
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |

10
.gitignore vendored
View File

@@ -49,5 +49,11 @@ api-results*.json
.clinic/
#docs
./api
**/docs/**
website/docs/api/**/*.md
website/docs/api/**/*.yml
!website/docs/api/index.md
packages/**/docs
# cypress
e2e/ui/cypress/videos/**/*
e2e/ui/cypress/screenshots/**/*

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -1,20 +1,19 @@
project_id_env: CROWDIN_VERDACCIO_PROJECT_ID
project_id: 295539
api_token_env: CROWDIN_VERDACCIO_API_KEY
# token local testing
# api_token: token_secret
preserve_hierarchy: true
files:
[
{
source: 'packages/plugins/ui-theme/src/i18n/crowdin/**/*',
translation: '/packages/plugins/ui-theme/src/i18n/download_translations/%locale%/**/%original_file_name%',
},
{
source: '/website/i18n/en/**/*',
translation: '/website/i18n/%locale%/**/%original_file_name%',
},
{
source: '/website/docs/**/*',
translation: '/website/i18n/%locale%/docusaurus-plugin-content-docs/current/**/%original_file_name%',
}
]
- source: /packages/plugins/ui-theme/src/i18n/crowdin/*.json
translation: '/packages/plugins/ui-theme/src/i18n/download_translations/%locale%/%original_file_name%'
- source: /website/i18n/en/**/*
translation: '/website/i18n/%locale%/**/%original_file_name%'
- source: /website/docs/**/*
translation: '/website/i18n/%locale%/docusaurus-plugin-content-docs/current/**/%original_file_name%'
ignore: [/website/docs/api/**/*]
- source: /website/versioned_docs/**/*
translation: /website/i18n/%locale%/docusaurus-plugin-content-docs/**/%original_file_name%
ignore: [/website/versioned_docs/version-5.x/api/**/*]

3
docs/plugins.md Normal file
View File

@@ -0,0 +1,3 @@
## Developing plugins
TBA

View File

@@ -3,4 +3,4 @@
This folder is composed of two strategies:
- E2E for CLI
- E2E for the UI (puppeteer)
- E2E for the UI (cypress)

View File

@@ -19,6 +19,7 @@
| deprecate | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⛔ | ⛔ | ⛔ | ⛔ |
| ping | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⛔ | ⛔ | ⛔ | ⛔ |
| search | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⛔ | ⛔ | ⛔ | ⛔ |
| star | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⛔ | ⛔ | ⛔ | ⛔ |
| dist-tag | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
> notes:

View File

@@ -5,9 +5,9 @@
"main": "./build/index.js",
"types": "./build/index.d.ts",
"devDependencies": {
"verdaccio": "workspace:6.0.0-6-next.48",
"@verdaccio/core": "workspace:6.0.0-6-next.48",
"@verdaccio/config": "workspace:6.0.0-6-next.48",
"verdaccio": "workspace:6.0.0-6-next.49",
"@verdaccio/core": "workspace:6.0.0-6-next.49",
"@verdaccio/config": "workspace:6.0.0-6-next.49",
"@verdaccio/types": "workspace:11.0.0-6-next.17",
"yaml": "2.1.1",
"debug": "4.3.4",

View File

@@ -0,0 +1,69 @@
import {
addRegistry,
initialSetup,
npmUtils,
prepareGenericEmptyProject,
} from '@verdaccio/test-cli-commons';
import { npm } from './utils';
describe('star a package', () => {
jest.setTimeout(20000);
let registry;
beforeAll(async () => {
const setup = await initialSetup();
registry = setup.registry;
await registry.init();
});
test.each([['@verdaccio/foo']])('should star a package %s', async (pkgName) => {
const { tempFolder } = await prepareGenericEmptyProject(
pkgName,
'1.0.0-patch',
registry.port,
registry.getToken(),
registry.getRegistryUrl()
);
await npmUtils.publish(npm, tempFolder, pkgName, registry);
const resp = await npm(
{ cwd: tempFolder },
'star',
pkgName,
...addRegistry(registry.getRegistryUrl())
);
expect(resp.stdout).toEqual(`${pkgName}`);
});
test.each([['@verdaccio/bar']])('should unstar a package %s', async (pkgName) => {
const { tempFolder } = await prepareGenericEmptyProject(
pkgName,
'1.0.0-patch',
registry.port,
registry.getToken(),
registry.getRegistryUrl()
);
await npmUtils.publish(npm, tempFolder, pkgName, registry);
const resp = await npm(
{ cwd: tempFolder },
'star',
pkgName,
...addRegistry(registry.getRegistryUrl())
);
expect(resp.stdout).toEqual(`${pkgName}`);
const resp1 = await npm(
{ cwd: tempFolder },
'unstar',
pkgName,
...addRegistry(registry.getRegistryUrl())
);
expect(resp1.stdout).toEqual(`${pkgName}`);
});
afterAll(async () => {
registry.stop();
});
});

View File

@@ -0,0 +1,69 @@
import {
addRegistry,
initialSetup,
npmUtils,
prepareGenericEmptyProject,
} from '@verdaccio/test-cli-commons';
import { npm } from './utils';
describe('star a package', () => {
jest.setTimeout(20000);
let registry;
beforeAll(async () => {
const setup = await initialSetup();
registry = setup.registry;
await registry.init();
});
test.each([['@verdaccio/foo']])('should star a package %s', async (pkgName) => {
const { tempFolder } = await prepareGenericEmptyProject(
pkgName,
'1.0.0-patch',
registry.port,
registry.getToken(),
registry.getRegistryUrl()
);
await npmUtils.publish(npm, tempFolder, pkgName, registry);
const resp = await npm(
{ cwd: tempFolder },
'star',
pkgName,
...addRegistry(registry.getRegistryUrl())
);
expect(resp.stdout).toEqual(`${pkgName}`);
});
test.each([['@verdaccio/bar']])('should unstar a package %s', async (pkgName) => {
const { tempFolder } = await prepareGenericEmptyProject(
pkgName,
'1.0.0-patch',
registry.port,
registry.getToken(),
registry.getRegistryUrl()
);
await npmUtils.publish(npm, tempFolder, pkgName, registry);
const resp = await npm(
{ cwd: tempFolder },
'star',
pkgName,
...addRegistry(registry.getRegistryUrl())
);
expect(resp.stdout).toEqual(`${pkgName}`);
const resp1 = await npm(
{ cwd: tempFolder },
'unstar',
pkgName,
...addRegistry(registry.getRegistryUrl())
);
expect(resp1.stdout).toEqual(`${pkgName}`);
});
afterAll(async () => {
registry.stop();
});
});

View File

@@ -0,0 +1,69 @@
import {
addRegistry,
initialSetup,
npmUtils,
prepareGenericEmptyProject,
} from '@verdaccio/test-cli-commons';
import { npm } from './utils';
describe('star a package', () => {
jest.setTimeout(20000);
let registry;
beforeAll(async () => {
const setup = await initialSetup();
registry = setup.registry;
await registry.init();
});
test.each([['@verdaccio/foo']])('should star a package %s', async (pkgName) => {
const { tempFolder } = await prepareGenericEmptyProject(
pkgName,
'1.0.0-patch',
registry.port,
registry.getToken(),
registry.getRegistryUrl()
);
await npmUtils.publish(npm, tempFolder, pkgName, registry);
const resp = await npm(
{ cwd: tempFolder },
'star',
pkgName,
...addRegistry(registry.getRegistryUrl())
);
expect(resp.stdout).toEqual(`${pkgName}`);
});
test.each([['@verdaccio/bar']])('should unstar a package %s', async (pkgName) => {
const { tempFolder } = await prepareGenericEmptyProject(
pkgName,
'1.0.0-patch',
registry.port,
registry.getToken(),
registry.getRegistryUrl()
);
await npmUtils.publish(npm, tempFolder, pkgName, registry);
const resp = await npm(
{ cwd: tempFolder },
'star',
pkgName,
...addRegistry(registry.getRegistryUrl())
);
expect(resp.stdout).toEqual(`${pkgName}`);
const resp1 = await npm(
{ cwd: tempFolder },
'unstar',
pkgName,
...addRegistry(registry.getRegistryUrl())
);
expect(resp1.stdout).toEqual(`${pkgName}`);
});
afterAll(async () => {
registry.stop();
});
});

View File

@@ -0,0 +1,69 @@
import {
addRegistry,
initialSetup,
npmUtils,
prepareGenericEmptyProject,
} from '@verdaccio/test-cli-commons';
import { npm } from './utils';
describe('star a package', () => {
jest.setTimeout(20000);
let registry;
beforeAll(async () => {
const setup = await initialSetup();
registry = setup.registry;
await registry.init();
});
test.each([['@verdaccio/foo']])('should star a package %s', async (pkgName) => {
const { tempFolder } = await prepareGenericEmptyProject(
pkgName,
'1.0.0-patch',
registry.port,
registry.getToken(),
registry.getRegistryUrl()
);
await npmUtils.publish(npm, tempFolder, pkgName, registry);
const resp = await npm(
{ cwd: tempFolder },
'star',
pkgName,
...addRegistry(registry.getRegistryUrl())
);
expect(resp.stdout).toEqual(`${pkgName}`);
});
test.each([['@verdaccio/bar']])('should unstar a package %s', async (pkgName) => {
const { tempFolder } = await prepareGenericEmptyProject(
pkgName,
'1.0.0-patch',
registry.port,
registry.getToken(),
registry.getRegistryUrl()
);
await npmUtils.publish(npm, tempFolder, pkgName, registry);
const resp = await npm(
{ cwd: tempFolder },
'star',
pkgName,
...addRegistry(registry.getRegistryUrl())
);
expect(resp.stdout).toEqual(`${pkgName}`);
const resp1 = await npm(
{ cwd: tempFolder },
'unstar',
pkgName,
...addRegistry(registry.getRegistryUrl())
);
expect(resp1.stdout).toEqual(`${pkgName}`);
});
afterAll(async () => {
registry.stop();
});
});

View File

@@ -0,0 +1,69 @@
import {
addRegistry,
initialSetup,
pnpmUtils,
prepareGenericEmptyProject,
} from '@verdaccio/test-cli-commons';
import { pnpm } from './utils';
describe('star a package', () => {
jest.setTimeout(20000);
let registry;
beforeAll(async () => {
const setup = await initialSetup();
registry = setup.registry;
await registry.init();
});
test.each([['@verdaccio/foo']])('should star a package %s', async (pkgName) => {
const { tempFolder } = await prepareGenericEmptyProject(
pkgName,
'1.0.0-patch',
registry.port,
registry.getToken(),
registry.getRegistryUrl()
);
await pnpmUtils.publish(pnpm, tempFolder, pkgName, registry);
const resp = await pnpm(
{ cwd: tempFolder },
'star',
pkgName,
...addRegistry(registry.getRegistryUrl())
);
expect(resp.stdout).toEqual(`${pkgName}`);
});
test.each([['@verdaccio/bar']])('should unstar a package %s', async (pkgName) => {
const { tempFolder } = await prepareGenericEmptyProject(
pkgName,
'1.0.0-patch',
registry.port,
registry.getToken(),
registry.getRegistryUrl()
);
await pnpmUtils.publish(pnpm, tempFolder, pkgName, registry);
const resp = await pnpm(
{ cwd: tempFolder },
'star',
pkgName,
...addRegistry(registry.getRegistryUrl())
);
expect(resp.stdout).toEqual(`${pkgName}`);
const resp1 = await pnpm(
{ cwd: tempFolder },
'unstar',
pkgName,
...addRegistry(registry.getRegistryUrl())
);
expect(resp1.stdout).toEqual(`${pkgName}`);
});
afterAll(async () => {
registry.stop();
});
});

View File

@@ -0,0 +1,69 @@
import {
addRegistry,
initialSetup,
pnpmUtils,
prepareGenericEmptyProject,
} from '@verdaccio/test-cli-commons';
import { pnpm } from './utils';
describe('star a package', () => {
jest.setTimeout(20000);
let registry;
beforeAll(async () => {
const setup = await initialSetup();
registry = setup.registry;
await registry.init();
});
test.each([['@verdaccio/foo']])('should star a package %s', async (pkgName) => {
const { tempFolder } = await prepareGenericEmptyProject(
pkgName,
'1.0.0-patch',
registry.port,
registry.getToken(),
registry.getRegistryUrl()
);
await pnpmUtils.publish(pnpm, tempFolder, pkgName, registry);
const resp = await pnpm(
{ cwd: tempFolder },
'star',
pkgName,
...addRegistry(registry.getRegistryUrl())
);
expect(resp.stdout).toEqual(`${pkgName}`);
});
test.each([['@verdaccio/bar']])('should unstar a package %s', async (pkgName) => {
const { tempFolder } = await prepareGenericEmptyProject(
pkgName,
'1.0.0-patch',
registry.port,
registry.getToken(),
registry.getRegistryUrl()
);
await pnpmUtils.publish(pnpm, tempFolder, pkgName, registry);
const resp = await pnpm(
{ cwd: tempFolder },
'star',
pkgName,
...addRegistry(registry.getRegistryUrl())
);
expect(resp.stdout).toEqual(`${pkgName}`);
const resp1 = await pnpm(
{ cwd: tempFolder },
'unstar',
pkgName,
...addRegistry(registry.getRegistryUrl())
);
expect(resp1.stdout).toEqual(`${pkgName}`);
});
afterAll(async () => {
registry.stop();
});
});

View File

@@ -1,9 +0,0 @@
{
"globals": {
"VERDACCIO_API_URL": true,
"__DEBUG__": true
},
"env": {
"browser": true
}
}

View File

@@ -1,29 +0,0 @@
# @verdaccio/e2e-ui
## 2.0.0-6-next.3
### Major Changes
- 000d4374: feat: upgrade to material ui 5
## 1.1.0-6-next.2
### Minor Changes
- 154b2ecd: refactor: remove @verdaccio/commons-api in favor @verdaccio/core and remove duplications
## 1.1.0-6-next.1
### Minor Changes
- 1b217fd3: Some verdaccio modules depend on 'mkdirp' library which provides recursive directory creation functionality.
NodeJS can do this out of the box since v.10.12. The last commit in 'mkdirp' was made in early 2016, and it's mid 2021 now.
Time to stick with a built-in library solution!
- All 'mkdirp' calls are replaced with appropriate 'fs' calls.
## 1.0.1-alpha.0
### Patch Changes
- fecbb9be: chore: add release step to private regisry on merge changeset pr

View File

@@ -7,3 +7,7 @@
- Check navigation
- Check sidebar
- Check protected packages works
## Contribute
More tests could be added to verify UI works as expected.

View File

@@ -1,64 +0,0 @@
const { join } = require('path');
const { fileUtils } = require('@verdaccio/core');
const { parseConfigFile } = require('@verdaccio/config');
const { Registry, ServerQuery } = require('verdaccio');
describe('basic functionality', () => {
let registry1;
let page;
beforeAll(async () => {
const configProtected = parseConfigFile(join(__dirname, './config/config.yaml'));
const registry1storage = await fileUtils.createTempStorageFolder('storage-1');
const protectedRegistry = await Registry.fromConfigToPath({
...configProtected,
storage: registry1storage,
});
registry1 = new Registry(protectedRegistry.configPath);
await registry1.init();
const query1 = new ServerQuery(registry1.getRegistryUrl());
await query1.createUser('test', 'test');
page = await global.__BROWSER__.newPage();
await page.goto(`http://0.0.0.0:${registry1.getPort()}`);
// eslint-disable-next-line no-console
page.on('console', (msg) => console.log('PAGE LOG:', msg.text()));
});
afterAll(async () => {
await page.close();
registry1.stop();
});
// this might be increased based on the delays included in all test
jest.setTimeout(20000);
test('should display title', async () => {
const text = await page.title();
await page.waitForTimeout(1000);
expect(text).toContain('verdaccio-server-e2e');
});
test('should match title with no packages published', async () => {
const text = await page.evaluate(() => document.querySelector('#help-card__title').textContent);
expect(text).toMatch('No Package Published Yet.');
});
test('should match title with first step', async () => {
const text = await page.evaluate(() => document.querySelector('#help-card').textContent);
expect(text).toContain(`npm adduser --registry http://0.0.0.0:${registry1.getPort()}`);
});
test('should match title with second step', async () => {
const text = await page.evaluate(() => document.querySelector('#help-card').textContent);
expect(text).toContain(`npm publish --registry http://0.0.0.0:${registry1.getPort()}`);
});
test('should go to 404 page', async () => {
await page.goto(`http://0.0.0.0:${registry1.getPort()}/-/web/detail/@verdaccio/not-found`);
await page.waitForTimeout(500);
const text = await page.evaluate(() => document.querySelector('.not-found-text').textContent);
expect(text).toMatch("Sorry, we couldn't find it...");
});
});

60
e2e/ui/cypress.config.ts Normal file
View File

@@ -0,0 +1,60 @@
import { defineConfig } from 'cypress';
import { join } from 'path';
import { Registry, ServerQuery } from 'verdaccio';
import { parseConfigFile } from '@verdaccio/config';
import { HEADERS, fileUtils } from '@verdaccio/core';
import { generatePackageMetadata } from '@verdaccio/test-helper';
let registry1;
export default defineConfig({
e2e: {
setupNodeEvents(on) {
on('before:run', async () => {
const configProtected = parseConfigFile(join(__dirname, './config/config.yaml'));
const registry1storage = await fileUtils.createTempStorageFolder('storage-1');
const protectedRegistry = await Registry.fromConfigToPath({
...configProtected,
storage: registry1storage,
});
registry1 = new Registry(protectedRegistry.configPath, {
createUser: true,
credentials: { user: 'test', password: 'test' },
});
await registry1.init();
});
on('after:run', async () => {
registry1.stop();
});
on('task', {
publishScoped() {
const scopedPackageMetadata = generatePackageMetadata('pkg-scoped', '1.0.6');
const server = new ServerQuery(registry1.getRegistryUrl());
server
.putPackage(scopedPackageMetadata.name, scopedPackageMetadata, {
[HEADERS.AUTHORIZATION]: `Bearer ${registry1.getToken()}`,
})
.then(() => {});
return null;
},
publishProtected({ pkgName }) {
const protectedPackageMetadata = generatePackageMetadata(pkgName, '5.0.5');
const server = new ServerQuery(registry1.getRegistryUrl());
server
.putPackage(protectedPackageMetadata.name, protectedPackageMetadata, {
[HEADERS.AUTHORIZATION]: `Bearer ${registry1.getToken()}`,
})
.then(() => {});
},
registry() {
return {
registryUrl: registry1.getRegistryUrl(),
port: registry1.getPort(),
};
},
});
},
},
});

View File

@@ -0,0 +1,30 @@
describe('home spec', () => {
let ctx: any = {};
beforeEach(async () => {
// @ts-expect-error
const registry = await cy.task('registry');
ctx.url = registry.registryUrl;
});
it('title should be correct', () => {
cy.visit(ctx.url);
cy.location('pathname').should('include', '/');
cy.title().should('eq', 'verdaccio-server-e2e');
});
it('should match title with no packages published', () => {
cy.visit(ctx.url);
cy.getByTestId('help-card').contains('No Package Published Yet.');
});
it('should display instructions on help card', () => {
cy.visit(ctx.url);
cy.getByTestId('help-card').contains(`npm adduser --registry ${ctx.url}`);
cy.getByTestId('help-card').contains(`npm publish --registry ${ctx.url}`);
});
it('should go to 404 page', () => {
cy.visit(`${ctx.url}/-/web/detail/@verdaccio/not-found`);
cy.getByTestId('404').contains(`Sorry, we couldn't find it.`);
});
});

View File

@@ -0,0 +1,88 @@
describe('publish spec', () => {
let ctx: any = {};
const credentials = { user: 'test', password: 'test' };
beforeEach(async () => {
// @ts-expect-error
const registry = await cy.task('registry');
ctx.url = registry.registryUrl;
cy.intercept('POST', '/-/verdaccio/sec/login').as('sign');
cy.intercept('GET', '/-/verdaccio/data/packages').as('pkgs');
cy.intercept('GET', '/-/verdaccio/data/sidebar/pkg-scoped').as('sidebar');
cy.intercept('GET', '/-/verdaccio/data/package/readme/pkg-scoped').as('readme');
cy.task('publishScoped', { pkgName: 'pkg-protected' });
});
it('should have one published package', () => {
cy.visit(ctx.url);
cy.login(credentials.user, credentials.password);
cy.wait('@sign');
cy.getByTestId('package-title').should('have.length', 1);
});
it('should navigate to page detail', () => {
cy.visit(ctx.url);
cy.login(credentials.user, credentials.password);
cy.wait('@sign');
cy.wait('@pkgs');
cy.wait(300);
cy.getByTestId('package-title').click();
cy.wait('@sidebar');
cy.wait('@readme');
});
it('should have readme content', () => {
cy.visit(ctx.url);
cy.login(credentials.user, credentials.password);
cy.wait('@sign');
cy.wait('@pkgs');
cy.getByTestId('package-title').click();
cy.wait('@sidebar');
cy.wait('@readme');
cy.get('.markdown-body').should('have.length', 1);
cy.contains('.markdown-body', /test/);
});
it('should click on dependencies tab', () => {
cy.visit(ctx.url);
cy.login(credentials.user, credentials.password);
cy.wait('@sign');
cy.wait('@pkgs');
cy.getByTestId('package-title').click();
cy.wait('@sidebar');
cy.wait('@readme');
cy.getByTestId('dependencies-tab').click();
cy.wait(100);
cy.getByTestId('dependencies').should('have.length', 1);
cy.getByTestId('verdaccio')
.children()
.invoke('text')
.should('match', /verdaccio/);
cy.screenshot();
});
it('should click on versions tab', () => {
cy.visit(ctx.url);
cy.login(credentials.user, credentials.password);
cy.wait('@sign');
cy.wait('@pkgs');
cy.getByTestId('package-title').click();
cy.wait('@sidebar');
cy.wait('@readme');
cy.getByTestId('versions-tab').click();
cy.getByTestId('tag-latest').children().invoke('text').should('match', /1.0.6/);
cy.screenshot();
});
it('should click on uplinks tab', () => {
cy.visit(ctx.url);
cy.login(credentials.user, credentials.password);
cy.wait('@sign');
cy.wait('@pkgs');
cy.getByTestId('package-title').click();
cy.wait('@sidebar');
cy.wait('@readme');
cy.getByTestId('uplinks-tab').click();
cy.getByTestId('no-uplinks').should('be.visible');
cy.screenshot();
});
});

View File

@@ -0,0 +1,31 @@
describe('sign spec', () => {
let ctx: any = {};
const credentials = { user: 'test', password: 'test' };
beforeEach(async () => {
// @ts-expect-error
const registry = await cy.task('registry');
ctx.url = registry.registryUrl;
cy.intercept('POST', '/-/verdaccio/sec/login').as('sign');
});
it('should login user', () => {
cy.visit(ctx.url);
cy.login(credentials.user, credentials.password);
cy.wait('@sign');
cy.getByTestId('logInDialogIcon').click();
cy.wait(100);
cy.getByTestId('greetings-label').contains(credentials.user);
});
it('should logout an user', () => {
cy.visit(ctx.url);
cy.login(credentials.user, credentials.password);
cy.wait('@sign');
cy.getByTestId('logInDialogIcon').click();
cy.wait(100);
cy.getByTestId('logOutDialogIcon').click();
cy.screenshot();
cy.getByTestId('header--button-login').contains('Login');
cy.screenshot();
});
});

View File

@@ -0,0 +1,37 @@
/* eslint-disable no-undef */
// / <reference types="cypress" />;
Cypress.Commands.add('getByTestId', (selector, ...args) => {
return cy.get(`[data-testid=${selector}]`, ...args);
});
// -- This is a parent command --
Cypress.Commands.add('login', (user, password) => {
cy.getByTestId('header--button-login').click();
cy.get('#login--dialog-username').type(user);
cy.get('#login--dialog-password').type(password);
cy.get('#login--dialog-button-submit').click();
});
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
//
// declare global {
// namespace Cypress {
// interface Chainable {
// login(email: string, password: string): Chainable<void>
// drag(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
// dismiss(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
// visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element>
// }
// }
// }

View File

@@ -0,0 +1,19 @@
// ***********************************************************
// This example support/e2e.ts is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands';
// Alternatively you can use CommonJS syntax:
// require('./commands')

View File

@@ -1,36 +0,0 @@
export const credentials = { user: 'test', password: 'test' };
export const clickElement = async function (page, selector, options = { delay: 100 }) {
const button = await page.$(selector);
await button.focus();
await button.click(options);
};
export const evaluateSignIn = async function (page, matchText = 'Login') {
const text = await page.evaluate(() => {
return document.querySelector('button[data-testid="header--button-login"]')?.textContent;
});
expect(text).toMatch(matchText);
};
export const getPackages = async function (page) {
return await page.$$('.package-title');
};
export const logIn = async function (page) {
await clickElement(page, 'div[data-testid="dialogContentLogin"]');
const userInput = await page.$('#login--dialog-username');
expect(userInput).not.toBeNull();
const passInput = await page.$('#login--dialog-password');
expect(passInput).not.toBeNull();
await userInput.type(credentials.user, { delay: 100 });
await passInput.type(credentials.password, { delay: 100 });
await passInput.dispose();
// click on log in
const loginButton = await page.$('#login--dialog-button-submit');
expect(loginButton).toBeDefined();
await loginButton.focus();
await loginButton.click({ delay: 100 });
await page.waitForTimeout(500);
};

View File

@@ -1,8 +0,0 @@
const config = require('../../jest/config');
module.exports = Object.assign({}, config, {
verbose: false,
collectCoverage: false,
globalSetup: './pre-setup.js',
globalTeardown: './teardown.js',
});

View File

@@ -3,17 +3,15 @@
"name": "@verdaccio/e2e-ui",
"version": "2.0.0-6-next.3",
"devDependencies": {
"verdaccio": "workspace:6.0.0-6-next.48",
"@verdaccio/core": "workspace:6.0.0-6-next.48",
"@verdaccio/config": "workspace:6.0.0-6-next.48",
"@verdaccio/test-helper": "workspace:2.0.0-6-next.5",
"verdaccio": "workspace:6.0.0-6-next.49",
"@verdaccio/core": "workspace:6.0.0-6-next.49",
"@verdaccio/config": "workspace:6.0.0-6-next.49",
"@verdaccio/test-helper": "workspace:2.0.0-6-next.6",
"debug": "4.3.4",
"colorette": "2.0.19",
"lodash": "^4.17.21",
"puppeteer": "17.1.3",
"rimraf": "3.0.2"
"cypress": "10.10.0"
},
"scripts": {
"test": "jest --runInBand"
"cypress:open": "cypress open",
"test": "cypress run"
}
}

View File

@@ -1,118 +0,0 @@
const { join } = require('path');
const { generatePackageMetadata } = require('@verdaccio/test-helper');
const { fileUtils, HEADERS } = require('@verdaccio/core');
const { parseConfigFile } = require('@verdaccio/config');
const { Registry, ServerQuery } = require('verdaccio');
const { getPackages } = require('./helper');
const protectedPackageMetadata = generatePackageMetadata('pkg-protected', '5.0.5');
const scopedPackageMetadata = generatePackageMetadata('pkg-scoped', '1.0.6');
describe('publish package', () => {
let registry1;
let page;
beforeAll(async () => {
const configProtected = parseConfigFile(join(__dirname, './config/config.yaml'));
const registry1storage = await fileUtils.createTempStorageFolder('storage-1');
const protectedRegistry = await Registry.fromConfigToPath({
...configProtected,
storage: registry1storage,
});
registry1 = new Registry(protectedRegistry.configPath, { createUser: true });
await registry1.init();
const query1 = new ServerQuery(registry1.getRegistryUrl());
await query1.createUser('test', 'test');
page = await global.__BROWSER__.newPage();
await page.goto(`http://0.0.0.0:${registry1.getPort()}`);
// eslint-disable-next-line no-console
page.on('console', (msg) => console.log('PAGE LOG:', msg.text()));
});
afterAll(async () => {
await page.close();
registry1.stop();
});
// this might be increased based on the delays included in all test
jest.setTimeout(20000);
test('should publish a package', async () => {
const server = new ServerQuery(registry1.getRegistryUrl());
await server.putPackage(scopedPackageMetadata.name, scopedPackageMetadata, {
[HEADERS.AUTHORIZATION]: `Bearer ${registry1.getToken()}`,
});
await page.waitForTimeout(1000);
await page.reload();
await page.waitForTimeout(1000);
const packagesList = await getPackages(page);
expect(packagesList).toHaveLength(1);
});
//
test('should navigate to the package detail', async () => {
const packagesList = await getPackages(page);
const firstPackage = packagesList[0];
await firstPackage.click({ delay: 200 });
await page.waitForTimeout(1000);
const readmeText = await page.evaluate(
() => document.querySelector('.markdown-body').textContent
);
expect(readmeText).toMatch('test');
});
test('should contains last sync information', async () => {
const versionList = await page.$$('.sidebar-info .detail-info');
expect(versionList).toHaveLength(1);
});
test('should display dependencies tab', async () => {
const dependenciesTab = await page.$$('#dependencies-tab');
expect(dependenciesTab).toHaveLength(1);
await dependenciesTab[0].click({ delay: 200 });
await page.waitForTimeout(1000);
const tags = await page.$$('.dep-tag');
const tag = tags[0];
const label = await page.evaluate((el) => el.innerText, tag);
expect(label).toMatch('verdaccio@');
});
test('should display version tab', async () => {
const versionsTab = await page.$$('#versions-tab');
expect(versionsTab).toHaveLength(1);
await versionsTab[0].click({ delay: 200 });
await page.waitForTimeout(1000);
const versionItems = await page.$$('.version-item');
expect(versionItems).toHaveLength(2);
});
test('should display uplinks tab', async () => {
const upLinksTab = await page.$$('#uplinks-tab');
expect(upLinksTab).toHaveLength(1);
await upLinksTab[0].click({ delay: 200 });
await page.waitForTimeout(1000);
});
test('should display readme tab', async () => {
const readmeTab = await page.$$('#readme-tab');
expect(readmeTab).toHaveLength(1);
await readmeTab[0].click({ delay: 200 });
await page.waitForTimeout(1000);
});
test('should publish a second package', async () => {
await page.goto(`http://0.0.0.0:${registry1.getPort()}`);
await page.waitForTimeout(500);
const server = new ServerQuery(registry1.getRegistryUrl());
await server.putPackage(protectedPackageMetadata.name, protectedPackageMetadata, {
[HEADERS.AUTHORIZATION]: `Bearer ${registry1.getToken()}`,
});
await page.waitForTimeout(500);
await page.reload();
await page.waitForTimeout(500);
const packagesList = await getPackages(page);
expect(packagesList).toHaveLength(2);
});
});

View File

@@ -1,80 +0,0 @@
const { join } = require('path');
const { fileUtils } = require('@verdaccio/core');
const { parseConfigFile } = require('@verdaccio/config');
const { Registry, ServerQuery } = require('verdaccio');
const { clickElement, logIn } = require('./helper');
describe('sign in user', () => {
let registry1;
let page;
beforeAll(async () => {
const configProtected = parseConfigFile(join(__dirname, './config/config.yaml'));
const registry1storage = await fileUtils.createTempStorageFolder('storage-1');
const protectedRegistry = await Registry.fromConfigToPath({
...configProtected,
storage: registry1storage,
});
registry1 = new Registry(protectedRegistry.configPath);
await registry1.init();
const query1 = new ServerQuery(registry1.getRegistryUrl());
await query1.createUser('test', 'test');
page = await global.__BROWSER__.newPage();
await page.goto(`http://0.0.0.0:${registry1.getPort()}`);
// eslint-disable-next-line no-console
page.on('console', (msg) => console.log('PAGE LOG:', msg.text()));
});
afterAll(async () => {
await page.close();
registry1.stop();
});
// this might be increased based on the delays included in all test
jest.setTimeout(20000);
const evaluateSignIn = async function (matchText = 'Login') {
const text = await page.evaluate(() => {
return document.querySelector('button[data-testid="header--button-login"]').textContent;
});
expect(text).toMatch(matchText);
};
test('should match button Login to sign in', async () => {
await evaluateSignIn();
});
test('should click on sign in button', async () => {
const signInButton = await page.$('button[data-testid="header--button-login"]');
await signInButton.click();
await page.waitForTimeout(1000);
const signInDialog = await page.$('#login--dialog');
expect(signInDialog).not.toBeNull();
const closeButton = await page.$('button[data-testid="close-login-dialog-button"]');
await closeButton.click();
await page.waitForTimeout(500);
});
test('should log in an user', async () => {
// we open the dialog
await logIn(page);
// verify if logged in
const accountButton = await page.$('#header--button-account');
expect(accountButton).toBeDefined();
// check whether user is logged
const buttonLogout = await page.$('#logOutDialogIcon');
expect(buttonLogout).toBeDefined();
});
test('should logout an user', async () => {
await page.waitForTimeout(10000);
// we assume the user is logged already
await clickElement(page, '#header--button-account', { delay: 500 });
await page.waitForTimeout(1000);
await clickElement(page, '#logOutDialogIcon > span', { delay: 500 });
await page.waitForTimeout(1000);
await evaluateSignIn();
});
});

18
e2e/ui/tsconfig.json Normal file
View File

@@ -0,0 +1,18 @@
{
"extends": "../../tsconfig.reference.json",
"include": ["./cypress/**/*.ts", "./*.ts"],
"references": [
{
"path": "../../packages/core/core"
},
{
"path": "../../packages/verdaccio"
},
{
"path": "../../packages/config"
},
{
"path": "../../packages/tools/helpers"
}
]
}

View File

@@ -10,7 +10,7 @@ module.exports = {
coveragePathIgnorePatterns: ['node_modules', 'fixtures'],
coverageThreshold: {
global: {
lines: 90,
lines: 85,
},
},
};

View File

@@ -15,11 +15,11 @@
"url": "https://opencollective.com/verdaccio"
},
"devDependencies": {
"@babel/cli": "7.18.10",
"@babel/core": "7.19.1",
"@babel/cli": "7.19.3",
"@babel/core": "7.19.3",
"@babel/node": "7.19.1",
"@babel/plugin-proposal-class-properties": "7.18.6",
"@babel/plugin-proposal-decorators": "7.19.1",
"@babel/plugin-proposal-decorators": "7.19.3",
"@babel/plugin-proposal-export-namespace-from": "7.18.9",
"@babel/plugin-proposal-function-sent": "7.18.6",
"@babel/plugin-proposal-json-strings": "7.18.6",
@@ -33,7 +33,7 @@
"@babel/plugin-transform-async-to-generator": "7.18.6",
"@babel/plugin-transform-classes": "7.19.0",
"@babel/plugin-transform-runtime": "7.19.1",
"@babel/preset-env": "7.19.1",
"@babel/preset-env": "7.19.3",
"@babel/preset-react": "7.18.6",
"@babel/preset-typescript": "7.18.6",
"@babel/register": "7.18.9",
@@ -49,45 +49,46 @@
"@types/express": "4.17.14",
"@types/http-errors": "1.8.2",
"@types/jest": "27.5.2",
"@types/lodash": "4.14.185",
"@types/lodash": "4.14.186",
"@types/mime": "2.0.3",
"@types/minimatch": "3.0.5",
"@types/node": "16.11.60",
"@types/node": "16.11.62",
"@types/jsonwebtoken": "8.5.9",
"@types/request": "2.48.8",
"@types/semver": "7.3.12",
"@types/node-fetch": "2.6.2",
"@types/supertest": "2.0.12",
"@types/testing-library__jest-dom": "5.14.5",
"@types/validator": "13.7.6",
"@types/validator": "13.7.7",
"@types/webpack": "5.28.0",
"@types/webpack-env": "1.18.0",
"@typescript-eslint/eslint-plugin": "5.37.0",
"@typescript-eslint/parser": "5.37.0",
"@verdaccio/benchmark": "workspace:*",
"@verdaccio/crowdin-translations": "workspace:*",
"@verdaccio/eslint-config": "workspace:*",
"@verdaccio/types": "workspace:*",
"@verdaccio/ui-theme": "workspace:*",
"autocannon": "7.10.0",
"babel-core": "7.0.0-bridge.0",
"babel-eslint": "10.1.0",
"babel-jest": "29.0.3",
"babel-jest": "29.1.0",
"babel-plugin-dynamic-import-node": "2.3.3",
"babel-plugin-emotion": "10.2.2",
"concurrently": "6.5.1",
"core-js": "3.25.2",
"core-js": "3.25.3",
"cross-env": "7.0.3",
"debug": "4.3.4",
"detect-secrets": "1.0.6",
"jest-diff": "29.0.3",
"jest-diff": "29.1.0",
"eslint": "8.23.1",
"fs-extra": "10.1.0",
"husky": "7.0.4",
"in-publish": "2.0.1",
"jest": "29.0.3",
"jest-environment-jsdom": "29.0.3",
"jest": "29.1.1",
"jest-environment-jsdom": "29.1.1",
"jest-environment-jsdom-global": "3.1.2",
"jest-environment-node": "29.0.3",
"jest-environment-node": "29.1.1",
"jest-junit": "12.3.0",
"kleur": "3.0.3",
"lint-staged": "11.2.6",
@@ -100,7 +101,7 @@
"selfsigned": "1.10.14",
"supertest": "6.2.4",
"ts-node": "10.9.1",
"typescript": "4.8.3",
"typescript": "4.8.4",
"update-ts-references": "2.4.1",
"verdaccio-audit": "workspace:*",
"verdaccio-auth-memory": "workspace:*",
@@ -110,7 +111,7 @@
"scripts": {
"prepare": "husky install",
"husky:pre-commit": "lint-staged",
"clean": "pnpm run clean",
"clean": "pnpm run clean --filter=./packages",
"build": "pnpm run build --filter=./packages && pnpm run build --filter=./e2e",
"docker": "docker build -t verdaccio/verdaccio:local . --no-cache",
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,yml,yaml,md}\"",
@@ -141,6 +142,7 @@
"ci:publish": "changeset publish",
"ts:ref": "update-ts-references --discardComments",
"website": "pnpm build --filter ...@verdaccio/website",
"translations": "local-crowdin-api translations",
"crowdin:upload": "crowdin upload sources --auto-update --config ./crowdin.yaml",
"crowdin:download": "crowdin download --verbose --config ./crowdin.yaml",
"crowdin:sync": "pnpm crowdin:upload && pnpm crowdin:download --verbose",
@@ -149,6 +151,12 @@
"local:publish": "cross-env npm_config_registry=http://localhost:4873 pnpm ci:publish",
"local:publish:release": "concurrently \"pnpm local:registry\" \"pnpm local:publish\""
},
"pnpm": {
"overrides": {
"got": "11.8.5",
"p-cancelable": "2.1.1"
}
},
"engines": {
"node": ">=16.5",
"pnpm": ">=6.32.3 <7.0.0"

View File

@@ -1,5 +1,22 @@
# @verdaccio/api
## 6.0.0-6-next.32
### Minor Changes
- ce013d2f: refactor: npm star command support reimplemented
### Patch Changes
- Updated dependencies [ce013d2f]
- @verdaccio/store@6.0.0-6-next.29
- @verdaccio/core@6.0.0-6-next.49
- @verdaccio/config@6.0.0-6-next.49
- @verdaccio/auth@6.0.0-6-next.28
- @verdaccio/logger@6.0.0-6-next.17
- @verdaccio/middleware@6.0.0-6-next.28
- @verdaccio/utils@6.0.0-6-next.17
## 6.0.0-6-next.31
### Major Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@verdaccio/api",
"version": "6.0.0-6-next.31",
"version": "6.0.0-6-next.32",
"description": "loaders logic",
"main": "./build/index.js",
"types": "build/index.d.ts",
@@ -39,13 +39,13 @@
},
"license": "MIT",
"dependencies": {
"@verdaccio/auth": "workspace:6.0.0-6-next.27",
"@verdaccio/config": "workspace:6.0.0-6-next.48",
"@verdaccio/core": "workspace:6.0.0-6-next.48",
"@verdaccio/logger": "workspace:6.0.0-6-next.16",
"@verdaccio/middleware": "workspace:6.0.0-6-next.27",
"@verdaccio/store": "workspace:6.0.0-6-next.28",
"@verdaccio/utils": "workspace:6.0.0-6-next.16",
"@verdaccio/auth": "workspace:6.0.0-6-next.28",
"@verdaccio/config": "workspace:6.0.0-6-next.49",
"@verdaccio/core": "workspace:6.0.0-6-next.49",
"@verdaccio/logger": "workspace:6.0.0-6-next.17",
"@verdaccio/middleware": "workspace:6.0.0-6-next.28",
"@verdaccio/store": "workspace:6.0.0-6-next.29",
"@verdaccio/utils": "workspace:6.0.0-6-next.17",
"abortcontroller-polyfill": "1.7.3",
"cookies": "0.8.0",
"debug": "4.3.4",
@@ -56,10 +56,10 @@
"semver": "7.3.7"
},
"devDependencies": {
"@types/node": "16.11.60",
"@verdaccio/server": "workspace:6.0.0-6-next.37",
"@types/node": "16.11.62",
"@verdaccio/server": "workspace:6.0.0-6-next.38",
"@verdaccio/types": "workspace:11.0.0-6-next.17",
"@verdaccio/test-helper": "workspace:2.0.0-6-next.5",
"@verdaccio/test-helper": "workspace:2.0.0-6-next.6",
"supertest": "6.2.4",
"nock": "13.2.9",
"mockdate": "3.0.5"

View File

@@ -2,14 +2,14 @@ import { Router } from 'express';
import _ from 'lodash';
import mime from 'mime';
import { IAuth } from '@verdaccio/auth';
import { Auth } from '@verdaccio/auth';
import { constants, errorUtils } from '@verdaccio/core';
import { allow, media } from '@verdaccio/middleware';
import { Storage } from '@verdaccio/store';
import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types/custom';
export default function (route: Router, auth: IAuth, storage: Storage): void {
export default function (route: Router, auth: Auth, storage: Storage): void {
const can = allow(auth);
const addTagPackageVersionMiddleware = async function (
req: $RequestExtend,

View File

@@ -1,7 +1,7 @@
import bodyParser from 'body-parser';
import express, { Router } from 'express';
import { IAuth } from '@verdaccio/auth';
import { Auth } from '@verdaccio/auth';
import {
antiLoop,
encodeScopePackage,
@@ -24,7 +24,7 @@ import v1Search from './v1/search';
import token from './v1/token';
import whoami from './whoami';
export default function (config: Config, auth: IAuth, storage: Storage): Router {
export default function (config: Config, auth: Auth, storage: Storage): Router {
/* eslint new-cap:off */
const app = express.Router();
/* eslint new-cap:off */

View File

@@ -1,7 +1,7 @@
import buildDebug from 'debug';
import { Router } from 'express';
import { IAuth } from '@verdaccio/auth';
import { Auth } from '@verdaccio/auth';
import { HEADERS, HEADER_TYPE, stringUtils } from '@verdaccio/core';
import { allow } from '@verdaccio/middleware';
import { Storage } from '@verdaccio/store';
@@ -10,7 +10,7 @@ import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types/cust
const debug = buildDebug('verdaccio:api:package');
export default function (route: Router, auth: IAuth, storage: Storage): void {
export default function (route: Router, auth: Auth, storage: Storage): void {
const can = allow(auth);
route.get(

View File

@@ -2,7 +2,7 @@ import buildDebug from 'debug';
import { Router } from 'express';
import mime from 'mime';
import { IAuth } from '@verdaccio/auth';
import { Auth } from '@verdaccio/auth';
import { API_MESSAGE, HTTP_STATUS } from '@verdaccio/core';
import { logger } from '@verdaccio/logger';
import { allow, expectJson, media } from '@verdaccio/middleware';
@@ -11,7 +11,6 @@ import { Storage } from '@verdaccio/store';
import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '../types/custom';
// import star from './star';
// import { isPublishablePackage, isRelatedToDeprecation } from './utils';
const debug = buildDebug('verdaccio:api:publish');
@@ -93,7 +92,7 @@ const debug = buildDebug('verdaccio:api:publish');
}
*
*/
export default function publish(router: Router, auth: IAuth, storage: Storage): void {
export default function publish(router: Router, auth: Auth, storage: Storage): void {
const can = allow(auth);
router.put(
'/:package',
@@ -177,17 +176,17 @@ export default function publish(router: Router, auth: IAuth, storage: Storage):
export function publishPackage(storage: Storage): any {
return async function (
req: $RequestExtend,
_res: $ResponseExtend,
res: $ResponseExtend,
next: $NextFunctionVer
): Promise<void> {
const ac = new AbortController();
const packageName = req.params.package;
const { revision } = req.params;
const metadata = req.body;
const username = req?.remote_user?.name;
try {
debug('publishing %s', packageName);
await storage.updateManifest(metadata, {
const message = await storage.updateManifest(metadata, {
name: packageName,
revision,
signal: ac.signal,
@@ -196,16 +195,15 @@ export function publishPackage(storage: Storage): any {
protocol: req.protocol,
// @ts-ignore
headers: req.headers,
username,
},
});
_res.status(HTTP_STATUS.CREATED);
res.status(HTTP_STATUS.CREATED);
return next({
// TODO: this could be also Package Updated based on the
// action, deprecate, star, publish new version, or create a package
// the message some return from the method
ok: API_MESSAGE.PKG_CREATED,
success: true,
ok: message,
});
} catch (err: any) {
// TODO: review if we need the abort controller here

View File

@@ -2,7 +2,7 @@ import buildDebug from 'debug';
import { Response, Router } from 'express';
import { getApiToken } from '@verdaccio/auth';
import { IAuth } from '@verdaccio/auth';
import { Auth } from '@verdaccio/auth';
import { createRemoteUser } from '@verdaccio/config';
import { API_ERROR, API_MESSAGE, HTTP_STATUS, errorUtils, validatioUtils } from '@verdaccio/core';
import { logger } from '@verdaccio/logger';
@@ -13,7 +13,7 @@ import { $NextFunctionVer, $RequestExtend } from '../types/custom';
const debug = buildDebug('verdaccio:api:user');
export default function (route: Router, auth: IAuth, config: Config): void {
export default function (route: Router, auth: Auth, config: Config): void {
route.get(
'/-/user/:org_couchdb_user',
function (req: $RequestExtend, res: Response, next: $NextFunctionVer): void {
@@ -41,7 +41,7 @@ export default function (route: Router, auth: IAuth, config: Config): void {
*
* @export
* @param {Router} route
* @param {IAuth} auth
* @param {Auth} auth
* @param {Config} config
*/
route.put(
@@ -67,7 +67,7 @@ export default function (route: Router, auth: IAuth, config: Config): void {
);
}
const restoredRemoteUser: RemoteUser = createRemoteUser(name, user.groups || []);
const restoredRemoteUser: RemoteUser = createRemoteUser(name, user?.groups || []);
const token = await getApiToken(auth, config, restoredRemoteUser, password);
debug('login: new token');
if (!token) {
@@ -112,7 +112,9 @@ export default function (route: Router, auth: IAuth, config: Config): void {
}
const token =
name && password ? await getApiToken(auth, config, user, password) : undefined;
name && password
? await getApiToken(auth, config, user as RemoteUser, password)
: undefined;
if (token) {
debug('adduser: new token %o', mask(token as string, 4));
}

View File

@@ -1,7 +1,7 @@
import { Response, Router } from 'express';
import _ from 'lodash';
import { IAuth } from '@verdaccio/auth';
import { Auth } from '@verdaccio/auth';
import {
API_ERROR,
APP_ERROR,
@@ -25,7 +25,7 @@ export interface Profile {
fullname: string;
}
export default function (route: Router, auth: IAuth, config: Config): void {
export default function (route: Router, auth: Auth, config: Config): void {
function buildProfile(name: string): Profile {
return {
tfa: false,

View File

@@ -1,7 +1,7 @@
import buildDebug from 'debug';
import _ from 'lodash';
import { IAuth } from '@verdaccio/auth';
import { Auth } from '@verdaccio/auth';
import { HTTP_STATUS, searchUtils } from '@verdaccio/core';
import { logger } from '@verdaccio/logger';
import { Storage } from '@verdaccio/store';
@@ -15,7 +15,7 @@ const debug = buildDebug('verdaccio:api:search');
* - {"objects":[],"total":0,"time":"Sun Jul 25 2021 14:09:11 GMT+0000 (Coordinated Universal Time)"}
* req: 'GET /-/v1/search?text=react&size=20&frpom=0&quality=0.65&popularity=0.98&maintenance=0.5'
*/
export default function (route, auth: IAuth, storage: Storage): void {
export default function (route, auth: Auth, storage: Storage): void {
function checkAccess(pkg: any, auth: any, remoteUser): Promise<Manifest | null> {
return new Promise((resolve, reject) => {
auth.allow_access({ packageName: pkg?.package?.name }, remoteUser, function (err, allowed) {

View File

@@ -2,7 +2,7 @@ import { Response, Router } from 'express';
import _ from 'lodash';
import { getApiToken } from '@verdaccio/auth';
import { IAuth } from '@verdaccio/auth';
import { Auth } from '@verdaccio/auth';
import { HTTP_STATUS, SUPPORT_ERRORS, errorUtils } from '@verdaccio/core';
import { logger } from '@verdaccio/logger';
import { Storage } from '@verdaccio/store';
@@ -23,7 +23,7 @@ function normalizeToken(token: Token): NormalizeToken {
}
// https://github.com/npm/npm-profile/blob/latest/lib/index.js
export default function (route: Router, auth: IAuth, storage: Storage, config: Config): void {
export default function (route: Router, auth: Auth, storage: Storage, config: Config): void {
route.get(
'/-/npm/v1/tokens',
async function (req: $RequestExtend, res: Response, next: $NextFunctionVer) {
@@ -61,7 +61,7 @@ export default function (route: Router, auth: IAuth, storage: Storage, config: C
return next(errorUtils.getCode(HTTP_STATUS.BAD_DATA, SUPPORT_ERRORS.PARAMETERS_NOT_VALID));
}
auth.authenticate(name, password, async (err, user: RemoteUser) => {
auth.authenticate(name, password, async (err, user?: RemoteUser) => {
if (err) {
const errorCode = err.message ? HTTP_STATUS.UNAUTHORIZED : HTTP_STATUS.INTERNAL_ERROR;
return next(errorUtils.getCode(errorCode, err.message));
@@ -76,7 +76,7 @@ export default function (route: Router, auth: IAuth, storage: Storage, config: C
}
try {
const token = await getApiToken(auth, config, user, password);
const token = await getApiToken(auth, config, user as RemoteUser, password);
if (!token) {
throw errorUtils.getInternalError();
}

View File

@@ -160,13 +160,13 @@ describe('publish', () => {
decodeURIComponent(pkgName),
'1.0.1-patch'
).expect(HTTP_STATUS.CREATED);
expect(response.body.ok).toEqual(API_MESSAGE.PKG_CREATED);
expect(response.body.ok).toEqual(API_MESSAGE.PKG_CHANGED);
const response2 = await publishVersion(
app,
decodeURIComponent(pkgName),
'1.0.2-patch'
).expect(HTTP_STATUS.CREATED);
expect(response2.body.ok).toEqual(API_MESSAGE.PKG_CREATED);
expect(response2.body.ok).toEqual(API_MESSAGE.PKG_CHANGED);
}
);
});

View File

@@ -1,5 +1,16 @@
# @verdaccio/auth
## 6.0.0-6-next.28
### Patch Changes
- @verdaccio/core@6.0.0-6-next.49
- @verdaccio/config@6.0.0-6-next.49
- @verdaccio/loaders@6.0.0-6-next.18
- @verdaccio/logger@6.0.0-6-next.17
- verdaccio-htpasswd@11.0.0-6-next.19
- @verdaccio/utils@6.0.0-6-next.17
## 6.0.0-6-next.27
### Major Changes

View File

@@ -1,9 +1,9 @@
{
"name": "@verdaccio/auth",
"version": "6.0.0-6-next.27",
"version": "6.0.0-6-next.28",
"description": "logger",
"main": "./build/index.js",
"types": "build/index.d.ts",
"types": "./build/index.d.ts",
"author": {
"name": "Juan Picado",
"email": "juanpicado19@gmail.com"
@@ -39,16 +39,16 @@
},
"license": "MIT",
"dependencies": {
"@verdaccio/core": "workspace:6.0.0-6-next.48",
"@verdaccio/config": "workspace:6.0.0-6-next.48",
"@verdaccio/loaders": "workspace:6.0.0-6-next.17",
"@verdaccio/logger": "workspace:6.0.0-6-next.16",
"@verdaccio/utils": "workspace:6.0.0-6-next.16",
"@verdaccio/core": "workspace:6.0.0-6-next.49",
"@verdaccio/config": "workspace:6.0.0-6-next.49",
"@verdaccio/loaders": "workspace:6.0.0-6-next.18",
"@verdaccio/logger": "workspace:6.0.0-6-next.17",
"@verdaccio/utils": "workspace:6.0.0-6-next.17",
"debug": "4.3.4",
"express": "4.18.1",
"jsonwebtoken": "8.5.1",
"lodash": "4.17.21",
"verdaccio-htpasswd": "workspace:11.0.0-6-next.18"
"verdaccio-htpasswd": "workspace:11.0.0-6-next.19"
},
"devDependencies": {
"@verdaccio/types": "workspace:11.0.0-6-next.17"

View File

@@ -1,5 +1,5 @@
import buildDebug from 'debug';
import { NextFunction, Request, Response } from 'express';
import { NextFunction, Request, RequestHandler, Response } from 'express';
import _ from 'lodash';
import { HTPasswd } from 'verdaccio-htpasswd';
@@ -11,18 +11,17 @@ import {
TOKEN_BEARER,
VerdaccioError,
errorUtils,
pluginUtils,
} from '@verdaccio/core';
import { asyncLoadPlugin } from '@verdaccio/loaders';
import { logger } from '@verdaccio/logger';
import {
AllowAccess,
AuthPluginPackage,
Callback,
Config,
IPluginAuth,
JWTSignOptions,
Logger,
PackageAccess,
PluginOptions,
RemoteUser,
Security,
} from '@verdaccio/types';
@@ -41,20 +40,8 @@ import {
verifyJWTPayload,
} from './utils';
/* eslint-disable @typescript-eslint/no-var-requires */
const LoggerApi = require('@verdaccio/logger');
const debug = buildDebug('verdaccio:auth');
export interface IBasicAuth<T> {
config: T & Config;
authenticate(user: string, password: string, cb: Callback): void;
invalidateToken?(token: string): Promise<void>;
changePassword(user: string, password: string, newPassword: string, cb: Callback): void;
allow_access(pkg: AuthPluginPackage, user: RemoteUser, callback: Callback): void;
add_user(user: string, password: string, cb: Callback): any;
}
export interface TokenEncryption {
jwtEncrypt(user: RemoteUser, signOptions: JWTSignOptions): Promise<string>;
aesEncrypt(buf: string): string | void;
@@ -64,36 +51,22 @@ export interface AESPayload {
user: string;
password: string;
}
export type $RequestExtend = Request & { remote_user?: any; log: Logger };
export type $ResponseExtend = Response & { cookies?: any };
export type $NextFunctionVer = NextFunction & any;
export interface IAuthMiddleware {
apiJWTmiddleware(): $NextFunctionVer;
webUIJWTmiddleware(): $NextFunctionVer;
}
export interface IAuth extends IBasicAuth<Config>, IAuthMiddleware, TokenEncryption {
config: Config;
logger: Logger;
secret: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
plugins: any[];
allow_unpublish(pkg: AuthPluginPackage, user: RemoteUser, callback: Callback): void;
invalidateToken(token: string): Promise<void>;
init(): Promise<void>;
}
export type $RequestExtend = Request & { remote_user?: any; log: Logger };
export type $ResponseExtend = Response & { cookies?: any };
export type $NextFunctionVer = NextFunction & any;
class Auth implements IAuth {
class Auth implements IAuthMiddleware, TokenEncryption, pluginUtils.IBasicAuth {
public config: Config;
public logger: Logger;
public secret: string;
public plugins: IPluginAuth<Config>[];
public plugins: pluginUtils.Auth<Config>[];
public constructor(config: Config) {
this.config = config;
this.logger = LoggerApi.logger.child({ sub: 'auth' });
this.secret = config.secret;
this.plugins = [];
if (!this.secret) {
@@ -102,7 +75,7 @@ class Auth implements IAuth {
}
public async init() {
let plugins = await this.loadPlugin();
let plugins = (await this.loadPlugin()) as pluginUtils.Auth<unknown>[];
debug('auth plugins found %s', plugins.length);
if (!plugins || plugins.length === 0) {
plugins = this.loadDefaultPlugin();
@@ -114,33 +87,40 @@ class Auth implements IAuth {
private loadDefaultPlugin() {
debug('load default auth plugin');
const pluginOptions: PluginOptions = {
const pluginOptions: pluginUtils.PluginOptions = {
config: this.config,
logger: this.logger,
logger,
};
let authPlugin;
try {
authPlugin = new HTPasswd({ file: './htpasswd' }, pluginOptions as any as PluginOptions);
authPlugin = new HTPasswd(
{ file: './htpasswd' },
pluginOptions as any as pluginUtils.PluginOptions
);
} catch (error: any) {
debug('error on loading auth htpasswd plugin stack: %o', error);
logger.info({}, 'no auth plugin has been found');
return [];
}
return [authPlugin];
}
private async loadPlugin(): Promise<IPluginAuth<Config>[]> {
return asyncLoadPlugin<IPluginAuth<Config>>(
private async loadPlugin() {
return asyncLoadPlugin<pluginUtils.Auth<unknown>>(
this.config.auth,
{
config: this.config,
logger: this.logger,
logger,
},
(plugin: IPluginAuth<Config>): boolean => {
(plugin): boolean => {
const { authenticate, allow_access, allow_publish } = plugin;
// @ts-ignore
return authenticate || allow_access || allow_publish;
return (
typeof authenticate !== 'undefined' ||
typeof allow_access !== 'undefined' ||
typeof allow_publish !== 'undefined'
);
},
this.config?.serverSettings?.pluginPrefix
);
@@ -148,7 +128,7 @@ class Auth implements IAuth {
private _applyDefaultPlugins(): void {
// TODO: rename to applyFallbackPluginMethods
this.plugins.push(getDefaultPlugins(this.logger));
this.plugins.push(getDefaultPlugins(logger));
}
public changePassword(
@@ -171,7 +151,7 @@ class Auth implements IAuth {
debug('updating password for %o', username);
plugin.changePassword!(username, password, newPassword, (err, profile): void => {
if (err) {
this.logger.error(
logger.error(
{ username, err },
`An error has been produced
updating the password for @{username}. Error: @{err.message}`
@@ -192,17 +172,21 @@ class Auth implements IAuth {
return Promise.resolve();
}
public authenticate(username: string, password: string, cb: Callback): void {
public authenticate(
username: string,
password: string,
cb: (error: VerdaccioError | null, user?: RemoteUser) => void
): void {
const plugins = this.plugins.slice(0);
(function next(): void {
const plugin = plugins.shift() as IPluginAuth<Config>;
const plugin = plugins.shift() as pluginUtils.Auth<Config>;
if (isFunction(plugin.authenticate) === false) {
return next();
}
debug('authenticating %o', username);
plugin.authenticate(username, password, function (err, groups): void {
plugin.authenticate(username, password, function (err: VerdaccioError | null, groups): void {
if (err) {
debug('authenticating for user %o failed. Error: %o', username, err?.message);
return cb(err);
@@ -233,37 +217,42 @@ class Auth implements IAuth {
})();
}
public add_user(user: string, password: string, cb: Callback): void {
public add_user(
user: string,
password: string,
cb: (error: VerdaccioError | null, user?: RemoteUser) => void
): void {
const self = this;
const plugins = this.plugins.slice(0);
debug('add user %o', user);
(function next(): void {
const plugin = plugins.shift() as IPluginAuth<Config>;
let method = 'adduser';
if (isFunction(plugin[method]) === false) {
method = 'add_user';
self.logger.warn(
'the plugin method add_user in the auth plugin is deprecated and will' +
' be removed in next major release, notify to the plugin author'
);
}
if (isFunction(plugin[method]) === false) {
const plugin = plugins.shift() as pluginUtils.Auth<Config>;
if (typeof plugin.adduser !== 'function') {
next();
} else {
// p.add_user() execution
plugin[method](user, password, function (err, ok): void {
if (err) {
debug('the user %o could not being added. Error: %o', user, err?.message);
return cb(err);
// @ts-expect-error future major (7.x) should remove this section
if (typeof plugin.adduser === 'undefined' && typeof plugin.add_user === 'function') {
throw errorUtils.getInternalError(
'add_user method not longer supported, rename to adduser'
);
}
plugin.adduser(
user,
password,
function (err: VerdaccioError | null, ok?: boolean | string): void {
if (err) {
debug('the user %o could not being added. Error: %o', user, err?.message);
return cb(err);
}
if (ok) {
debug('the user %o has been added', user);
return self.authenticate(user, password, cb);
}
next();
}
if (ok) {
debug('the user %o has been added', user);
return self.authenticate(user, password, cb);
}
next();
});
);
}
})();
}
@@ -272,27 +261,27 @@ class Auth implements IAuth {
* Allow user to access a package.
*/
public allow_access(
{ packageName, packageVersion }: AuthPluginPackage,
{ packageName, packageVersion }: pluginUtils.AuthPluginPackage,
user: RemoteUser,
callback: Callback
callback: pluginUtils.AccessCallback
): void {
const plugins = this.plugins.slice(0);
const pkgAllowAcces: AllowAccess = { name: packageName, version: packageVersion };
const pkgAllowAccess: AllowAccess = { name: packageName, version: packageVersion };
const pkg = Object.assign(
{},
pkgAllowAcces,
pkgAllowAccess,
getMatchedPackagesSpec(packageName, this.config.packages)
) as AllowAccess & PackageAccess;
debug('allow access for %o', packageName);
(function next(): void {
const plugin: IPluginAuth<Config> = plugins.shift() as IPluginAuth<Config>;
const plugin: pluginUtils.Auth<unknown> = plugins.shift() as pluginUtils.Auth<unknown>;
if (_.isNil(plugin) || isFunction(plugin.allow_access) === false) {
return next();
}
plugin.allow_access!(user, pkg, function (err, ok: boolean): void {
plugin.allow_access!(user, pkg, function (err: VerdaccioError | null, ok?: boolean): void {
if (err) {
debug('forbidden access for %o. Error: %o', packageName, err?.message);
return callback(err);
@@ -309,7 +298,7 @@ class Auth implements IAuth {
}
public allow_unpublish(
{ packageName, packageVersion }: AuthPluginPackage,
{ packageName, packageVersion }: pluginUtils.AuthPluginPackage,
user: RemoteUser,
callback: Callback
): void {
@@ -354,7 +343,7 @@ class Auth implements IAuth {
* Allow user to publish a package.
*/
public allow_publish(
{ packageName, packageVersion }: AuthPluginPackage,
{ packageName, packageVersion }: pluginUtils.AuthPluginPackage,
user: RemoteUser,
callback: Callback
): void {
@@ -362,42 +351,35 @@ class Auth implements IAuth {
const pkg = Object.assign(
{ name: packageName, version: packageVersion },
getMatchedPackagesSpec(packageName, this.config.packages)
);
) as any;
debug('allow publish for %o init | plugins: %o', packageName, plugins.length);
(function next(): void {
const plugin = plugins.shift();
if (isFunction(plugin?.allow_publish) === false) {
if (typeof plugin?.allow_publish !== 'function') {
debug('allow publish for %o plugin does not implement allow_publish', packageName);
return next();
}
// @ts-ignore
plugin.allow_publish(
user,
// @ts-ignore
pkg,
// @ts-ignore
(err: VerdaccioError, ok: boolean): void => {
if (_.isNil(err) === false && _.isError(err)) {
debug('forbidden publish for %o', packageName);
return callback(err);
}
if (ok) {
debug('allowed publish for %o', packageName);
return callback(null, ok);
}
debug('allow publish skip validation for %o', packageName);
next(); // cb(null, false) causes next plugin to roll
plugin.allow_publish(user, pkg, (err: VerdaccioError | null, ok?: boolean): void => {
if (_.isNil(err) === false && _.isError(err)) {
debug('forbidden publish for %o', packageName);
return callback(err);
}
);
if (ok) {
debug('allowed publish for %o', packageName);
return callback(null, ok);
}
debug('allow publish skip validation for %o', packageName);
next(); // cb(null, false) causes next plugin to roll
});
})();
}
public apiJWTmiddleware(): Function {
public apiJWTmiddleware(): RequestHandler {
debug('jwt middleware');
const plugins = this.plugins.slice(0);
const helpers = { createAnonymousRemoteUser, createRemoteUser };
@@ -407,10 +389,11 @@ class Auth implements IAuth {
}
}
return (req: $RequestExtend, res: $ResponseExtend, _next: NextFunction): void => {
// @ts-ignore
return (req: $RequestExtend, res: $ResponseExtend, _next: NextFunction) => {
req.pause();
const next = function (err: VerdaccioError | void): void {
const next = function (err?: VerdaccioError): any {
req.resume();
// uncomment this to reject users with bad auth headers
// return _next.apply(null, arguments)
@@ -419,6 +402,7 @@ class Auth implements IAuth {
if (err) {
req.remote_user.error = err.message;
}
return _next();
};
@@ -469,7 +453,7 @@ class Auth implements IAuth {
const credentials = convertPayloadToBase64(token).toString();
const { user, password } = parseBasicPayload(credentials) as AESPayload;
debug('authenticating %o', user);
this.authenticate(user, password, (err, user): void => {
this.authenticate(user, password, (err: VerdaccioError | null, user): void => {
if (!err) {
debug('generating a remote user');
req.remote_user = user;
@@ -529,14 +513,15 @@ class Auth implements IAuth {
}
}
private _isRemoteUserValid(remote_user: RemoteUser): boolean {
return _.isUndefined(remote_user) === false && _.isUndefined(remote_user.name) === false;
private _isRemoteUserValid(remote_user?: RemoteUser): boolean {
return _.isUndefined(remote_user) === false && _.isUndefined(remote_user?.name) === false;
}
/**
* JWT middleware for WebUI
*/
public webUIJWTmiddleware(): Function {
public webUIJWTmiddleware(): RequestHandler {
// @ts-ignore
return (req: $RequestExtend, res: $ResponseExtend, _next: NextFunction): void => {
if (this._isRemoteUserValid(req.remote_user)) {
return _next();
@@ -567,7 +552,7 @@ class Auth implements IAuth {
return next();
}
let credentials;
let credentials: RemoteUser | undefined;
try {
credentials = verifyJWTPayload(token, this.config.secret);
} catch (err: any) {
@@ -575,9 +560,8 @@ class Auth implements IAuth {
}
if (this._isRemoteUserValid(credentials)) {
const { name, groups } = credentials;
// $FlowFixMe
req.remote_user = createRemoteUser(name, groups);
const { name, groups } = credentials as RemoteUser;
req.remote_user = createRemoteUser(name as string, groups);
} else {
req.remote_user = createAnonymousRemoteUser();
}

View File

@@ -1,4 +1,4 @@
export { Auth, IAuth, TokenEncryption, IBasicAuth } from './auth';
export { Auth, TokenEncryption } from './auth';
export * from './utils';
export * from './legacy-token';
export * from './jwt-token';

View File

@@ -9,15 +9,9 @@ import {
TOKEN_BEARER,
VerdaccioError,
errorUtils,
pluginUtils,
} from '@verdaccio/core';
import {
AuthPackageAllow,
Callback,
Config,
IPluginAuth,
RemoteUser,
Security,
} from '@verdaccio/types';
import { AuthPackageAllow, Config, Logger, RemoteUser, Security } from '@verdaccio/types';
import { AESPayload, TokenEncryption } from './auth';
import { verifyPayload } from './jwt-token';
@@ -161,13 +155,18 @@ export function isAuthHeaderValid(authorization: string): boolean {
return authorization.split(' ').length === 2;
}
export function getDefaultPlugins(logger: any): IPluginAuth<Config> {
/**
* Return a default configuration for authentication if none is provided.
* @param logger {Logger}
* @returns object of default implementations.
*/
export function getDefaultPlugins(logger: Logger): pluginUtils.Auth<Config> {
return {
authenticate(user: string, password: string, cb: Callback): void {
authenticate(_user: string, _password: string, cb: pluginUtils.AuthCallback): void {
cb(errorUtils.getForbidden(API_ERROR.BAD_USERNAME_PASSWORD));
},
adduser(user: string, password: string, cb: Callback): void {
adduser(_user: string, _password: string, cb: pluginUtils.AuthUserCallback): void {
return cb(errorUtils.getConflict(API_ERROR.BAD_USERNAME_PASSWORD));
},
@@ -182,7 +181,7 @@ export function getDefaultPlugins(logger: any): IPluginAuth<Config> {
export type ActionsAllowed = 'publish' | 'unpublish' | 'access';
export function allow_action(action: ActionsAllowed, logger): AllowAction {
export function allow_action(action: ActionsAllowed, logger: Logger): AllowAction {
return function allowActionCallback(
user: RemoteUser,
pkg: AuthPackageAllow,
@@ -217,7 +216,7 @@ export function allow_action(action: ActionsAllowed, logger): AllowAction {
/**
*
*/
export function handleSpecialUnpublish(logger): any {
export function handleSpecialUnpublish(logger: Logger): any {
return function (user: RemoteUser, pkg: AuthPackageAllow, callback: AllowActionCallback): void {
const action = 'unpublish';
// verify whether the unpublish prop has been defined

View File

@@ -24,7 +24,6 @@ import type { AllowActionCallbackResponse } from '@verdaccio/utils';
import {
ActionsAllowed,
Auth,
IAuth,
aesDecrypt,
allow_action,
getApiToken,
@@ -70,7 +69,7 @@ describe('Auth utilities', () => {
methodNotBeenCalled: string
): Promise<string> {
const config: Config = getConfig(configFileName, secret);
const auth: IAuth = new Auth(config);
const auth: Auth = new Auth(config);
await auth.init();
// @ts-ignore
const spy = jest.spyOn(auth, methodToSpy);
@@ -409,7 +408,7 @@ describe('Auth utilities', () => {
test.concurrent('should return empty credential corrupted payload', async () => {
const secret = 'b2df428b9929d3ace7c598bbf4e496b2';
const config: Config = getConfig('security-legacy', secret);
const auth: IAuth = new Auth(config);
const auth: Auth = new Auth(config);
await auth.init();
const token = auth.aesEncrypt(null);
const security: Security = config.security;

View File

@@ -1,6 +1,5 @@
import path from 'path';
import { IAuth } from '@verdaccio/auth';
import { Config as AppConfig, ROLES, getDefaultConfig } from '@verdaccio/config';
import { errorUtils } from '@verdaccio/core';
import { setup } from '@verdaccio/logger';
@@ -9,14 +8,14 @@ import { Config } from '@verdaccio/types';
import { Auth } from '../src';
import { authPluginFailureConf, authPluginPassThrougConf, authProfileConf } from './helper/plugin';
setup([]);
setup({});
describe('AuthTest', () => {
test('should init correctly', async () => {
const config: Config = new AppConfig({ ...authProfileConf });
config.checkSecretKey('12345');
const auth: IAuth = new Auth(config);
const auth: Auth = new Auth(config);
await auth.init();
expect(auth).toBeDefined();
});
@@ -25,7 +24,7 @@ describe('AuthTest', () => {
const config: Config = new AppConfig({ ...authProfileConf, auth: undefined });
config.checkSecretKey('12345');
const auth: IAuth = new Auth(config);
const auth: Auth = new Auth(config);
await auth.init();
expect(auth).toBeDefined();
});
@@ -35,7 +34,7 @@ describe('AuthTest', () => {
test('should be a success login', async () => {
const config: Config = new AppConfig({ ...authProfileConf });
config.checkSecretKey('12345');
const auth: IAuth = new Auth(config);
const auth: Auth = new Auth(config);
await auth.init();
expect(auth).toBeDefined();
@@ -62,7 +61,7 @@ describe('AuthTest', () => {
test('should be a fail on login', async () => {
const config: Config = new AppConfig(authPluginFailureConf);
config.checkSecretKey('12345');
const auth: IAuth = new Auth(config);
const auth: Auth = new Auth(config);
await auth.init();
expect(auth).toBeDefined();
@@ -81,7 +80,7 @@ describe('AuthTest', () => {
test('should skip falsy values', async () => {
const config: Config = new AppConfig({ ...authPluginPassThrougConf });
config.checkSecretKey('12345');
const auth: IAuth = new Auth(config);
const auth: Auth = new Auth(config);
await auth.init();
expect(auth).toBeDefined();
@@ -101,7 +100,7 @@ describe('AuthTest', () => {
test('should error truthy non-array', async () => {
const config: Config = new AppConfig({ ...authPluginPassThrougConf });
config.checkSecretKey('12345');
const auth: IAuth = new Auth(config);
const auth: Auth = new Auth(config);
await auth.init();
expect(auth).toBeDefined();
@@ -119,7 +118,7 @@ describe('AuthTest', () => {
test('should skip empty array', async () => {
const config: Config = new AppConfig({ ...authPluginPassThrougConf });
config.checkSecretKey('12345');
const auth: IAuth = new Auth(config);
const auth: Auth = new Auth(config);
await auth.init();
expect(auth).toBeDefined();
@@ -136,7 +135,7 @@ describe('AuthTest', () => {
test('should accept valid array', async () => {
const config: Config = new AppConfig({ ...authPluginPassThrougConf });
config.checkSecretKey('12345');
const auth: IAuth = new Auth(config);
const auth: Auth = new Auth(config);
await auth.init();
expect(auth).toBeDefined();
@@ -165,7 +164,7 @@ describe('AuthTest', () => {
},
});
config.checkSecretKey('12345');
const auth: IAuth = new Auth(config);
const auth: Auth = new Auth(config);
await auth.init();
return new Promise((resolve) => {

View File

@@ -2,8 +2,8 @@
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./build"
"outDir": "./build",
"noImplicitAny": false
},
"include": ["src/**/*"],
"exclude": ["src/**/*.test.ts"]
"include": ["src/**/*"]
}

View File

@@ -2,16 +2,19 @@
"extends": "../../tsconfig.reference",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./build"
"outDir": "./build",
"noImplicitAny": true
},
"include": ["src/**/*.ts"],
"exclude": ["src/**/*.test.ts"],
"references": [
{
"path": "../config"
},
{
"path": "../core/htpasswd"
"path": "../plugins/htpasswd"
},
{
"path": "../core/core"
},
{
"path": "../loaders"
@@ -19,9 +22,6 @@
{
"path": "../logger"
},
{
"path": "../mock"
},
{
"path": "../utils"
}

View File

@@ -1,5 +1,14 @@
# @verdaccio/cli
## 6.0.0-6-next.49
### Patch Changes
- @verdaccio/node-api@6.0.0-6-next.49
- @verdaccio/core@6.0.0-6-next.49
- @verdaccio/config@6.0.0-6-next.49
- @verdaccio/logger@6.0.0-6-next.17
## 6.0.0-6-next.48
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@verdaccio/cli",
"version": "6.0.0-6-next.48",
"version": "6.0.0-6-next.49",
"author": {
"name": "Juan Picado",
"email": "juanpicado19@gmail.com"
@@ -44,10 +44,10 @@
"start": "ts-node src/index.ts"
},
"dependencies": {
"@verdaccio/core": "workspace:6.0.0-6-next.48",
"@verdaccio/config": "workspace:6.0.0-6-next.48",
"@verdaccio/logger": "workspace:6.0.0-6-next.16",
"@verdaccio/node-api": "workspace:6.0.0-6-next.48",
"@verdaccio/core": "workspace:6.0.0-6-next.49",
"@verdaccio/config": "workspace:6.0.0-6-next.49",
"@verdaccio/logger": "workspace:6.0.0-6-next.17",
"@verdaccio/node-api": "workspace:6.0.0-6-next.49",
"clipanion": "3.1.0",
"envinfo": "7.8.1",
"kleur": "3.0.3",

View File

@@ -10,9 +10,6 @@
{
"path": "../config"
},
{
"path": "../core/cli-ui"
},
{
"path": "../core/core"
},

View File

@@ -1,5 +1,12 @@
# @verdaccio/config
## 6.0.0-6-next.49
### Patch Changes
- @verdaccio/core@6.0.0-6-next.49
- @verdaccio/utils@6.0.0-6-next.17
## 6.0.0-6-next.48
### Major Changes

View File

@@ -1,72 +1,5 @@
# @verdaccio/config
[![backers](https://opencollective.com/verdaccio/tiers/backer/badge.svg?label=Backer&color=brightgreen)](https://opencollective.com/verdaccio)
[![stackshare](https://img.shields.io/badge/Follow%20on-StackShare-blue.svg?logo=stackshare&style=flat)](https://stackshare.io/verdaccio)
[![MIT](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/verdaccio/verdaccio/blob/master/LICENSE)
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/verdaccio/localized.svg)](https://crowdin.com/project/verdaccio)
[![TODOs](https://badgen.net/https/api.tickgit.com/badgen/github.com/verdaccio/verdaccio)](https://www.tickgit.com/browse?repo=github.com/verdaccio/verdaccio)
[![Twitter followers](https://img.shields.io/twitter/follow/verdaccio_npm.svg?style=social&label=Follow)](https://twitter.com/verdaccio_npm)
[![Github](https://img.shields.io/github/stars/verdaccio/verdaccio.svg?style=social&label=Stars)](https://github.com/verdaccio/verdaccio/stargazers)
## 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#sponsor)]
[![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#backer)]
[![backers](https://opencollective.com/verdaccio/backers.svg?width=890)](https://opencollective.com/verdaccio#backers)
## Special Thanks
Thanks to the following companies to help us to achieve our goals providing free open source licenses.
[![jetbrain](assets/thanks/jetbrains/logo.png)](https://www.jetbrains.com/)
[![crowdin](assets/thanks/crowdin/logo.png)](https://crowdin.com/)
[![balsamiq](assets/thanks/balsamiq/logo.jpg)](https://balsamiq.com/)
## Contributors
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
[![contributors](https://opencollective.com/verdaccio/contributors.svg?width=890&button=true)](../../graphs/contributors)
### FAQ / Contact / Troubleshoot
If you have any issue you can try the following options, do no desist 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/verdaccio/verdaccio/issues?q=is%3Aissue+is%3Aopen+label%3Adiscuss)
- [Chat](http://chat.verdaccio.org/)
- [Logos](https://verdaccio.org/docs/en/logo)
- [Docker Examples](https://github.com/verdaccio/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)

View File

@@ -1,6 +1,6 @@
{
"name": "@verdaccio/config",
"version": "6.0.0-6-next.48",
"version": "6.0.0-6-next.49",
"description": "logger",
"main": "./build/index.js",
"types": "build/index.d.ts",
@@ -39,8 +39,8 @@
"build": "pnpm run build:js && pnpm run build:types"
},
"dependencies": {
"@verdaccio/core": "workspace:6.0.0-6-next.48",
"@verdaccio/utils": "workspace:6.0.0-6-next.16",
"@verdaccio/core": "workspace:6.0.0-6-next.49",
"@verdaccio/utils": "workspace:6.0.0-6-next.17",
"debug": "4.3.4",
"yaml": "2.1.1",
"lodash": "4.17.21",

View File

@@ -1,5 +1,7 @@
# @verdaccio/core
## 6.0.0-6-next.49
## 6.0.0-6-next.48
### Minor Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@verdaccio/core",
"version": "6.0.0-6-next.48",
"version": "6.0.0-6-next.49",
"description": "core utilities",
"keywords": [
"private",
@@ -39,16 +39,19 @@
"semver": "7.3.7",
"ajv": "8.11.0",
"process-warning": "1.0.0",
"core-js": "3.25.2"
"core-js": "3.25.3"
},
"devDependencies": {
"lodash": "4.17.21",
"typedoc": "0.23.15",
"typedoc-plugin-missing-exports": "latest",
"@verdaccio/types": "workspace:11.0.0-6-next.17"
},
"scripts": {
"clean": "rimraf ./build",
"test": "jest",
"type-check": "tsc --noEmit -p tsconfig.build.json",
"build:docs": "typedoc --options ./typedoc.json --tsconfig 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",

View File

@@ -1,24 +1,174 @@
import { Config, IPackageStorage, Token, TokenFilter } from '@verdaccio/types';
import { Express, RequestHandler } from 'express';
import { Readable, Writable } from 'stream';
import { searchUtils } from '.';
import {
AllowAccess,
Callback,
Config,
Logger,
Manifest,
PackageAccess,
RemoteUser,
Token,
TokenFilter,
} from '@verdaccio/types';
interface IPlugin {
version?: string;
// In case a plugin needs to be cleaned up/removed
close?(): void;
import { VerdaccioError, searchUtils } from '.';
export interface AuthPluginPackage {
packageName: string;
packageVersion?: string;
tag?: string;
}
export interface PluginOptions {
config: Config;
logger: Logger;
}
export interface IPluginStorage<T> extends IPlugin {
config: T & Config;
/**
* The base plugin class, set of utilities for developing
* plugins.
* @alpha
* */
export class Plugin<PluginConfig> {
static version = 1;
public readonly version: number;
public readonly config: PluginConfig | unknown;
public readonly options: PluginOptions;
public constructor(config: PluginConfig, options: PluginOptions) {
this.version = Plugin.version;
this.config = config;
this.options = options;
}
public getVersion() {
return this.version;
}
}
export interface StorageHandler {
logger: Logger;
deletePackage(fileName: string): Promise<void>;
removePackage(): Promise<void>;
// next packages migration (this list is meant to replace the callback parent functions)
updatePackage(
packageName: string,
handleUpdate: (manifest: Manifest) => Promise<Manifest>
): Promise<Manifest>;
readPackage(name: string): Promise<Manifest>;
savePackage(pkgName: string, value: Manifest): Promise<void>;
readTarball(pkgName: string, { signal }: { signal: AbortSignal }): Promise<Readable>;
createPackage(name: string, manifest: Manifest): Promise<void>;
writeTarball(tarballName: string, { signal }: { signal: AbortSignal }): Promise<Writable>;
// verify if tarball exist in the storage
hasTarball(fileName: string): Promise<boolean>;
// verify if package exist in the storage
hasPackage(): Promise<boolean>;
}
export interface Storage<PluginConfig> extends Plugin<PluginConfig> {
add(name: string): Promise<void>;
remove(name: string): Promise<void>;
get(): Promise<any>;
init(): Promise<void>;
getSecret(): Promise<string>;
setSecret(secret: string): Promise<any>;
getPackageStorage(packageInfo: string): IPackageStorage;
getPackageStorage(packageInfo: string): StorageHandler;
search(query: searchUtils.SearchQuery): Promise<searchUtils.SearchItem[]>;
saveToken(token: Token): Promise<any>;
deleteToken(user: string, tokenKey: string): Promise<any>;
readTokens(filter: TokenFilter): Promise<Token[]>;
}
/**
* This function allow add additional middleware to the application.
*
* ```ts
* import express, { Request, Response } from 'express';
*
* class Middleware extends Plugin {
* // instances of auth and storage are injected
* register_middlewares(app, auth, storage) {
* const router = express.Router();
* router.post('/my-endpoint', (req: Request, res: Response): void => {
res.status(200).end();
});
* }
* }
*
*
* const [plugin] = await asyncLoadPlugin(...);
* plugin.register_middlewares(app, auth, storage);
* ```
*/
export interface ExpressMiddleware<PluginConfig, Storage, Auth> extends Plugin<PluginConfig> {
register_middlewares(app: Express, auth: Auth, storage: Storage): void;
}
/**
* dasdsa
*/
export type AuthCallback = (error: VerdaccioError | null, groups?: string[] | false) => void;
export type AuthAccessCallback = (error: VerdaccioError | null, access?: boolean) => void;
export type AuthUserCallback = (error: VerdaccioError | null, access?: boolean | string) => void;
export type AuthChangePasswordCallback = (error: VerdaccioError | null, access?: boolean) => void;
export type AccessCallback = (error: VerdaccioError | null, ok?: boolean) => void;
export interface Auth<T> extends Plugin<T> {
/**
* Handles the authenticated method.
* ```ts
* class Auth {
public authenticate(user: string, password: string, done: AuthCallback): void {
if (!password) {
return done(errorUtils.getUnauthorized(API_ERROR.BAD_USERNAME_PASSWORD));
}
// always return an array of users
return done(null, [user]);
* }
* ```
*/
authenticate(user: string, password: string, cb: AuthCallback): void;
/**
* Handles the authenticated method.
* ```ts
* class Auth {
public adduser(user: string, password: string, done: AuthCallback): void {
if (!password) {
return done(errorUtils.getUnauthorized(API_ERROR.BAD_USERNAME_PASSWORD));
}
// return boolean
return done(null, true);
* }
* ```
*/
adduser?(user: string, password: string, cb: AuthUserCallback): void;
changePassword?(
user: string,
password: string,
newPassword: string,
cb: AuthChangePasswordCallback
): void;
allow_publish?(user: RemoteUser, pkg: T & PackageAccess, cb: AuthAccessCallback): void;
allow_publish?(user: RemoteUser, pkg: AllowAccess & PackageAccess, cb: AuthAccessCallback): void;
allow_access?(user: RemoteUser, pkg: T & PackageAccess, cb: AccessCallback): void;
allow_access?(user: RemoteUser, pkg: AllowAccess & PackageAccess, cb: AccessCallback): void;
allow_unpublish?(user: RemoteUser, pkg: T & PackageAccess, cb: AuthAccessCallback): void;
allow_unpublish?(
user: RemoteUser,
pkg: AllowAccess & PackageAccess,
cb: AuthAccessCallback
): void;
apiJWTmiddleware?(helpers: any): RequestHandler;
}
export interface IBasicAuth {
authenticate(user: string, password: string, cb: Callback): void;
invalidateToken?(token: string): Promise<void>;
changePassword(user: string, password: string, newPassword: string, cb: Callback): void;
allow_access(pkg: AuthPluginPackage, user: RemoteUser, callback: Callback): void;
add_user(user: string, password: string, cb: Callback): any;
}
export interface ManifestFilter<T> extends Plugin<T> {
filterMetadata(packageInfo: Manifest): Promise<Manifest>;
}

View File

@@ -1,90 +1,9 @@
import { PassThrough, Transform, TransformOptions } from 'stream';
export interface IReadTarball {
abort?: () => void;
}
export interface IUploadTarball {
done?: () => void;
abort?: () => void;
}
/**
* This stream is used to read tarballs from repository.
* @param {*} options
* @return {Stream}
*/
class ReadTarball extends PassThrough implements IReadTarball {
/**
*
* @param {Object} options
*/
public constructor(options: TransformOptions) {
super(options);
// called when data is not needed anymore
addAbstractMethods(this, 'abort');
}
public abort(): void {}
}
/**
* This stream is used to upload tarballs to a repository.
* @param {*} options
* @return {Stream}
*/
class UploadTarball extends PassThrough implements IUploadTarball {
/**
*
* @param {Object} options
*/
public constructor(options: any) {
super(options);
// called when user closes connection before upload finishes
addAbstractMethods(this, 'abort');
// called when upload finishes successfully
addAbstractMethods(this, 'done');
}
public abort(): void {}
public done(): void {}
}
/**
* This function intercepts abstract calls and replays them allowing.
* us to attach those functions after we are ready to do so
* @param {*} self
* @param {*} name
*/
// Perhaps someone knows a better way to write this
function addAbstractMethods(self: any, name: any): void {
self._called_methods = self._called_methods || {};
self.__defineGetter__(name, function () {
return function (): void {
self._called_methods[name] = true;
};
});
self.__defineSetter__(name, function (fn: any) {
delete self[name];
self[name] = fn;
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
if (self._called_methods && self._called_methods[name]) {
delete self._called_methods[name];
self[name]();
}
});
}
import { Readable, Transform } from 'stream';
/**
* Converts a buffer stream to a string.
*/
const readableToString = async (stream) => {
const readableToString = async (stream: Readable) => {
const chunks: Buffer[] = [];
for await (let chunk of stream) {
chunks.push(Buffer.from(chunk));
@@ -106,4 +25,4 @@ const transformObjectToString = () => {
});
};
export { ReadTarball, UploadTarball, readableToString, transformObjectToString };
export { readableToString, transformObjectToString };

View File

@@ -29,7 +29,7 @@ export function getByQualityPriorityValue(headerValue: string | undefined | null
}
return acc;
}, [] as any)
.sort(function (a, b) {
.sort(function (a: number[], b: number[]) {
return b[1] - a[1];
});
return header[0];

View File

@@ -45,6 +45,6 @@ warningInstance.create(
'multiple addresses will be deprecated in the next major, only use one'
);
export function emit(code, a?: string, b?: string, c?: string) {
export function emit(code: string, a?: string, b?: string, c?: string) {
warningInstance.emit(code, a, b, c);
}

View File

@@ -1,34 +1,8 @@
import { Stream } from 'stream';
import { ReadTarball, UploadTarball, readableToString } from '../src/stream-utils';
import { readableToString } from '../src/stream-utils';
describe('mystreams', () => {
test('should delay events on ReadTarball abort', (cb) => {
const readTballStream = new ReadTarball({});
readTballStream.abort();
setTimeout(function () {
readTballStream.abort = function (): void {
cb();
};
readTballStream.abort = function (): never {
throw Error('fail');
};
}, 10);
});
test('should delay events on UploadTarball abort', (cb) => {
const uploadTballStream = new UploadTarball({});
uploadTballStream.abort();
setTimeout(function () {
uploadTballStream.abort = function (): void {
cb();
};
uploadTballStream.abort = function (): never {
throw Error('fail');
};
}, 10);
});
test('readableToString single string', async () => {
expect(await readableToString(Stream.Readable.from('foo'))).toEqual('foo');
});

View File

@@ -2,7 +2,8 @@
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./build"
"outDir": "./build",
"noImplicitAny": true
},
"include": ["src/**/*"],
"exclude": ["src/**/*.test.ts"]

View File

@@ -4,7 +4,8 @@
"rootDir": "./src",
"outDir": "./build",
"composite": true,
"declaration": true
"declaration": true,
"noImplicitAny": true
},
"include": ["src/**/*.ts"],
"exclude": ["src/**/*.test.ts"]

View File

@@ -0,0 +1,5 @@
{
"$schema": "https://typedoc.org/schema.json",
"entryPoints": ["src/index.ts"],
"sort": ["source-order"]
}

View File

@@ -1,5 +1,14 @@
# Change Log
## 11.0.0-6-next.18
### Patch Changes
- Updated dependencies [ce013d2f]
- @verdaccio/url@11.0.0-6-next.15
- @verdaccio/core@6.0.0-6-next.49
- @verdaccio/utils@6.0.0-6-next.17
## 11.0.0-6-next.17
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@verdaccio/tarball",
"version": "11.0.0-6-next.17",
"version": "11.0.0-6-next.18",
"description": "tarball utilities resolver",
"keywords": [
"private",
@@ -35,9 +35,9 @@
},
"dependencies": {
"debug": "4.3.4",
"@verdaccio/core": "workspace:6.0.0-6-next.48",
"@verdaccio/url": "workspace:11.0.0-6-next.14",
"@verdaccio/utils": "workspace:6.0.0-6-next.16",
"@verdaccio/core": "workspace:6.0.0-6-next.49",
"@verdaccio/url": "workspace:11.0.0-6-next.15",
"@verdaccio/utils": "workspace:6.0.0-6-next.17",
"lodash": "4.17.21"
},
"devDependencies": {

View File

@@ -1,26 +1,3 @@
# @verdaccio/types
TypeScript definitions for Verdaccio plugins and internal code.
### Usage
```ts
import type {ILocalData, LocalStorage, Logger, Config} from '@verdaccio/types';
class LocalData implements ILocalData {
path: string;
logger: Logger;
data: LocalStorage;
config: Config;
locked: boolean;
...
}
```
### Run docs
Generate the package types documentation at `./docs` folder.
```bash
pnpm build:docs
```

View File

@@ -36,14 +36,13 @@
"scripts": {
"clean": "rimraf ./build",
"test": "pnpm type-check",
"build:docs": "typedoc --options ./typedoc.json --excludeExternals --tsconfig tsconfig.build.json",
"build:docs": "typedoc --options ./typedoc.json --tsconfig tsconfig.build.json",
"type-check": "tsc --noEmit -p tsconfig.build.json",
"build": "tsc --emitDeclarationOnly -p tsconfig.build.json"
},
"devDependencies": {
"@types/node": "16.11.60",
"typedoc": "beta",
"typedoc-plugin-missing-exports": "1.0.0"
"@types/node": "16.11.62",
"typedoc": "0.23.15"
},
"typedoc": {
"entryPoint": "./src/types.ts",

View File

@@ -15,7 +15,7 @@ export interface RemoteUser {
export type StringValue = string | void | null;
// FIXME: error should be export type `VerdaccioError = HttpError & { code: number };`
// instead of AuthError
// instead of VerdaccioError
// but this type is on @verdaccio/core and cannot be used here yet (I don't know why)
export interface HttpError extends Error {
status: number;

View File

@@ -1,22 +0,0 @@
import { Callback, HttpError, RemoteUser } from '../commons';
import { Config } from '../configuration';
export interface AuthPluginPackage {
packageName: string;
packageVersion?: string;
tag?: string;
}
export type AuthError = HttpError & { code: number };
export type AuthAccessCallback = (error: AuthError | null, access: boolean) => void;
export type AuthCallback = (error: AuthError | null, groups: string[] | false) => void;
// @deprecated use IBasicAuth from @verdaccio/auth
export interface IBasicAuth<T> {
config: T & Config;
aesEncrypt(buf: Buffer): Buffer;
authenticate(user: string, password: string, cb: Callback): void;
changePassword(user: string, password: string, newPassword: string, cb: Callback): void;
allow_access(pkg: AuthPluginPackage, user: RemoteUser, callback: Callback): void;
add_user(user: string, password: string, cb: Callback): any;
}

View File

@@ -1,15 +0,0 @@
import { Config, Logger } from '../configuration';
export class Plugin<T> {
public constructor(config: T, options: PluginOptions) {}
}
export interface IPlugin<T> {
// TODO: not used on core yet
version?: string;
}
export interface PluginOptions {
config: Config;
logger: Logger;
}

View File

@@ -1,6 +0,0 @@
import { Manifest } from '../manifest';
import { IPlugin } from './commons';
export interface IPluginStorageFilter<T> extends IPlugin<T> {
filterMetadata(packageInfo: Manifest): Promise<Manifest>;
}

View File

@@ -1,31 +1 @@
import { RemoteUser } from '../commons';
import { AllowAccess } from '../configuration';
import { PackageAccess } from '../manifest';
import { AuthAccessCallback, AuthCallback } from './auth';
import { IPlugin } from './commons';
export interface IPluginAuth<T> extends IPlugin<T> {
/**
* @param props user from Application component
*/
authenticate(user: string, password: string, cb: AuthCallback): void;
adduser?(user: string, password: string, cb: AuthCallback): void;
changePassword?(user: string, password: string, newPassword: string, cb: AuthCallback): void;
allow_publish?(user: RemoteUser, pkg: T & PackageAccess, cb: AuthAccessCallback): void;
allow_access?(user: RemoteUser, pkg: T & PackageAccess, cb: AuthAccessCallback): void;
allow_unpublish?(user: RemoteUser, pkg: T & PackageAccess, cb: AuthAccessCallback): void;
allow_publish?(user: RemoteUser, pkg: AllowAccess & PackageAccess, cb: AuthAccessCallback): void;
allow_access?(user: RemoteUser, pkg: AllowAccess & PackageAccess, cb: AuthAccessCallback): void;
allow_unpublish?(
user: RemoteUser,
pkg: AllowAccess & PackageAccess,
cb: AuthAccessCallback
): void;
apiJWTmiddleware?(helpers: any): Function;
}
export * from './auth';
export * from './storage';
export * from './middleware';
export * from './commons';
export * from './filter';

View File

@@ -1,8 +0,0 @@
import { Config } from '../configuration';
import { IBasicAuth } from './auth';
import { IPlugin } from './commons';
// TODO: convert to generic storage should come from implementation
export interface IPluginMiddleware<T, K> extends IPlugin<T> {
register_middlewares(app: any, auth: IBasicAuth<T>, storage: K): void;
}

View File

@@ -1,17 +1,8 @@
import { PassThrough, PipelinePromise, Readable, Stream, Writable } from 'stream';
import { Callback, CallbackAction, StringValue } from '../commons';
import { Config, Logger } from '../configuration';
import { Manifest, MergeTags, Token, Version } from '../manifest';
import { IPlugin } from './commons';
import { Callback, CallbackAction } from '../commons';
import { Manifest, Token } from '../manifest';
export type StorageList = string[];
export interface LocalStorage {
list: any;
secret: string;
}
export interface ILocalStorage {
add(name: string): void;
remove(name: string): void;
@@ -53,43 +44,3 @@ export type StorageUpdateCallback = (data: Manifest, cb: CallbackAction) => void
export type StorageWriteCallback = (name: string, json: Manifest, callback: Callback) => void;
export type PackageTransformer = (pkg: Manifest) => Manifest;
export type ReadPackageCallback = (err: any | null, data?: Manifest) => void;
export interface ILocalPackageManager {
logger: Logger;
deletePackage(fileName: string): Promise<void>;
removePackage(): Promise<void>;
// next packages migration (this list is meant to replace the callback parent functions)
updatePackage(
packageName: string,
handleUpdate: (manifest: Manifest) => Promise<Manifest>
): Promise<Manifest>;
readPackage(name: string): Promise<Manifest>;
savePackage(pkgName: string, value: Manifest): Promise<void>;
readTarball(pkgName: string, { signal }): Promise<Readable>;
createPackage(name: string, manifest: Manifest): Promise<void>;
writeTarball(tarballName: string, { signal }): Promise<Writable>;
// verify if tarball exist in the storage
hasTarball(fileName: string): Promise<boolean>;
// verify if package exist in the storage
hasPackage(): Promise<boolean>;
}
export type IPackageStorage = ILocalPackageManager | void;
export type IPluginStorage<T> = ILocalData<T>;
export type IPackageStorageManager = ILocalPackageManager;
/**
* @deprecated use @verdaccio/core pluginUtils instead
*/
interface ILocalData<T> extends IPlugin<T>, ITokenActions {
logger: Logger;
config: T;
add(name: string): Promise<void>;
remove(name: string): Promise<void>;
get(): Promise<any>;
init(): Promise<void>;
getSecret(): Promise<string>;
setSecret(secret: string): Promise<any>;
getPackageStorage(packageInfo: string): IPackageStorage;
}

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