@verdaccio/web 9.0.0-next-9.21 → 9.0.0-next-9.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- declare const _default: (auth: any, storage: any, config: any) => import('express-serve-static-core').Router;
2
1
  export default _default;
2
+ declare function _default(auth: any, storage: any, config: any): import("express-serve-static-core").Router;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/api/index.ts"],"sourcesContent":["import { Router } from 'express';\n\nimport { WebUrlsNamespace, rateLimit } from '@verdaccio/middleware';\n\nimport { hasLogin } from '../web-utils';\nimport packageApi from './package';\nimport readme from './readme';\nimport search from './search';\nimport sidebar from './sidebar';\nimport user from './user';\n\nexport default (auth, storage, config) => {\n const route = Router(); /* eslint new-cap: 0 */\n route.use(\n WebUrlsNamespace.data,\n rateLimit({\n windowMs: 2 * 60 * 1000, // 2 minutes\n max: 5000, // limit each IP to 1000 requests per windowMs\n ...config?.web?.rateLimit,\n })\n );\n route.use(WebUrlsNamespace.data, packageApi(storage, auth, config));\n route.use(WebUrlsNamespace.data, search(storage, auth));\n route.use(WebUrlsNamespace.data, sidebar(config, storage, auth));\n route.use(WebUrlsNamespace.data, readme(storage, auth));\n if (hasLogin(config)) {\n route.use(WebUrlsNamespace.sec, user(auth, config, storage));\n }\n return route;\n};\n"],"mappings":";;;;;;;;;AAWA,IAAA,eAAgB,MAAM,SAAS,WAAW;CACxC,MAAM,SAAA,GAAA,QAAA,QAAe;CACrB,MAAM,IACJ,sBAAA,iBAAiB,OAAA,GAAA,sBAAA,WACP;EACR,UAAU,MAAS;EACnB,KAAK;EACL,GAAG,QAAQ,KAAK;CAClB,CAAC,CACH;CACA,MAAM,IAAI,sBAAA,iBAAiB,MAAM,gBAAA,QAAW,SAAS,MAAM,MAAM,CAAC;CAClE,MAAM,IAAI,sBAAA,iBAAiB,MAAM,eAAA,QAAO,SAAS,IAAI,CAAC;CACtD,MAAM,IAAI,sBAAA,iBAAiB,MAAM,gBAAA,QAAQ,QAAQ,SAAS,IAAI,CAAC;CAC/D,MAAM,IAAI,sBAAA,iBAAiB,MAAM,eAAA,QAAO,SAAS,IAAI,CAAC;CACtD,IAAI,kBAAA,SAAS,MAAM,GACjB,MAAM,IAAI,sBAAA,iBAAiB,KAAK,aAAA,QAAK,MAAM,QAAQ,OAAO,CAAC;CAE7D,OAAO;AACT"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/api/index.ts"],"sourcesContent":["import { Router } from 'express';\n\nimport { WebUrlsNamespace, rateLimit } from '@verdaccio/middleware';\n\nimport { hasLogin } from '../web-utils';\nimport packageApi from './package';\nimport readme from './readme';\nimport search from './search';\nimport sidebar from './sidebar';\nimport user from './user';\n\nexport default (auth, storage, config) => {\n const route = Router(); /* eslint new-cap: 0 */\n route.use(\n WebUrlsNamespace.data,\n rateLimit({\n windowMs: 2 * 60 * 1000, // 2 minutes\n max: 5000, // limit each IP to 1000 requests per windowMs\n ...config?.web?.rateLimit,\n })\n );\n route.use(WebUrlsNamespace.data, packageApi(storage, auth, config));\n route.use(WebUrlsNamespace.data, search(storage, auth));\n route.use(WebUrlsNamespace.data, sidebar(config, storage, auth));\n route.use(WebUrlsNamespace.data, readme(storage, auth));\n if (hasLogin(config)) {\n route.use(WebUrlsNamespace.sec, user(auth, config, storage));\n }\n return route;\n};\n"],"mappings":";;;;;;;;;AAWA,IAAA,eAAgB,MAAM,SAAS,WAAW;CACxC,MAAM,SAAA,GAAA,QAAA,OAAA,CAAe;CACrB,MAAM,IACJ,sBAAA,iBAAiB,OAAA,GAAA,sBAAA,UAAA,CACP;EACR,UAAU,MAAS;EACnB,KAAK;EACL,GAAG,QAAQ,KAAK;CAClB,CAAC,CACH;CACA,MAAM,IAAI,sBAAA,iBAAiB,MAAM,gBAAA,QAAW,SAAS,MAAM,MAAM,CAAC;CAClE,MAAM,IAAI,sBAAA,iBAAiB,MAAM,eAAA,QAAO,SAAS,IAAI,CAAC;CACtD,MAAM,IAAI,sBAAA,iBAAiB,MAAM,gBAAA,QAAQ,QAAQ,SAAS,IAAI,CAAC;CAC/D,MAAM,IAAI,sBAAA,iBAAiB,MAAM,eAAA,QAAO,SAAS,IAAI,CAAC;CACtD,IAAI,kBAAA,SAAS,MAAM,GACjB,MAAM,IAAI,sBAAA,iBAAiB,KAAK,aAAA,QAAK,MAAM,QAAQ,OAAO,CAAC;CAE7D,OAAO;AACT"}
@@ -1,8 +1,8 @@
1
1
  import { Router } from 'express';
2
- import { Auth } from '@verdaccio/auth';
2
+ import type { Auth } from '@verdaccio/auth';
3
3
  import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '@verdaccio/middleware';
4
- import { Storage } from '@verdaccio/store';
5
- import { Config } from '@verdaccio/types';
4
+ import type { Storage } from '@verdaccio/store';
5
+ import type { Config } from '@verdaccio/types';
6
6
  export { $RequestExtend, $ResponseExtend, $NextFunctionVer };
7
7
  declare function addPackageWebApi(storage: Storage, auth: Auth, config: Config): Router;
8
8
  export default addPackageWebApi;
@@ -1 +1 @@
1
- {"version":3,"file":"package.js","names":[],"sources":["../../src/api/package.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Router } from 'express';\nimport { isNil } from 'lodash-es';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { createAnonymousRemoteUser } from '@verdaccio/config';\nimport { logger } from '@verdaccio/logger';\nimport {\n $NextFunctionVer,\n $RequestExtend,\n $ResponseExtend,\n WebUrls,\n getRequestOptions,\n} from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport { getLocalRegistryTarballUri } from '@verdaccio/tarball';\nimport type { Config, RemoteUser, Version } from '@verdaccio/types';\n\nimport { formatAuthor, generateGravatarUrl } from '../author-utils';\nimport { hasLogin, sortByName, sortByTime } from '../web-utils';\n\nexport { $RequestExtend, $ResponseExtend, $NextFunctionVer }; // Was required by other packages\n\nconst getField = (field = 'name') => {\n return field === 'name' || field === 'time' ? field : 'name';\n};\n\nconst getOrder = (order = 'asc') => {\n return order === 'asc';\n};\n\nconst debug = buildDebug('verdaccio:web:api:package');\n\nfunction addPackageWebApi(storage: Storage, auth: Auth, config: Config): Router {\n const isLoginEnabled = hasLogin(config);\n debug('is login enabled: %o', isLoginEnabled);\n const pkgRouter = Router(); /* eslint new-cap: 0 */\n const anonymousRemoteUser: RemoteUser = createAnonymousRemoteUser();\n\n debug('initialized package web api');\n const checkAllow = (name: string, remoteUser: RemoteUser): Promise<boolean> =>\n new Promise((resolve, reject): void => {\n const remoteUserAccess = !isLoginEnabled ? anonymousRemoteUser : remoteUser;\n try {\n auth.allow_access({ packageName: name }, remoteUserAccess, (err, allowed): void => {\n if (err) {\n resolve(false);\n }\n return resolve(allowed as boolean);\n });\n } catch (err: any) {\n reject(err);\n }\n });\n\n async function processPackages(packages: Version[] = [], req): Promise<Version[]> {\n const permissions: Version[] = [];\n const packagesToProcess = packages.slice();\n debug('process packages %o', packagesToProcess.length);\n const requestOptions = getRequestOptions(req);\n for (const pkg of packagesToProcess) {\n const pkgCopy = { ...pkg };\n pkgCopy.author = formatAuthor(pkg.author);\n try {\n if (await checkAllow(pkg.name, req.remote_user)) {\n if (config.web) {\n // @ts-ignore\n pkgCopy.author.avatar = generateGravatarUrl(pkgCopy.author.email, config.web.gravatar);\n }\n // convert any remote dist to a local reference\n // eg: if the dist points to npmjs, switch to localhost:4873/prefix/etc.tar.gz\n if (!isNil(pkgCopy.dist) && pkgCopy.dist.tarball !== null) {\n pkgCopy.dist.tarball = getLocalRegistryTarballUri(\n pkgCopy.dist.tarball,\n pkg.name,\n requestOptions,\n config?.url_prefix\n );\n }\n permissions.push(pkgCopy);\n }\n } catch (err: any) {\n debug('process packages error %o', err);\n logger.error(\n { name: pkg.name, error: err },\n 'permission process for @{name} has failed: @{error}'\n );\n throw err;\n }\n }\n\n debug('allowed %o packages', permissions.length);\n return permissions;\n }\n\n // Get list of all visible package\n pkgRouter.get(\n WebUrls.packages_all,\n async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n debug('hit package web api %o');\n\n try {\n const localPackages: Version[] = await storage.getLocalDatabase();\n\n const field = getField(config?.web?.sort_field);\n const order = getOrder(config?.web?.sort_packages);\n debug('order by %o %o', field, order);\n const pkgs = await processPackages(localPackages, req);\n if (field === 'name') next(sortByName(pkgs, order));\n else next(sortByTime(pkgs, order));\n } catch (error: any) {\n next(error);\n }\n }\n );\n\n return pkgRouter;\n}\n\nexport default addPackageWebApi;\n"],"mappings":";;;;;;;;;;;;AAuBA,IAAM,YAAY,QAAQ,WAAW;CACnC,OAAO,UAAU,UAAU,UAAU,SAAS,QAAQ;AACxD;AAEA,IAAM,YAAY,QAAQ,UAAU;CAClC,OAAO,UAAU;AACnB;AAEA,IAAM,WAAA,GAAA,MAAA,SAAmB,2BAA2B;AAEpD,SAAS,iBAAiB,SAAkB,MAAY,QAAwB;CAC9E,MAAM,iBAAiB,kBAAA,SAAS,MAAM;CACtC,QAAM,wBAAwB,cAAc;CAC5C,MAAM,aAAA,GAAA,QAAA,QAAmB;CACzB,MAAM,uBAAA,GAAA,kBAAA,2BAA4D;CAElE,QAAM,6BAA6B;CACnC,MAAM,cAAc,MAAc,eAChC,IAAI,SAAS,SAAS,WAAiB;EACrC,MAAM,mBAAmB,CAAC,iBAAiB,sBAAsB;EACjE,IAAI;GACF,KAAK,aAAa,EAAE,aAAa,KAAK,GAAG,mBAAmB,KAAK,YAAkB;IACjF,IAAI,KACF,QAAQ,KAAK;IAEf,OAAO,QAAQ,OAAkB;GACnC,CAAC;EACH,SAAS,KAAU;GACjB,OAAO,GAAG;EACZ;CACF,CAAC;CAEH,eAAe,gBAAgB,WAAsB,CAAC,GAAG,KAAyB;EAChF,MAAM,cAAyB,CAAC;EAChC,MAAM,oBAAoB,SAAS,MAAM;EACzC,QAAM,uBAAuB,kBAAkB,MAAM;EACrD,MAAM,kBAAA,GAAA,sBAAA,mBAAmC,GAAG;EAC5C,KAAK,MAAM,OAAO,mBAAmB;GACnC,MAAM,UAAU,EAAE,GAAG,IAAI;GACzB,QAAQ,SAAS,qBAAA,aAAa,IAAI,MAAM;GACxC,IAAI;IACF,IAAI,MAAM,WAAW,IAAI,MAAM,IAAI,WAAW,GAAG;KAC/C,IAAI,OAAO,KAET,QAAQ,OAAO,SAAS,qBAAA,oBAAoB,QAAQ,OAAO,OAAO,OAAO,IAAI,QAAQ;KAIvF,IAAI,EAAA,GAAA,UAAA,OAAO,QAAQ,IAAI,KAAK,QAAQ,KAAK,YAAY,MACnD,QAAQ,KAAK,WAAA,GAAA,mBAAA,4BACX,QAAQ,KAAK,SACb,IAAI,MACJ,gBACA,QAAQ,UACV;KAEF,YAAY,KAAK,OAAO;IAC1B;GACF,SAAS,KAAU;IACjB,QAAM,6BAA6B,GAAG;IACtC,kBAAA,OAAO,MACL;KAAE,MAAM,IAAI;KAAM,OAAO;IAAI,GAC7B,qDACF;IACA,MAAM;GACR;EACF;EAEA,QAAM,uBAAuB,YAAY,MAAM;EAC/C,OAAO;CACT;CAGA,UAAU,IACR,sBAAA,QAAQ,cACR,eACE,KACA,KACA,MACe;EACf,QAAM,wBAAwB;EAE9B,IAAI;GACF,MAAM,gBAA2B,MAAM,QAAQ,iBAAiB;GAEhE,MAAM,QAAQ,SAAS,QAAQ,KAAK,UAAU;GAC9C,MAAM,QAAQ,SAAS,QAAQ,KAAK,aAAa;GACjD,QAAM,kBAAkB,OAAO,KAAK;GACpC,MAAM,OAAO,MAAM,gBAAgB,eAAe,GAAG;GACrD,IAAI,UAAU,QAAQ,KAAK,kBAAA,WAAW,MAAM,KAAK,CAAC;QAC7C,KAAK,kBAAA,WAAW,MAAM,KAAK,CAAC;EACnC,SAAS,OAAY;GACnB,KAAK,KAAK;EACZ;CACF,CACF;CAEA,OAAO;AACT"}
1
+ {"version":3,"file":"package.js","names":[],"sources":["../../src/api/package.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Router } from 'express';\nimport { isNil } from 'lodash-es';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { createAnonymousRemoteUser } from '@verdaccio/config';\nimport { logger } from '@verdaccio/logger';\nimport {\n $NextFunctionVer,\n $RequestExtend,\n $ResponseExtend,\n WebUrls,\n getRequestOptions,\n} from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport { getLocalRegistryTarballUri } from '@verdaccio/tarball';\nimport type { Config, RemoteUser, Version } from '@verdaccio/types';\n\nimport { formatAuthor, generateGravatarUrl } from '../author-utils';\nimport { hasLogin, sortByName, sortByTime } from '../web-utils';\n\nexport { $RequestExtend, $ResponseExtend, $NextFunctionVer }; // Was required by other packages\n\nconst getField = (field = 'name') => {\n return field === 'name' || field === 'time' ? field : 'name';\n};\n\nconst getOrder = (order = 'asc') => {\n return order === 'asc';\n};\n\nconst debug = buildDebug('verdaccio:web:api:package');\n\nfunction addPackageWebApi(storage: Storage, auth: Auth, config: Config): Router {\n const isLoginEnabled = hasLogin(config);\n debug('is login enabled: %o', isLoginEnabled);\n const pkgRouter = Router(); /* eslint new-cap: 0 */\n const anonymousRemoteUser: RemoteUser = createAnonymousRemoteUser();\n\n debug('initialized package web api');\n const checkAllow = (name: string, remoteUser: RemoteUser): Promise<boolean> =>\n new Promise((resolve, reject): void => {\n const remoteUserAccess = !isLoginEnabled ? anonymousRemoteUser : remoteUser;\n try {\n auth.allow_access({ packageName: name }, remoteUserAccess, (err, allowed): void => {\n if (err) {\n resolve(false);\n }\n return resolve(allowed as boolean);\n });\n } catch (err: any) {\n reject(err);\n }\n });\n\n async function processPackages(packages: Version[] = [], req): Promise<Version[]> {\n const permissions: Version[] = [];\n const packagesToProcess = packages.slice();\n debug('process packages %o', packagesToProcess.length);\n const requestOptions = getRequestOptions(req);\n for (const pkg of packagesToProcess) {\n const pkgCopy = { ...pkg };\n pkgCopy.author = formatAuthor(pkg.author);\n try {\n if (await checkAllow(pkg.name, req.remote_user)) {\n if (config.web) {\n // @ts-ignore\n pkgCopy.author.avatar = generateGravatarUrl(pkgCopy.author.email, config.web.gravatar);\n }\n // convert any remote dist to a local reference\n // eg: if the dist points to npmjs, switch to localhost:4873/prefix/etc.tar.gz\n if (!isNil(pkgCopy.dist) && pkgCopy.dist.tarball !== null) {\n pkgCopy.dist.tarball = getLocalRegistryTarballUri(\n pkgCopy.dist.tarball,\n pkg.name,\n requestOptions,\n config?.url_prefix\n );\n }\n permissions.push(pkgCopy);\n }\n } catch (err: any) {\n debug('process packages error %o', err);\n logger.error(\n { name: pkg.name, error: err },\n 'permission process for @{name} has failed: @{error}'\n );\n throw err;\n }\n }\n\n debug('allowed %o packages', permissions.length);\n return permissions;\n }\n\n // Get list of all visible package\n pkgRouter.get(\n WebUrls.packages_all,\n async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n debug('hit package web api %o');\n\n try {\n const localPackages: Version[] = await storage.getLocalDatabase();\n\n const field = getField(config?.web?.sort_field);\n const order = getOrder(config?.web?.sort_packages);\n debug('order by %o %o', field, order);\n const pkgs = await processPackages(localPackages, req);\n if (field === 'name') next(sortByName(pkgs, order));\n else next(sortByTime(pkgs, order));\n } catch (error: any) {\n next(error);\n }\n }\n );\n\n return pkgRouter;\n}\n\nexport default addPackageWebApi;\n"],"mappings":";;;;;;;;;;;;AAuBA,IAAM,YAAY,QAAQ,WAAW;CACnC,OAAO,UAAU,UAAU,UAAU,SAAS,QAAQ;AACxD;AAEA,IAAM,YAAY,QAAQ,UAAU;CAClC,OAAO,UAAU;AACnB;AAEA,IAAM,WAAA,GAAA,MAAA,QAAA,CAAmB,2BAA2B;AAEpD,SAAS,iBAAiB,SAAkB,MAAY,QAAwB;CAC9E,MAAM,iBAAiB,kBAAA,SAAS,MAAM;CACtC,QAAM,wBAAwB,cAAc;CAC5C,MAAM,aAAA,GAAA,QAAA,OAAA,CAAmB;CACzB,MAAM,uBAAA,GAAA,kBAAA,0BAAA,CAA4D;CAElE,QAAM,6BAA6B;CACnC,MAAM,cAAc,MAAc,eAChC,IAAI,SAAS,SAAS,WAAiB;EACrC,MAAM,mBAAmB,CAAC,iBAAiB,sBAAsB;EACjE,IAAI;GACF,KAAK,aAAa,EAAE,aAAa,KAAK,GAAG,mBAAmB,KAAK,YAAkB;IACjF,IAAI,KACF,QAAQ,KAAK;IAEf,OAAO,QAAQ,OAAkB;GACnC,CAAC;EACH,SAAS,KAAU;GACjB,OAAO,GAAG;EACZ;CACF,CAAC;CAEH,eAAe,gBAAgB,WAAsB,CAAC,GAAG,KAAyB;EAChF,MAAM,cAAyB,CAAC;EAChC,MAAM,oBAAoB,SAAS,MAAM;EACzC,QAAM,uBAAuB,kBAAkB,MAAM;EACrD,MAAM,kBAAA,GAAA,sBAAA,kBAAA,CAAmC,GAAG;EAC5C,KAAK,MAAM,OAAO,mBAAmB;GACnC,MAAM,UAAU,EAAE,GAAG,IAAI;GACzB,QAAQ,SAAS,qBAAA,aAAa,IAAI,MAAM;GACxC,IAAI;IACF,IAAI,MAAM,WAAW,IAAI,MAAM,IAAI,WAAW,GAAG;KAC/C,IAAI,OAAO,KAET,QAAQ,OAAO,SAAS,qBAAA,oBAAoB,QAAQ,OAAO,OAAO,OAAO,IAAI,QAAQ;KAIvF,IAAI,EAAA,GAAA,UAAA,MAAA,CAAO,QAAQ,IAAI,KAAK,QAAQ,KAAK,YAAY,MACnD,QAAQ,KAAK,WAAA,GAAA,mBAAA,2BAAA,CACX,QAAQ,KAAK,SACb,IAAI,MACJ,gBACA,QAAQ,UACV;KAEF,YAAY,KAAK,OAAO;IAC1B;GACF,SAAS,KAAU;IACjB,QAAM,6BAA6B,GAAG;IACtC,kBAAA,OAAO,MACL;KAAE,MAAM,IAAI;KAAM,OAAO;IAAI,GAC7B,qDACF;IACA,MAAM;GACR;EACF;EAEA,QAAM,uBAAuB,YAAY,MAAM;EAC/C,OAAO;CACT;CAGA,UAAU,IACR,sBAAA,QAAQ,cACR,eACE,KACA,KACA,MACe;EACf,QAAM,wBAAwB;EAE9B,IAAI;GACF,MAAM,gBAA2B,MAAM,QAAQ,iBAAiB;GAEhE,MAAM,QAAQ,SAAS,QAAQ,KAAK,UAAU;GAC9C,MAAM,QAAQ,SAAS,QAAQ,KAAK,aAAa;GACjD,QAAM,kBAAkB,OAAO,KAAK;GACpC,MAAM,OAAO,MAAM,gBAAgB,eAAe,GAAG;GACrD,IAAI,UAAU,QAAQ,KAAK,kBAAA,WAAW,MAAM,KAAK,CAAC;QAC7C,KAAK,kBAAA,WAAW,MAAM,KAAK,CAAC;EACnC,SAAS,OAAY;GACnB,KAAK,KAAK;EACZ;CACF,CACF;CAEA,OAAO;AACT"}
@@ -1,7 +1,7 @@
1
1
  import { Router } from 'express';
2
- import { Auth } from '@verdaccio/auth';
2
+ import type { Auth } from '@verdaccio/auth';
3
3
  import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '@verdaccio/middleware';
4
- import { Storage } from '@verdaccio/store';
4
+ import type { Storage } from '@verdaccio/store';
5
5
  export { $RequestExtend, $ResponseExtend, $NextFunctionVer };
6
6
  export declare const NOT_README_FOUND = "ERROR: No README data found!";
7
7
  declare function addReadmeWebApi(storage: Storage, auth: Auth): Router;
@@ -41,9 +41,9 @@ function addReadmeWebApi(storage, auth) {
41
41
  const pkgRouter = (0, express.Router)();
42
42
  pkgRouter.get([_verdaccio_middleware.WebUrls.readme_package_scoped_version, _verdaccio_middleware.WebUrls.readme_package_version], can("access"), async function(req, res, next) {
43
43
  debug$1("readme hit");
44
- const rawScope = req.params.scope;
45
- const scope = rawScope ? rawScope.slice(1) : null;
46
- const name = scope ? require_web_utils.addScope(scope, req.params.package) : req.params.package;
44
+ const scope = _verdaccio_core.reqUtils.paramToString(req.params.scope).replace(/^@/, "");
45
+ const packageName = _verdaccio_core.reqUtils.paramToString(req.params.package);
46
+ const name = scope ? require_web_utils.addScope(scope, packageName) : packageName;
47
47
  debug$1("readme name %o", name);
48
48
  const requestOptions = (0, _verdaccio_middleware.getRequestOptions)(req);
49
49
  try {
@@ -64,6 +64,7 @@ function addReadmeWebApi(storage, auth) {
64
64
  return pkgRouter;
65
65
  }
66
66
  //#endregion
67
+ exports.NOT_README_FOUND = NOT_README_FOUND;
67
68
  exports.default = addReadmeWebApi;
68
69
 
69
70
  //# sourceMappingURL=readme.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"readme.js","names":[],"sources":["../../src/api/readme.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Router } from 'express';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { DIST_TAGS, HEADERS, HEADER_TYPE } from '@verdaccio/core';\nimport { logger } from '@verdaccio/logger';\nimport {\n $NextFunctionVer,\n $RequestExtend,\n $ResponseExtend,\n WebUrls,\n allow,\n getRequestOptions,\n} from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Manifest } from '@verdaccio/types';\n\nimport { addScope, isVersionValid } from '../web-utils';\n\nexport { $RequestExtend, $ResponseExtend, $NextFunctionVer }; // Was required by other packages\n\nexport const NOT_README_FOUND = 'ERROR: No README data found!';\n\nconst debug = buildDebug('verdaccio:web:api:readme');\n\nconst getReadme = (readme) => {\n if (typeof readme === 'string' && readme.length === 0) {\n return NOT_README_FOUND;\n }\n if (typeof readme !== 'string') {\n return NOT_README_FOUND;\n } else {\n return readme;\n }\n};\n\nconst getReadmeFromManifest = (manifest: Manifest, v?: any): string | undefined => {\n let id;\n let readme;\n if (typeof v === 'string' && isVersionValid(manifest, v)) {\n id = 'version';\n readme = manifest.versions[v].readme;\n }\n if (!readme && isVersionValid(manifest, manifest[DIST_TAGS]?.latest)) {\n id = 'latest';\n readme = manifest.versions[manifest[DIST_TAGS].latest].readme;\n }\n if (!readme && manifest.readme) {\n id = 'root';\n readme = manifest.readme;\n }\n debug('readme: %o %o', v, id);\n return readme;\n};\n\nfunction addReadmeWebApi(storage: Storage, auth: Auth): Router {\n debug('initialized readme web api');\n const can = allow(auth, {\n beforeAll: (a, b) => logger.trace(a, b),\n afterAll: (a, b) => logger.trace(a, b),\n });\n const pkgRouter = Router(); /* eslint new-cap: 0 */\n\n pkgRouter.get(\n [WebUrls.readme_package_scoped_version, WebUrls.readme_package_version],\n can('access'),\n async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n debug('readme hit');\n const rawScope = req.params.scope; // May include '@'\n const scope = rawScope ? rawScope.slice(1) : null; // Remove '@' if present\n const name = scope ? addScope(scope, req.params.package) : req.params.package;\n debug('readme name %o', name);\n const requestOptions = getRequestOptions(req);\n try {\n const manifest = (await storage.getPackageByOptions({\n name,\n uplinksLook: true,\n abbreviated: false,\n requestOptions,\n })) as Manifest;\n debug('readme pkg %o', manifest?.name);\n res.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.TEXT_PLAIN_UTF8);\n // TODO: sanitize query\n const { v } = req.query;\n const readme = getReadmeFromManifest(manifest, v);\n next(getReadme(readme));\n } catch (err) {\n next(err);\n }\n }\n );\n return pkgRouter;\n}\n\nexport default addReadmeWebApi;\n"],"mappings":";;;;;;;;;AAqBA,IAAa,mBAAmB;AAEhC,IAAM,WAAA,GAAA,MAAA,SAAmB,0BAA0B;AAEnD,IAAM,aAAa,WAAW;CAC5B,IAAI,OAAO,WAAW,YAAY,OAAO,WAAW,GAClD,OAAO;CAET,IAAI,OAAO,WAAW,UACpB,OAAO;MAEP,OAAO;AAEX;AAEA,IAAM,yBAAyB,UAAoB,MAAgC;CACjF,IAAI;CACJ,IAAI;CACJ,IAAI,OAAO,MAAM,YAAY,kBAAA,eAAe,UAAU,CAAC,GAAG;EACxD,KAAK;EACL,SAAS,SAAS,SAAS,GAAG;CAChC;CACA,IAAI,CAAC,UAAU,kBAAA,eAAe,UAAU,SAAS,gBAAA,YAAY,MAAM,GAAG;EACpE,KAAK;EACL,SAAS,SAAS,SAAS,SAAS,gBAAA,WAAW,QAAQ;CACzD;CACA,IAAI,CAAC,UAAU,SAAS,QAAQ;EAC9B,KAAK;EACL,SAAS,SAAS;CACpB;CACA,QAAM,iBAAiB,GAAG,EAAE;CAC5B,OAAO;AACT;AAEA,SAAS,gBAAgB,SAAkB,MAAoB;CAC7D,QAAM,4BAA4B;CAClC,MAAM,OAAA,GAAA,sBAAA,OAAY,MAAM;EACtB,YAAY,GAAG,MAAM,kBAAA,OAAO,MAAM,GAAG,CAAC;EACtC,WAAW,GAAG,MAAM,kBAAA,OAAO,MAAM,GAAG,CAAC;CACvC,CAAC;CACD,MAAM,aAAA,GAAA,QAAA,QAAmB;CAEzB,UAAU,IACR,CAAC,sBAAA,QAAQ,+BAA+B,sBAAA,QAAQ,sBAAsB,GACtE,IAAI,QAAQ,GACZ,eACE,KACA,KACA,MACe;EACf,QAAM,YAAY;EAClB,MAAM,WAAW,IAAI,OAAO;EAC5B,MAAM,QAAQ,WAAW,SAAS,MAAM,CAAC,IAAI;EAC7C,MAAM,OAAO,QAAQ,kBAAA,SAAS,OAAO,IAAI,OAAO,OAAO,IAAI,IAAI,OAAO;EACtE,QAAM,kBAAkB,IAAI;EAC5B,MAAM,kBAAA,GAAA,sBAAA,mBAAmC,GAAG;EAC5C,IAAI;GACF,MAAM,WAAY,MAAM,QAAQ,oBAAoB;IAClD;IACA,aAAa;IACb,aAAa;IACb;GACF,CAAC;GACD,QAAM,iBAAiB,UAAU,IAAI;GACrC,IAAI,IAAI,gBAAA,YAAY,cAAc,gBAAA,QAAQ,eAAe;GAEzD,MAAM,EAAE,MAAM,IAAI;GAElB,KAAK,UADU,sBAAsB,UAAU,CAChC,CAAM,CAAC;EACxB,SAAS,KAAK;GACZ,KAAK,GAAG;EACV;CACF,CACF;CACA,OAAO;AACT"}
1
+ {"version":3,"file":"readme.js","names":[],"sources":["../../src/api/readme.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Router } from 'express';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { DIST_TAGS, HEADERS, HEADER_TYPE, reqUtils } from '@verdaccio/core';\nimport { logger } from '@verdaccio/logger';\nimport {\n $NextFunctionVer,\n $RequestExtend,\n $ResponseExtend,\n WebUrls,\n allow,\n getRequestOptions,\n} from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Manifest } from '@verdaccio/types';\n\nimport { addScope, isVersionValid } from '../web-utils';\n\nexport { $RequestExtend, $ResponseExtend, $NextFunctionVer }; // Was required by other packages\n\nexport const NOT_README_FOUND = 'ERROR: No README data found!';\n\nconst debug = buildDebug('verdaccio:web:api:readme');\n\nconst getReadme = (readme) => {\n if (typeof readme === 'string' && readme.length === 0) {\n return NOT_README_FOUND;\n }\n if (typeof readme !== 'string') {\n return NOT_README_FOUND;\n } else {\n return readme;\n }\n};\n\nconst getReadmeFromManifest = (manifest: Manifest, v?: any): string | undefined => {\n let id;\n let readme;\n if (typeof v === 'string' && isVersionValid(manifest, v)) {\n id = 'version';\n readme = manifest.versions[v].readme;\n }\n if (!readme && isVersionValid(manifest, manifest[DIST_TAGS]?.latest)) {\n id = 'latest';\n readme = manifest.versions[manifest[DIST_TAGS].latest].readme;\n }\n if (!readme && manifest.readme) {\n id = 'root';\n readme = manifest.readme;\n }\n debug('readme: %o %o', v, id);\n return readme;\n};\n\nfunction addReadmeWebApi(storage: Storage, auth: Auth): Router {\n debug('initialized readme web api');\n const can = allow(auth, {\n beforeAll: (a, b) => logger.trace(a, b),\n afterAll: (a, b) => logger.trace(a, b),\n });\n const pkgRouter = Router(); /* eslint new-cap: 0 */\n\n pkgRouter.get(\n [WebUrls.readme_package_scoped_version, WebUrls.readme_package_version],\n can('access'),\n async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n debug('readme hit');\n const scope = reqUtils.paramToString(req.params.scope).replace(/^@/, '');\n const packageName = reqUtils.paramToString(req.params.package);\n const name = scope ? addScope(scope, packageName) : packageName;\n debug('readme name %o', name);\n const requestOptions = getRequestOptions(req);\n try {\n const manifest = (await storage.getPackageByOptions({\n name,\n uplinksLook: true,\n abbreviated: false,\n requestOptions,\n })) as Manifest;\n debug('readme pkg %o', manifest?.name);\n res.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.TEXT_PLAIN_UTF8);\n // TODO: sanitize query\n const { v } = req.query;\n const readme = getReadmeFromManifest(manifest, v);\n next(getReadme(readme));\n } catch (err) {\n next(err);\n }\n }\n );\n return pkgRouter;\n}\n\nexport default addReadmeWebApi;\n"],"mappings":";;;;;;;;;AAqBA,IAAa,mBAAmB;AAEhC,IAAM,WAAA,GAAA,MAAA,QAAA,CAAmB,0BAA0B;AAEnD,IAAM,aAAa,WAAW;CAC5B,IAAI,OAAO,WAAW,YAAY,OAAO,WAAW,GAClD,OAAO;CAET,IAAI,OAAO,WAAW,UACpB,OAAO;MAEP,OAAO;AAEX;AAEA,IAAM,yBAAyB,UAAoB,MAAgC;CACjF,IAAI;CACJ,IAAI;CACJ,IAAI,OAAO,MAAM,YAAY,kBAAA,eAAe,UAAU,CAAC,GAAG;EACxD,KAAK;EACL,SAAS,SAAS,SAAS,EAAE,CAAC;CAChC;CACA,IAAI,CAAC,UAAU,kBAAA,eAAe,UAAU,SAAS,gBAAA,UAAU,EAAE,MAAM,GAAG;EACpE,KAAK;EACL,SAAS,SAAS,SAAS,SAAS,gBAAA,UAAU,CAAC,OAAO,CAAC;CACzD;CACA,IAAI,CAAC,UAAU,SAAS,QAAQ;EAC9B,KAAK;EACL,SAAS,SAAS;CACpB;CACA,QAAM,iBAAiB,GAAG,EAAE;CAC5B,OAAO;AACT;AAEA,SAAS,gBAAgB,SAAkB,MAAoB;CAC7D,QAAM,4BAA4B;CAClC,MAAM,OAAA,GAAA,sBAAA,MAAA,CAAY,MAAM;EACtB,YAAY,GAAG,MAAM,kBAAA,OAAO,MAAM,GAAG,CAAC;EACtC,WAAW,GAAG,MAAM,kBAAA,OAAO,MAAM,GAAG,CAAC;CACvC,CAAC;CACD,MAAM,aAAA,GAAA,QAAA,OAAA,CAAmB;CAEzB,UAAU,IACR,CAAC,sBAAA,QAAQ,+BAA+B,sBAAA,QAAQ,sBAAsB,GACtE,IAAI,QAAQ,GACZ,eACE,KACA,KACA,MACe;EACf,QAAM,YAAY;EAClB,MAAM,QAAQ,gBAAA,SAAS,cAAc,IAAI,OAAO,KAAK,CAAC,CAAC,QAAQ,MAAM,EAAE;EACvE,MAAM,cAAc,gBAAA,SAAS,cAAc,IAAI,OAAO,OAAO;EAC7D,MAAM,OAAO,QAAQ,kBAAA,SAAS,OAAO,WAAW,IAAI;EACpD,QAAM,kBAAkB,IAAI;EAC5B,MAAM,kBAAA,GAAA,sBAAA,kBAAA,CAAmC,GAAG;EAC5C,IAAI;GACF,MAAM,WAAY,MAAM,QAAQ,oBAAoB;IAClD;IACA,aAAa;IACb,aAAa;IACb;GACF,CAAC;GACD,QAAM,iBAAiB,UAAU,IAAI;GACrC,IAAI,IAAI,gBAAA,YAAY,cAAc,gBAAA,QAAQ,eAAe;GAEzD,MAAM,EAAE,MAAM,IAAI;GAElB,KAAK,UADU,sBAAsB,UAAU,CAChC,CAAM,CAAC;EACxB,SAAS,KAAK;GACZ,KAAK,GAAG;EACV;CACF,CACF;CACA,OAAO;AACT"}
@@ -1,7 +1,7 @@
1
1
  import { addScope, isVersionValid } from "../web-utils.mjs";
2
2
  import buildDebug from "debug";
3
3
  import { Router } from "express";
4
- import { DIST_TAGS, HEADERS, HEADER_TYPE } from "@verdaccio/core";
4
+ import { DIST_TAGS, HEADERS, HEADER_TYPE, reqUtils } from "@verdaccio/core";
5
5
  import { logger } from "@verdaccio/logger";
6
6
  import { WebUrls, allow, getRequestOptions } from "@verdaccio/middleware";
7
7
  //#region src/api/readme.ts
@@ -39,9 +39,9 @@ function addReadmeWebApi(storage, auth) {
39
39
  const pkgRouter = Router();
40
40
  pkgRouter.get([WebUrls.readme_package_scoped_version, WebUrls.readme_package_version], can("access"), async function(req, res, next) {
41
41
  debug("readme hit");
42
- const rawScope = req.params.scope;
43
- const scope = rawScope ? rawScope.slice(1) : null;
44
- const name = scope ? addScope(scope, req.params.package) : req.params.package;
42
+ const scope = reqUtils.paramToString(req.params.scope).replace(/^@/, "");
43
+ const packageName = reqUtils.paramToString(req.params.package);
44
+ const name = scope ? addScope(scope, packageName) : packageName;
45
45
  debug("readme name %o", name);
46
46
  const requestOptions = getRequestOptions(req);
47
47
  try {
@@ -62,6 +62,6 @@ function addReadmeWebApi(storage, auth) {
62
62
  return pkgRouter;
63
63
  }
64
64
  //#endregion
65
- export { addReadmeWebApi as default };
65
+ export { NOT_README_FOUND, addReadmeWebApi as default };
66
66
 
67
67
  //# sourceMappingURL=readme.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"readme.mjs","names":[],"sources":["../../src/api/readme.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Router } from 'express';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { DIST_TAGS, HEADERS, HEADER_TYPE } from '@verdaccio/core';\nimport { logger } from '@verdaccio/logger';\nimport {\n $NextFunctionVer,\n $RequestExtend,\n $ResponseExtend,\n WebUrls,\n allow,\n getRequestOptions,\n} from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Manifest } from '@verdaccio/types';\n\nimport { addScope, isVersionValid } from '../web-utils';\n\nexport { $RequestExtend, $ResponseExtend, $NextFunctionVer }; // Was required by other packages\n\nexport const NOT_README_FOUND = 'ERROR: No README data found!';\n\nconst debug = buildDebug('verdaccio:web:api:readme');\n\nconst getReadme = (readme) => {\n if (typeof readme === 'string' && readme.length === 0) {\n return NOT_README_FOUND;\n }\n if (typeof readme !== 'string') {\n return NOT_README_FOUND;\n } else {\n return readme;\n }\n};\n\nconst getReadmeFromManifest = (manifest: Manifest, v?: any): string | undefined => {\n let id;\n let readme;\n if (typeof v === 'string' && isVersionValid(manifest, v)) {\n id = 'version';\n readme = manifest.versions[v].readme;\n }\n if (!readme && isVersionValid(manifest, manifest[DIST_TAGS]?.latest)) {\n id = 'latest';\n readme = manifest.versions[manifest[DIST_TAGS].latest].readme;\n }\n if (!readme && manifest.readme) {\n id = 'root';\n readme = manifest.readme;\n }\n debug('readme: %o %o', v, id);\n return readme;\n};\n\nfunction addReadmeWebApi(storage: Storage, auth: Auth): Router {\n debug('initialized readme web api');\n const can = allow(auth, {\n beforeAll: (a, b) => logger.trace(a, b),\n afterAll: (a, b) => logger.trace(a, b),\n });\n const pkgRouter = Router(); /* eslint new-cap: 0 */\n\n pkgRouter.get(\n [WebUrls.readme_package_scoped_version, WebUrls.readme_package_version],\n can('access'),\n async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n debug('readme hit');\n const rawScope = req.params.scope; // May include '@'\n const scope = rawScope ? rawScope.slice(1) : null; // Remove '@' if present\n const name = scope ? addScope(scope, req.params.package) : req.params.package;\n debug('readme name %o', name);\n const requestOptions = getRequestOptions(req);\n try {\n const manifest = (await storage.getPackageByOptions({\n name,\n uplinksLook: true,\n abbreviated: false,\n requestOptions,\n })) as Manifest;\n debug('readme pkg %o', manifest?.name);\n res.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.TEXT_PLAIN_UTF8);\n // TODO: sanitize query\n const { v } = req.query;\n const readme = getReadmeFromManifest(manifest, v);\n next(getReadme(readme));\n } catch (err) {\n next(err);\n }\n }\n );\n return pkgRouter;\n}\n\nexport default addReadmeWebApi;\n"],"mappings":";;;;;;;AAqBA,IAAa,mBAAmB;AAEhC,IAAM,QAAQ,WAAW,0BAA0B;AAEnD,IAAM,aAAa,WAAW;CAC5B,IAAI,OAAO,WAAW,YAAY,OAAO,WAAW,GAClD,OAAO;CAET,IAAI,OAAO,WAAW,UACpB,OAAO;MAEP,OAAO;AAEX;AAEA,IAAM,yBAAyB,UAAoB,MAAgC;CACjF,IAAI;CACJ,IAAI;CACJ,IAAI,OAAO,MAAM,YAAY,eAAe,UAAU,CAAC,GAAG;EACxD,KAAK;EACL,SAAS,SAAS,SAAS,GAAG;CAChC;CACA,IAAI,CAAC,UAAU,eAAe,UAAU,SAAS,YAAY,MAAM,GAAG;EACpE,KAAK;EACL,SAAS,SAAS,SAAS,SAAS,WAAW,QAAQ;CACzD;CACA,IAAI,CAAC,UAAU,SAAS,QAAQ;EAC9B,KAAK;EACL,SAAS,SAAS;CACpB;CACA,MAAM,iBAAiB,GAAG,EAAE;CAC5B,OAAO;AACT;AAEA,SAAS,gBAAgB,SAAkB,MAAoB;CAC7D,MAAM,4BAA4B;CAClC,MAAM,MAAM,MAAM,MAAM;EACtB,YAAY,GAAG,MAAM,OAAO,MAAM,GAAG,CAAC;EACtC,WAAW,GAAG,MAAM,OAAO,MAAM,GAAG,CAAC;CACvC,CAAC;CACD,MAAM,YAAY,OAAO;CAEzB,UAAU,IACR,CAAC,QAAQ,+BAA+B,QAAQ,sBAAsB,GACtE,IAAI,QAAQ,GACZ,eACE,KACA,KACA,MACe;EACf,MAAM,YAAY;EAClB,MAAM,WAAW,IAAI,OAAO;EAC5B,MAAM,QAAQ,WAAW,SAAS,MAAM,CAAC,IAAI;EAC7C,MAAM,OAAO,QAAQ,SAAS,OAAO,IAAI,OAAO,OAAO,IAAI,IAAI,OAAO;EACtE,MAAM,kBAAkB,IAAI;EAC5B,MAAM,iBAAiB,kBAAkB,GAAG;EAC5C,IAAI;GACF,MAAM,WAAY,MAAM,QAAQ,oBAAoB;IAClD;IACA,aAAa;IACb,aAAa;IACb;GACF,CAAC;GACD,MAAM,iBAAiB,UAAU,IAAI;GACrC,IAAI,IAAI,YAAY,cAAc,QAAQ,eAAe;GAEzD,MAAM,EAAE,MAAM,IAAI;GAElB,KAAK,UADU,sBAAsB,UAAU,CAChC,CAAM,CAAC;EACxB,SAAS,KAAK;GACZ,KAAK,GAAG;EACV;CACF,CACF;CACA,OAAO;AACT"}
1
+ {"version":3,"file":"readme.mjs","names":[],"sources":["../../src/api/readme.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Router } from 'express';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { DIST_TAGS, HEADERS, HEADER_TYPE, reqUtils } from '@verdaccio/core';\nimport { logger } from '@verdaccio/logger';\nimport {\n $NextFunctionVer,\n $RequestExtend,\n $ResponseExtend,\n WebUrls,\n allow,\n getRequestOptions,\n} from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Manifest } from '@verdaccio/types';\n\nimport { addScope, isVersionValid } from '../web-utils';\n\nexport { $RequestExtend, $ResponseExtend, $NextFunctionVer }; // Was required by other packages\n\nexport const NOT_README_FOUND = 'ERROR: No README data found!';\n\nconst debug = buildDebug('verdaccio:web:api:readme');\n\nconst getReadme = (readme) => {\n if (typeof readme === 'string' && readme.length === 0) {\n return NOT_README_FOUND;\n }\n if (typeof readme !== 'string') {\n return NOT_README_FOUND;\n } else {\n return readme;\n }\n};\n\nconst getReadmeFromManifest = (manifest: Manifest, v?: any): string | undefined => {\n let id;\n let readme;\n if (typeof v === 'string' && isVersionValid(manifest, v)) {\n id = 'version';\n readme = manifest.versions[v].readme;\n }\n if (!readme && isVersionValid(manifest, manifest[DIST_TAGS]?.latest)) {\n id = 'latest';\n readme = manifest.versions[manifest[DIST_TAGS].latest].readme;\n }\n if (!readme && manifest.readme) {\n id = 'root';\n readme = manifest.readme;\n }\n debug('readme: %o %o', v, id);\n return readme;\n};\n\nfunction addReadmeWebApi(storage: Storage, auth: Auth): Router {\n debug('initialized readme web api');\n const can = allow(auth, {\n beforeAll: (a, b) => logger.trace(a, b),\n afterAll: (a, b) => logger.trace(a, b),\n });\n const pkgRouter = Router(); /* eslint new-cap: 0 */\n\n pkgRouter.get(\n [WebUrls.readme_package_scoped_version, WebUrls.readme_package_version],\n can('access'),\n async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n debug('readme hit');\n const scope = reqUtils.paramToString(req.params.scope).replace(/^@/, '');\n const packageName = reqUtils.paramToString(req.params.package);\n const name = scope ? addScope(scope, packageName) : packageName;\n debug('readme name %o', name);\n const requestOptions = getRequestOptions(req);\n try {\n const manifest = (await storage.getPackageByOptions({\n name,\n uplinksLook: true,\n abbreviated: false,\n requestOptions,\n })) as Manifest;\n debug('readme pkg %o', manifest?.name);\n res.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.TEXT_PLAIN_UTF8);\n // TODO: sanitize query\n const { v } = req.query;\n const readme = getReadmeFromManifest(manifest, v);\n next(getReadme(readme));\n } catch (err) {\n next(err);\n }\n }\n );\n return pkgRouter;\n}\n\nexport default addReadmeWebApi;\n"],"mappings":";;;;;;;AAqBA,IAAa,mBAAmB;AAEhC,IAAM,QAAQ,WAAW,0BAA0B;AAEnD,IAAM,aAAa,WAAW;CAC5B,IAAI,OAAO,WAAW,YAAY,OAAO,WAAW,GAClD,OAAO;CAET,IAAI,OAAO,WAAW,UACpB,OAAO;MAEP,OAAO;AAEX;AAEA,IAAM,yBAAyB,UAAoB,MAAgC;CACjF,IAAI;CACJ,IAAI;CACJ,IAAI,OAAO,MAAM,YAAY,eAAe,UAAU,CAAC,GAAG;EACxD,KAAK;EACL,SAAS,SAAS,SAAS,EAAE,CAAC;CAChC;CACA,IAAI,CAAC,UAAU,eAAe,UAAU,SAAS,UAAU,EAAE,MAAM,GAAG;EACpE,KAAK;EACL,SAAS,SAAS,SAAS,SAAS,UAAU,CAAC,OAAO,CAAC;CACzD;CACA,IAAI,CAAC,UAAU,SAAS,QAAQ;EAC9B,KAAK;EACL,SAAS,SAAS;CACpB;CACA,MAAM,iBAAiB,GAAG,EAAE;CAC5B,OAAO;AACT;AAEA,SAAS,gBAAgB,SAAkB,MAAoB;CAC7D,MAAM,4BAA4B;CAClC,MAAM,MAAM,MAAM,MAAM;EACtB,YAAY,GAAG,MAAM,OAAO,MAAM,GAAG,CAAC;EACtC,WAAW,GAAG,MAAM,OAAO,MAAM,GAAG,CAAC;CACvC,CAAC;CACD,MAAM,YAAY,OAAO;CAEzB,UAAU,IACR,CAAC,QAAQ,+BAA+B,QAAQ,sBAAsB,GACtE,IAAI,QAAQ,GACZ,eACE,KACA,KACA,MACe;EACf,MAAM,YAAY;EAClB,MAAM,QAAQ,SAAS,cAAc,IAAI,OAAO,KAAK,CAAC,CAAC,QAAQ,MAAM,EAAE;EACvE,MAAM,cAAc,SAAS,cAAc,IAAI,OAAO,OAAO;EAC7D,MAAM,OAAO,QAAQ,SAAS,OAAO,WAAW,IAAI;EACpD,MAAM,kBAAkB,IAAI;EAC5B,MAAM,iBAAiB,kBAAkB,GAAG;EAC5C,IAAI;GACF,MAAM,WAAY,MAAM,QAAQ,oBAAoB;IAClD;IACA,aAAa;IACb,aAAa;IACb;GACF,CAAC;GACD,MAAM,iBAAiB,UAAU,IAAI;GACrC,IAAI,IAAI,YAAY,cAAc,QAAQ,eAAe;GAEzD,MAAM,EAAE,MAAM,IAAI;GAElB,KAAK,UADU,sBAAsB,UAAU,CAChC,CAAM,CAAC;EACxB,SAAS,KAAK;GACZ,KAAK,GAAG;EACV;CACF,CACF;CACA,OAAO;AACT"}
@@ -1,5 +1,5 @@
1
1
  import { Router } from 'express';
2
- import { Auth } from '@verdaccio/auth';
3
- import { Storage } from '@verdaccio/store';
2
+ import type { Auth } from '@verdaccio/auth';
3
+ import type { Storage } from '@verdaccio/store';
4
4
  declare function addSearchWebApi(storage: Storage, auth: Auth): Router;
5
5
  export default addSearchWebApi;
@@ -10,10 +10,8 @@ var debug$1 = (0, debug.default)("verdaccio:web:api:search");
10
10
  function checkAccess(pkg, auth, remoteUser) {
11
11
  return new Promise((resolve, reject) => {
12
12
  auth.allow_access({ packageName: pkg?.package?.name }, remoteUser, function(err, allowed) {
13
- if (err) if (err.status && String(err.status).match(/^4\d\d$/)) {
14
- allowed = false;
15
- return resolve(null);
16
- } else reject(err);
13
+ if (err) if (err.status && String(err.status).match(/^4\d\d$/)) return resolve(null);
14
+ else reject(err);
17
15
  else return resolve(allowed ? pkg : null);
18
16
  });
19
17
  });
@@ -28,7 +26,7 @@ function addSearchWebApi(storage, auth) {
28
26
  };
29
27
  req.socket.on("close", onClientClose);
30
28
  try {
31
- const text = req.params.anything ?? "";
29
+ const text = _verdaccio_core.reqUtils.paramToString(req.params.anything);
32
30
  const size = 20;
33
31
  const from = 0;
34
32
  const query = {
@@ -1 +1 @@
1
- {"version":3,"file":"search.js","names":[],"sources":["../../src/api/search.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Router } from 'express';\nimport { URLSearchParams } from 'node:url';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { errorUtils } from '@verdaccio/core';\nimport type { SearchQuery } from '@verdaccio/core/src/search-utils';\nimport { WebUrls } from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Manifest } from '@verdaccio/types';\n\nimport type { $NextFunctionVer, $RequestExtend, $ResponseExtend } from './package';\n\nconst debug = buildDebug('verdaccio:web:api:search');\n\nfunction checkAccess(pkg: any, auth: any, remoteUser): Promise<Manifest | null> {\n return new Promise((resolve, reject) => {\n auth.allow_access({ packageName: pkg?.package?.name }, remoteUser, function (err, allowed) {\n if (err) {\n if (err.status && String(err.status).match(/^4\\d\\d$/)) {\n // auth plugin returns 4xx user error,\n // that's equivalent of !allowed basically\n allowed = false;\n return resolve(null);\n } else {\n reject(err);\n }\n } else {\n return resolve(allowed ? pkg : null);\n }\n });\n });\n}\n\nfunction addSearchWebApi(storage: Storage, auth: Auth): Router {\n const router = Router(); /* eslint new-cap: 0 */\n router.get(\n WebUrls.search,\n async function (\n req: $RequestExtend,\n _res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n const abort = new AbortController();\n const onClientClose = (): void => {\n debug('search web aborted');\n abort.abort();\n };\n req.socket.on('close', onClientClose);\n try {\n const text: string = (req.params.anything as string) ?? '';\n // These values are declared as optimal by npm cli\n // FUTURE: could be overwritten by ui settings.\n const size = 20;\n const from = 0;\n const query: SearchQuery = {\n from: 0,\n maintenance: 0.5,\n popularity: 0.98,\n quality: 0.65,\n size: 20,\n text,\n };\n // @ts-ignore\n const urlParams = new URLSearchParams(query);\n debug('search web init');\n const data = await storage?.search({\n query,\n url: `/-/v1/search?${urlParams.toString()}`,\n abort,\n });\n const checkAccessPromises = await Promise.all(\n data.map((pkgItem) => {\n return checkAccess(pkgItem, auth, req.remote_user);\n })\n );\n\n const final = checkAccessPromises\n .filter((i): i is Manifest => i !== null)\n .slice(from, size);\n\n next(final);\n } catch (err: any) {\n next(errorUtils.getInternalError(err.message));\n } finally {\n req.socket.off('close', onClientClose);\n }\n }\n );\n return router;\n}\n\nexport default addSearchWebApi;\n"],"mappings":";;;;;;;;AAaA,IAAM,WAAA,GAAA,MAAA,SAAmB,0BAA0B;AAEnD,SAAS,YAAY,KAAU,MAAW,YAAsC;CAC9E,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,KAAK,aAAa,EAAE,aAAa,KAAK,SAAS,KAAK,GAAG,YAAY,SAAU,KAAK,SAAS;GACzF,IAAI,KACF,IAAI,IAAI,UAAU,OAAO,IAAI,MAAM,EAAE,MAAM,SAAS,GAAG;IAGrD,UAAU;IACV,OAAO,QAAQ,IAAI;GACrB,OACE,OAAO,GAAG;QAGZ,OAAO,QAAQ,UAAU,MAAM,IAAI;EAEvC,CAAC;CACH,CAAC;AACH;AAEA,SAAS,gBAAgB,SAAkB,MAAoB;CAC7D,MAAM,UAAA,GAAA,QAAA,QAAgB;CACtB,OAAO,IACL,sBAAA,QAAQ,QACR,eACE,KACA,MACA,MACe;EACf,MAAM,QAAQ,IAAI,gBAAgB;EAClC,MAAM,sBAA4B;GAChC,QAAM,oBAAoB;GAC1B,MAAM,MAAM;EACd;EACA,IAAI,OAAO,GAAG,SAAS,aAAa;EACpC,IAAI;GACF,MAAM,OAAgB,IAAI,OAAO,YAAuB;GAGxD,MAAM,OAAO;GACb,MAAM,OAAO;GACb,MAAM,QAAqB;IACzB,MAAM;IACN,aAAa;IACb,YAAY;IACZ,SAAS;IACT,MAAM;IACN;GACF;GAEA,MAAM,YAAY,IAAI,SAAA,gBAAgB,KAAK;GAC3C,QAAM,iBAAiB;GACvB,MAAM,OAAO,MAAM,SAAS,OAAO;IACjC;IACA,KAAK,gBAAgB,UAAU,SAAS;IACxC;GACF,CAAC;GAWD,MAJc,MANoB,QAAQ,IACxC,KAAK,KAAK,YAAY;IACpB,OAAO,YAAY,SAAS,MAAM,IAAI,WAAW;GACnD,CAAC,CACH,GAGG,QAAQ,MAAqB,MAAM,IAAI,EACvC,MAAM,MAAM,IAEV,CAAK;EACZ,SAAS,KAAU;GACjB,KAAK,gBAAA,WAAW,iBAAiB,IAAI,OAAO,CAAC;EAC/C,UAAU;GACR,IAAI,OAAO,IAAI,SAAS,aAAa;EACvC;CACF,CACF;CACA,OAAO;AACT"}
1
+ {"version":3,"file":"search.js","names":[],"sources":["../../src/api/search.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Router } from 'express';\nimport { URLSearchParams } from 'node:url';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { errorUtils, reqUtils } from '@verdaccio/core';\nimport type { searchUtils } from '@verdaccio/core';\nimport { WebUrls } from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Manifest } from '@verdaccio/types';\n\nimport type { $NextFunctionVer, $RequestExtend, $ResponseExtend } from './package';\n\nconst debug = buildDebug('verdaccio:web:api:search');\n\nfunction checkAccess(pkg: any, auth: any, remoteUser): Promise<Manifest | null> {\n return new Promise((resolve, reject) => {\n auth.allow_access({ packageName: pkg?.package?.name }, remoteUser, function (err, allowed) {\n if (err) {\n if (err.status && String(err.status).match(/^4\\d\\d$/)) {\n // auth plugin returns 4xx user error,\n // that's equivalent of !allowed basically\n return resolve(null);\n } else {\n reject(err);\n }\n } else {\n return resolve(allowed ? pkg : null);\n }\n });\n });\n}\n\nfunction addSearchWebApi(storage: Storage, auth: Auth): Router {\n const router = Router(); /* eslint new-cap: 0 */\n router.get(\n WebUrls.search,\n async function (\n req: $RequestExtend,\n _res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n const abort = new AbortController();\n const onClientClose = (): void => {\n debug('search web aborted');\n abort.abort();\n };\n req.socket.on('close', onClientClose);\n try {\n const text = reqUtils.paramToString(req.params.anything);\n // These values are declared as optimal by npm cli\n // FUTURE: could be overwritten by ui settings.\n const size = 20;\n const from = 0;\n const query: searchUtils.SearchQuery = {\n from: 0,\n maintenance: 0.5,\n popularity: 0.98,\n quality: 0.65,\n size: 20,\n text,\n };\n // @ts-ignore\n const urlParams = new URLSearchParams(query);\n debug('search web init');\n const data = await storage?.search({\n query,\n url: `/-/v1/search?${urlParams.toString()}`,\n abort,\n });\n const checkAccessPromises = await Promise.all(\n data.map((pkgItem) => {\n return checkAccess(pkgItem, auth, req.remote_user);\n })\n );\n\n const final = checkAccessPromises\n .filter((i): i is Manifest => i !== null)\n .slice(from, size);\n\n next(final);\n } catch (err: any) {\n next(errorUtils.getInternalError(err.message));\n } finally {\n req.socket.off('close', onClientClose);\n }\n }\n );\n return router;\n}\n\nexport default addSearchWebApi;\n"],"mappings":";;;;;;;;AAaA,IAAM,WAAA,GAAA,MAAA,QAAA,CAAmB,0BAA0B;AAEnD,SAAS,YAAY,KAAU,MAAW,YAAsC;CAC9E,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,KAAK,aAAa,EAAE,aAAa,KAAK,SAAS,KAAK,GAAG,YAAY,SAAU,KAAK,SAAS;GACzF,IAAI,KACF,IAAI,IAAI,UAAU,OAAO,IAAI,MAAM,CAAC,CAAC,MAAM,SAAS,GAGlD,OAAO,QAAQ,IAAI;QAEnB,OAAO,GAAG;QAGZ,OAAO,QAAQ,UAAU,MAAM,IAAI;EAEvC,CAAC;CACH,CAAC;AACH;AAEA,SAAS,gBAAgB,SAAkB,MAAoB;CAC7D,MAAM,UAAA,GAAA,QAAA,OAAA,CAAgB;CACtB,OAAO,IACL,sBAAA,QAAQ,QACR,eACE,KACA,MACA,MACe;EACf,MAAM,QAAQ,IAAI,gBAAgB;EAClC,MAAM,sBAA4B;GAChC,QAAM,oBAAoB;GAC1B,MAAM,MAAM;EACd;EACA,IAAI,OAAO,GAAG,SAAS,aAAa;EACpC,IAAI;GACF,MAAM,OAAO,gBAAA,SAAS,cAAc,IAAI,OAAO,QAAQ;GAGvD,MAAM,OAAO;GACb,MAAM,OAAO;GACb,MAAM,QAAiC;IACrC,MAAM;IACN,aAAa;IACb,YAAY;IACZ,SAAS;IACT,MAAM;IACN;GACF;GAEA,MAAM,YAAY,IAAI,SAAA,gBAAgB,KAAK;GAC3C,QAAM,iBAAiB;GACvB,MAAM,OAAO,MAAM,SAAS,OAAO;IACjC;IACA,KAAK,gBAAgB,UAAU,SAAS;IACxC;GACF,CAAC;GAWD,MAJc,MANoB,QAAQ,IACxC,KAAK,KAAK,YAAY;IACpB,OAAO,YAAY,SAAS,MAAM,IAAI,WAAW;GACnD,CAAC,CACH,EAAA,CAGG,QAAQ,MAAqB,MAAM,IAAI,CAAC,CACxC,MAAM,MAAM,IAEV,CAAK;EACZ,SAAS,KAAU;GACjB,KAAK,gBAAA,WAAW,iBAAiB,IAAI,OAAO,CAAC;EAC/C,UAAU;GACR,IAAI,OAAO,IAAI,SAAS,aAAa;EACvC;CACF,CACF;CACA,OAAO;AACT"}
@@ -1,6 +1,6 @@
1
1
  import buildDebug from "debug";
2
2
  import { Router } from "express";
3
- import { errorUtils } from "@verdaccio/core";
3
+ import { errorUtils, reqUtils } from "@verdaccio/core";
4
4
  import { WebUrls } from "@verdaccio/middleware";
5
5
  import { URLSearchParams } from "node:url";
6
6
  //#region src/api/search.ts
@@ -8,10 +8,8 @@ var debug = buildDebug("verdaccio:web:api:search");
8
8
  function checkAccess(pkg, auth, remoteUser) {
9
9
  return new Promise((resolve, reject) => {
10
10
  auth.allow_access({ packageName: pkg?.package?.name }, remoteUser, function(err, allowed) {
11
- if (err) if (err.status && String(err.status).match(/^4\d\d$/)) {
12
- allowed = false;
13
- return resolve(null);
14
- } else reject(err);
11
+ if (err) if (err.status && String(err.status).match(/^4\d\d$/)) return resolve(null);
12
+ else reject(err);
15
13
  else return resolve(allowed ? pkg : null);
16
14
  });
17
15
  });
@@ -26,7 +24,7 @@ function addSearchWebApi(storage, auth) {
26
24
  };
27
25
  req.socket.on("close", onClientClose);
28
26
  try {
29
- const text = req.params.anything ?? "";
27
+ const text = reqUtils.paramToString(req.params.anything);
30
28
  const size = 20;
31
29
  const from = 0;
32
30
  const query = {
@@ -1 +1 @@
1
- {"version":3,"file":"search.mjs","names":[],"sources":["../../src/api/search.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Router } from 'express';\nimport { URLSearchParams } from 'node:url';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { errorUtils } from '@verdaccio/core';\nimport type { SearchQuery } from '@verdaccio/core/src/search-utils';\nimport { WebUrls } from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Manifest } from '@verdaccio/types';\n\nimport type { $NextFunctionVer, $RequestExtend, $ResponseExtend } from './package';\n\nconst debug = buildDebug('verdaccio:web:api:search');\n\nfunction checkAccess(pkg: any, auth: any, remoteUser): Promise<Manifest | null> {\n return new Promise((resolve, reject) => {\n auth.allow_access({ packageName: pkg?.package?.name }, remoteUser, function (err, allowed) {\n if (err) {\n if (err.status && String(err.status).match(/^4\\d\\d$/)) {\n // auth plugin returns 4xx user error,\n // that's equivalent of !allowed basically\n allowed = false;\n return resolve(null);\n } else {\n reject(err);\n }\n } else {\n return resolve(allowed ? pkg : null);\n }\n });\n });\n}\n\nfunction addSearchWebApi(storage: Storage, auth: Auth): Router {\n const router = Router(); /* eslint new-cap: 0 */\n router.get(\n WebUrls.search,\n async function (\n req: $RequestExtend,\n _res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n const abort = new AbortController();\n const onClientClose = (): void => {\n debug('search web aborted');\n abort.abort();\n };\n req.socket.on('close', onClientClose);\n try {\n const text: string = (req.params.anything as string) ?? '';\n // These values are declared as optimal by npm cli\n // FUTURE: could be overwritten by ui settings.\n const size = 20;\n const from = 0;\n const query: SearchQuery = {\n from: 0,\n maintenance: 0.5,\n popularity: 0.98,\n quality: 0.65,\n size: 20,\n text,\n };\n // @ts-ignore\n const urlParams = new URLSearchParams(query);\n debug('search web init');\n const data = await storage?.search({\n query,\n url: `/-/v1/search?${urlParams.toString()}`,\n abort,\n });\n const checkAccessPromises = await Promise.all(\n data.map((pkgItem) => {\n return checkAccess(pkgItem, auth, req.remote_user);\n })\n );\n\n const final = checkAccessPromises\n .filter((i): i is Manifest => i !== null)\n .slice(from, size);\n\n next(final);\n } catch (err: any) {\n next(errorUtils.getInternalError(err.message));\n } finally {\n req.socket.off('close', onClientClose);\n }\n }\n );\n return router;\n}\n\nexport default addSearchWebApi;\n"],"mappings":";;;;;;AAaA,IAAM,QAAQ,WAAW,0BAA0B;AAEnD,SAAS,YAAY,KAAU,MAAW,YAAsC;CAC9E,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,KAAK,aAAa,EAAE,aAAa,KAAK,SAAS,KAAK,GAAG,YAAY,SAAU,KAAK,SAAS;GACzF,IAAI,KACF,IAAI,IAAI,UAAU,OAAO,IAAI,MAAM,EAAE,MAAM,SAAS,GAAG;IAGrD,UAAU;IACV,OAAO,QAAQ,IAAI;GACrB,OACE,OAAO,GAAG;QAGZ,OAAO,QAAQ,UAAU,MAAM,IAAI;EAEvC,CAAC;CACH,CAAC;AACH;AAEA,SAAS,gBAAgB,SAAkB,MAAoB;CAC7D,MAAM,SAAS,OAAO;CACtB,OAAO,IACL,QAAQ,QACR,eACE,KACA,MACA,MACe;EACf,MAAM,QAAQ,IAAI,gBAAgB;EAClC,MAAM,sBAA4B;GAChC,MAAM,oBAAoB;GAC1B,MAAM,MAAM;EACd;EACA,IAAI,OAAO,GAAG,SAAS,aAAa;EACpC,IAAI;GACF,MAAM,OAAgB,IAAI,OAAO,YAAuB;GAGxD,MAAM,OAAO;GACb,MAAM,OAAO;GACb,MAAM,QAAqB;IACzB,MAAM;IACN,aAAa;IACb,YAAY;IACZ,SAAS;IACT,MAAM;IACN;GACF;GAEA,MAAM,YAAY,IAAI,gBAAgB,KAAK;GAC3C,MAAM,iBAAiB;GACvB,MAAM,OAAO,MAAM,SAAS,OAAO;IACjC;IACA,KAAK,gBAAgB,UAAU,SAAS;IACxC;GACF,CAAC;GAWD,MAJc,MANoB,QAAQ,IACxC,KAAK,KAAK,YAAY;IACpB,OAAO,YAAY,SAAS,MAAM,IAAI,WAAW;GACnD,CAAC,CACH,GAGG,QAAQ,MAAqB,MAAM,IAAI,EACvC,MAAM,MAAM,IAEV,CAAK;EACZ,SAAS,KAAU;GACjB,KAAK,WAAW,iBAAiB,IAAI,OAAO,CAAC;EAC/C,UAAU;GACR,IAAI,OAAO,IAAI,SAAS,aAAa;EACvC;CACF,CACF;CACA,OAAO;AACT"}
1
+ {"version":3,"file":"search.mjs","names":[],"sources":["../../src/api/search.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Router } from 'express';\nimport { URLSearchParams } from 'node:url';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { errorUtils, reqUtils } from '@verdaccio/core';\nimport type { searchUtils } from '@verdaccio/core';\nimport { WebUrls } from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport type { Manifest } from '@verdaccio/types';\n\nimport type { $NextFunctionVer, $RequestExtend, $ResponseExtend } from './package';\n\nconst debug = buildDebug('verdaccio:web:api:search');\n\nfunction checkAccess(pkg: any, auth: any, remoteUser): Promise<Manifest | null> {\n return new Promise((resolve, reject) => {\n auth.allow_access({ packageName: pkg?.package?.name }, remoteUser, function (err, allowed) {\n if (err) {\n if (err.status && String(err.status).match(/^4\\d\\d$/)) {\n // auth plugin returns 4xx user error,\n // that's equivalent of !allowed basically\n return resolve(null);\n } else {\n reject(err);\n }\n } else {\n return resolve(allowed ? pkg : null);\n }\n });\n });\n}\n\nfunction addSearchWebApi(storage: Storage, auth: Auth): Router {\n const router = Router(); /* eslint new-cap: 0 */\n router.get(\n WebUrls.search,\n async function (\n req: $RequestExtend,\n _res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n const abort = new AbortController();\n const onClientClose = (): void => {\n debug('search web aborted');\n abort.abort();\n };\n req.socket.on('close', onClientClose);\n try {\n const text = reqUtils.paramToString(req.params.anything);\n // These values are declared as optimal by npm cli\n // FUTURE: could be overwritten by ui settings.\n const size = 20;\n const from = 0;\n const query: searchUtils.SearchQuery = {\n from: 0,\n maintenance: 0.5,\n popularity: 0.98,\n quality: 0.65,\n size: 20,\n text,\n };\n // @ts-ignore\n const urlParams = new URLSearchParams(query);\n debug('search web init');\n const data = await storage?.search({\n query,\n url: `/-/v1/search?${urlParams.toString()}`,\n abort,\n });\n const checkAccessPromises = await Promise.all(\n data.map((pkgItem) => {\n return checkAccess(pkgItem, auth, req.remote_user);\n })\n );\n\n const final = checkAccessPromises\n .filter((i): i is Manifest => i !== null)\n .slice(from, size);\n\n next(final);\n } catch (err: any) {\n next(errorUtils.getInternalError(err.message));\n } finally {\n req.socket.off('close', onClientClose);\n }\n }\n );\n return router;\n}\n\nexport default addSearchWebApi;\n"],"mappings":";;;;;;AAaA,IAAM,QAAQ,WAAW,0BAA0B;AAEnD,SAAS,YAAY,KAAU,MAAW,YAAsC;CAC9E,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,KAAK,aAAa,EAAE,aAAa,KAAK,SAAS,KAAK,GAAG,YAAY,SAAU,KAAK,SAAS;GACzF,IAAI,KACF,IAAI,IAAI,UAAU,OAAO,IAAI,MAAM,CAAC,CAAC,MAAM,SAAS,GAGlD,OAAO,QAAQ,IAAI;QAEnB,OAAO,GAAG;QAGZ,OAAO,QAAQ,UAAU,MAAM,IAAI;EAEvC,CAAC;CACH,CAAC;AACH;AAEA,SAAS,gBAAgB,SAAkB,MAAoB;CAC7D,MAAM,SAAS,OAAO;CACtB,OAAO,IACL,QAAQ,QACR,eACE,KACA,MACA,MACe;EACf,MAAM,QAAQ,IAAI,gBAAgB;EAClC,MAAM,sBAA4B;GAChC,MAAM,oBAAoB;GAC1B,MAAM,MAAM;EACd;EACA,IAAI,OAAO,GAAG,SAAS,aAAa;EACpC,IAAI;GACF,MAAM,OAAO,SAAS,cAAc,IAAI,OAAO,QAAQ;GAGvD,MAAM,OAAO;GACb,MAAM,OAAO;GACb,MAAM,QAAiC;IACrC,MAAM;IACN,aAAa;IACb,YAAY;IACZ,SAAS;IACT,MAAM;IACN;GACF;GAEA,MAAM,YAAY,IAAI,gBAAgB,KAAK;GAC3C,MAAM,iBAAiB;GACvB,MAAM,OAAO,MAAM,SAAS,OAAO;IACjC;IACA,KAAK,gBAAgB,UAAU,SAAS;IACxC;GACF,CAAC;GAWD,MAJc,MANoB,QAAQ,IACxC,KAAK,KAAK,YAAY;IACpB,OAAO,YAAY,SAAS,MAAM,IAAI,WAAW;GACnD,CAAC,CACH,EAAA,CAGG,QAAQ,MAAqB,MAAM,IAAI,CAAC,CACxC,MAAM,MAAM,IAEV,CAAK;EACZ,SAAS,KAAU;GACjB,KAAK,WAAW,iBAAiB,IAAI,OAAO,CAAC;EAC/C,UAAU;GACR,IAAI,OAAO,IAAI,SAAS,aAAa;EACvC;CACF,CACF;CACA,OAAO;AACT"}
@@ -1,8 +1,8 @@
1
1
  import { Router } from 'express';
2
- import { Auth } from '@verdaccio/auth';
2
+ import type { Auth } from '@verdaccio/auth';
3
3
  import { $NextFunctionVer, $RequestExtend, $ResponseExtend } from '@verdaccio/middleware';
4
- import { Storage } from '@verdaccio/store';
5
- import { Config } from '@verdaccio/types';
4
+ import type { Storage } from '@verdaccio/store';
5
+ import type { Config } from '@verdaccio/types';
6
6
  export { $RequestExtend, $ResponseExtend, $NextFunctionVer };
7
7
  declare function addSidebarWebApi(config: Config, storage: Storage, auth: Auth): Router;
8
8
  export default addSidebarWebApi;
@@ -18,9 +18,9 @@ function addSidebarWebApi(config, storage, auth) {
18
18
  afterAll: (a, b) => _verdaccio_logger.logger.trace(a, b)
19
19
  });
20
20
  router.get([_verdaccio_middleware.WebUrls.sidebar_scopped_package, _verdaccio_middleware.WebUrls.sidebar_package], can("access"), async function(req, res, next) {
21
- const rawScope = req.params.scope;
22
- const scope = rawScope ? rawScope.slice(1) : null;
23
- const name = scope ? require_web_utils.addScope(scope, req.params.package) : req.params.package;
21
+ const scope = _verdaccio_core.reqUtils.paramToString(req.params.scope).replace(/^@/, "");
22
+ const packageName = _verdaccio_core.reqUtils.paramToString(req.params.package);
23
+ const name = scope ? require_web_utils.addScope(scope, packageName) : packageName;
24
24
  const requestOptions = (0, _verdaccio_middleware.getRequestOptions)(req);
25
25
  try {
26
26
  const info = await storage.getPackageByOptions({
@@ -1 +1 @@
1
- {"version":3,"file":"sidebar.js","names":[],"sources":["../../src/api/sidebar.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Router } from 'express';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { DIST_TAGS, HTTP_STATUS } from '@verdaccio/core';\nimport { logger } from '@verdaccio/logger';\nimport {\n $NextFunctionVer,\n $RequestExtend,\n $ResponseExtend,\n WebUrls,\n allow,\n getRequestOptions,\n} from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport { convertDistRemoteToLocalTarballUrls } from '@verdaccio/tarball';\nimport type { Config, Manifest, WebManifest } from '@verdaccio/types';\n\nimport { addGravatarSupport, formatAuthor } from '../author-utils';\nimport { addScope, deleteProperties, isVersionValid } from '../web-utils';\n\nexport { $RequestExtend, $ResponseExtend, $NextFunctionVer }; // Was required by other packages\n\nconst debug = buildDebug('verdaccio:web:api:sidebar');\n\nfunction addSidebarWebApi(config: Config, storage: Storage, auth: Auth): Router {\n debug('initialized sidebar web api');\n const router = Router(); /* eslint new-cap: 0 */\n const can = allow(auth, {\n beforeAll: (a, b) => logger.trace(a, b),\n afterAll: (a, b) => logger.trace(a, b),\n });\n // Get package sidebar\n router.get(\n [WebUrls.sidebar_scopped_package, WebUrls.sidebar_package],\n can('access'),\n async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n const rawScope = req.params.scope; // May include '@'\n const scope = rawScope ? rawScope.slice(1) : null; // Remove '@' if present\n const name: string = scope ? addScope(scope, req.params.package) : req.params.package;\n const requestOptions = getRequestOptions(req);\n try {\n const info = (await storage.getPackageByOptions({\n name,\n uplinksLook: true,\n keepUpLinkData: true,\n requestOptions,\n })) as Manifest;\n // TODO: sanitize query\n const { v } = req.query;\n let sideBarInfo = { ...info } as WebManifest;\n sideBarInfo.versions = convertDistRemoteToLocalTarballUrls(\n info,\n requestOptions,\n config.url_prefix\n ).versions;\n if (typeof v === 'string' && isVersionValid(info, v)) {\n sideBarInfo.latest = sideBarInfo.versions[v];\n sideBarInfo.latest.author = formatAuthor(sideBarInfo.latest.author);\n } else {\n sideBarInfo.latest = sideBarInfo.versions[info[DIST_TAGS].latest];\n sideBarInfo.latest.author = formatAuthor(sideBarInfo.latest.author);\n }\n sideBarInfo = deleteProperties(['readme', '_attachments', '_rev', 'name'], sideBarInfo);\n const authorAvatar = config.web\n ? addGravatarSupport(sideBarInfo, config.web.gravatar)\n : addGravatarSupport(sideBarInfo);\n next(authorAvatar);\n } catch {\n res.status(HTTP_STATUS.NOT_FOUND);\n res.end();\n }\n }\n );\n\n return router;\n}\n\nexport default addSidebarWebApi;\n"],"mappings":";;;;;;;;;;;AAuBA,IAAM,WAAA,GAAA,MAAA,SAAmB,2BAA2B;AAEpD,SAAS,iBAAiB,QAAgB,SAAkB,MAAoB;CAC9E,QAAM,6BAA6B;CACnC,MAAM,UAAA,GAAA,QAAA,QAAgB;CACtB,MAAM,OAAA,GAAA,sBAAA,OAAY,MAAM;EACtB,YAAY,GAAG,MAAM,kBAAA,OAAO,MAAM,GAAG,CAAC;EACtC,WAAW,GAAG,MAAM,kBAAA,OAAO,MAAM,GAAG,CAAC;CACvC,CAAC;CAED,OAAO,IACL,CAAC,sBAAA,QAAQ,yBAAyB,sBAAA,QAAQ,eAAe,GACzD,IAAI,QAAQ,GACZ,eACE,KACA,KACA,MACe;EACf,MAAM,WAAW,IAAI,OAAO;EAC5B,MAAM,QAAQ,WAAW,SAAS,MAAM,CAAC,IAAI;EAC7C,MAAM,OAAe,QAAQ,kBAAA,SAAS,OAAO,IAAI,OAAO,OAAO,IAAI,IAAI,OAAO;EAC9E,MAAM,kBAAA,GAAA,sBAAA,mBAAmC,GAAG;EAC5C,IAAI;GACF,MAAM,OAAQ,MAAM,QAAQ,oBAAoB;IAC9C;IACA,aAAa;IACb,gBAAgB;IAChB;GACF,CAAC;GAED,MAAM,EAAE,MAAM,IAAI;GAClB,IAAI,cAAc,EAAE,GAAG,KAAK;GAC5B,YAAY,YAAA,GAAA,mBAAA,qCACV,MACA,gBACA,OAAO,UACT,EAAE;GACF,IAAI,OAAO,MAAM,YAAY,kBAAA,eAAe,MAAM,CAAC,GAAG;IACpD,YAAY,SAAS,YAAY,SAAS;IAC1C,YAAY,OAAO,SAAS,qBAAA,aAAa,YAAY,OAAO,MAAM;GACpE,OAAO;IACL,YAAY,SAAS,YAAY,SAAS,KAAK,gBAAA,WAAW;IAC1D,YAAY,OAAO,SAAS,qBAAA,aAAa,YAAY,OAAO,MAAM;GACpE;GACA,cAAc,kBAAA,iBAAiB;IAAC;IAAU;IAAgB;IAAQ;GAAM,GAAG,WAAW;GAItF,KAHqB,OAAO,MACxB,qBAAA,mBAAmB,aAAa,OAAO,IAAI,QAAQ,IACnD,qBAAA,mBAAmB,WAAW,CACjB;EACnB,QAAQ;GACN,IAAI,OAAO,gBAAA,YAAY,SAAS;GAChC,IAAI,IAAI;EACV;CACF,CACF;CAEA,OAAO;AACT"}
1
+ {"version":3,"file":"sidebar.js","names":[],"sources":["../../src/api/sidebar.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Router } from 'express';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { DIST_TAGS, HTTP_STATUS, reqUtils } from '@verdaccio/core';\nimport { logger } from '@verdaccio/logger';\nimport {\n $NextFunctionVer,\n $RequestExtend,\n $ResponseExtend,\n WebUrls,\n allow,\n getRequestOptions,\n} from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport { convertDistRemoteToLocalTarballUrls } from '@verdaccio/tarball';\nimport type { Config, Manifest, WebManifest } from '@verdaccio/types';\n\nimport { addGravatarSupport, formatAuthor } from '../author-utils';\nimport { addScope, deleteProperties, isVersionValid } from '../web-utils';\n\nexport { $RequestExtend, $ResponseExtend, $NextFunctionVer }; // Was required by other packages\n\nconst debug = buildDebug('verdaccio:web:api:sidebar');\n\nfunction addSidebarWebApi(config: Config, storage: Storage, auth: Auth): Router {\n debug('initialized sidebar web api');\n const router = Router(); /* eslint new-cap: 0 */\n const can = allow(auth, {\n beforeAll: (a, b) => logger.trace(a, b),\n afterAll: (a, b) => logger.trace(a, b),\n });\n // Get package sidebar\n router.get(\n [WebUrls.sidebar_scopped_package, WebUrls.sidebar_package],\n can('access'),\n async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n const scope = reqUtils.paramToString(req.params.scope).replace(/^@/, '');\n const packageName = reqUtils.paramToString(req.params.package);\n const name = scope ? addScope(scope, packageName) : packageName;\n const requestOptions = getRequestOptions(req);\n try {\n const info = (await storage.getPackageByOptions({\n name,\n uplinksLook: true,\n keepUpLinkData: true,\n requestOptions,\n })) as Manifest;\n // TODO: sanitize query\n const { v } = req.query;\n let sideBarInfo = { ...info } as WebManifest;\n sideBarInfo.versions = convertDistRemoteToLocalTarballUrls(\n info,\n requestOptions,\n config.url_prefix\n ).versions;\n if (typeof v === 'string' && isVersionValid(info, v)) {\n sideBarInfo.latest = sideBarInfo.versions[v];\n sideBarInfo.latest.author = formatAuthor(sideBarInfo.latest.author);\n } else {\n sideBarInfo.latest = sideBarInfo.versions[info[DIST_TAGS].latest];\n sideBarInfo.latest.author = formatAuthor(sideBarInfo.latest.author);\n }\n sideBarInfo = deleteProperties(['readme', '_attachments', '_rev', 'name'], sideBarInfo);\n const authorAvatar = config.web\n ? addGravatarSupport(sideBarInfo, config.web.gravatar)\n : addGravatarSupport(sideBarInfo);\n next(authorAvatar);\n } catch {\n res.status(HTTP_STATUS.NOT_FOUND);\n res.end();\n }\n }\n );\n\n return router;\n}\n\nexport default addSidebarWebApi;\n"],"mappings":";;;;;;;;;;;AAuBA,IAAM,WAAA,GAAA,MAAA,QAAA,CAAmB,2BAA2B;AAEpD,SAAS,iBAAiB,QAAgB,SAAkB,MAAoB;CAC9E,QAAM,6BAA6B;CACnC,MAAM,UAAA,GAAA,QAAA,OAAA,CAAgB;CACtB,MAAM,OAAA,GAAA,sBAAA,MAAA,CAAY,MAAM;EACtB,YAAY,GAAG,MAAM,kBAAA,OAAO,MAAM,GAAG,CAAC;EACtC,WAAW,GAAG,MAAM,kBAAA,OAAO,MAAM,GAAG,CAAC;CACvC,CAAC;CAED,OAAO,IACL,CAAC,sBAAA,QAAQ,yBAAyB,sBAAA,QAAQ,eAAe,GACzD,IAAI,QAAQ,GACZ,eACE,KACA,KACA,MACe;EACf,MAAM,QAAQ,gBAAA,SAAS,cAAc,IAAI,OAAO,KAAK,CAAC,CAAC,QAAQ,MAAM,EAAE;EACvE,MAAM,cAAc,gBAAA,SAAS,cAAc,IAAI,OAAO,OAAO;EAC7D,MAAM,OAAO,QAAQ,kBAAA,SAAS,OAAO,WAAW,IAAI;EACpD,MAAM,kBAAA,GAAA,sBAAA,kBAAA,CAAmC,GAAG;EAC5C,IAAI;GACF,MAAM,OAAQ,MAAM,QAAQ,oBAAoB;IAC9C;IACA,aAAa;IACb,gBAAgB;IAChB;GACF,CAAC;GAED,MAAM,EAAE,MAAM,IAAI;GAClB,IAAI,cAAc,EAAE,GAAG,KAAK;GAC5B,YAAY,YAAA,GAAA,mBAAA,oCAAA,CACV,MACA,gBACA,OAAO,UACT,CAAC,CAAC;GACF,IAAI,OAAO,MAAM,YAAY,kBAAA,eAAe,MAAM,CAAC,GAAG;IACpD,YAAY,SAAS,YAAY,SAAS;IAC1C,YAAY,OAAO,SAAS,qBAAA,aAAa,YAAY,OAAO,MAAM;GACpE,OAAO;IACL,YAAY,SAAS,YAAY,SAAS,KAAK,gBAAA,UAAU,CAAC;IAC1D,YAAY,OAAO,SAAS,qBAAA,aAAa,YAAY,OAAO,MAAM;GACpE;GACA,cAAc,kBAAA,iBAAiB;IAAC;IAAU;IAAgB;IAAQ;GAAM,GAAG,WAAW;GAItF,KAHqB,OAAO,MACxB,qBAAA,mBAAmB,aAAa,OAAO,IAAI,QAAQ,IACnD,qBAAA,mBAAmB,WAAW,CACjB;EACnB,QAAQ;GACN,IAAI,OAAO,gBAAA,YAAY,SAAS;GAChC,IAAI,IAAI;EACV;CACF,CACF;CAEA,OAAO;AACT"}
@@ -2,7 +2,7 @@ import { addScope, deleteProperties, isVersionValid } from "../web-utils.mjs";
2
2
  import { addGravatarSupport, formatAuthor } from "../author-utils.mjs";
3
3
  import buildDebug from "debug";
4
4
  import { Router } from "express";
5
- import { DIST_TAGS, HTTP_STATUS } from "@verdaccio/core";
5
+ import { DIST_TAGS, HTTP_STATUS, reqUtils } from "@verdaccio/core";
6
6
  import { logger } from "@verdaccio/logger";
7
7
  import { WebUrls, allow, getRequestOptions } from "@verdaccio/middleware";
8
8
  import { convertDistRemoteToLocalTarballUrls } from "@verdaccio/tarball";
@@ -16,9 +16,9 @@ function addSidebarWebApi(config, storage, auth) {
16
16
  afterAll: (a, b) => logger.trace(a, b)
17
17
  });
18
18
  router.get([WebUrls.sidebar_scopped_package, WebUrls.sidebar_package], can("access"), async function(req, res, next) {
19
- const rawScope = req.params.scope;
20
- const scope = rawScope ? rawScope.slice(1) : null;
21
- const name = scope ? addScope(scope, req.params.package) : req.params.package;
19
+ const scope = reqUtils.paramToString(req.params.scope).replace(/^@/, "");
20
+ const packageName = reqUtils.paramToString(req.params.package);
21
+ const name = scope ? addScope(scope, packageName) : packageName;
22
22
  const requestOptions = getRequestOptions(req);
23
23
  try {
24
24
  const info = await storage.getPackageByOptions({
@@ -1 +1 @@
1
- {"version":3,"file":"sidebar.mjs","names":[],"sources":["../../src/api/sidebar.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Router } from 'express';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { DIST_TAGS, HTTP_STATUS } from '@verdaccio/core';\nimport { logger } from '@verdaccio/logger';\nimport {\n $NextFunctionVer,\n $RequestExtend,\n $ResponseExtend,\n WebUrls,\n allow,\n getRequestOptions,\n} from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport { convertDistRemoteToLocalTarballUrls } from '@verdaccio/tarball';\nimport type { Config, Manifest, WebManifest } from '@verdaccio/types';\n\nimport { addGravatarSupport, formatAuthor } from '../author-utils';\nimport { addScope, deleteProperties, isVersionValid } from '../web-utils';\n\nexport { $RequestExtend, $ResponseExtend, $NextFunctionVer }; // Was required by other packages\n\nconst debug = buildDebug('verdaccio:web:api:sidebar');\n\nfunction addSidebarWebApi(config: Config, storage: Storage, auth: Auth): Router {\n debug('initialized sidebar web api');\n const router = Router(); /* eslint new-cap: 0 */\n const can = allow(auth, {\n beforeAll: (a, b) => logger.trace(a, b),\n afterAll: (a, b) => logger.trace(a, b),\n });\n // Get package sidebar\n router.get(\n [WebUrls.sidebar_scopped_package, WebUrls.sidebar_package],\n can('access'),\n async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n const rawScope = req.params.scope; // May include '@'\n const scope = rawScope ? rawScope.slice(1) : null; // Remove '@' if present\n const name: string = scope ? addScope(scope, req.params.package) : req.params.package;\n const requestOptions = getRequestOptions(req);\n try {\n const info = (await storage.getPackageByOptions({\n name,\n uplinksLook: true,\n keepUpLinkData: true,\n requestOptions,\n })) as Manifest;\n // TODO: sanitize query\n const { v } = req.query;\n let sideBarInfo = { ...info } as WebManifest;\n sideBarInfo.versions = convertDistRemoteToLocalTarballUrls(\n info,\n requestOptions,\n config.url_prefix\n ).versions;\n if (typeof v === 'string' && isVersionValid(info, v)) {\n sideBarInfo.latest = sideBarInfo.versions[v];\n sideBarInfo.latest.author = formatAuthor(sideBarInfo.latest.author);\n } else {\n sideBarInfo.latest = sideBarInfo.versions[info[DIST_TAGS].latest];\n sideBarInfo.latest.author = formatAuthor(sideBarInfo.latest.author);\n }\n sideBarInfo = deleteProperties(['readme', '_attachments', '_rev', 'name'], sideBarInfo);\n const authorAvatar = config.web\n ? addGravatarSupport(sideBarInfo, config.web.gravatar)\n : addGravatarSupport(sideBarInfo);\n next(authorAvatar);\n } catch {\n res.status(HTTP_STATUS.NOT_FOUND);\n res.end();\n }\n }\n );\n\n return router;\n}\n\nexport default addSidebarWebApi;\n"],"mappings":";;;;;;;;;AAuBA,IAAM,QAAQ,WAAW,2BAA2B;AAEpD,SAAS,iBAAiB,QAAgB,SAAkB,MAAoB;CAC9E,MAAM,6BAA6B;CACnC,MAAM,SAAS,OAAO;CACtB,MAAM,MAAM,MAAM,MAAM;EACtB,YAAY,GAAG,MAAM,OAAO,MAAM,GAAG,CAAC;EACtC,WAAW,GAAG,MAAM,OAAO,MAAM,GAAG,CAAC;CACvC,CAAC;CAED,OAAO,IACL,CAAC,QAAQ,yBAAyB,QAAQ,eAAe,GACzD,IAAI,QAAQ,GACZ,eACE,KACA,KACA,MACe;EACf,MAAM,WAAW,IAAI,OAAO;EAC5B,MAAM,QAAQ,WAAW,SAAS,MAAM,CAAC,IAAI;EAC7C,MAAM,OAAe,QAAQ,SAAS,OAAO,IAAI,OAAO,OAAO,IAAI,IAAI,OAAO;EAC9E,MAAM,iBAAiB,kBAAkB,GAAG;EAC5C,IAAI;GACF,MAAM,OAAQ,MAAM,QAAQ,oBAAoB;IAC9C;IACA,aAAa;IACb,gBAAgB;IAChB;GACF,CAAC;GAED,MAAM,EAAE,MAAM,IAAI;GAClB,IAAI,cAAc,EAAE,GAAG,KAAK;GAC5B,YAAY,WAAW,oCACrB,MACA,gBACA,OAAO,UACT,EAAE;GACF,IAAI,OAAO,MAAM,YAAY,eAAe,MAAM,CAAC,GAAG;IACpD,YAAY,SAAS,YAAY,SAAS;IAC1C,YAAY,OAAO,SAAS,aAAa,YAAY,OAAO,MAAM;GACpE,OAAO;IACL,YAAY,SAAS,YAAY,SAAS,KAAK,WAAW;IAC1D,YAAY,OAAO,SAAS,aAAa,YAAY,OAAO,MAAM;GACpE;GACA,cAAc,iBAAiB;IAAC;IAAU;IAAgB;IAAQ;GAAM,GAAG,WAAW;GAItF,KAHqB,OAAO,MACxB,mBAAmB,aAAa,OAAO,IAAI,QAAQ,IACnD,mBAAmB,WAAW,CACjB;EACnB,QAAQ;GACN,IAAI,OAAO,YAAY,SAAS;GAChC,IAAI,IAAI;EACV;CACF,CACF;CAEA,OAAO;AACT"}
1
+ {"version":3,"file":"sidebar.mjs","names":[],"sources":["../../src/api/sidebar.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { Router } from 'express';\n\nimport type { Auth } from '@verdaccio/auth';\nimport { DIST_TAGS, HTTP_STATUS, reqUtils } from '@verdaccio/core';\nimport { logger } from '@verdaccio/logger';\nimport {\n $NextFunctionVer,\n $RequestExtend,\n $ResponseExtend,\n WebUrls,\n allow,\n getRequestOptions,\n} from '@verdaccio/middleware';\nimport type { Storage } from '@verdaccio/store';\nimport { convertDistRemoteToLocalTarballUrls } from '@verdaccio/tarball';\nimport type { Config, Manifest, WebManifest } from '@verdaccio/types';\n\nimport { addGravatarSupport, formatAuthor } from '../author-utils';\nimport { addScope, deleteProperties, isVersionValid } from '../web-utils';\n\nexport { $RequestExtend, $ResponseExtend, $NextFunctionVer }; // Was required by other packages\n\nconst debug = buildDebug('verdaccio:web:api:sidebar');\n\nfunction addSidebarWebApi(config: Config, storage: Storage, auth: Auth): Router {\n debug('initialized sidebar web api');\n const router = Router(); /* eslint new-cap: 0 */\n const can = allow(auth, {\n beforeAll: (a, b) => logger.trace(a, b),\n afterAll: (a, b) => logger.trace(a, b),\n });\n // Get package sidebar\n router.get(\n [WebUrls.sidebar_scopped_package, WebUrls.sidebar_package],\n can('access'),\n async function (\n req: $RequestExtend,\n res: $ResponseExtend,\n next: $NextFunctionVer\n ): Promise<void> {\n const scope = reqUtils.paramToString(req.params.scope).replace(/^@/, '');\n const packageName = reqUtils.paramToString(req.params.package);\n const name = scope ? addScope(scope, packageName) : packageName;\n const requestOptions = getRequestOptions(req);\n try {\n const info = (await storage.getPackageByOptions({\n name,\n uplinksLook: true,\n keepUpLinkData: true,\n requestOptions,\n })) as Manifest;\n // TODO: sanitize query\n const { v } = req.query;\n let sideBarInfo = { ...info } as WebManifest;\n sideBarInfo.versions = convertDistRemoteToLocalTarballUrls(\n info,\n requestOptions,\n config.url_prefix\n ).versions;\n if (typeof v === 'string' && isVersionValid(info, v)) {\n sideBarInfo.latest = sideBarInfo.versions[v];\n sideBarInfo.latest.author = formatAuthor(sideBarInfo.latest.author);\n } else {\n sideBarInfo.latest = sideBarInfo.versions[info[DIST_TAGS].latest];\n sideBarInfo.latest.author = formatAuthor(sideBarInfo.latest.author);\n }\n sideBarInfo = deleteProperties(['readme', '_attachments', '_rev', 'name'], sideBarInfo);\n const authorAvatar = config.web\n ? addGravatarSupport(sideBarInfo, config.web.gravatar)\n : addGravatarSupport(sideBarInfo);\n next(authorAvatar);\n } catch {\n res.status(HTTP_STATUS.NOT_FOUND);\n res.end();\n }\n }\n );\n\n return router;\n}\n\nexport default addSidebarWebApi;\n"],"mappings":";;;;;;;;;AAuBA,IAAM,QAAQ,WAAW,2BAA2B;AAEpD,SAAS,iBAAiB,QAAgB,SAAkB,MAAoB;CAC9E,MAAM,6BAA6B;CACnC,MAAM,SAAS,OAAO;CACtB,MAAM,MAAM,MAAM,MAAM;EACtB,YAAY,GAAG,MAAM,OAAO,MAAM,GAAG,CAAC;EACtC,WAAW,GAAG,MAAM,OAAO,MAAM,GAAG,CAAC;CACvC,CAAC;CAED,OAAO,IACL,CAAC,QAAQ,yBAAyB,QAAQ,eAAe,GACzD,IAAI,QAAQ,GACZ,eACE,KACA,KACA,MACe;EACf,MAAM,QAAQ,SAAS,cAAc,IAAI,OAAO,KAAK,CAAC,CAAC,QAAQ,MAAM,EAAE;EACvE,MAAM,cAAc,SAAS,cAAc,IAAI,OAAO,OAAO;EAC7D,MAAM,OAAO,QAAQ,SAAS,OAAO,WAAW,IAAI;EACpD,MAAM,iBAAiB,kBAAkB,GAAG;EAC5C,IAAI;GACF,MAAM,OAAQ,MAAM,QAAQ,oBAAoB;IAC9C;IACA,aAAa;IACb,gBAAgB;IAChB;GACF,CAAC;GAED,MAAM,EAAE,MAAM,IAAI;GAClB,IAAI,cAAc,EAAE,GAAG,KAAK;GAC5B,YAAY,WAAW,oCACrB,MACA,gBACA,OAAO,UACT,CAAC,CAAC;GACF,IAAI,OAAO,MAAM,YAAY,eAAe,MAAM,CAAC,GAAG;IACpD,YAAY,SAAS,YAAY,SAAS;IAC1C,YAAY,OAAO,SAAS,aAAa,YAAY,OAAO,MAAM;GACpE,OAAO;IACL,YAAY,SAAS,YAAY,SAAS,KAAK,UAAU,CAAC;IAC1D,YAAY,OAAO,SAAS,aAAa,YAAY,OAAO,MAAM;GACpE;GACA,cAAc,iBAAiB;IAAC;IAAU;IAAgB;IAAQ;GAAM,GAAG,WAAW;GAItF,KAHqB,OAAO,MACxB,mBAAmB,aAAa,OAAO,IAAI,QAAQ,IACnD,mBAAmB,WAAW,CACjB;EACnB,QAAQ;GACN,IAAI,OAAO,YAAY,SAAS;GAChC,IAAI,IAAI;EACV;CACF,CACF;CAEA,OAAO;AACT"}
@@ -1,5 +1,5 @@
1
1
  import { Router } from 'express';
2
- import { Auth } from '@verdaccio/auth';
3
- import { Config } from '@verdaccio/types';
2
+ import { type Auth } from '@verdaccio/auth';
3
+ import type { Config } from '@verdaccio/types';
4
4
  declare function addUserAuthApi(auth: Auth, config: Config, storage: Storage): Router;
5
5
  export default addUserAuthApi;
@@ -1 +1 @@
1
- {"version":3,"file":"user.js","names":[],"sources":["../../src/api/user.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport type { Request, Response } from 'express';\nimport { Router } from 'express';\nimport { isNil } from 'lodash-es';\n\nimport { type Auth, getApiToken } from '@verdaccio/auth';\nimport type { VerdaccioError } from '@verdaccio/core';\nimport {\n API_ERROR,\n APP_ERROR,\n HEADERS,\n HTTP_STATUS,\n TOKEN_BEARER,\n cryptoUtils,\n errorUtils,\n validationUtils,\n} from '@verdaccio/core';\nimport { WebUrls, rateLimit } from '@verdaccio/middleware';\nimport type { Config, JWTSignOptions, RemoteUser } from '@verdaccio/types';\n\nimport type { $NextFunctionVer } from './package';\n\nconst debug = buildDebug('verdaccio:web:api:user');\n\nconst WEB_LOGIN_SESSION_ID = 'web-login-sessionId';\n\nfunction addUserAuthApi(auth: Auth, config: Config, storage: Storage): Router {\n const route = Router(); /* eslint new-cap: 0 */\n route.post(\n WebUrls.user_login,\n rateLimit(config?.userRateLimit),\n function (req: Request, res: Response, next: $NextFunctionVer): void {\n const { username, password } = req.body;\n debug('authenticate %o', username);\n auth.authenticate(\n username,\n password,\n async (err: VerdaccioError | null, user?: RemoteUser): Promise<void> => {\n if (err) {\n const errorCode = err.message ? HTTP_STATUS.UNAUTHORIZED : HTTP_STATUS.INTERNAL_ERROR;\n debug('error authenticate %o', errorCode);\n // Prevent the final middleware from adding WWW-Authenticate header,\n // which triggers the browser's native basic auth popup instead of\n // letting the WebUI handle the error in JavaScript.\n if (errorCode === HTTP_STATUS.UNAUTHORIZED) {\n res.set(HEADERS.WWW_AUTH, TOKEN_BEARER);\n }\n return next(errorUtils.getCode(errorCode, err.message));\n } else {\n req.remote_user = user as RemoteUser;\n const jWTSignOptions: JWTSignOptions = config.security.web.sign;\n res.set(HEADERS.CACHE_CONTROL, HEADERS.NO_CACHE);\n return next({\n token: await auth.jwtEncrypt(user as RemoteUser, jWTSignOptions),\n username: req.remote_user.name,\n });\n }\n }\n );\n }\n );\n\n if (config?.flags?.createUser === true) {\n route.put(\n WebUrls.user_signup,\n rateLimit(config?.userRateLimit),\n function (req: Request, res: Response, next: $NextFunctionVer): void {\n const { name, password, email, sessionId } = req.body;\n debug('login or adduser');\n\n // TOOD: reuse with login.ts file\n if (typeof sessionId !== 'string' || sessionId.length !== 36) {\n debug('sessionId is invalid length: %o', sessionId.length);\n return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.SESSION_ID_INVALID));\n }\n\n if (!name || !password || !email) {\n return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.BAD_DATA));\n }\n\n auth.add_user(name, password, async function (err, user): Promise<void> {\n if (err) {\n if (err.status >= HTTP_STATUS.BAD_REQUEST && err.status < HTTP_STATUS.INTERNAL_ERROR) {\n debug('adduser: error on create user');\n // With npm registering is the same as logging in,\n // and npm accepts only an 409 error.\n // So, changing status code here.\n return next(\n errorUtils.getCode(err.status, err.message) || errorUtils.getConflict(err.message)\n );\n }\n return next(err);\n }\n\n const tokens = await storage.readTokens({ user: sessionId });\n debug('tokens found for sessionId %o: %o', sessionId, tokens.length);\n if (tokens && tokens.length === 1 && tokens[0].key === WEB_LOGIN_SESSION_ID) {\n if (tokens[0].token.length === 0) {\n debug('waiting for authentication');\n // Poll again after short delay\n // TODO: make this configurable (default 5 seconds)\n res.status(HTTP_STATUS.ACCEPTED);\n res.set(HEADERS.RETRY_AFTER, '5');\n res.json({});\n return;\n }\n\n // session token can only be used once\n debug('deleting session token');\n await storage.deleteToken(sessionId, tokens[0].key);\n\n // Check if token has expired\n // TODO: make this configurable (default 2 minutes)\n const tokenCreatedDate = new Date(tokens[0].created);\n const minutesAgo = new Date(Date.now() - 2 * 60 * 1000);\n if (tokenCreatedDate < minutesAgo) {\n debug('session token expired');\n return next(errorUtils.getUnauthorized(API_ERROR.SESSION_TOKEN_EXPIRED));\n }\n\n debug('session token is valid, login successful');\n res.status(HTTP_STATUS.OK);\n res.json({ token: tokens[0].token });\n return;\n }\n\n const token =\n name && password\n ? await getApiToken(auth, config, user as RemoteUser, password)\n : undefined;\n if (token) {\n debug('adduser: new token %o', cryptoUtils.mask(token as string, 4));\n return next({ token, username: name });\n }\n return next(errorUtils.getUnauthorized());\n });\n }\n );\n }\n\n if (config?.flags?.changePassword === true) {\n route.put(\n WebUrls.reset_password,\n rateLimit(config?.userRateLimit),\n function (req: Request, res: Response, next: $NextFunctionVer): void {\n if (isNil(req.remote_user.name)) {\n res.status(HTTP_STATUS.UNAUTHORIZED);\n return next({\n // FUTURE: update to a more meaningful message\n message: API_ERROR.MUST_BE_LOGGED,\n });\n }\n\n const { password } = req.body;\n const { name } = req.remote_user;\n\n if (\n validationUtils.validatePassword(\n password.new,\n config?.server?.passwordValidationRegex\n ) === false\n ) {\n return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, APP_ERROR.PASSWORD_VALIDATION));\n }\n\n auth.changePassword(name as string, password.old, password.new, (err, isUpdated): void => {\n if (isNil(err) && isUpdated) {\n next({\n ok: true,\n });\n } else {\n return next(errorUtils.getInternalError(API_ERROR.INTERNAL_SERVER_ERROR));\n }\n });\n }\n );\n }\n\n return route;\n}\n\nexport default addUserAuthApi;\n"],"mappings":"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"}
1
+ {"version":3,"file":"user.js","names":[],"sources":["../../src/api/user.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport type { Request, Response } from 'express';\nimport { Router } from 'express';\nimport { isNil } from 'lodash-es';\n\nimport { type Auth, getApiToken } from '@verdaccio/auth';\nimport type { VerdaccioError } from '@verdaccio/core';\nimport {\n API_ERROR,\n APP_ERROR,\n HEADERS,\n HTTP_STATUS,\n TOKEN_BEARER,\n cryptoUtils,\n errorUtils,\n validationUtils,\n} from '@verdaccio/core';\nimport { WebUrls, rateLimit } from '@verdaccio/middleware';\nimport type { Config, JWTSignOptions, RemoteUser } from '@verdaccio/types';\n\nimport type { $NextFunctionVer } from './package';\n\nconst debug = buildDebug('verdaccio:web:api:user');\n\nconst WEB_LOGIN_SESSION_ID = 'web-login-sessionId';\n\nfunction addUserAuthApi(auth: Auth, config: Config, storage: Storage): Router {\n const route = Router(); /* eslint new-cap: 0 */\n route.post(\n WebUrls.user_login,\n rateLimit(config?.userRateLimit),\n function (req: Request, res: Response, next: $NextFunctionVer): void {\n const { username, password } = req.body;\n debug('authenticate %o', username);\n auth.authenticate(\n username,\n password,\n async (err: VerdaccioError | null, user?: RemoteUser): Promise<void> => {\n if (err) {\n const errorCode = err.message ? HTTP_STATUS.UNAUTHORIZED : HTTP_STATUS.INTERNAL_ERROR;\n debug('error authenticate %o', errorCode);\n // Prevent the final middleware from adding WWW-Authenticate header,\n // which triggers the browser's native basic auth popup instead of\n // letting the WebUI handle the error in JavaScript.\n if (errorCode === HTTP_STATUS.UNAUTHORIZED) {\n res.set(HEADERS.WWW_AUTH, TOKEN_BEARER);\n }\n return next(errorUtils.getCode(errorCode, err.message));\n } else {\n req.remote_user = user as RemoteUser;\n const jWTSignOptions: JWTSignOptions = config.security.web.sign;\n res.set(HEADERS.CACHE_CONTROL, HEADERS.NO_CACHE);\n return next({\n token: await auth.jwtEncrypt(user as RemoteUser, jWTSignOptions),\n username: req.remote_user.name,\n });\n }\n }\n );\n }\n );\n\n if (config?.flags?.createUser === true) {\n route.put(\n WebUrls.user_signup,\n rateLimit(config?.userRateLimit),\n function (req: Request, res: Response, next: $NextFunctionVer): void {\n const { name, password, email, sessionId } = req.body;\n debug('login or adduser');\n\n // TOOD: reuse with login.ts file\n if (typeof sessionId !== 'string' || sessionId.length !== 36) {\n debug('sessionId is invalid length: %o', sessionId.length);\n return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.SESSION_ID_INVALID));\n }\n\n if (!name || !password || !email) {\n return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.BAD_DATA));\n }\n\n auth.add_user(name, password, async function (err, user): Promise<void> {\n if (err) {\n if (err.status >= HTTP_STATUS.BAD_REQUEST && err.status < HTTP_STATUS.INTERNAL_ERROR) {\n debug('adduser: error on create user');\n // With npm registering is the same as logging in,\n // and npm accepts only an 409 error.\n // So, changing status code here.\n return next(\n errorUtils.getCode(err.status, err.message) || errorUtils.getConflict(err.message)\n );\n }\n return next(err);\n }\n\n const tokens = await storage.readTokens({ user: sessionId });\n debug('tokens found for sessionId %o: %o', sessionId, tokens.length);\n if (tokens && tokens.length === 1 && tokens[0].key === WEB_LOGIN_SESSION_ID) {\n if (tokens[0].token.length === 0) {\n debug('waiting for authentication');\n // Poll again after short delay\n // TODO: make this configurable (default 5 seconds)\n res.status(HTTP_STATUS.ACCEPTED);\n res.set(HEADERS.RETRY_AFTER, '5');\n res.json({});\n return;\n }\n\n // session token can only be used once\n debug('deleting session token');\n await storage.deleteToken(sessionId, tokens[0].key);\n\n // Check if token has expired\n // TODO: make this configurable (default 2 minutes)\n const tokenCreatedDate = new Date(tokens[0].created);\n const minutesAgo = new Date(Date.now() - 2 * 60 * 1000);\n if (tokenCreatedDate < minutesAgo) {\n debug('session token expired');\n return next(errorUtils.getUnauthorized(API_ERROR.SESSION_TOKEN_EXPIRED));\n }\n\n debug('session token is valid, login successful');\n res.status(HTTP_STATUS.OK);\n res.json({ token: tokens[0].token });\n return;\n }\n\n const token =\n name && password\n ? await getApiToken(auth, config, user as RemoteUser, password)\n : undefined;\n if (token) {\n debug('adduser: new token %o', cryptoUtils.mask(token as string, 4));\n return next({ token, username: name });\n }\n return next(errorUtils.getUnauthorized());\n });\n }\n );\n }\n\n if (config?.flags?.changePassword === true) {\n route.put(\n WebUrls.reset_password,\n rateLimit(config?.userRateLimit),\n function (req: Request, res: Response, next: $NextFunctionVer): void {\n if (isNil(req.remote_user.name)) {\n res.status(HTTP_STATUS.UNAUTHORIZED);\n return next({\n // FUTURE: update to a more meaningful message\n message: API_ERROR.MUST_BE_LOGGED,\n });\n }\n\n const { password } = req.body;\n const { name } = req.remote_user;\n\n if (\n validationUtils.validatePassword(\n password.new,\n config?.server?.passwordValidationRegex\n ) === false\n ) {\n return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, APP_ERROR.PASSWORD_VALIDATION));\n }\n\n auth.changePassword(name as string, password.old, password.new, (err, isUpdated): void => {\n if (isNil(err) && isUpdated) {\n next({\n ok: true,\n });\n } else {\n return next(errorUtils.getInternalError(API_ERROR.INTERNAL_SERVER_ERROR));\n }\n });\n }\n );\n }\n\n return route;\n}\n\nexport default addUserAuthApi;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"user.mjs","names":[],"sources":["../../src/api/user.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport type { Request, Response } from 'express';\nimport { Router } from 'express';\nimport { isNil } from 'lodash-es';\n\nimport { type Auth, getApiToken } from '@verdaccio/auth';\nimport type { VerdaccioError } from '@verdaccio/core';\nimport {\n API_ERROR,\n APP_ERROR,\n HEADERS,\n HTTP_STATUS,\n TOKEN_BEARER,\n cryptoUtils,\n errorUtils,\n validationUtils,\n} from '@verdaccio/core';\nimport { WebUrls, rateLimit } from '@verdaccio/middleware';\nimport type { Config, JWTSignOptions, RemoteUser } from '@verdaccio/types';\n\nimport type { $NextFunctionVer } from './package';\n\nconst debug = buildDebug('verdaccio:web:api:user');\n\nconst WEB_LOGIN_SESSION_ID = 'web-login-sessionId';\n\nfunction addUserAuthApi(auth: Auth, config: Config, storage: Storage): Router {\n const route = Router(); /* eslint new-cap: 0 */\n route.post(\n WebUrls.user_login,\n rateLimit(config?.userRateLimit),\n function (req: Request, res: Response, next: $NextFunctionVer): void {\n const { username, password } = req.body;\n debug('authenticate %o', username);\n auth.authenticate(\n username,\n password,\n async (err: VerdaccioError | null, user?: RemoteUser): Promise<void> => {\n if (err) {\n const errorCode = err.message ? HTTP_STATUS.UNAUTHORIZED : HTTP_STATUS.INTERNAL_ERROR;\n debug('error authenticate %o', errorCode);\n // Prevent the final middleware from adding WWW-Authenticate header,\n // which triggers the browser's native basic auth popup instead of\n // letting the WebUI handle the error in JavaScript.\n if (errorCode === HTTP_STATUS.UNAUTHORIZED) {\n res.set(HEADERS.WWW_AUTH, TOKEN_BEARER);\n }\n return next(errorUtils.getCode(errorCode, err.message));\n } else {\n req.remote_user = user as RemoteUser;\n const jWTSignOptions: JWTSignOptions = config.security.web.sign;\n res.set(HEADERS.CACHE_CONTROL, HEADERS.NO_CACHE);\n return next({\n token: await auth.jwtEncrypt(user as RemoteUser, jWTSignOptions),\n username: req.remote_user.name,\n });\n }\n }\n );\n }\n );\n\n if (config?.flags?.createUser === true) {\n route.put(\n WebUrls.user_signup,\n rateLimit(config?.userRateLimit),\n function (req: Request, res: Response, next: $NextFunctionVer): void {\n const { name, password, email, sessionId } = req.body;\n debug('login or adduser');\n\n // TOOD: reuse with login.ts file\n if (typeof sessionId !== 'string' || sessionId.length !== 36) {\n debug('sessionId is invalid length: %o', sessionId.length);\n return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.SESSION_ID_INVALID));\n }\n\n if (!name || !password || !email) {\n return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.BAD_DATA));\n }\n\n auth.add_user(name, password, async function (err, user): Promise<void> {\n if (err) {\n if (err.status >= HTTP_STATUS.BAD_REQUEST && err.status < HTTP_STATUS.INTERNAL_ERROR) {\n debug('adduser: error on create user');\n // With npm registering is the same as logging in,\n // and npm accepts only an 409 error.\n // So, changing status code here.\n return next(\n errorUtils.getCode(err.status, err.message) || errorUtils.getConflict(err.message)\n );\n }\n return next(err);\n }\n\n const tokens = await storage.readTokens({ user: sessionId });\n debug('tokens found for sessionId %o: %o', sessionId, tokens.length);\n if (tokens && tokens.length === 1 && tokens[0].key === WEB_LOGIN_SESSION_ID) {\n if (tokens[0].token.length === 0) {\n debug('waiting for authentication');\n // Poll again after short delay\n // TODO: make this configurable (default 5 seconds)\n res.status(HTTP_STATUS.ACCEPTED);\n res.set(HEADERS.RETRY_AFTER, '5');\n res.json({});\n return;\n }\n\n // session token can only be used once\n debug('deleting session token');\n await storage.deleteToken(sessionId, tokens[0].key);\n\n // Check if token has expired\n // TODO: make this configurable (default 2 minutes)\n const tokenCreatedDate = new Date(tokens[0].created);\n const minutesAgo = new Date(Date.now() - 2 * 60 * 1000);\n if (tokenCreatedDate < minutesAgo) {\n debug('session token expired');\n return next(errorUtils.getUnauthorized(API_ERROR.SESSION_TOKEN_EXPIRED));\n }\n\n debug('session token is valid, login successful');\n res.status(HTTP_STATUS.OK);\n res.json({ token: tokens[0].token });\n return;\n }\n\n const token =\n name && password\n ? await getApiToken(auth, config, user as RemoteUser, password)\n : undefined;\n if (token) {\n debug('adduser: new token %o', cryptoUtils.mask(token as string, 4));\n return next({ token, username: name });\n }\n return next(errorUtils.getUnauthorized());\n });\n }\n );\n }\n\n if (config?.flags?.changePassword === true) {\n route.put(\n WebUrls.reset_password,\n rateLimit(config?.userRateLimit),\n function (req: Request, res: Response, next: $NextFunctionVer): void {\n if (isNil(req.remote_user.name)) {\n res.status(HTTP_STATUS.UNAUTHORIZED);\n return next({\n // FUTURE: update to a more meaningful message\n message: API_ERROR.MUST_BE_LOGGED,\n });\n }\n\n const { password } = req.body;\n const { name } = req.remote_user;\n\n if (\n validationUtils.validatePassword(\n password.new,\n config?.server?.passwordValidationRegex\n ) === false\n ) {\n return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, APP_ERROR.PASSWORD_VALIDATION));\n }\n\n auth.changePassword(name as string, password.old, password.new, (err, isUpdated): void => {\n if (isNil(err) && isUpdated) {\n next({\n ok: true,\n });\n } else {\n return next(errorUtils.getInternalError(API_ERROR.INTERNAL_SERVER_ERROR));\n }\n });\n }\n );\n }\n\n return route;\n}\n\nexport default addUserAuthApi;\n"],"mappings":"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"}
1
+ {"version":3,"file":"user.mjs","names":[],"sources":["../../src/api/user.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport type { Request, Response } from 'express';\nimport { Router } from 'express';\nimport { isNil } from 'lodash-es';\n\nimport { type Auth, getApiToken } from '@verdaccio/auth';\nimport type { VerdaccioError } from '@verdaccio/core';\nimport {\n API_ERROR,\n APP_ERROR,\n HEADERS,\n HTTP_STATUS,\n TOKEN_BEARER,\n cryptoUtils,\n errorUtils,\n validationUtils,\n} from '@verdaccio/core';\nimport { WebUrls, rateLimit } from '@verdaccio/middleware';\nimport type { Config, JWTSignOptions, RemoteUser } from '@verdaccio/types';\n\nimport type { $NextFunctionVer } from './package';\n\nconst debug = buildDebug('verdaccio:web:api:user');\n\nconst WEB_LOGIN_SESSION_ID = 'web-login-sessionId';\n\nfunction addUserAuthApi(auth: Auth, config: Config, storage: Storage): Router {\n const route = Router(); /* eslint new-cap: 0 */\n route.post(\n WebUrls.user_login,\n rateLimit(config?.userRateLimit),\n function (req: Request, res: Response, next: $NextFunctionVer): void {\n const { username, password } = req.body;\n debug('authenticate %o', username);\n auth.authenticate(\n username,\n password,\n async (err: VerdaccioError | null, user?: RemoteUser): Promise<void> => {\n if (err) {\n const errorCode = err.message ? HTTP_STATUS.UNAUTHORIZED : HTTP_STATUS.INTERNAL_ERROR;\n debug('error authenticate %o', errorCode);\n // Prevent the final middleware from adding WWW-Authenticate header,\n // which triggers the browser's native basic auth popup instead of\n // letting the WebUI handle the error in JavaScript.\n if (errorCode === HTTP_STATUS.UNAUTHORIZED) {\n res.set(HEADERS.WWW_AUTH, TOKEN_BEARER);\n }\n return next(errorUtils.getCode(errorCode, err.message));\n } else {\n req.remote_user = user as RemoteUser;\n const jWTSignOptions: JWTSignOptions = config.security.web.sign;\n res.set(HEADERS.CACHE_CONTROL, HEADERS.NO_CACHE);\n return next({\n token: await auth.jwtEncrypt(user as RemoteUser, jWTSignOptions),\n username: req.remote_user.name,\n });\n }\n }\n );\n }\n );\n\n if (config?.flags?.createUser === true) {\n route.put(\n WebUrls.user_signup,\n rateLimit(config?.userRateLimit),\n function (req: Request, res: Response, next: $NextFunctionVer): void {\n const { name, password, email, sessionId } = req.body;\n debug('login or adduser');\n\n // TOOD: reuse with login.ts file\n if (typeof sessionId !== 'string' || sessionId.length !== 36) {\n debug('sessionId is invalid length: %o', sessionId.length);\n return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.SESSION_ID_INVALID));\n }\n\n if (!name || !password || !email) {\n return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.BAD_DATA));\n }\n\n auth.add_user(name, password, async function (err, user): Promise<void> {\n if (err) {\n if (err.status >= HTTP_STATUS.BAD_REQUEST && err.status < HTTP_STATUS.INTERNAL_ERROR) {\n debug('adduser: error on create user');\n // With npm registering is the same as logging in,\n // and npm accepts only an 409 error.\n // So, changing status code here.\n return next(\n errorUtils.getCode(err.status, err.message) || errorUtils.getConflict(err.message)\n );\n }\n return next(err);\n }\n\n const tokens = await storage.readTokens({ user: sessionId });\n debug('tokens found for sessionId %o: %o', sessionId, tokens.length);\n if (tokens && tokens.length === 1 && tokens[0].key === WEB_LOGIN_SESSION_ID) {\n if (tokens[0].token.length === 0) {\n debug('waiting for authentication');\n // Poll again after short delay\n // TODO: make this configurable (default 5 seconds)\n res.status(HTTP_STATUS.ACCEPTED);\n res.set(HEADERS.RETRY_AFTER, '5');\n res.json({});\n return;\n }\n\n // session token can only be used once\n debug('deleting session token');\n await storage.deleteToken(sessionId, tokens[0].key);\n\n // Check if token has expired\n // TODO: make this configurable (default 2 minutes)\n const tokenCreatedDate = new Date(tokens[0].created);\n const minutesAgo = new Date(Date.now() - 2 * 60 * 1000);\n if (tokenCreatedDate < minutesAgo) {\n debug('session token expired');\n return next(errorUtils.getUnauthorized(API_ERROR.SESSION_TOKEN_EXPIRED));\n }\n\n debug('session token is valid, login successful');\n res.status(HTTP_STATUS.OK);\n res.json({ token: tokens[0].token });\n return;\n }\n\n const token =\n name && password\n ? await getApiToken(auth, config, user as RemoteUser, password)\n : undefined;\n if (token) {\n debug('adduser: new token %o', cryptoUtils.mask(token as string, 4));\n return next({ token, username: name });\n }\n return next(errorUtils.getUnauthorized());\n });\n }\n );\n }\n\n if (config?.flags?.changePassword === true) {\n route.put(\n WebUrls.reset_password,\n rateLimit(config?.userRateLimit),\n function (req: Request, res: Response, next: $NextFunctionVer): void {\n if (isNil(req.remote_user.name)) {\n res.status(HTTP_STATUS.UNAUTHORIZED);\n return next({\n // FUTURE: update to a more meaningful message\n message: API_ERROR.MUST_BE_LOGGED,\n });\n }\n\n const { password } = req.body;\n const { name } = req.remote_user;\n\n if (\n validationUtils.validatePassword(\n password.new,\n config?.server?.passwordValidationRegex\n ) === false\n ) {\n return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, APP_ERROR.PASSWORD_VALIDATION));\n }\n\n auth.changePassword(name as string, password.old, password.new, (err, isUpdated): void => {\n if (isNil(err) && isUpdated) {\n next({\n ok: true,\n });\n } else {\n return next(errorUtils.getInternalError(API_ERROR.INTERNAL_SERVER_ERROR));\n }\n });\n }\n );\n }\n\n return route;\n}\n\nexport default addUserAuthApi;\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { Author, Person, WebManifest } from '@verdaccio/types';
1
+ import type { Author, Person, WebManifest } from '@verdaccio/types';
2
2
  export declare const GENERIC_AVATAR: string;
3
3
  /**
4
4
  * Formats author field for webui.
@@ -1 +1 @@
1
- {"version":3,"file":"author-utils.js","names":[],"sources":["../src/author-utils.ts"],"sourcesContent":["import { DEFAULT_USER, cryptoUtils } from '@verdaccio/core';\nimport type { Author, Person, WebManifest } from '@verdaccio/types';\n\nconst AVATAR_PROVIDER = 'https://www.gravatar.com/avatar/';\n\nexport const GENERIC_AVATAR =\n 'data:image/svg+xml;utf8,' +\n encodeURIComponent(\n '<svg height=\"100\" viewBox=\"-27 24 100 100\" width=\"100\" xmlns=\"http://www.w3.org/' +\n '2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"><defs><circle cx=\"23\" cy=\"7' +\n '4\" id=\"a\" r=\"50\"/></defs><use fill=\"#F5EEE5\" overflow=\"visible\" xlink:href=\"#a\"/' +\n '><clipPath id=\"b\"><use overflow=\"visible\" xlink:href=\"#a\"/></clipPath><g clip-pa' +\n 'th=\"url(#b)\"><defs><path d=\"M36 95.9c0 4 4.7 5.2 7.1 5.8 7.6 2 22.8 5.9 22.8 5.9' +\n ' 3.2 1.1 5.7 3.5 7.1 6.6v9.8H-27v-9.8c1.3-3.1 3.9-5.5 7.1-6.6 0 0 15.2-3.9 22.8-' +\n '5.9 2.4-.6 7.1-1.8 7.1-5.8V85h26v10.9z\" id=\"c\"/></defs><use fill=\"#E6C19C\" overf' +\n 'low=\"visible\" xlink:href=\"#c\"/><clipPath id=\"d\"><use overflow=\"visible\" xlink:hr' +\n 'ef=\"#c\"/></clipPath><path clip-path=\"url(#d)\" d=\"M23.2 35h.2c3.3 0 8.2.2 11.4 2 ' +\n '3.3 1.9 7.3 5.6 8.5 12.1 2.4 13.7-2.1 35.4-6.3 42.4-4 6.7-9.8 9.2-13.5 9.4H23h-.' +\n '1c-3.7-.2-9.5-2.7-13.5-9.4-4.2-7-8.7-28.7-6.3-42.4 1.2-6.5 5.2-10.2 8.5-12.1 3.2' +\n '-1.8 8.1-2 11.4-2h.2z\" fill=\"#D4B08C\"/></g><path d=\"M22.6 40c19.1 0 20.7 13.8 20' +\n '.8 15.1 1.1 11.9-3 28.1-6.8 33.7-4 5.9-9.8 8.1-13.5 8.3h-.5c-3.8-.3-9.6-2.5-13.6' +\n '-8.4-3.8-5.6-7.9-21.8-6.8-33.8C2.3 53.7 3.5 40 22.6 40z\" fill=\"#F2CEA5\"/></svg>'\n );\n\n/**\n * Formats author field for webui.\n * @see https://docs.npmjs.com/files/package.json#author\n * @param {string|object|undefined} author\n */\nexport function formatAuthor(author?: Person): any {\n let authorDetails = {\n name: DEFAULT_USER,\n email: '',\n url: '',\n };\n\n if (author === null || author === undefined) {\n return authorDetails;\n }\n\n if (typeof author === 'string') {\n authorDetails = {\n ...authorDetails,\n name: author as string,\n };\n }\n\n if (typeof author === 'object') {\n authorDetails = {\n ...authorDetails,\n ...(author as Author),\n };\n }\n\n return authorDetails;\n}\n\nexport function addGravatarSupport(pkgInfo: WebManifest, online: boolean = true): Author {\n const pkgInfoCopy = { ...pkgInfo } as any;\n const author: any = pkgInfo?.latest?.author ?? null;\n const contributors: Person[] = normalizeAuthors(pkgInfo?.latest?.contributors ?? []);\n const maintainers: Person[] = normalizeAuthors(pkgInfo?.latest?.maintainers ?? []);\n\n // for author\n if (author && typeof author === 'object') {\n const { email } = author as Author;\n pkgInfoCopy.latest.author._avatar = generateGravatarUrl(email, online);\n } else if (author && typeof author === 'string') {\n const { email, name, url } = splitAuthorString(author);\n pkgInfoCopy.latest.author = {\n _avatar: generateGravatarUrl(email, online),\n email,\n name,\n url,\n };\n }\n\n // for contributors\n if (contributors && contributors.length > 0) {\n pkgInfoCopy.latest.contributors = contributors.map((contributor): Person => {\n if (typeof contributor === 'object') {\n contributor._avatar = generateGravatarUrl(contributor.email, online);\n } else if (typeof contributor === 'string') {\n const { email, name, url } = splitAuthorString(contributor);\n contributor = {\n _avatar: generateGravatarUrl(email, online),\n email,\n name,\n url,\n };\n }\n\n return contributor;\n });\n }\n\n // for maintainers\n if (maintainers && maintainers.length > 0) {\n pkgInfoCopy.latest.maintainers = maintainers.map((maintainer): Person => {\n if (typeof maintainer === 'object') {\n maintainer._avatar = generateGravatarUrl(maintainer.email, online);\n } else if (typeof maintainer === 'string') {\n const { email, name, url } = splitAuthorString(maintainer);\n maintainer = {\n _avatar: generateGravatarUrl(email, online),\n email,\n name,\n url,\n };\n }\n\n return maintainer;\n });\n }\n\n return pkgInfoCopy;\n}\n\n/**\n * Generate gravatar url from email address\n */\nexport function generateGravatarUrl(email: string | void = '', online: boolean = true): string {\n if (online && typeof email === 'string' && email.length > 0) {\n email = email.trim().toLocaleLowerCase();\n const emailMD5 = cryptoUtils.stringToMD5(email);\n return `${AVATAR_PROVIDER}${emailMD5}`;\n }\n return GENERIC_AVATAR;\n}\n\nfunction normalizeAuthors(authors: Person[]): Person[] {\n if (authors === null || authors === undefined) {\n return [];\n } else if (authors && !Array.isArray(authors)) {\n return [authors];\n } else if (typeof authors === 'string') {\n return [\n {\n name: authors,\n },\n ];\n }\n\n return authors;\n}\n\n/**\n * Split author string into name, email and url\n * Example: \"Barney Rubble <b@rubble.com> (http://barnyrubble.tumblr.com/)\"\n * @param {string} author\n * @returns {Object} { name: string, email: string, url: string }\n */\nfunction splitAuthorString(author: string): { name: string; email?: string; url: string } {\n // Limit author string length to prevent ReDoS\n const maxLength = 256;\n const truncatedAuthor = author.slice(0, maxLength);\n\n // Split on fixed delimiters rather than using regex\n const emailStart = truncatedAuthor.indexOf('<');\n const emailEnd = truncatedAuthor.indexOf('>');\n const urlStart = truncatedAuthor.indexOf('(');\n const urlEnd = truncatedAuthor.indexOf(')');\n\n let name = truncatedAuthor;\n let email = '';\n let url = '';\n\n if (emailStart >= 0 && emailEnd > emailStart) {\n name = truncatedAuthor.slice(0, emailStart).trim();\n email = truncatedAuthor.slice(emailStart + 1, emailEnd).trim();\n }\n\n if (urlStart >= 0 && urlEnd > urlStart) {\n url = truncatedAuthor.slice(urlStart + 1, urlEnd).trim();\n }\n\n return {\n name: name || author,\n email,\n url,\n };\n}\n"],"mappings":";;AAGA,IAAM,kBAAkB;AAExB,IAAa,iBACX,6BACA,mBACE,2pCAcF;;;;;;AAOF,SAAgB,aAAa,QAAsB;CACjD,IAAI,gBAAgB;EAClB,MAAM,gBAAA;EACN,OAAO;EACP,KAAK;CACP;CAEA,IAAI,WAAW,QAAQ,WAAW,KAAA,GAChC,OAAO;CAGT,IAAI,OAAO,WAAW,UACpB,gBAAgB;EACd,GAAG;EACH,MAAM;CACR;CAGF,IAAI,OAAO,WAAW,UACpB,gBAAgB;EACd,GAAG;EACH,GAAI;CACN;CAGF,OAAO;AACT;AAEA,SAAgB,mBAAmB,SAAsB,SAAkB,MAAc;CACvF,MAAM,cAAc,EAAE,GAAG,QAAQ;CACjC,MAAM,SAAc,SAAS,QAAQ,UAAU;CAC/C,MAAM,eAAyB,iBAAiB,SAAS,QAAQ,gBAAgB,CAAC,CAAC;CACnF,MAAM,cAAwB,iBAAiB,SAAS,QAAQ,eAAe,CAAC,CAAC;CAGjF,IAAI,UAAU,OAAO,WAAW,UAAU;EACxC,MAAM,EAAE,UAAU;EAClB,YAAY,OAAO,OAAO,UAAU,oBAAoB,OAAO,MAAM;CACvE,OAAO,IAAI,UAAU,OAAO,WAAW,UAAU;EAC/C,MAAM,EAAE,OAAO,MAAM,QAAQ,kBAAkB,MAAM;EACrD,YAAY,OAAO,SAAS;GAC1B,SAAS,oBAAoB,OAAO,MAAM;GAC1C;GACA;GACA;EACF;CACF;CAGA,IAAI,gBAAgB,aAAa,SAAS,GACxC,YAAY,OAAO,eAAe,aAAa,KAAK,gBAAwB;EAC1E,IAAI,OAAO,gBAAgB,UACzB,YAAY,UAAU,oBAAoB,YAAY,OAAO,MAAM;OAC9D,IAAI,OAAO,gBAAgB,UAAU;GAC1C,MAAM,EAAE,OAAO,MAAM,QAAQ,kBAAkB,WAAW;GAC1D,cAAc;IACZ,SAAS,oBAAoB,OAAO,MAAM;IAC1C;IACA;IACA;GACF;EACF;EAEA,OAAO;CACT,CAAC;CAIH,IAAI,eAAe,YAAY,SAAS,GACtC,YAAY,OAAO,cAAc,YAAY,KAAK,eAAuB;EACvE,IAAI,OAAO,eAAe,UACxB,WAAW,UAAU,oBAAoB,WAAW,OAAO,MAAM;OAC5D,IAAI,OAAO,eAAe,UAAU;GACzC,MAAM,EAAE,OAAO,MAAM,QAAQ,kBAAkB,UAAU;GACzD,aAAa;IACX,SAAS,oBAAoB,OAAO,MAAM;IAC1C;IACA;IACA;GACF;EACF;EAEA,OAAO;CACT,CAAC;CAGH,OAAO;AACT;;;;AAKA,SAAgB,oBAAoB,QAAuB,IAAI,SAAkB,MAAc;CAC7F,IAAI,UAAU,OAAO,UAAU,YAAY,MAAM,SAAS,GAAG;EAC3D,QAAQ,MAAM,KAAK,EAAE,kBAAkB;EAEvC,OAAO,GAAG,kBADO,gBAAA,YAAY,YAAY,KACb;CAC9B;CACA,OAAO;AACT;AAEA,SAAS,iBAAiB,SAA6B;CACrD,IAAI,YAAY,QAAQ,YAAY,KAAA,GAClC,OAAO,CAAC;MACH,IAAI,WAAW,CAAC,MAAM,QAAQ,OAAO,GAC1C,OAAO,CAAC,OAAO;MACV,IAAI,OAAO,YAAY,UAC5B,OAAO,CACL,EACE,MAAM,QACR,CACF;CAGF,OAAO;AACT;;;;;;;AAQA,SAAS,kBAAkB,QAA+D;CAGxF,MAAM,kBAAkB,OAAO,MAAM,GAAG,GAAS;CAGjD,MAAM,aAAa,gBAAgB,QAAQ,GAAG;CAC9C,MAAM,WAAW,gBAAgB,QAAQ,GAAG;CAC5C,MAAM,WAAW,gBAAgB,QAAQ,GAAG;CAC5C,MAAM,SAAS,gBAAgB,QAAQ,GAAG;CAE1C,IAAI,OAAO;CACX,IAAI,QAAQ;CACZ,IAAI,MAAM;CAEV,IAAI,cAAc,KAAK,WAAW,YAAY;EAC5C,OAAO,gBAAgB,MAAM,GAAG,UAAU,EAAE,KAAK;EACjD,QAAQ,gBAAgB,MAAM,aAAa,GAAG,QAAQ,EAAE,KAAK;CAC/D;CAEA,IAAI,YAAY,KAAK,SAAS,UAC5B,MAAM,gBAAgB,MAAM,WAAW,GAAG,MAAM,EAAE,KAAK;CAGzD,OAAO;EACL,MAAM,QAAQ;EACd;EACA;CACF;AACF"}
1
+ {"version":3,"file":"author-utils.js","names":[],"sources":["../src/author-utils.ts"],"sourcesContent":["import { DEFAULT_USER, cryptoUtils } from '@verdaccio/core';\nimport type { Author, Person, WebManifest } from '@verdaccio/types';\n\nconst AVATAR_PROVIDER = 'https://www.gravatar.com/avatar/';\n\nexport const GENERIC_AVATAR =\n 'data:image/svg+xml;utf8,' +\n encodeURIComponent(\n '<svg height=\"100\" viewBox=\"-27 24 100 100\" width=\"100\" xmlns=\"http://www.w3.org/' +\n '2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"><defs><circle cx=\"23\" cy=\"7' +\n '4\" id=\"a\" r=\"50\"/></defs><use fill=\"#F5EEE5\" overflow=\"visible\" xlink:href=\"#a\"/' +\n '><clipPath id=\"b\"><use overflow=\"visible\" xlink:href=\"#a\"/></clipPath><g clip-pa' +\n 'th=\"url(#b)\"><defs><path d=\"M36 95.9c0 4 4.7 5.2 7.1 5.8 7.6 2 22.8 5.9 22.8 5.9' +\n ' 3.2 1.1 5.7 3.5 7.1 6.6v9.8H-27v-9.8c1.3-3.1 3.9-5.5 7.1-6.6 0 0 15.2-3.9 22.8-' +\n '5.9 2.4-.6 7.1-1.8 7.1-5.8V85h26v10.9z\" id=\"c\"/></defs><use fill=\"#E6C19C\" overf' +\n 'low=\"visible\" xlink:href=\"#c\"/><clipPath id=\"d\"><use overflow=\"visible\" xlink:hr' +\n 'ef=\"#c\"/></clipPath><path clip-path=\"url(#d)\" d=\"M23.2 35h.2c3.3 0 8.2.2 11.4 2 ' +\n '3.3 1.9 7.3 5.6 8.5 12.1 2.4 13.7-2.1 35.4-6.3 42.4-4 6.7-9.8 9.2-13.5 9.4H23h-.' +\n '1c-3.7-.2-9.5-2.7-13.5-9.4-4.2-7-8.7-28.7-6.3-42.4 1.2-6.5 5.2-10.2 8.5-12.1 3.2' +\n '-1.8 8.1-2 11.4-2h.2z\" fill=\"#D4B08C\"/></g><path d=\"M22.6 40c19.1 0 20.7 13.8 20' +\n '.8 15.1 1.1 11.9-3 28.1-6.8 33.7-4 5.9-9.8 8.1-13.5 8.3h-.5c-3.8-.3-9.6-2.5-13.6' +\n '-8.4-3.8-5.6-7.9-21.8-6.8-33.8C2.3 53.7 3.5 40 22.6 40z\" fill=\"#F2CEA5\"/></svg>'\n );\n\n/**\n * Formats author field for webui.\n * @see https://docs.npmjs.com/files/package.json#author\n * @param {string|object|undefined} author\n */\nexport function formatAuthor(author?: Person): any {\n let authorDetails = {\n name: DEFAULT_USER,\n email: '',\n url: '',\n };\n\n if (author === null || author === undefined) {\n return authorDetails;\n }\n\n if (typeof author === 'string') {\n authorDetails = {\n ...authorDetails,\n name: author as string,\n };\n }\n\n if (typeof author === 'object') {\n authorDetails = {\n ...authorDetails,\n ...(author as Author),\n };\n }\n\n return authorDetails;\n}\n\nexport function addGravatarSupport(pkgInfo: WebManifest, online: boolean = true): Author {\n const pkgInfoCopy = { ...pkgInfo } as any;\n const author: any = pkgInfo?.latest?.author ?? null;\n const contributors: Person[] = normalizeAuthors(pkgInfo?.latest?.contributors ?? []);\n const maintainers: Person[] = normalizeAuthors(pkgInfo?.latest?.maintainers ?? []);\n\n // for author\n if (author && typeof author === 'object') {\n const { email } = author as Author;\n pkgInfoCopy.latest.author._avatar = generateGravatarUrl(email, online);\n } else if (author && typeof author === 'string') {\n const { email, name, url } = splitAuthorString(author);\n pkgInfoCopy.latest.author = {\n _avatar: generateGravatarUrl(email, online),\n email,\n name,\n url,\n };\n }\n\n // for contributors\n if (contributors && contributors.length > 0) {\n pkgInfoCopy.latest.contributors = contributors.map((contributor): Person => {\n if (typeof contributor === 'object') {\n contributor._avatar = generateGravatarUrl(contributor.email, online);\n } else if (typeof contributor === 'string') {\n const { email, name, url } = splitAuthorString(contributor);\n contributor = {\n _avatar: generateGravatarUrl(email, online),\n email,\n name,\n url,\n };\n }\n\n return contributor;\n });\n }\n\n // for maintainers\n if (maintainers && maintainers.length > 0) {\n pkgInfoCopy.latest.maintainers = maintainers.map((maintainer): Person => {\n if (typeof maintainer === 'object') {\n maintainer._avatar = generateGravatarUrl(maintainer.email, online);\n } else if (typeof maintainer === 'string') {\n const { email, name, url } = splitAuthorString(maintainer);\n maintainer = {\n _avatar: generateGravatarUrl(email, online),\n email,\n name,\n url,\n };\n }\n\n return maintainer;\n });\n }\n\n return pkgInfoCopy;\n}\n\n/**\n * Generate gravatar url from email address\n */\nexport function generateGravatarUrl(email: string | void = '', online: boolean = true): string {\n if (online && typeof email === 'string' && email.length > 0) {\n email = email.trim().toLocaleLowerCase();\n const emailMD5 = cryptoUtils.stringToMD5(email);\n return `${AVATAR_PROVIDER}${emailMD5}`;\n }\n return GENERIC_AVATAR;\n}\n\nfunction normalizeAuthors(authors: Person[]): Person[] {\n if (authors === null || authors === undefined) {\n return [];\n } else if (authors && !Array.isArray(authors)) {\n return [authors];\n } else if (typeof authors === 'string') {\n return [\n {\n name: authors,\n },\n ];\n }\n\n return authors;\n}\n\n/**\n * Split author string into name, email and url\n * Example: \"Barney Rubble <b@rubble.com> (http://barnyrubble.tumblr.com/)\"\n * @param {string} author\n * @returns {Object} { name: string, email: string, url: string }\n */\nfunction splitAuthorString(author: string): { name: string; email?: string; url: string } {\n // Limit author string length to prevent ReDoS\n const maxLength = 256;\n const truncatedAuthor = author.slice(0, maxLength);\n\n // Split on fixed delimiters rather than using regex\n const emailStart = truncatedAuthor.indexOf('<');\n const emailEnd = truncatedAuthor.indexOf('>');\n const urlStart = truncatedAuthor.indexOf('(');\n const urlEnd = truncatedAuthor.indexOf(')');\n\n let name = truncatedAuthor;\n let email = '';\n let url = '';\n\n if (emailStart >= 0 && emailEnd > emailStart) {\n name = truncatedAuthor.slice(0, emailStart).trim();\n email = truncatedAuthor.slice(emailStart + 1, emailEnd).trim();\n }\n\n if (urlStart >= 0 && urlEnd > urlStart) {\n url = truncatedAuthor.slice(urlStart + 1, urlEnd).trim();\n }\n\n return {\n name: name || author,\n email,\n url,\n };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"author-utils.mjs","names":[],"sources":["../src/author-utils.ts"],"sourcesContent":["import { DEFAULT_USER, cryptoUtils } from '@verdaccio/core';\nimport type { Author, Person, WebManifest } from '@verdaccio/types';\n\nconst AVATAR_PROVIDER = 'https://www.gravatar.com/avatar/';\n\nexport const GENERIC_AVATAR =\n 'data:image/svg+xml;utf8,' +\n encodeURIComponent(\n '<svg height=\"100\" viewBox=\"-27 24 100 100\" width=\"100\" xmlns=\"http://www.w3.org/' +\n '2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"><defs><circle cx=\"23\" cy=\"7' +\n '4\" id=\"a\" r=\"50\"/></defs><use fill=\"#F5EEE5\" overflow=\"visible\" xlink:href=\"#a\"/' +\n '><clipPath id=\"b\"><use overflow=\"visible\" xlink:href=\"#a\"/></clipPath><g clip-pa' +\n 'th=\"url(#b)\"><defs><path d=\"M36 95.9c0 4 4.7 5.2 7.1 5.8 7.6 2 22.8 5.9 22.8 5.9' +\n ' 3.2 1.1 5.7 3.5 7.1 6.6v9.8H-27v-9.8c1.3-3.1 3.9-5.5 7.1-6.6 0 0 15.2-3.9 22.8-' +\n '5.9 2.4-.6 7.1-1.8 7.1-5.8V85h26v10.9z\" id=\"c\"/></defs><use fill=\"#E6C19C\" overf' +\n 'low=\"visible\" xlink:href=\"#c\"/><clipPath id=\"d\"><use overflow=\"visible\" xlink:hr' +\n 'ef=\"#c\"/></clipPath><path clip-path=\"url(#d)\" d=\"M23.2 35h.2c3.3 0 8.2.2 11.4 2 ' +\n '3.3 1.9 7.3 5.6 8.5 12.1 2.4 13.7-2.1 35.4-6.3 42.4-4 6.7-9.8 9.2-13.5 9.4H23h-.' +\n '1c-3.7-.2-9.5-2.7-13.5-9.4-4.2-7-8.7-28.7-6.3-42.4 1.2-6.5 5.2-10.2 8.5-12.1 3.2' +\n '-1.8 8.1-2 11.4-2h.2z\" fill=\"#D4B08C\"/></g><path d=\"M22.6 40c19.1 0 20.7 13.8 20' +\n '.8 15.1 1.1 11.9-3 28.1-6.8 33.7-4 5.9-9.8 8.1-13.5 8.3h-.5c-3.8-.3-9.6-2.5-13.6' +\n '-8.4-3.8-5.6-7.9-21.8-6.8-33.8C2.3 53.7 3.5 40 22.6 40z\" fill=\"#F2CEA5\"/></svg>'\n );\n\n/**\n * Formats author field for webui.\n * @see https://docs.npmjs.com/files/package.json#author\n * @param {string|object|undefined} author\n */\nexport function formatAuthor(author?: Person): any {\n let authorDetails = {\n name: DEFAULT_USER,\n email: '',\n url: '',\n };\n\n if (author === null || author === undefined) {\n return authorDetails;\n }\n\n if (typeof author === 'string') {\n authorDetails = {\n ...authorDetails,\n name: author as string,\n };\n }\n\n if (typeof author === 'object') {\n authorDetails = {\n ...authorDetails,\n ...(author as Author),\n };\n }\n\n return authorDetails;\n}\n\nexport function addGravatarSupport(pkgInfo: WebManifest, online: boolean = true): Author {\n const pkgInfoCopy = { ...pkgInfo } as any;\n const author: any = pkgInfo?.latest?.author ?? null;\n const contributors: Person[] = normalizeAuthors(pkgInfo?.latest?.contributors ?? []);\n const maintainers: Person[] = normalizeAuthors(pkgInfo?.latest?.maintainers ?? []);\n\n // for author\n if (author && typeof author === 'object') {\n const { email } = author as Author;\n pkgInfoCopy.latest.author._avatar = generateGravatarUrl(email, online);\n } else if (author && typeof author === 'string') {\n const { email, name, url } = splitAuthorString(author);\n pkgInfoCopy.latest.author = {\n _avatar: generateGravatarUrl(email, online),\n email,\n name,\n url,\n };\n }\n\n // for contributors\n if (contributors && contributors.length > 0) {\n pkgInfoCopy.latest.contributors = contributors.map((contributor): Person => {\n if (typeof contributor === 'object') {\n contributor._avatar = generateGravatarUrl(contributor.email, online);\n } else if (typeof contributor === 'string') {\n const { email, name, url } = splitAuthorString(contributor);\n contributor = {\n _avatar: generateGravatarUrl(email, online),\n email,\n name,\n url,\n };\n }\n\n return contributor;\n });\n }\n\n // for maintainers\n if (maintainers && maintainers.length > 0) {\n pkgInfoCopy.latest.maintainers = maintainers.map((maintainer): Person => {\n if (typeof maintainer === 'object') {\n maintainer._avatar = generateGravatarUrl(maintainer.email, online);\n } else if (typeof maintainer === 'string') {\n const { email, name, url } = splitAuthorString(maintainer);\n maintainer = {\n _avatar: generateGravatarUrl(email, online),\n email,\n name,\n url,\n };\n }\n\n return maintainer;\n });\n }\n\n return pkgInfoCopy;\n}\n\n/**\n * Generate gravatar url from email address\n */\nexport function generateGravatarUrl(email: string | void = '', online: boolean = true): string {\n if (online && typeof email === 'string' && email.length > 0) {\n email = email.trim().toLocaleLowerCase();\n const emailMD5 = cryptoUtils.stringToMD5(email);\n return `${AVATAR_PROVIDER}${emailMD5}`;\n }\n return GENERIC_AVATAR;\n}\n\nfunction normalizeAuthors(authors: Person[]): Person[] {\n if (authors === null || authors === undefined) {\n return [];\n } else if (authors && !Array.isArray(authors)) {\n return [authors];\n } else if (typeof authors === 'string') {\n return [\n {\n name: authors,\n },\n ];\n }\n\n return authors;\n}\n\n/**\n * Split author string into name, email and url\n * Example: \"Barney Rubble <b@rubble.com> (http://barnyrubble.tumblr.com/)\"\n * @param {string} author\n * @returns {Object} { name: string, email: string, url: string }\n */\nfunction splitAuthorString(author: string): { name: string; email?: string; url: string } {\n // Limit author string length to prevent ReDoS\n const maxLength = 256;\n const truncatedAuthor = author.slice(0, maxLength);\n\n // Split on fixed delimiters rather than using regex\n const emailStart = truncatedAuthor.indexOf('<');\n const emailEnd = truncatedAuthor.indexOf('>');\n const urlStart = truncatedAuthor.indexOf('(');\n const urlEnd = truncatedAuthor.indexOf(')');\n\n let name = truncatedAuthor;\n let email = '';\n let url = '';\n\n if (emailStart >= 0 && emailEnd > emailStart) {\n name = truncatedAuthor.slice(0, emailStart).trim();\n email = truncatedAuthor.slice(emailStart + 1, emailEnd).trim();\n }\n\n if (urlStart >= 0 && urlEnd > urlStart) {\n url = truncatedAuthor.slice(urlStart + 1, urlEnd).trim();\n }\n\n return {\n name: name || author,\n email,\n url,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"author-utils.mjs","names":[],"sources":["../src/author-utils.ts"],"sourcesContent":["import { DEFAULT_USER, cryptoUtils } from '@verdaccio/core';\nimport type { Author, Person, WebManifest } from '@verdaccio/types';\n\nconst AVATAR_PROVIDER = 'https://www.gravatar.com/avatar/';\n\nexport const GENERIC_AVATAR =\n 'data:image/svg+xml;utf8,' +\n encodeURIComponent(\n '<svg height=\"100\" viewBox=\"-27 24 100 100\" width=\"100\" xmlns=\"http://www.w3.org/' +\n '2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"><defs><circle cx=\"23\" cy=\"7' +\n '4\" id=\"a\" r=\"50\"/></defs><use fill=\"#F5EEE5\" overflow=\"visible\" xlink:href=\"#a\"/' +\n '><clipPath id=\"b\"><use overflow=\"visible\" xlink:href=\"#a\"/></clipPath><g clip-pa' +\n 'th=\"url(#b)\"><defs><path d=\"M36 95.9c0 4 4.7 5.2 7.1 5.8 7.6 2 22.8 5.9 22.8 5.9' +\n ' 3.2 1.1 5.7 3.5 7.1 6.6v9.8H-27v-9.8c1.3-3.1 3.9-5.5 7.1-6.6 0 0 15.2-3.9 22.8-' +\n '5.9 2.4-.6 7.1-1.8 7.1-5.8V85h26v10.9z\" id=\"c\"/></defs><use fill=\"#E6C19C\" overf' +\n 'low=\"visible\" xlink:href=\"#c\"/><clipPath id=\"d\"><use overflow=\"visible\" xlink:hr' +\n 'ef=\"#c\"/></clipPath><path clip-path=\"url(#d)\" d=\"M23.2 35h.2c3.3 0 8.2.2 11.4 2 ' +\n '3.3 1.9 7.3 5.6 8.5 12.1 2.4 13.7-2.1 35.4-6.3 42.4-4 6.7-9.8 9.2-13.5 9.4H23h-.' +\n '1c-3.7-.2-9.5-2.7-13.5-9.4-4.2-7-8.7-28.7-6.3-42.4 1.2-6.5 5.2-10.2 8.5-12.1 3.2' +\n '-1.8 8.1-2 11.4-2h.2z\" fill=\"#D4B08C\"/></g><path d=\"M22.6 40c19.1 0 20.7 13.8 20' +\n '.8 15.1 1.1 11.9-3 28.1-6.8 33.7-4 5.9-9.8 8.1-13.5 8.3h-.5c-3.8-.3-9.6-2.5-13.6' +\n '-8.4-3.8-5.6-7.9-21.8-6.8-33.8C2.3 53.7 3.5 40 22.6 40z\" fill=\"#F2CEA5\"/></svg>'\n );\n\n/**\n * Formats author field for webui.\n * @see https://docs.npmjs.com/files/package.json#author\n * @param {string|object|undefined} author\n */\nexport function formatAuthor(author?: Person): any {\n let authorDetails = {\n name: DEFAULT_USER,\n email: '',\n url: '',\n };\n\n if (author === null || author === undefined) {\n return authorDetails;\n }\n\n if (typeof author === 'string') {\n authorDetails = {\n ...authorDetails,\n name: author as string,\n };\n }\n\n if (typeof author === 'object') {\n authorDetails = {\n ...authorDetails,\n ...(author as Author),\n };\n }\n\n return authorDetails;\n}\n\nexport function addGravatarSupport(pkgInfo: WebManifest, online: boolean = true): Author {\n const pkgInfoCopy = { ...pkgInfo } as any;\n const author: any = pkgInfo?.latest?.author ?? null;\n const contributors: Person[] = normalizeAuthors(pkgInfo?.latest?.contributors ?? []);\n const maintainers: Person[] = normalizeAuthors(pkgInfo?.latest?.maintainers ?? []);\n\n // for author\n if (author && typeof author === 'object') {\n const { email } = author as Author;\n pkgInfoCopy.latest.author._avatar = generateGravatarUrl(email, online);\n } else if (author && typeof author === 'string') {\n const { email, name, url } = splitAuthorString(author);\n pkgInfoCopy.latest.author = {\n _avatar: generateGravatarUrl(email, online),\n email,\n name,\n url,\n };\n }\n\n // for contributors\n if (contributors && contributors.length > 0) {\n pkgInfoCopy.latest.contributors = contributors.map((contributor): Person => {\n if (typeof contributor === 'object') {\n contributor._avatar = generateGravatarUrl(contributor.email, online);\n } else if (typeof contributor === 'string') {\n const { email, name, url } = splitAuthorString(contributor);\n contributor = {\n _avatar: generateGravatarUrl(email, online),\n email,\n name,\n url,\n };\n }\n\n return contributor;\n });\n }\n\n // for maintainers\n if (maintainers && maintainers.length > 0) {\n pkgInfoCopy.latest.maintainers = maintainers.map((maintainer): Person => {\n if (typeof maintainer === 'object') {\n maintainer._avatar = generateGravatarUrl(maintainer.email, online);\n } else if (typeof maintainer === 'string') {\n const { email, name, url } = splitAuthorString(maintainer);\n maintainer = {\n _avatar: generateGravatarUrl(email, online),\n email,\n name,\n url,\n };\n }\n\n return maintainer;\n });\n }\n\n return pkgInfoCopy;\n}\n\n/**\n * Generate gravatar url from email address\n */\nexport function generateGravatarUrl(email: string | void = '', online: boolean = true): string {\n if (online && typeof email === 'string' && email.length > 0) {\n email = email.trim().toLocaleLowerCase();\n const emailMD5 = cryptoUtils.stringToMD5(email);\n return `${AVATAR_PROVIDER}${emailMD5}`;\n }\n return GENERIC_AVATAR;\n}\n\nfunction normalizeAuthors(authors: Person[]): Person[] {\n if (authors === null || authors === undefined) {\n return [];\n } else if (authors && !Array.isArray(authors)) {\n return [authors];\n } else if (typeof authors === 'string') {\n return [\n {\n name: authors,\n },\n ];\n }\n\n return authors;\n}\n\n/**\n * Split author string into name, email and url\n * Example: \"Barney Rubble <b@rubble.com> (http://barnyrubble.tumblr.com/)\"\n * @param {string} author\n * @returns {Object} { name: string, email: string, url: string }\n */\nfunction splitAuthorString(author: string): { name: string; email?: string; url: string } {\n // Limit author string length to prevent ReDoS\n const maxLength = 256;\n const truncatedAuthor = author.slice(0, maxLength);\n\n // Split on fixed delimiters rather than using regex\n const emailStart = truncatedAuthor.indexOf('<');\n const emailEnd = truncatedAuthor.indexOf('>');\n const urlStart = truncatedAuthor.indexOf('(');\n const urlEnd = truncatedAuthor.indexOf(')');\n\n let name = truncatedAuthor;\n let email = '';\n let url = '';\n\n if (emailStart >= 0 && emailEnd > emailStart) {\n name = truncatedAuthor.slice(0, emailStart).trim();\n email = truncatedAuthor.slice(emailStart + 1, emailEnd).trim();\n }\n\n if (urlStart >= 0 && urlEnd > urlStart) {\n url = truncatedAuthor.slice(urlStart + 1, urlEnd).trim();\n }\n\n return {\n name: name || author,\n email,\n url,\n };\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
1
  export declare const DEFAULT_PLUGIN_UI_THEME = "@verdaccio/ui-theme";
2
2
  export declare function loadTheme(config: any): Promise<any>;
3
- declare const _default: (config: any, auth: any, storage: any, logger: any) => Promise<import('express-serve-static-core').Router>;
4
3
  export default _default;
4
+ declare function _default(config: any, auth: any, storage: any, logger: any): Promise<import("express-serve-static-core").Router>;
@@ -52,5 +52,6 @@ var middleware_default = async (config, auth, storage, logger) => {
52
52
  //#endregion
53
53
  exports.DEFAULT_PLUGIN_UI_THEME = DEFAULT_PLUGIN_UI_THEME;
54
54
  exports.default = middleware_default;
55
+ exports.loadTheme = loadTheme;
55
56
 
56
57
  //# sourceMappingURL=middleware.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.js","names":[],"sources":["../src/middleware.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport express from 'express';\nimport { head, isNil } from 'lodash-es';\n\nimport { PLUGIN_CATEGORY, PLUGIN_UI_PREFIX } from '@verdaccio/core';\nimport { asyncLoadPlugin } from '@verdaccio/loaders';\nimport { logger } from '@verdaccio/logger';\nimport { webMiddleware } from '@verdaccio/middleware';\nimport defaultTheme from '@verdaccio/ui-theme';\n\nimport webEndpointsApi from './api';\n\nconst debug = buildDebug('verdaccio:web:middleware');\n\nexport const DEFAULT_PLUGIN_UI_THEME = '@verdaccio/ui-theme';\n\nexport async function loadTheme(config: any) {\n if (isNil(config.theme) === false) {\n const plugin = await asyncLoadPlugin(\n config.theme,\n { config, logger },\n // TODO: add types { staticPath: string; manifest: unknown; manifestFiles: unknown }\n function (plugin: any) {\n /**\n *\n - `staticPath`: is the same data returned in Verdaccio 5.\n - `manifest`: A manifest object.\n - `manifestFiles`: A object with one property `js` and the array (order matters) of the manifest id to be loaded in the template dynamically.\n */\n return plugin.staticPath && plugin.manifest && plugin.manifestFiles;\n },\n false,\n config.server?.pluginPrefix ?? PLUGIN_UI_PREFIX,\n PLUGIN_CATEGORY.THEME\n );\n if (plugin.length > 1) {\n logger.warn('multiple ui themes are not supported; only the first plugin is used');\n }\n\n return head(plugin);\n }\n}\n\nexport default async (config, auth, storage, logger) => {\n let pluginOptions = await loadTheme(config);\n if (!pluginOptions) {\n debug('no theme plugin found, using default theme');\n pluginOptions = defaultTheme(config.web);\n logger.info(\n { name: DEFAULT_PLUGIN_UI_THEME, pluginCategory: PLUGIN_CATEGORY.THEME },\n 'plugin @{name} successfully loaded (@{pluginCategory})'\n );\n }\n\n const router = express.Router();\n // load application\n router.use(\n webMiddleware(\n config,\n {\n tokenMiddleware: auth.webUIJWTmiddleware(),\n webEndpointsApi: webEndpointsApi(auth, storage, config),\n },\n pluginOptions\n )\n );\n return router;\n};\n"],"mappings":";;;;;;;;;;;;;;AAYA,IAAM,WAAA,GAAA,MAAA,SAAmB,0BAA0B;AAEnD,IAAa,0BAA0B;AAEvC,eAAsB,UAAU,QAAa;CAC3C,KAAA,GAAA,UAAA,OAAU,OAAO,KAAK,MAAM,OAAO;EACjC,MAAM,SAAS,OAAA,GAAA,mBAAA,iBACb,OAAO,OACP;GAAE;GAAQ,QAAA,kBAAA;EAAO,GAEjB,SAAU,QAAa;;;;;;;GAOrB,OAAO,OAAO,cAAc,OAAO,YAAY,OAAO;EACxD,GACA,OACA,OAAO,QAAQ,gBAAgB,gBAAA,kBAC/B,gBAAA,gBAAgB,KAClB;EACA,IAAI,OAAO,SAAS,GAClB,kBAAA,OAAO,KAAK,qEAAqE;EAGnF,QAAA,GAAA,UAAA,MAAY,MAAM;CACpB;AACF;AAEA,IAAA,qBAAe,OAAO,QAAQ,MAAM,SAAS,WAAW;CACtD,IAAI,gBAAgB,MAAM,UAAU,MAAM;CAC1C,IAAI,CAAC,eAAe;EAClB,QAAM,4CAA4C;EAClD,iBAAA,GAAA,oBAAA,SAA6B,OAAO,GAAG;EACvC,OAAO,KACL;GAAE,MAAM;GAAyB,gBAAgB,gBAAA,gBAAgB;EAAM,GACvE,wDACF;CACF;CAEA,MAAM,SAAS,QAAA,QAAQ,OAAO;CAE9B,OAAO,KAAA,GAAA,sBAAA,eAEH,QACA;EACE,iBAAiB,KAAK,mBAAmB;EACzC,iBAAiB,cAAA,QAAgB,MAAM,SAAS,MAAM;CACxD,GACA,aACF,CACF;CACA,OAAO;AACT"}
1
+ {"version":3,"file":"middleware.js","names":[],"sources":["../src/middleware.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport express from 'express';\nimport { head, isNil } from 'lodash-es';\n\nimport { PLUGIN_CATEGORY, PLUGIN_UI_PREFIX } from '@verdaccio/core';\nimport { asyncLoadPlugin } from '@verdaccio/loaders';\nimport { logger } from '@verdaccio/logger';\nimport { webMiddleware } from '@verdaccio/middleware';\nimport defaultTheme from '@verdaccio/ui-theme';\n\nimport webEndpointsApi from './api';\n\nconst debug = buildDebug('verdaccio:web:middleware');\n\nexport const DEFAULT_PLUGIN_UI_THEME = '@verdaccio/ui-theme';\n\nexport async function loadTheme(config: any) {\n if (isNil(config.theme) === false) {\n const plugin = await asyncLoadPlugin(\n config.theme,\n { config, logger },\n // TODO: add types { staticPath: string; manifest: unknown; manifestFiles: unknown }\n function (plugin: any) {\n /**\n *\n - `staticPath`: is the same data returned in Verdaccio 5.\n - `manifest`: A manifest object.\n - `manifestFiles`: A object with one property `js` and the array (order matters) of the manifest id to be loaded in the template dynamically.\n */\n return plugin.staticPath && plugin.manifest && plugin.manifestFiles;\n },\n false,\n config.server?.pluginPrefix ?? PLUGIN_UI_PREFIX,\n PLUGIN_CATEGORY.THEME\n );\n if (plugin.length > 1) {\n logger.warn('multiple ui themes are not supported; only the first plugin is used');\n }\n\n return head(plugin);\n }\n}\n\nexport default async (config, auth, storage, logger) => {\n let pluginOptions = await loadTheme(config);\n if (!pluginOptions) {\n debug('no theme plugin found, using default theme');\n pluginOptions = defaultTheme(config.web);\n logger.info(\n { name: DEFAULT_PLUGIN_UI_THEME, pluginCategory: PLUGIN_CATEGORY.THEME },\n 'plugin @{name} successfully loaded (@{pluginCategory})'\n );\n }\n\n const router = express.Router();\n // load application\n router.use(\n webMiddleware(\n config,\n {\n tokenMiddleware: auth.webUIJWTmiddleware(),\n webEndpointsApi: webEndpointsApi(auth, storage, config),\n },\n pluginOptions\n )\n );\n return router;\n};\n"],"mappings":";;;;;;;;;;;;;;AAYA,IAAM,WAAA,GAAA,MAAA,QAAA,CAAmB,0BAA0B;AAEnD,IAAa,0BAA0B;AAEvC,eAAsB,UAAU,QAAa;CAC3C,KAAA,GAAA,UAAA,MAAA,CAAU,OAAO,KAAK,MAAM,OAAO;EACjC,MAAM,SAAS,OAAA,GAAA,mBAAA,gBAAA,CACb,OAAO,OACP;GAAE;GAAQ,QAAA,kBAAA;EAAO,GAEjB,SAAU,QAAa;;;;;;;GAOrB,OAAO,OAAO,cAAc,OAAO,YAAY,OAAO;EACxD,GACA,OACA,OAAO,QAAQ,gBAAgB,gBAAA,kBAC/B,gBAAA,gBAAgB,KAClB;EACA,IAAI,OAAO,SAAS,GAClB,kBAAA,OAAO,KAAK,qEAAqE;EAGnF,QAAA,GAAA,UAAA,KAAA,CAAY,MAAM;CACpB;AACF;AAEA,IAAA,qBAAe,OAAO,QAAQ,MAAM,SAAS,WAAW;CACtD,IAAI,gBAAgB,MAAM,UAAU,MAAM;CAC1C,IAAI,CAAC,eAAe;EAClB,QAAM,4CAA4C;EAClD,iBAAA,GAAA,oBAAA,QAAA,CAA6B,OAAO,GAAG;EACvC,OAAO,KACL;GAAE,MAAM;GAAyB,gBAAgB,gBAAA,gBAAgB;EAAM,GACvE,wDACF;CACF;CAEA,MAAM,SAAS,QAAA,QAAQ,OAAO;CAE9B,OAAO,KAAA,GAAA,sBAAA,cAAA,CAEH,QACA;EACE,iBAAiB,KAAK,mBAAmB;EACzC,iBAAiB,cAAA,QAAgB,MAAM,SAAS,MAAM;CACxD,GACA,aACF,CACF;CACA,OAAO;AACT"}
@@ -46,6 +46,6 @@ var middleware_default = async (config, auth, storage, logger) => {
46
46
  return router;
47
47
  };
48
48
  //#endregion
49
- export { DEFAULT_PLUGIN_UI_THEME, middleware_default as default };
49
+ export { DEFAULT_PLUGIN_UI_THEME, middleware_default as default, loadTheme };
50
50
 
51
51
  //# sourceMappingURL=middleware.mjs.map
@@ -1,4 +1,4 @@
1
- import { ConfigYaml, Manifest } from '@verdaccio/types';
1
+ import type { ConfigYaml, Manifest } from '@verdaccio/types';
2
2
  export declare function hasLogin(config: ConfigYaml): boolean;
3
3
  export declare function sortByName(packages: any[], orderAscending?: boolean | void): string[];
4
4
  export declare function sortByTime(packages: any[], orderAscending?: boolean | void): string[];
@@ -1 +1 @@
1
- {"version":3,"file":"web-utils.js","names":[],"sources":["../src/web-utils.ts"],"sourcesContent":["import { forEach, isNil } from 'lodash-es';\n\nimport type { ConfigYaml, Manifest } from '@verdaccio/types';\n\nexport function hasLogin(config: ConfigYaml) {\n return isNil(config?.web?.login) || config?.web?.login === true;\n}\n\nexport function sortByName(packages: any[], orderAscending: boolean | void = true): string[] {\n return packages.slice().sort(function (a, b): number {\n const comparatorNames = a.name.toLowerCase() < b.name.toLowerCase();\n return orderAscending ? (comparatorNames ? -1 : 1) : comparatorNames ? 1 : -1;\n });\n}\n\nexport function sortByTime(packages: any[], orderAscending: boolean | void = true): string[] {\n return packages.slice().sort(function (a, b): number {\n const comparatorTime = a.time < b.time;\n return orderAscending ? (comparatorTime ? -1 : 1) : comparatorTime ? 1 : -1;\n });\n}\n\nexport function addScope(scope: string, packageName: string): string {\n return `@${scope}/${packageName}`;\n}\n\nexport function deleteProperties(propertiesToDelete: string[], objectItem: any): any {\n forEach(propertiesToDelete, (property): any => {\n delete objectItem[property];\n });\n\n return objectItem;\n}\n\nexport function isVersionValid(packageMeta: Manifest, packageVersion: string): boolean {\n const hasVersion = typeof packageVersion !== 'undefined';\n if (!hasVersion) {\n return false;\n }\n\n const hasMatchVersion = Object.keys(packageMeta.versions).includes(packageVersion);\n return hasMatchVersion;\n}\n"],"mappings":";;AAIA,SAAgB,SAAS,QAAoB;CAC3C,QAAA,GAAA,UAAA,OAAa,QAAQ,KAAK,KAAK,KAAK,QAAQ,KAAK,UAAU;AAC7D;AAEA,SAAgB,WAAW,UAAiB,iBAAiC,MAAgB;CAC3F,OAAO,SAAS,MAAM,EAAE,KAAK,SAAU,GAAG,GAAW;EACnD,MAAM,kBAAkB,EAAE,KAAK,YAAY,IAAI,EAAE,KAAK,YAAY;EAClE,OAAO,iBAAkB,kBAAkB,KAAK,IAAK,kBAAkB,IAAI;CAC7E,CAAC;AACH;AAEA,SAAgB,WAAW,UAAiB,iBAAiC,MAAgB;CAC3F,OAAO,SAAS,MAAM,EAAE,KAAK,SAAU,GAAG,GAAW;EACnD,MAAM,iBAAiB,EAAE,OAAO,EAAE;EAClC,OAAO,iBAAkB,iBAAiB,KAAK,IAAK,iBAAiB,IAAI;CAC3E,CAAC;AACH;AAEA,SAAgB,SAAS,OAAe,aAA6B;CACnE,OAAO,IAAI,MAAM,GAAG;AACtB;AAEA,SAAgB,iBAAiB,oBAA8B,YAAsB;CACnF,CAAA,GAAA,UAAA,SAAQ,qBAAqB,aAAkB;EAC7C,OAAO,WAAW;CACpB,CAAC;CAED,OAAO;AACT;AAEA,SAAgB,eAAe,aAAuB,gBAAiC;CAErF,IAAI,EADe,OAAO,mBAAmB,cAE3C,OAAO;CAIT,OADwB,OAAO,KAAK,YAAY,QAAQ,EAAE,SAAS,cAC5D;AACT"}
1
+ {"version":3,"file":"web-utils.js","names":[],"sources":["../src/web-utils.ts"],"sourcesContent":["import { forEach, isNil } from 'lodash-es';\n\nimport type { ConfigYaml, Manifest } from '@verdaccio/types';\n\nexport function hasLogin(config: ConfigYaml) {\n return isNil(config?.web?.login) || config?.web?.login === true;\n}\n\nexport function sortByName(packages: any[], orderAscending: boolean | void = true): string[] {\n return packages.slice().sort(function (a, b): number {\n const comparatorNames = a.name.toLowerCase() < b.name.toLowerCase();\n return orderAscending ? (comparatorNames ? -1 : 1) : comparatorNames ? 1 : -1;\n });\n}\n\nexport function sortByTime(packages: any[], orderAscending: boolean | void = true): string[] {\n return packages.slice().sort(function (a, b): number {\n const comparatorTime = a.time < b.time;\n return orderAscending ? (comparatorTime ? -1 : 1) : comparatorTime ? 1 : -1;\n });\n}\n\nexport function addScope(scope: string, packageName: string): string {\n return `@${scope}/${packageName}`;\n}\n\nexport function deleteProperties(propertiesToDelete: string[], objectItem: any): any {\n forEach(propertiesToDelete, (property): any => {\n delete objectItem[property];\n });\n\n return objectItem;\n}\n\nexport function isVersionValid(packageMeta: Manifest, packageVersion: string): boolean {\n const hasVersion = typeof packageVersion !== 'undefined';\n if (!hasVersion) {\n return false;\n }\n\n const hasMatchVersion = Object.keys(packageMeta.versions).includes(packageVersion);\n return hasMatchVersion;\n}\n"],"mappings":";;AAIA,SAAgB,SAAS,QAAoB;CAC3C,QAAA,GAAA,UAAA,MAAA,CAAa,QAAQ,KAAK,KAAK,KAAK,QAAQ,KAAK,UAAU;AAC7D;AAEA,SAAgB,WAAW,UAAiB,iBAAiC,MAAgB;CAC3F,OAAO,SAAS,MAAM,CAAC,CAAC,KAAK,SAAU,GAAG,GAAW;EACnD,MAAM,kBAAkB,EAAE,KAAK,YAAY,IAAI,EAAE,KAAK,YAAY;EAClE,OAAO,iBAAkB,kBAAkB,KAAK,IAAK,kBAAkB,IAAI;CAC7E,CAAC;AACH;AAEA,SAAgB,WAAW,UAAiB,iBAAiC,MAAgB;CAC3F,OAAO,SAAS,MAAM,CAAC,CAAC,KAAK,SAAU,GAAG,GAAW;EACnD,MAAM,iBAAiB,EAAE,OAAO,EAAE;EAClC,OAAO,iBAAkB,iBAAiB,KAAK,IAAK,iBAAiB,IAAI;CAC3E,CAAC;AACH;AAEA,SAAgB,SAAS,OAAe,aAA6B;CACnE,OAAO,IAAI,MAAM,GAAG;AACtB;AAEA,SAAgB,iBAAiB,oBAA8B,YAAsB;CACnF,CAAA,GAAA,UAAA,QAAA,CAAQ,qBAAqB,aAAkB;EAC7C,OAAO,WAAW;CACpB,CAAC;CAED,OAAO;AACT;AAEA,SAAgB,eAAe,aAAuB,gBAAiC;CAErF,IAAI,EADe,OAAO,mBAAmB,cAE3C,OAAO;CAIT,OADwB,OAAO,KAAK,YAAY,QAAQ,CAAC,CAAC,SAAS,cAC5D;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"web-utils.mjs","names":[],"sources":["../src/web-utils.ts"],"sourcesContent":["import { forEach, isNil } from 'lodash-es';\n\nimport type { ConfigYaml, Manifest } from '@verdaccio/types';\n\nexport function hasLogin(config: ConfigYaml) {\n return isNil(config?.web?.login) || config?.web?.login === true;\n}\n\nexport function sortByName(packages: any[], orderAscending: boolean | void = true): string[] {\n return packages.slice().sort(function (a, b): number {\n const comparatorNames = a.name.toLowerCase() < b.name.toLowerCase();\n return orderAscending ? (comparatorNames ? -1 : 1) : comparatorNames ? 1 : -1;\n });\n}\n\nexport function sortByTime(packages: any[], orderAscending: boolean | void = true): string[] {\n return packages.slice().sort(function (a, b): number {\n const comparatorTime = a.time < b.time;\n return orderAscending ? (comparatorTime ? -1 : 1) : comparatorTime ? 1 : -1;\n });\n}\n\nexport function addScope(scope: string, packageName: string): string {\n return `@${scope}/${packageName}`;\n}\n\nexport function deleteProperties(propertiesToDelete: string[], objectItem: any): any {\n forEach(propertiesToDelete, (property): any => {\n delete objectItem[property];\n });\n\n return objectItem;\n}\n\nexport function isVersionValid(packageMeta: Manifest, packageVersion: string): boolean {\n const hasVersion = typeof packageVersion !== 'undefined';\n if (!hasVersion) {\n return false;\n }\n\n const hasMatchVersion = Object.keys(packageMeta.versions).includes(packageVersion);\n return hasMatchVersion;\n}\n"],"mappings":";;AAIA,SAAgB,SAAS,QAAoB;CAC3C,OAAO,MAAM,QAAQ,KAAK,KAAK,KAAK,QAAQ,KAAK,UAAU;AAC7D;AAEA,SAAgB,WAAW,UAAiB,iBAAiC,MAAgB;CAC3F,OAAO,SAAS,MAAM,EAAE,KAAK,SAAU,GAAG,GAAW;EACnD,MAAM,kBAAkB,EAAE,KAAK,YAAY,IAAI,EAAE,KAAK,YAAY;EAClE,OAAO,iBAAkB,kBAAkB,KAAK,IAAK,kBAAkB,IAAI;CAC7E,CAAC;AACH;AAEA,SAAgB,WAAW,UAAiB,iBAAiC,MAAgB;CAC3F,OAAO,SAAS,MAAM,EAAE,KAAK,SAAU,GAAG,GAAW;EACnD,MAAM,iBAAiB,EAAE,OAAO,EAAE;EAClC,OAAO,iBAAkB,iBAAiB,KAAK,IAAK,iBAAiB,IAAI;CAC3E,CAAC;AACH;AAEA,SAAgB,SAAS,OAAe,aAA6B;CACnE,OAAO,IAAI,MAAM,GAAG;AACtB;AAEA,SAAgB,iBAAiB,oBAA8B,YAAsB;CACnF,QAAQ,qBAAqB,aAAkB;EAC7C,OAAO,WAAW;CACpB,CAAC;CAED,OAAO;AACT;AAEA,SAAgB,eAAe,aAAuB,gBAAiC;CAErF,IAAI,EADe,OAAO,mBAAmB,cAE3C,OAAO;CAIT,OADwB,OAAO,KAAK,YAAY,QAAQ,EAAE,SAAS,cAC5D;AACT"}
1
+ {"version":3,"file":"web-utils.mjs","names":[],"sources":["../src/web-utils.ts"],"sourcesContent":["import { forEach, isNil } from 'lodash-es';\n\nimport type { ConfigYaml, Manifest } from '@verdaccio/types';\n\nexport function hasLogin(config: ConfigYaml) {\n return isNil(config?.web?.login) || config?.web?.login === true;\n}\n\nexport function sortByName(packages: any[], orderAscending: boolean | void = true): string[] {\n return packages.slice().sort(function (a, b): number {\n const comparatorNames = a.name.toLowerCase() < b.name.toLowerCase();\n return orderAscending ? (comparatorNames ? -1 : 1) : comparatorNames ? 1 : -1;\n });\n}\n\nexport function sortByTime(packages: any[], orderAscending: boolean | void = true): string[] {\n return packages.slice().sort(function (a, b): number {\n const comparatorTime = a.time < b.time;\n return orderAscending ? (comparatorTime ? -1 : 1) : comparatorTime ? 1 : -1;\n });\n}\n\nexport function addScope(scope: string, packageName: string): string {\n return `@${scope}/${packageName}`;\n}\n\nexport function deleteProperties(propertiesToDelete: string[], objectItem: any): any {\n forEach(propertiesToDelete, (property): any => {\n delete objectItem[property];\n });\n\n return objectItem;\n}\n\nexport function isVersionValid(packageMeta: Manifest, packageVersion: string): boolean {\n const hasVersion = typeof packageVersion !== 'undefined';\n if (!hasVersion) {\n return false;\n }\n\n const hasMatchVersion = Object.keys(packageMeta.versions).includes(packageVersion);\n return hasMatchVersion;\n}\n"],"mappings":";;AAIA,SAAgB,SAAS,QAAoB;CAC3C,OAAO,MAAM,QAAQ,KAAK,KAAK,KAAK,QAAQ,KAAK,UAAU;AAC7D;AAEA,SAAgB,WAAW,UAAiB,iBAAiC,MAAgB;CAC3F,OAAO,SAAS,MAAM,CAAC,CAAC,KAAK,SAAU,GAAG,GAAW;EACnD,MAAM,kBAAkB,EAAE,KAAK,YAAY,IAAI,EAAE,KAAK,YAAY;EAClE,OAAO,iBAAkB,kBAAkB,KAAK,IAAK,kBAAkB,IAAI;CAC7E,CAAC;AACH;AAEA,SAAgB,WAAW,UAAiB,iBAAiC,MAAgB;CAC3F,OAAO,SAAS,MAAM,CAAC,CAAC,KAAK,SAAU,GAAG,GAAW;EACnD,MAAM,iBAAiB,EAAE,OAAO,EAAE;EAClC,OAAO,iBAAkB,iBAAiB,KAAK,IAAK,iBAAiB,IAAI;CAC3E,CAAC;AACH;AAEA,SAAgB,SAAS,OAAe,aAA6B;CACnE,OAAO,IAAI,MAAM,GAAG;AACtB;AAEA,SAAgB,iBAAiB,oBAA8B,YAAsB;CACnF,QAAQ,qBAAqB,aAAkB;EAC7C,OAAO,WAAW;CACpB,CAAC;CAED,OAAO;AACT;AAEA,SAAgB,eAAe,aAAuB,gBAAiC;CAErF,IAAI,EADe,OAAO,mBAAmB,cAE3C,OAAO;CAIT,OADwB,OAAO,KAAK,YAAY,QAAQ,CAAC,CAAC,SAAS,cAC5D;AACT"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@verdaccio/web",
3
- "version": "9.0.0-next-9.21",
3
+ "version": "9.0.0-next-9.22",
4
4
  "description": "Verdaccio Web Middleware",
5
5
  "main": "./build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -28,30 +28,30 @@
28
28
  },
29
29
  "license": "MIT",
30
30
  "dependencies": {
31
- "@verdaccio/auth": "9.0.0-next-9.21",
32
- "@verdaccio/config": "9.0.0-next-9.21",
33
- "@verdaccio/core": "9.0.0-next-9.21",
34
- "@verdaccio/loaders": "9.0.0-next-9.21",
35
- "@verdaccio/logger": "9.0.0-next-9.21",
36
- "@verdaccio/middleware": "9.0.0-next-9.21",
37
- "@verdaccio/store": "9.0.0-next-9.21",
38
- "@verdaccio/tarball": "14.0.0-next-9.21",
39
- "@verdaccio/ui-theme": "9.0.0-next-9.21",
31
+ "@verdaccio/auth": "9.0.0-next-9.22",
32
+ "@verdaccio/config": "9.0.0-next-9.22",
33
+ "@verdaccio/core": "9.0.0-next-9.22",
34
+ "@verdaccio/loaders": "9.0.0-next-9.22",
35
+ "@verdaccio/logger": "9.0.0-next-9.22",
36
+ "@verdaccio/middleware": "9.0.0-next-9.22",
37
+ "@verdaccio/store": "9.0.0-next-9.22",
38
+ "@verdaccio/tarball": "14.0.0-next-9.22",
39
+ "@verdaccio/ui-theme": "9.0.0-next-9.22",
40
40
  "debug": "4.4.3",
41
41
  "express": "5.2.1",
42
42
  "lodash-es": "4.18.1"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@types/lodash-es": "4.17.12",
46
- "@verdaccio/api": "9.0.0-next-9.21",
47
- "@verdaccio/test-helper": "5.0.0-next-9.22",
46
+ "@verdaccio/api": "9.0.0-next-9.22",
47
+ "@verdaccio/test-helper": "5.0.0-next-9.23",
48
48
  "@verdaccio/types": "14.0.0-next-9.11",
49
- "jsdom": "28.1.0",
49
+ "jsdom": "29.1.1",
50
50
  "nock": "13.5.6",
51
51
  "supertest": "7.1.4",
52
52
  "rimraf": "6.1.3",
53
- "vite": "8.0.16",
54
- "vitest": "4.1.7"
53
+ "vite": "8.1.4",
54
+ "vitest": "4.1.10"
55
55
  },
56
56
  "funding": {
57
57
  "type": "opencollective",