@verdaccio/search 7.0.0-next.2 → 8.0.0-next-8.1

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.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2021 Verdaccio contributors
3
+ Copyright (c) 2024 Verdaccio contributors
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/build/index.d.ts CHANGED
@@ -1 +1,2 @@
1
- export { default as SearchMemoryIndexer } from './indexer';
1
+ export { Search as default } from './search';
2
+ export * from './search-utils';
package/build/index.js ADDED
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ var _exportNames = {};
7
+ Object.defineProperty(exports, "default", {
8
+ enumerable: true,
9
+ get: function () {
10
+ return _search.Search;
11
+ }
12
+ });
13
+ var _search = require("./search");
14
+ var _searchUtils = require("./search-utils");
15
+ Object.keys(_searchUtils).forEach(function (key) {
16
+ if (key === "default" || key === "__esModule") return;
17
+ if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
18
+ if (key in exports && exports[key] === _searchUtils[key]) return;
19
+ Object.defineProperty(exports, key, {
20
+ enumerable: true,
21
+ get: function () {
22
+ return _searchUtils[key];
23
+ }
24
+ });
25
+ });
26
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["_search","require","_searchUtils","Object","keys","forEach","key","prototype","hasOwnProperty","call","_exportNames","exports","defineProperty","enumerable","get"],"sources":["../src/index.ts"],"sourcesContent":["export { Search as default } from './search';\nexport * from './search-utils';\n"],"mappings":";;;;;;;;;;;;AAAA,IAAAA,OAAA,GAAAC,OAAA;AACA,IAAAC,YAAA,GAAAD,OAAA;AAAAE,MAAA,CAAAC,IAAA,CAAAF,YAAA,EAAAG,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAH,MAAA,CAAAI,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAC,YAAA,EAAAJ,GAAA;EAAA,IAAAA,GAAA,IAAAK,OAAA,IAAAA,OAAA,CAAAL,GAAA,MAAAJ,YAAA,CAAAI,GAAA;EAAAH,MAAA,CAAAS,cAAA,CAAAD,OAAA,EAAAL,GAAA;IAAAO,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAZ,YAAA,CAAAI,GAAA;IAAA;EAAA;AAAA","ignoreList":[]}
@@ -0,0 +1,2 @@
1
+ import { searchUtils } from '@verdaccio/core';
2
+ export declare function removeDuplicates(results: searchUtils.SearchPackageItem[]): searchUtils.SearchPackageItem[];
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.removeDuplicates = removeDuplicates;
7
+ var _lodash = require("lodash");
8
+ function removeDuplicates(results) {
9
+ const pkgNames = [];
10
+ const orderByResults = (0, _lodash.orderBy)(results, ['verdaccioPrivate', 'asc']);
11
+ return orderByResults.filter(pkg => {
12
+ var _pkg$package, _pkg$package2;
13
+ if (pkgNames.includes(pkg === null || pkg === void 0 || (_pkg$package = pkg.package) === null || _pkg$package === void 0 ? void 0 : _pkg$package.name)) {
14
+ return false;
15
+ }
16
+ pkgNames.push(pkg === null || pkg === void 0 || (_pkg$package2 = pkg.package) === null || _pkg$package2 === void 0 ? void 0 : _pkg$package2.name);
17
+ return true;
18
+ });
19
+ }
20
+ //# sourceMappingURL=search-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-utils.js","names":["_lodash","require","removeDuplicates","results","pkgNames","orderByResults","orderBy","filter","pkg","_pkg$package","_pkg$package2","includes","package","name","push"],"sources":["../src/search-utils.ts"],"sourcesContent":["import { orderBy } from 'lodash';\n\nimport { searchUtils } from '@verdaccio/core';\n\nexport function removeDuplicates(results: searchUtils.SearchPackageItem[]) {\n const pkgNames: any[] = [];\n const orderByResults = orderBy(results, ['verdaccioPrivate', 'asc']);\n return orderByResults.filter((pkg) => {\n if (pkgNames.includes(pkg?.package?.name)) {\n return false;\n }\n pkgNames.push(pkg?.package?.name);\n return true;\n });\n}\n"],"mappings":";;;;;;AAAA,IAAAA,OAAA,GAAAC,OAAA;AAIO,SAASC,gBAAgBA,CAACC,OAAwC,EAAE;EACzE,MAAMC,QAAe,GAAG,EAAE;EAC1B,MAAMC,cAAc,GAAG,IAAAC,eAAO,EAACH,OAAO,EAAE,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAAC;EACpE,OAAOE,cAAc,CAACE,MAAM,CAAEC,GAAG,IAAK;IAAA,IAAAC,YAAA,EAAAC,aAAA;IACpC,IAAIN,QAAQ,CAACO,QAAQ,CAACH,GAAG,aAAHA,GAAG,gBAAAC,YAAA,GAAHD,GAAG,CAAEI,OAAO,cAAAH,YAAA,uBAAZA,YAAA,CAAcI,IAAI,CAAC,EAAE;MACzC,OAAO,KAAK;IACd;IACAT,QAAQ,CAACU,IAAI,CAACN,GAAG,aAAHA,GAAG,gBAAAE,aAAA,GAAHF,GAAG,CAAEI,OAAO,cAAAF,aAAA,uBAAZA,aAAA,CAAcG,IAAI,CAAC;IACjC,OAAO,IAAI;EACb,CAAC,CAAC;AACJ","ignoreList":[]}
@@ -0,0 +1,21 @@
1
+ import { searchUtils } from '@verdaccio/core';
2
+ import { ProxyInstanceList, ProxySearchParams } from '@verdaccio/proxy';
3
+ import { Config, Logger } from '@verdaccio/types';
4
+ declare class Search {
5
+ readonly uplinks: ProxyInstanceList;
6
+ readonly logger: Logger;
7
+ constructor(config: Config, logger: Logger);
8
+ private getProxyList;
9
+ /**
10
+ * Handle search on packages and proxies.
11
+ * Iterate all proxies configured and search in all endpoints in v2 and pipe all responses
12
+ * to a stream, once the proxies request has finished search in local storage for all packages
13
+ * (privated and cached).
14
+ */
15
+ search(options: ProxySearchParams): Promise<searchUtils.SearchPackageItem[]>;
16
+ /**
17
+ * Consume the upstream and pipe it to a transformable stream.
18
+ */
19
+ private consumeSearchStream;
20
+ }
21
+ export { Search };
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.Search = void 0;
7
+ var _debug = _interopRequireDefault(require("debug"));
8
+ var _lodash = _interopRequireDefault(require("lodash"));
9
+ var _stream = require("stream");
10
+ var _proxy = require("@verdaccio/proxy");
11
+ var _searchUtils = require("./search-utils");
12
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
13
+ const debug = (0, _debug.default)('verdaccio:search');
14
+ class Search {
15
+ constructor(config, logger) {
16
+ this.logger = logger.child({
17
+ module: 'proxy'
18
+ });
19
+ this.uplinks = (0, _proxy.setupUpLinks)(config, this.logger);
20
+ }
21
+ getProxyList() {
22
+ const uplinksList = Object.keys(this.uplinks);
23
+ return uplinksList;
24
+ }
25
+
26
+ /**
27
+ * Handle search on packages and proxies.
28
+ * Iterate all proxies configured and search in all endpoints in v2 and pipe all responses
29
+ * to a stream, once the proxies request has finished search in local storage for all packages
30
+ * (privated and cached).
31
+ */
32
+ async search(options) {
33
+ const results = [];
34
+ const upLinkList = this.getProxyList();
35
+ // const transformResults = new TransFormResults({ objectMode: true });
36
+ const streamPassThrough = new _stream.PassThrough({
37
+ objectMode: true
38
+ });
39
+ debug('uplinks found %s', upLinkList.length);
40
+ const searchUplinksStreams = upLinkList.map(uplinkId => {
41
+ const uplink = this.uplinks[uplinkId];
42
+ if (!uplink) {
43
+ // this line should never happens
44
+ this.logger.error({
45
+ uplinkId
46
+ }, 'uplink @upLinkId not found');
47
+ }
48
+ return this.consumeSearchStream(uplinkId, uplink, options, streamPassThrough);
49
+ });
50
+ try {
51
+ debug('searching on %s uplinks...', searchUplinksStreams === null || searchUplinksStreams === void 0 ? void 0 : searchUplinksStreams.length);
52
+ // only process those streams end successfully, if all request fails
53
+ // just include local storage results (if local fails then return 500)
54
+ await Promise.allSettled([...searchUplinksStreams]);
55
+ streamPassThrough.end();
56
+ for await (const chunk of streamPassThrough) {
57
+ if (_lodash.default.isArray(chunk)) {
58
+ chunk.filter(pkgItem => {
59
+ var _pkgItem$package;
60
+ debug(`streaming remote pkg name ${pkgItem === null || pkgItem === void 0 || (_pkgItem$package = pkgItem.package) === null || _pkgItem$package === void 0 ? void 0 : _pkgItem$package.name}`);
61
+ return true;
62
+ }).forEach(pkgItem => {
63
+ // @ts-ignore
64
+ return results.push({
65
+ ...pkgItem,
66
+ verdaccioPkgCached: false,
67
+ verdaccioPrivate: false
68
+ });
69
+ });
70
+ }
71
+ }
72
+ debug('searching all uplinks done');
73
+ } catch (err) {
74
+ this.logger.error({
75
+ err: err === null || err === void 0 ? void 0 : err.message
76
+ }, ' error on uplinks search @{err}');
77
+ throw err;
78
+ }
79
+ return (0, _searchUtils.removeDuplicates)(results);
80
+ }
81
+
82
+ /**
83
+ * Consume the upstream and pipe it to a transformable stream.
84
+ */
85
+ consumeSearchStream(uplinkId, uplink, options, searchPassThrough) {
86
+ return uplink.search({
87
+ ...options
88
+ }).then(bodyStream => {
89
+ bodyStream.pipe(searchPassThrough, {
90
+ end: false
91
+ });
92
+ bodyStream.on('error', err => {
93
+ this.logger.error({
94
+ uplinkId,
95
+ err: err
96
+ }, 'search error for uplink @{uplinkId}: @{err?.message}');
97
+ searchPassThrough.end();
98
+ });
99
+ return new Promise(resolve => bodyStream.on('end', resolve));
100
+ });
101
+ }
102
+ }
103
+ exports.Search = Search;
104
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.js","names":["_debug","_interopRequireDefault","require","_lodash","_stream","_proxy","_searchUtils","e","__esModule","default","debug","buildDebug","Search","constructor","config","logger","child","module","uplinks","setupUpLinks","getProxyList","uplinksList","Object","keys","search","options","results","upLinkList","streamPassThrough","PassThrough","objectMode","length","searchUplinksStreams","map","uplinkId","uplink","error","consumeSearchStream","Promise","allSettled","end","chunk","_","isArray","filter","pkgItem","_pkgItem$package","package","name","forEach","push","verdaccioPkgCached","verdaccioPrivate","err","message","removeDuplicates","searchPassThrough","then","bodyStream","pipe","on","resolve","exports"],"sources":["../src/search.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport _ from 'lodash';\nimport { PassThrough } from 'stream';\n\nimport { searchUtils } from '@verdaccio/core';\nimport { IProxy, ProxyInstanceList, ProxySearchParams, setupUpLinks } from '@verdaccio/proxy';\nimport { Config, Logger } from '@verdaccio/types';\n\nimport { removeDuplicates } from './search-utils';\n\nconst debug = buildDebug('verdaccio:search');\n\nclass Search {\n public readonly uplinks: ProxyInstanceList;\n public readonly logger: Logger;\n constructor(config: Config, logger: Logger) {\n this.logger = logger.child({ module: 'proxy' });\n this.uplinks = setupUpLinks(config, this.logger);\n }\n\n private getProxyList() {\n const uplinksList = Object.keys(this.uplinks);\n\n return uplinksList;\n }\n\n /**\n * Handle search on packages and proxies.\n * Iterate all proxies configured and search in all endpoints in v2 and pipe all responses\n * to a stream, once the proxies request has finished search in local storage for all packages\n * (privated and cached).\n */\n public async search(options: ProxySearchParams): Promise<searchUtils.SearchPackageItem[]> {\n const results: searchUtils.SearchPackageItem[] = [];\n const upLinkList = this.getProxyList();\n // const transformResults = new TransFormResults({ objectMode: true });\n const streamPassThrough = new PassThrough({ objectMode: true });\n debug('uplinks found %s', upLinkList.length);\n const searchUplinksStreams = upLinkList.map((uplinkId: string) => {\n const uplink = this.uplinks[uplinkId];\n if (!uplink) {\n // this line should never happens\n this.logger.error({ uplinkId }, 'uplink @upLinkId not found');\n }\n return this.consumeSearchStream(uplinkId, uplink, options, streamPassThrough);\n });\n\n try {\n debug('searching on %s uplinks...', searchUplinksStreams?.length);\n // only process those streams end successfully, if all request fails\n // just include local storage results (if local fails then return 500)\n await Promise.allSettled([...searchUplinksStreams]);\n streamPassThrough.end();\n\n for await (const chunk of streamPassThrough) {\n if (_.isArray(chunk)) {\n (chunk as searchUtils.SearchItem[])\n .filter((pkgItem) => {\n debug(`streaming remote pkg name ${pkgItem?.package?.name}`);\n return true;\n })\n .forEach((pkgItem) => {\n // @ts-ignore\n return results.push({\n ...pkgItem,\n verdaccioPkgCached: false,\n verdaccioPrivate: false,\n });\n });\n }\n }\n debug('searching all uplinks done');\n } catch (err: any) {\n this.logger.error({ err: err?.message }, ' error on uplinks search @{err}');\n throw err;\n }\n\n return removeDuplicates(results);\n }\n\n /**\n * Consume the upstream and pipe it to a transformable stream.\n */\n private consumeSearchStream(\n uplinkId: string,\n uplink: IProxy,\n options: ProxySearchParams,\n searchPassThrough: PassThrough\n ): Promise<any> {\n return uplink.search({ ...options }).then((bodyStream) => {\n bodyStream.pipe(searchPassThrough, { end: false });\n bodyStream.on('error', (err: any): void => {\n this.logger.error(\n { uplinkId, err: err },\n 'search error for uplink @{uplinkId}: @{err?.message}'\n );\n searchPassThrough.end();\n });\n return new Promise((resolve) => bodyStream.on('end', resolve));\n });\n }\n}\n\nexport { Search };\n"],"mappings":"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","ignoreList":[]}
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@verdaccio/search",
3
- "version": "7.0.0-next.2",
4
- "description": "verdaccio search utitlity tools",
5
- "main": "./build/dist.js",
3
+ "version": "8.0.0-next-8.1",
4
+ "description": "verdaccio search proxy",
5
+ "main": "./build/index.js",
6
6
  "types": "build/index.d.ts",
7
7
  "author": {
8
8
  "name": "Juan Picado",
@@ -26,13 +26,21 @@
26
26
  "verdaccio"
27
27
  ],
28
28
  "engines": {
29
- "node": ">=12"
29
+ "node": ">=18"
30
+ },
31
+ "dependencies": {
32
+ "debug": "4.3.7",
33
+ "lodash": "4.17.21",
34
+ "@verdaccio/config": "8.0.0-next-8.1",
35
+ "@verdaccio/core": "8.0.0-next-8.1",
36
+ "@verdaccio/logger": "8.0.0-next-8.1",
37
+ "@verdaccio/proxy": "8.0.0-next-8.1"
30
38
  },
31
39
  "devDependencies": {
32
- "@verdaccio/types": "12.0.0-next.1",
33
- "@orama/orama": "1.2.4",
34
- "debug": "4.3.4",
35
- "esbuild": "0.14.10"
40
+ "@verdaccio/types": "13.0.0-next-8.0",
41
+ "mockdate": "3.0.5",
42
+ "nock": "13.5.1",
43
+ "node-mocks-http": "1.14.1"
36
44
  },
37
45
  "funding": {
38
46
  "type": "opencollective",
@@ -44,6 +52,7 @@
44
52
  "type-check": "tsc --noEmit -p tsconfig.build.json",
45
53
  "build:types": "tsc --emitDeclarationOnly -p tsconfig.build.json",
46
54
  "build:js": "babel src/ --out-dir build/ --copy-files --extensions \".ts,.tsx\" --source-maps",
47
- "build": "esbuild src/index.ts --bundle --outfile=build/dist.js --platform=node --target=node12 && pnpm run build:types"
55
+ "watch": "pnpm build:js -- --watch",
56
+ "build": "pnpm run build:js && pnpm run build:types"
48
57
  }
49
58
  }
package/src/index.ts CHANGED
@@ -1 +1,2 @@
1
- export { default as SearchMemoryIndexer } from './indexer';
1
+ export { Search as default } from './search';
2
+ export * from './search-utils';
@@ -0,0 +1,15 @@
1
+ import { orderBy } from 'lodash';
2
+
3
+ import { searchUtils } from '@verdaccio/core';
4
+
5
+ export function removeDuplicates(results: searchUtils.SearchPackageItem[]) {
6
+ const pkgNames: any[] = [];
7
+ const orderByResults = orderBy(results, ['verdaccioPrivate', 'asc']);
8
+ return orderByResults.filter((pkg) => {
9
+ if (pkgNames.includes(pkg?.package?.name)) {
10
+ return false;
11
+ }
12
+ pkgNames.push(pkg?.package?.name);
13
+ return true;
14
+ });
15
+ }
package/src/search.ts ADDED
@@ -0,0 +1,104 @@
1
+ import buildDebug from 'debug';
2
+ import _ from 'lodash';
3
+ import { PassThrough } from 'stream';
4
+
5
+ import { searchUtils } from '@verdaccio/core';
6
+ import { IProxy, ProxyInstanceList, ProxySearchParams, setupUpLinks } from '@verdaccio/proxy';
7
+ import { Config, Logger } from '@verdaccio/types';
8
+
9
+ import { removeDuplicates } from './search-utils';
10
+
11
+ const debug = buildDebug('verdaccio:search');
12
+
13
+ class Search {
14
+ public readonly uplinks: ProxyInstanceList;
15
+ public readonly logger: Logger;
16
+ constructor(config: Config, logger: Logger) {
17
+ this.logger = logger.child({ module: 'proxy' });
18
+ this.uplinks = setupUpLinks(config, this.logger);
19
+ }
20
+
21
+ private getProxyList() {
22
+ const uplinksList = Object.keys(this.uplinks);
23
+
24
+ return uplinksList;
25
+ }
26
+
27
+ /**
28
+ * Handle search on packages and proxies.
29
+ * Iterate all proxies configured and search in all endpoints in v2 and pipe all responses
30
+ * to a stream, once the proxies request has finished search in local storage for all packages
31
+ * (privated and cached).
32
+ */
33
+ public async search(options: ProxySearchParams): Promise<searchUtils.SearchPackageItem[]> {
34
+ const results: searchUtils.SearchPackageItem[] = [];
35
+ const upLinkList = this.getProxyList();
36
+ // const transformResults = new TransFormResults({ objectMode: true });
37
+ const streamPassThrough = new PassThrough({ objectMode: true });
38
+ debug('uplinks found %s', upLinkList.length);
39
+ const searchUplinksStreams = upLinkList.map((uplinkId: string) => {
40
+ const uplink = this.uplinks[uplinkId];
41
+ if (!uplink) {
42
+ // this line should never happens
43
+ this.logger.error({ uplinkId }, 'uplink @upLinkId not found');
44
+ }
45
+ return this.consumeSearchStream(uplinkId, uplink, options, streamPassThrough);
46
+ });
47
+
48
+ try {
49
+ debug('searching on %s uplinks...', searchUplinksStreams?.length);
50
+ // only process those streams end successfully, if all request fails
51
+ // just include local storage results (if local fails then return 500)
52
+ await Promise.allSettled([...searchUplinksStreams]);
53
+ streamPassThrough.end();
54
+
55
+ for await (const chunk of streamPassThrough) {
56
+ if (_.isArray(chunk)) {
57
+ (chunk as searchUtils.SearchItem[])
58
+ .filter((pkgItem) => {
59
+ debug(`streaming remote pkg name ${pkgItem?.package?.name}`);
60
+ return true;
61
+ })
62
+ .forEach((pkgItem) => {
63
+ // @ts-ignore
64
+ return results.push({
65
+ ...pkgItem,
66
+ verdaccioPkgCached: false,
67
+ verdaccioPrivate: false,
68
+ });
69
+ });
70
+ }
71
+ }
72
+ debug('searching all uplinks done');
73
+ } catch (err: any) {
74
+ this.logger.error({ err: err?.message }, ' error on uplinks search @{err}');
75
+ throw err;
76
+ }
77
+
78
+ return removeDuplicates(results);
79
+ }
80
+
81
+ /**
82
+ * Consume the upstream and pipe it to a transformable stream.
83
+ */
84
+ private consumeSearchStream(
85
+ uplinkId: string,
86
+ uplink: IProxy,
87
+ options: ProxySearchParams,
88
+ searchPassThrough: PassThrough
89
+ ): Promise<any> {
90
+ return uplink.search({ ...options }).then((bodyStream) => {
91
+ bodyStream.pipe(searchPassThrough, { end: false });
92
+ bodyStream.on('error', (err: any): void => {
93
+ this.logger.error(
94
+ { uplinkId, err: err },
95
+ 'search error for uplink @{uplinkId}: @{err?.message}'
96
+ );
97
+ searchPassThrough.end();
98
+ });
99
+ return new Promise((resolve) => bodyStream.on('end', resolve));
100
+ });
101
+ }
102
+ }
103
+
104
+ export { Search };