diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 86123d2da..e46d91e31 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -134,6 +134,18 @@ More details in the debug section We use [`debug`](https://www.npmjs.com/package/debug) to add helpful debugging output to the code. Each package has it owns namespace. +#### Useful Scripts + +To run the application from the source code, ensure the project has been built with `pnpm build`, once this is done, there are few commands that helps to run server: + +- `pnpm start`: Run the server and the UI with `concurrently`, the + server runs in the port `8000` and the UI on the port `4873`. This command + is useful if you want to contribute mostly on the UI. +- `pnpm debug`: Run the server in debug mode `--inspect`, the UI is included but does not have hot reload. For automatic break use `pnpm debug:break`. +- `pnpm debug:fastify`: To contribute on the [fastify migration](https://github.com/verdaccio/verdaccio/discussions/2155) this is a temporary command for such purpose. +- `pnpm website`: Build the website, for more commands to run the _website_, run `cd website` and then `pnpm serve`, website will run on port `3000`. +- `pnpm docker`: Build the docker image. Requires `docker` command available in your system. + #### Debugging compiled code Currently you can only run pre-compiled packages in debug mode. To enable debug diff --git a/package.json b/package.json index 17155cf34..d98acc2c4 100644 --- a/package.json +++ b/package.json @@ -127,6 +127,7 @@ "_debug:reload": "nodemon -d 3 packages/verdaccio/debug/bootstrap.js", "start:ts": "ts-node packages/verdaccio/src/start.ts -- --listen 8000", "debug": "node --trace-warnings --trace-uncaught --inspect packages/verdaccio/debug/bootstrap.js", + "debug:fastify": "node --trace-warnings --trace-uncaught --inspect packages/verdaccio/debug/bootstrap.js -- fastify-server", "debug:break": "node --trace-warnings --trace-uncaught --inspect-brk packages/verdaccio/debug/bootstrap.js", "changeset": "changeset", "changeset:check": "changeset status --since-master", diff --git a/packages/auth/src/auth.ts b/packages/auth/src/auth.ts index 29eef4381..83f61a06b 100644 --- a/packages/auth/src/auth.ts +++ b/packages/auth/src/auth.ts @@ -52,6 +52,7 @@ const debug = buildDebug('verdaccio:auth'); export interface IBasicAuth { config: T & Config; authenticate(user: string, password: string, cb: Callback): void; + invalidateToken?(token: string): Promise; 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; @@ -83,6 +84,7 @@ export interface IAuth extends IBasicAuth, IAuthMiddleware, TokenEncrypt // eslint-disable-next-line @typescript-eslint/no-explicit-any plugins: any[]; allow_unpublish(pkg: AuthPluginPackage, user: RemoteUser, callback: Callback): void; + invalidateToken(token: string): Promise; } class Auth implements IAuth { @@ -177,6 +179,12 @@ class Auth implements IAuth { } } + public async invalidateToken(token: string) { + // eslint-disable-next-line no-console + console.log('invalidate token pending to implement', token); + return Promise.resolve(); + } + public authenticate(username: string, password: string, cb: Callback): void { const plugins = this.plugins.slice(0); (function next(): void { @@ -413,7 +421,9 @@ class Auth implements IAuth { } // in case auth header does not exist we return anonymous function - req.remote_user = createAnonymousRemoteUser(); + const remoteUser = createAnonymousRemoteUser(); + req.remote_user = remoteUser; + res.locals.remote_user = remoteUser; const { authorization } = req.headers; if (_.isNil(authorization)) { diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index f16739979..05753ed00 100644 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -3,7 +3,7 @@ import { Cli } from 'clipanion'; import { InfoCommand } from './commands/info'; import { InitCommand } from './commands/init'; import { VersionCommand } from './commands/version'; -import { NewServer } from './commands/newServer'; +import { FastifyServer } from './commands/FastifyServer'; import { isVersionValid, MIN_NODE_VERSION } from './utils'; if (process.getuid && process.getuid() === 0) { @@ -28,7 +28,7 @@ const cli = new Cli({ cli.register(InfoCommand); cli.register(InitCommand); cli.register(VersionCommand); -cli.register(NewServer); +cli.register(FastifyServer); cli.runExit(args, Cli.defaultContext); process.on('uncaughtException', function (err) { diff --git a/packages/cli/src/commands/newServer.ts b/packages/cli/src/commands/FastifyServer.ts similarity index 79% rename from packages/cli/src/commands/newServer.ts rename to packages/cli/src/commands/FastifyServer.ts index 0120be4a5..f0b70629d 100644 --- a/packages/cli/src/commands/newServer.ts +++ b/packages/cli/src/commands/FastifyServer.ts @@ -9,9 +9,12 @@ export const DEFAULT_PROCESS_NAME: string = 'verdaccio'; /** * This command is intended to run the server with Fastify * as a migration step. + * More info: https://github.com/verdaccio/verdaccio/discussions/2155 + * To try out. + * pnpm debug:fastify */ -export class NewServer extends Command { - public static paths = [['new']]; +export class FastifyServer extends Command { + public static paths = [['fastify-server']]; private port = Option.String('-l,-p,--listen,--port', { description: 'host:port number to listen on (default: localhost:4873)', @@ -41,9 +44,11 @@ export class NewServer extends Command { this.initLogger(configParsed); process.title = web?.title || DEFAULT_PROCESS_NAME; + // FIXME: need a way to get version of the package. // const { version, name } = require('../../package.json'); const ser = await server({ logger, config: configParsed }); - await ser.listen(4873); + // FIXME: harcoded, this would need to come from the configuration and the --listen flag. + await ser.listen(process.env.PORT || 4873); } catch (err: any) { console.error(err); process.exit(1); diff --git a/packages/core/server/package.json b/packages/core/server/package.json index d9fe71cc1..5c83893a3 100644 --- a/packages/core/server/package.json +++ b/packages/core/server/package.json @@ -34,15 +34,19 @@ "access": "public" }, "dependencies": { + "@verdaccio/core": "workspace:6.0.0-6-next.2", "@verdaccio/config": "workspace:6.0.0-6-next.9", "@verdaccio/auth": "workspace:6.0.0-6-next.13", "@verdaccio/logger": "workspace:6.0.0-6-next.6", "@verdaccio/store": "workspace:6.0.0-6-next.14", + "@verdaccio/tarball": "workspace:11.0.0-6-next.8", + "@verdaccio/utils": "workspace:6.0.0-6-next.7", "abortcontroller-polyfill": "1.7.3", "core-js": "3.17.2", "debug": "4.3.2", "fastify": "3.22.1", "fastify-plugin": "3.0.0", + "lodash": "4.17.21", "semver": "7.3.5" }, "devDependencies": { diff --git a/packages/core/server/src/endpoints/ping.ts b/packages/core/server/src/endpoints/ping.ts index cf45a3e76..470e14509 100644 --- a/packages/core/server/src/endpoints/ping.ts +++ b/packages/core/server/src/endpoints/ping.ts @@ -1,13 +1,13 @@ /* eslint-disable no-console */ /* eslint-disable no-invalid-this */ import { logger } from '@verdaccio/logger'; +import { FastifyInstance } from 'fastify'; -async function pingRoute(fastify) { +async function pingRoute(fastify: FastifyInstance) { fastify.get('/-/ping', async () => { logger.http('ping endpoint'); - // @ts-ignore console.log('-storage->', fastify.storage); - console.log('-config->', fastify.config); + console.log('-config->', fastify.configInstance); return {}; }); } diff --git a/packages/core/server/src/endpoints/search.ts b/packages/core/server/src/endpoints/search.ts index f9b1c8a00..0369ab56e 100644 --- a/packages/core/server/src/endpoints/search.ts +++ b/packages/core/server/src/endpoints/search.ts @@ -1,8 +1,9 @@ /* eslint-disable no-console */ /* eslint-disable no-invalid-this */ import { logger } from '@verdaccio/logger'; +import { FastifyInstance } from 'fastify'; -async function searchRoute(fastify) { +async function searchRoute(fastify: FastifyInstance) { fastify.get('/-/v1/search', async (request, reply) => { // TODO: apply security layer here like in // packages/api/src/v1/search.ts @@ -10,11 +11,11 @@ async function searchRoute(fastify) { // TODO: review which query fields are mandatory const abort = new AbortController(); - - request.on('aborted', () => { + request.socket.on('aborted', () => { abort.abort(); }); - const { url, query } = request; + // @ts-ignore + const { url, query } = request.query; const storage = fastify.storage; const data = await storage.searchManager?.search({ diff --git a/packages/core/server/src/endpoints/user.ts b/packages/core/server/src/endpoints/user.ts new file mode 100644 index 000000000..a1eee5d22 --- /dev/null +++ b/packages/core/server/src/endpoints/user.ts @@ -0,0 +1,130 @@ +/* eslint-disable no-console */ +/* eslint-disable no-invalid-this */ +import _ from 'lodash'; +import { getAuthenticatedMessage, validatePassword } from '@verdaccio/utils'; +import { RemoteUser } from '@verdaccio/types'; +import { logger } from '@verdaccio/logger'; +import { createRemoteUser } from '@verdaccio/config'; +import { getApiToken } from '@verdaccio/auth'; +import buildDebug from 'debug'; +import { FastifyInstance, FastifyRequest } from 'fastify'; + +const debug = buildDebug('verdaccio:api:user'); + +async function userRoute(fastify: FastifyInstance) { + fastify.get('/:org_couchdb_user', async (request, reply) => { + // @ts-expect-error + const message = getAuthenticatedMessage(request.userRemote.name); + logger.info('user authenticated message %o', message); + reply.code(fastify.httpStatuscode.OK); + return { ok: message }; + }); + + fastify.delete('/token/:token', async (request: FastifyRequest, reply) => { + debug('loging out'); + // FIXME: type params correctly + // @ts-ignore + const { token } = request.params; + const userRemote: RemoteUser = request.userRemote; + await fastify.auth.invalidateToken(token); + console.log('userRoute', userRemote); + reply.code(fastify.httpStatuscode.OK); + return { ok: fastify.apiMessage.LOGGED_OUT }; + }); + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + fastify.put<{ + Body: { name: string; password: string }; + }>('/:username', async (request, reply) => { + const { name, password } = request.body; + const remoteName = request.userRemote.name; + if (_.isNil(remoteName) === false && _.isNil(name) === false && remoteName === name) { + // debug('login: no remote user detected'); + fastify.auth.authenticate( + name, + password, + async function callbackAuthenticate(err, user): Promise { + if (err) { + logger.trace( + { name, err }, + 'authenticating for user @{username} failed. Error: @{err.message}' + ); + reply + .code(fastify.httpStatuscode.UNAUTHORIZED) + .send( + fastify.errorUtils.getCode( + fastify.httpStatuscode.UNAUTHORIZED, + fastify.apiError.BAD_USERNAME_PASSWORD + ) + ); + } + const restoredRemoteUser: RemoteUser = createRemoteUser(name, user.groups || []); + const token = await getApiToken( + fastify.auth, + fastify.configInstance, + restoredRemoteUser, + password + ); + debug('login: new token'); + if (!token) { + return reply.send(fastify.errorUtils.getUnauthorized()); + } else { + reply.code(fastify.httpStatuscode.CREATED); + const message = getAuthenticatedMessage(remoteName); + debug('login: created user message %o', message); + reply.send({ + ok: message, + token, + }); + } + } + ); + } else { + if (validatePassword(password) === false) { + debug('adduser: invalid password'); + reply.code(fastify.httpStatuscode.BAD_REQUEST).send( + fastify.errorUtils.getCode( + fastify.httpStatuscode.BAD_REQUEST, + // eslint-disable-next-line new-cap + fastify.apiError.PASSWORD_SHORT() + ) + ); + return; + } + fastify.auth.add_user(name, password, async function (err, user): Promise { + if (err) { + if ( + err.status >= fastify.httpStatuscode.BAD_REQUEST && + err.status < fastify.httpStatuscode.INTERNAL_ERROR + ) { + debug('adduser: error on create user'); + // With npm registering is the same as logging in, + // and npm accepts only an 409 error. + // So, changing status code here. + const addUserError = + fastify.errorUtils.getCode(err.status, err.message) || + fastify.errorUtils.getConflict(err.message); + + reply.send(addUserError); + return; + } + } + const token = + name && password + ? await getApiToken(fastify.auth, fastify.configInstance, user, password) + : undefined; + debug('adduser: new token %o', token); + if (!token) { + return reply.send(fastify.errorUtils.getUnauthorized()); + } + debug('adduser: user has been created'); + reply.code(fastify.httpStatuscode.CREATED).send({ + ok: `user '${name}' created`, + token, + }); + }); + } + }); +} + +export default userRoute; diff --git a/packages/core/server/src/index.ts b/packages/core/server/src/index.ts index 202dc66f6..109520c53 100644 --- a/packages/core/server/src/index.ts +++ b/packages/core/server/src/index.ts @@ -1,5 +1,6 @@ import semver from 'semver'; +// FUTURE: remove when v15 is the minimum requirement if (semver.lte(process.version, 'v15.0.0')) { global.AbortController = require('abortcontroller-polyfill/dist/cjs-ponyfill').AbortController; } diff --git a/packages/core/server/src/plugins/auth.ts b/packages/core/server/src/plugins/auth.ts new file mode 100644 index 000000000..142ab011c --- /dev/null +++ b/packages/core/server/src/plugins/auth.ts @@ -0,0 +1,21 @@ +import fp from 'fastify-plugin'; +import { Config as IConfig } from '@verdaccio/types'; +import { Auth, IAuth } from '@verdaccio/auth'; +import { FastifyInstance } from 'fastify'; + +export default fp( + async function (fastify: FastifyInstance, opts: { config: IConfig; filters?: unknown }) { + const { config } = opts; + const auth: IAuth = new Auth(config); + fastify.decorate('auth', auth); + }, + { + fastify: '>=3.x', + } +); + +declare module 'fastify' { + interface FastifyInstance { + auth: IAuth; + } +} diff --git a/packages/core/server/src/plugins/config.ts b/packages/core/server/src/plugins/config.ts new file mode 100644 index 000000000..40ab1deec --- /dev/null +++ b/packages/core/server/src/plugins/config.ts @@ -0,0 +1,22 @@ +import fp from 'fastify-plugin'; + +import { Config as IConfig, ConfigRuntime } from '@verdaccio/types'; +import { Config as AppConfig } from '@verdaccio/config'; +import { FastifyInstance } from 'fastify'; + +export default fp( + async function (fastify: FastifyInstance, opts: { config: ConfigRuntime }) { + const { config } = opts; + const configInstance: IConfig = new AppConfig(Object.assign({}, config)); + fastify.decorate('configInstance', configInstance); + }, + { + fastify: '>=3.x', + } +); + +declare module 'fastify' { + interface FastifyInstance { + configInstance: IConfig; + } +} diff --git a/packages/core/server/src/plugins/coreUtils.ts b/packages/core/server/src/plugins/coreUtils.ts new file mode 100644 index 000000000..86f6c7143 --- /dev/null +++ b/packages/core/server/src/plugins/coreUtils.ts @@ -0,0 +1,25 @@ +import fp from 'fastify-plugin'; + +import { errorUtils, validatioUtils, API_ERROR, API_MESSAGE, HTTP_STATUS } from '@verdaccio/core'; + +export default fp( + async function (fastify) { + fastify.decorate('errorUtils', errorUtils); + fastify.decorate('apiError', API_ERROR); + fastify.decorate('apiMessage', API_MESSAGE); + fastify.decorate('validatioUtils', validatioUtils); + fastify.decorate('statusCode', HTTP_STATUS); + }, + { + fastify: '>=3.x', + } +); + +declare module 'fastify' { + interface FastifyInstance { + apiError: typeof API_ERROR; + apiMessage: typeof API_MESSAGE; + httpStatuscode: typeof HTTP_STATUS; + errorUtils: typeof errorUtils; + } +} diff --git a/packages/core/server/src/plugins/storage.ts b/packages/core/server/src/plugins/storage.ts index 5f95b3699..4d4d28bb2 100644 --- a/packages/core/server/src/plugins/storage.ts +++ b/packages/core/server/src/plugins/storage.ts @@ -1,10 +1,23 @@ +import fp from 'fastify-plugin'; +import { Config as IConfig } from '@verdaccio/types'; import { Storage } from '@verdaccio/store'; +import { FastifyInstance } from 'fastify'; -export async function storageService(fastify, opts, done) { - const { config, filters } = opts; - // @ts-ignore - const storage: Storage = new Storage(config); - await storage.init(config, filters ?? {}); - fastify.decorate('storage', storage); - done(); +export default fp( + async function (fastify: FastifyInstance, opts: { config: IConfig; filters?: unknown }) { + const { config } = opts; + const storage: Storage = new Storage(config); + // @ts-ignore + await storage.init(config, {}); + fastify.decorate('storage', storage); + }, + { + fastify: '>=3.x', + } +); + +declare module 'fastify' { + interface FastifyInstance { + storage: Storage; + } } diff --git a/packages/core/server/src/server.ts b/packages/core/server/src/server.ts index 171fdbc72..9e01e1db6 100644 --- a/packages/core/server/src/server.ts +++ b/packages/core/server/src/server.ts @@ -1,39 +1,51 @@ -import { Config as IConfig } from '@verdaccio/types'; -import { Config as AppConfig } from '@verdaccio/config'; +import { Config as IConfig, RemoteUser } from '@verdaccio/types'; +import { Config as AppConfig, createAnonymousRemoteUser } from '@verdaccio/config'; import fastify from 'fastify'; import buildDebug from 'debug'; -import fp from 'fastify-plugin'; -import ping from './endpoints/ping'; import search from './endpoints/search'; -import { storageService } from './plugins/storage'; +import storagePlugin from './plugins/storage'; +import authPlugin from './plugins/auth'; +import coreUtils from './plugins/coreUtils'; +import configPlugin from './plugins/config'; +import ping from './endpoints/ping'; +import user from './endpoints/user'; const debug = buildDebug('verdaccio:fastify'); async function startServer({ logger, config }) { + // eslint-disable-next-line prettier/prettier const configInstance: IConfig = new AppConfig(Object.assign({}, config)); debug('start server'); - const app = fastify({ logger }); - - app.decorate('config', configInstance); - app.register(fp(storageService), { config: configInstance }); + const fastifyInstance = fastify({ logger }); + fastifyInstance.decorateRequest('userRemote', createAnonymousRemoteUser()); + fastifyInstance.register(configPlugin, { config }); + fastifyInstance.register(coreUtils); + fastifyInstance.register(authPlugin, { config: configInstance }); + fastifyInstance.register(storagePlugin, { config: configInstance }); // api - app.register((instance, opts, done) => { - instance.decorate('utility', new Map()); + fastifyInstance.register((instance, opts, done) => { instance.register(ping); + instance.register(user, { prefix: '/-/user' }); instance.register(search); done(); }); // web - app.register((instance, opts, done) => { + fastifyInstance.register((instance, opts, done) => { instance.register(ping, { prefix: '/web' }); done(); }); - return app; + return fastifyInstance; +} + +declare module 'fastify' { + interface FastifyRequest { + userRemote: RemoteUser; + } } export default startServer; diff --git a/packages/core/server/tsconfig.json b/packages/core/server/tsconfig.json index 20dd33fae..2242b0127 100644 --- a/packages/core/server/tsconfig.json +++ b/packages/core/server/tsconfig.json @@ -8,16 +8,22 @@ "exclude": ["src/**/*.test.ts"], "references": [ { - "path": "../store" + "path": "../../store" }, { - "path": "../config" + "path": "../../config" }, { - "path": "../auth" + "path": "../../auth" }, { - "path": "../logger" + "path": "../../logger" + }, + { + "path": "../../utils" + }, + { + "path": "../../core/core" } ] } diff --git a/packages/core/types/package.json b/packages/core/types/package.json index 66ca199c5..0bd204ed2 100644 --- a/packages/core/types/package.json +++ b/packages/core/types/package.json @@ -38,7 +38,8 @@ "build": "exit 0" }, "devDependencies": { - "@types/node": "14.6.0" + "@types/node": "14.6.0", + "tsd": "0.18.0" }, "funding": { "type": "opencollective", diff --git a/packages/server/src/server.ts b/packages/server/src/server.ts index 7dbdce453..1329951c5 100644 --- a/packages/server/src/server.ts +++ b/packages/server/src/server.ts @@ -7,7 +7,7 @@ import RateLimit from 'express-rate-limit'; import { HttpError } from 'http-errors'; import { loadPlugin } from '@verdaccio/loaders'; -import { Auth } from '@verdaccio/auth'; +import { Auth, IBasicAuth } from '@verdaccio/auth'; import apiEndpoint from '@verdaccio/api'; import { API_ERROR, HTTP_STATUS, errorUtils } from '@verdaccio/core'; import { Config as AppConfig } from '@verdaccio/config'; @@ -15,7 +15,6 @@ import { Config as AppConfig } from '@verdaccio/config'; import webMiddleware from '@verdaccio/web'; import { ConfigRuntime } from '@verdaccio/types'; -import { IAuth, IBasicAuth } from '@verdaccio/auth'; import { Storage } from '@verdaccio/store'; import { logger } from '@verdaccio/logger'; import { log, final, errorReportingMiddleware } from '@verdaccio/middleware'; @@ -33,7 +32,7 @@ export interface IPluginMiddleware extends IPlugin { const debug = buildDebug('verdaccio:server'); const defineAPI = function (config: IConfig, storage: Storage): any { - const auth: IAuth = new Auth(config); + const auth: Auth = new Auth(config); const app: Application = express(); const limiter = new RateLimit(config.serverSettings.rateLimit); // run in production mode by default, just in case diff --git a/packages/utils/src/replace-lodash.ts b/packages/utils/src/replace-lodash.ts index b8bbaf8e2..cc0bd5f89 100644 --- a/packages/utils/src/replace-lodash.ts +++ b/packages/utils/src/replace-lodash.ts @@ -1,5 +1,5 @@ export function isNil(value: any): boolean { - return value == null; + return value === null || typeof value === 'undefined'; } export function isFunction(value): boolean { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f8bd90884..6007b4384 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -348,26 +348,34 @@ importers: '@types/node': 16.9.1 '@verdaccio/auth': workspace:6.0.0-6-next.13 '@verdaccio/config': workspace:6.0.0-6-next.9 + '@verdaccio/core': workspace:6.0.0-6-next.2 '@verdaccio/logger': workspace:6.0.0-6-next.6 '@verdaccio/store': workspace:6.0.0-6-next.14 + '@verdaccio/tarball': workspace:11.0.0-6-next.8 '@verdaccio/types': workspace:11.0.0-6-next.9 + '@verdaccio/utils': workspace:6.0.0-6-next.7 abortcontroller-polyfill: 1.7.3 core-js: 3.17.2 debug: 4.3.2 fastify: 3.22.1 fastify-plugin: 3.0.0 + lodash: 4.17.21 semver: 7.3.5 ts-node: 10.2.1 dependencies: '@verdaccio/auth': link:../../auth '@verdaccio/config': link:../../config + '@verdaccio/core': link:../core '@verdaccio/logger': link:../../logger '@verdaccio/store': link:../../store + '@verdaccio/tarball': link:../tarball + '@verdaccio/utils': link:../../utils abortcontroller-polyfill: 1.7.3 core-js: 3.17.2 debug: 4.3.2 fastify: 3.22.1 fastify-plugin: 3.0.0 + lodash: 4.17.21 semver: 7.3.5 devDependencies: '@types/node': 16.9.1 @@ -402,8 +410,10 @@ importers: packages/core/types: specifiers: '@types/node': 14.6.0 + tsd: 0.18.0 devDependencies: '@types/node': 14.6.0 + tsd: 0.18.0 packages/core/url: specifiers: @@ -6085,6 +6095,11 @@ packages: resolution: {integrity: sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==} dev: true + /@tsd/typescript/4.4.4: + resolution: {integrity: sha512-XNaotnbhU6sKSXYg9rVz4L9i9g+j+x1IIgMPztK8KumtMEsrLXcqPBKp/qzmUKwAZEqgHs4+TTz90dUu5/aIqQ==} + hasBin: true + dev: true + /@types/activedirectory2/1.2.3: resolution: {integrity: sha512-yZERTOJFrOAax2HbDyBBhAKyUEa1PC/GXMe9UGBGyeOF0ZRRBKnIMNXVAYfveJMyrhUBhdRoObwe3CBPoekyjQ==} dependencies: @@ -6179,6 +6194,13 @@ packages: '@types/estree': 0.0.50 '@types/json-schema': 7.0.8 + /@types/eslint/7.28.2: + resolution: {integrity: sha512-KubbADPkfoU75KgKeKLsFHXnU4ipH7wYg0TRT33NK3N3yiu7jlFAAoygIWBV+KbuHx/G+AvuGX6DllnK35gfJA==} + dependencies: + '@types/estree': 0.0.50 + '@types/json-schema': 7.0.8 + dev: true + /@types/estree/0.0.47: resolution: {integrity: sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg==} dev: false @@ -10886,6 +10908,20 @@ packages: eslint: 7.32.0 dev: false + /eslint-formatter-pretty/4.1.0: + resolution: {integrity: sha512-IsUTtGxF1hrH6lMWiSl1WbGaiP01eT6kzywdY1U+zLc0MP+nwEnUiS9UI8IaOTUhTeQJLlCEWIbXINBH4YJbBQ==} + engines: {node: '>=10'} + dependencies: + '@types/eslint': 7.28.2 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + eslint-rule-docs: 1.1.231 + log-symbols: 4.1.0 + plur: 4.0.0 + string-width: 4.2.2 + supports-hyperlinks: 2.2.0 + dev: true + /eslint-import-resolver-node/0.3.4: resolution: {integrity: sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==} dependencies: @@ -11048,6 +11084,10 @@ packages: engines: {node: '>=4.0.0'} dev: false + /eslint-rule-docs/1.1.231: + resolution: {integrity: sha512-egHz9A1WG7b8CS0x1P6P/Rj5FqZOjray/VjpJa14tMZalfRKvpE2ONJ3plCM7+PcinmU4tcmbPLv0VtwzSdLVA==} + dev: true + /eslint-scope/5.1.1: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} @@ -13129,6 +13169,11 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} + /irregular-plurals/3.3.0: + resolution: {integrity: sha512-MVBLKUTangM3EfRPFROhmWQQKRDsrgI83J8GS3jXy+OwYqiR2/aoWndYQ5416jLE3uaGgLH7ncme3X9y09gZ3g==} + engines: {node: '>=8'} + dev: true + /is-absolute-url/3.0.3: resolution: {integrity: sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==} engines: {node: '>=8'} @@ -16512,6 +16557,13 @@ packages: semver-compare: 1.0.0 dev: true + /plur/4.0.0: + resolution: {integrity: sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg==} + engines: {node: '>=10'} + dependencies: + irregular-plurals: 3.3.0 + dev: true + /pn/1.1.0: resolution: {integrity: sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==} dev: true @@ -20478,6 +20530,19 @@ packages: strip-bom: 3.0.0 dev: false + /tsd/0.18.0: + resolution: {integrity: sha512-UIkxm2CLmSjXlQs4zqxgVV9UmzK8VgJ63eBpgkH/ZsMkiUdzxxHvdCCg8F314HDxzfQl2muJEy/TEcXHIFIPXg==} + engines: {node: '>=12'} + hasBin: true + dependencies: + '@tsd/typescript': 4.4.4 + eslint-formatter-pretty: 4.1.0 + globby: 11.0.4 + meow: 9.0.0 + path-exists: 4.0.0 + read-pkg-up: 7.0.1 + dev: true + /tslib/1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}