@verdaccio/local-storage 14.0.0-next-9.6 → 14.0.0-next-9.8

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,4 +1,4 @@
1
- import { default as low } from 'lowdb';
1
+ import { Low } from 'lowdb';
2
2
  import { searchUtils, pluginUtils } from '@verdaccio/core';
3
3
  import { Config, Logger, Token, TokenFilter } from '@verdaccio/types';
4
4
  import { LocalStorage } from './pkg-utils';
@@ -11,7 +11,7 @@ declare class LocalDatabase extends pluginUtils.Plugin<{}> implements Storage {
11
11
  readonly storages: Map<string, string>;
12
12
  data: LocalStorage | undefined;
13
13
  locked: boolean;
14
- tokenDb: low.LowdbAsync<any> | null;
14
+ tokenDb: Low<Record<string, Token[]>> | null;
15
15
  constructor(config: Config, logger: Logger);
16
16
  init(): Promise<void>;
17
17
  getSecret(): Promise<string>;
@@ -6,14 +6,9 @@ const require_pkg_utils = require("./pkg-utils.js");
6
6
  const require_utils = require("./utils.js");
7
7
  let debug = require("debug");
8
8
  debug = require_runtime.__toESM(debug);
9
- let lodash = require("lodash");
10
- lodash = require_runtime.__toESM(lodash);
9
+ let lodash_es = require("lodash-es");
11
10
  let lowdb = require("lowdb");
12
- lowdb = require_runtime.__toESM(lowdb);
13
- let lowdb_adapters_FileAsync = require("lowdb/adapters/FileAsync");
14
- lowdb_adapters_FileAsync = require_runtime.__toESM(lowdb_adapters_FileAsync);
15
- let lowdb_adapters_Memory = require("lowdb/adapters/Memory");
16
- lowdb_adapters_Memory = require_runtime.__toESM(lowdb_adapters_Memory);
11
+ let lowdb_node = require("lowdb/node");
17
12
  let node_path = require("node:path");
18
13
  node_path = require_runtime.__toESM(node_path);
19
14
  let _verdaccio_core = require("@verdaccio/core");
@@ -92,7 +87,7 @@ var LocalDatabase = class extends _verdaccio_core.pluginUtils.Plugin {
92
87
  **/
93
88
  async filterByQuery(results, query) {
94
89
  return results.filter((item) => {
95
- const safeText = lodash.default.escapeRegExp(query.text);
90
+ const safeText = (0, lodash_es.escapeRegExp)(query.text);
96
91
  return item?.name?.match(safeText) !== null;
97
92
  });
98
93
  }
@@ -154,7 +149,7 @@ var LocalDatabase = class extends _verdaccio_core.pluginUtils.Plugin {
154
149
  const packageAccess = _verdaccio_core.authUtils.getMatchedPackagesSpec(packageName, this.config.packages);
155
150
  const packagePath = this._getLocalStoragePath(packageAccess ? packageAccess.storage : void 0);
156
151
  debug$1("storage path selected %o", packagePath);
157
- if (lodash.default.isString(packagePath) === false) {
152
+ if (typeof packagePath !== "string") {
158
153
  debug$1("the package %o has no storage defined ", packageName);
159
154
  throw _verdaccio_core.errorUtils.getInternalError("storage not found or implemented");
160
155
  }
@@ -207,7 +202,7 @@ var LocalDatabase = class extends _verdaccio_core.pluginUtils.Plugin {
207
202
  }
208
203
  _getLocalStoragePath(storage) {
209
204
  const globalConfigStorage = this.getStoragePath();
210
- if (lodash.default.isNil(globalConfigStorage)) {
205
+ if ((0, lodash_es.isNil)(globalConfigStorage)) {
211
206
  this.logger.error("property storage in config.yaml is required for using this plugin");
212
207
  throw new Error("property storage in config.yaml is required for using this plugin");
213
208
  } else {
@@ -236,44 +231,46 @@ var LocalDatabase = class extends _verdaccio_core.pluginUtils.Plugin {
236
231
  let adapter;
237
232
  if (process.env.NODE_ENV === "test") {
238
233
  debug$1("token memory adapter");
239
- adapter = new lowdb_adapters_Memory.default("");
234
+ adapter = new lowdb.Memory();
240
235
  } else {
241
236
  debug$1("token async adapter");
242
- adapter = new lowdb_adapters_FileAsync.default(require_utils._dbGenPath(TOKEN_DB_NAME, this.config));
237
+ adapter = new lowdb_node.JSONFile(require_utils._dbGenPath(TOKEN_DB_NAME, this.config));
243
238
  }
244
- debug$1("token bd generated");
245
- this.tokenDb = await (0, lowdb.default)(adapter);
239
+ debug$1("token db generated");
240
+ this.tokenDb = new lowdb.Low(adapter, {});
241
+ await this.tokenDb.read();
246
242
  }
247
243
  return this.tokenDb;
248
244
  }
249
245
  async saveToken(token) {
250
246
  debug$1("token key %o", token.key);
251
247
  const db = await this.getTokenDb();
252
- const userData = await db.get(token.user).value();
248
+ const userData = db.data[token.user];
253
249
  debug$1("user data %o", userData);
254
- if (lodash.default.isNil(userData)) {
255
- await db.set(token.user, [token]).write();
250
+ if ((0, lodash_es.isNil)(userData)) {
251
+ db.data[token.user] = [token];
256
252
  debug$1("token user %o new database", token.user);
257
- } else await db.get(token.user).push(token).write();
258
- debug$1("data %o", await db.getState());
253
+ } else db.data[token.user].push(token);
254
+ await db.write();
255
+ debug$1("data %o", db.data);
259
256
  debug$1("token saved %o", token.user);
260
257
  }
261
258
  async deleteToken(user, tokenKey) {
262
259
  const db = await this.getTokenDb();
263
- const userTokens = await db.get(user).value();
264
- if (lodash.default.isNil(userTokens)) throw new Error("user not found");
260
+ const userTokens = db.data[user];
261
+ if ((0, lodash_es.isNil)(userTokens)) throw new Error("user not found");
265
262
  debug$1("tokens %o - %o", userTokens, userTokens.length);
266
- const remainingTokens = userTokens.filter(({ key }) => {
263
+ db.data[user] = userTokens.filter(({ key }) => {
267
264
  debug$1("key %o", key);
268
265
  return key !== tokenKey;
269
266
  });
270
- await db.set(user, remainingTokens).write();
267
+ await db.write();
271
268
  debug$1("removed tokens key %o", tokenKey);
272
269
  }
273
270
  async readTokens(filter) {
274
271
  const { user } = filter;
275
272
  debug$1("read tokens with %o", user);
276
- return await (await this.getTokenDb()).get(user).value() || [];
273
+ return (await this.getTokenDb()).data[user] || [];
277
274
  }
278
275
  };
279
276
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"local-database.js","names":[],"sources":["../src/local-database.ts"],"sourcesContent":["// import LRU from 'lru-cache';\nimport buildDebug from 'debug';\nimport _ from 'lodash';\nimport low from 'lowdb';\nimport FileAsync from 'lowdb/adapters/FileAsync';\nimport FileMemory from 'lowdb/adapters/Memory';\nimport path from 'node:path';\n\nimport type { searchUtils } from '@verdaccio/core';\nimport { authUtils, errorUtils, fileUtils, pluginUtils } from '@verdaccio/core';\nimport type { Config, Logger, Token, TokenFilter } from '@verdaccio/types';\n\nimport { searchOnStorage } from './dir-utils';\nimport { mkdirPromise, writeFilePromise } from './fs';\nimport LocalDriver, { noSuchFile } from './local-fs';\nimport type { LocalStorage } from './pkg-utils';\nimport { loadPrivatePackages } from './pkg-utils';\nimport { _dbGenPath } from './utils';\n\nconst TOKEN_DB_NAME = '.token-db.json';\nconst DB_NAME = process.env.VERDACCIO_STORAGE_NAME ?? fileUtils.Files.DatabaseName;\n\nconst debug = buildDebug('verdaccio:plugin:local-storage');\n\nexport const ERROR_DB_LOCKED =\n 'Database is locked, please check error message printed during startup to prevent data loss';\n\ntype Storage = pluginUtils.Storage<{}>;\n\nclass LocalDatabase extends pluginUtils.Plugin<{}> implements Storage {\n private readonly path: string;\n private readonly logger: Logger;\n public readonly config: Config;\n public readonly storages: Map<string, string>;\n public data: LocalStorage | undefined;\n public locked: boolean;\n public tokenDb: low.LowdbAsync<any> | null;\n\n public constructor(config: Config, logger: Logger) {\n // TODO: fix double config\n super(config, { config, logger });\n this.tokenDb = null;\n this.config = config;\n this.logger = logger;\n this.locked = false;\n this.data = undefined;\n debug('config path %o', config.configPath);\n this.path = _dbGenPath(DB_NAME, config);\n this.storages = this._getCustomPackageLocalStorages();\n this.logger.info({ path: this.path }, 'local storage path: @{path}');\n debug('plugin storage path %o', this.path);\n }\n\n public async init(): Promise<void> {\n debug('plugin init');\n this.data = await this.fetchLocalPackages();\n debug('local packages loaded');\n await this._sync();\n }\n\n public async getSecret(): Promise<string> {\n if (typeof this.data === 'undefined') {\n throw Error('no data secret available');\n }\n\n return Promise.resolve(this.data.secret);\n }\n\n public async setSecret(secret: string): Promise<void> {\n if (typeof this.data === 'undefined') {\n throw Error('no data secret available');\n } else {\n this.data.secret = secret;\n }\n\n await this._sync();\n }\n\n public async add(name: string): Promise<void> {\n if (typeof this.data === 'undefined') {\n throw Error('no data secret available');\n }\n\n if (this.data.list.indexOf(name) === -1) {\n this.data.list.push(name);\n debug('the private package %s has been added', name);\n await this._sync();\n } else {\n debug('the private package %s already exist on database', name);\n return Promise.resolve();\n }\n }\n\n /**\n * The field storage could be absolute or relative.\n * If relative, it will be resolved against the config path.\n * If absolute, it will be returned as is.\n **/\n private getStoragePath() {\n const { storage } = this.config;\n if (typeof storage !== 'string') {\n throw new TypeError('storage field is mandatory');\n }\n\n const storagePath = path.isAbsolute(storage)\n ? storage\n : path.normalize(path.join(this.getBaseConfigPath(), storage));\n debug('storage path %o', storagePath);\n return storagePath;\n }\n\n private getBaseConfigPath(): string {\n return path.dirname(this.config.configPath);\n }\n\n /**\n * Filter and only match those values that the query define.\n **/\n public async filterByQuery(results: searchUtils.SearchItemPkg[], query: searchUtils.SearchQuery) {\n // FUTURE: apply new filters, keyword, version, ...\n return results.filter((item: searchUtils.SearchItemPkg) => {\n // Sanitize user input\n const safeText = _.escapeRegExp(query.text);\n return item?.name?.match(safeText) !== null;\n }) as searchUtils.SearchItemPkg[];\n }\n\n public async getScore(_pkg: searchUtils.SearchItemPkg): Promise<searchUtils.Score> {\n // TODO: there is no particular reason to predefined scores\n // could be improved by using\n return Promise.resolve({\n final: 1,\n detail: {\n maintenance: 0,\n popularity: 1,\n quality: 1,\n },\n });\n }\n\n /**\n *\n * @param query\n * @returns\n */\n public async search(query: searchUtils.SearchQuery): Promise<searchUtils.SearchItem[]> {\n debug('search query to %o', query.text);\n const results: searchUtils.SearchItem[] = [];\n const storagePath = this.getStoragePath();\n const localResults = await searchOnStorage(storagePath, this.storages);\n const packagesOnStorage = await this.filterByQuery(localResults, query);\n debug('packages found %o', packagesOnStorage.length);\n for (const storage of packagesOnStorage) {\n // check if package is listed on the cache private database\n const isPrivate = (this.data as LocalStorage).list.includes(storage.name);\n const score = await this.getScore(storage);\n results.push({\n package: storage,\n verdaccioPrivate: isPrivate,\n verdaccioPkgCached: !isPrivate,\n score,\n });\n }\n return results;\n }\n\n public async remove(name: string): Promise<void> {\n try {\n if (typeof this.data === 'undefined') {\n throw Error('no data secret available');\n }\n\n const data = await this.get();\n\n const pkgName = data.indexOf(name);\n if (pkgName !== -1) {\n this.data.list.splice(pkgName, 1);\n debug('package %o has been removed', name);\n }\n await this._sync();\n } catch (err) {\n this.logger.error({ err }, 'remove the private package has failed @{err}');\n throw errorUtils.getInternalError('error remove private package');\n }\n }\n\n public async get(): Promise<any> {\n if (typeof this.data === 'undefined') {\n throw Error('no data secret available');\n }\n\n const { list } = this.data;\n const totalItems = list?.length;\n debug('get full list of packages (%o) has been fetched', totalItems);\n return Promise.resolve(list);\n }\n\n public getPackageStorage(packageName: string): pluginUtils.StorageHandler {\n const packageAccess = authUtils.getMatchedPackagesSpec(packageName, this.config.packages);\n\n const packagePath: string = this._getLocalStoragePath(\n packageAccess ? packageAccess.storage : undefined\n );\n debug('storage path selected %o', packagePath);\n if (_.isString(packagePath) === false) {\n debug('the package %o has no storage defined ', packageName);\n throw errorUtils.getInternalError('storage not found or implemented');\n }\n\n const packageStoragePath = path.resolve(path.join(packagePath, packageName));\n\n // Verify that the file path is under the storage root directory\n // to avoid \"uncontrolled data used in path expression\" issues\n const storageRoot = path.resolve(this.getStoragePath());\n\n debug('packageStoragePath %o, storageRoot %o', packageStoragePath, storageRoot);\n\n if (\n !packageStoragePath.startsWith(storageRoot + path.sep) &&\n packageStoragePath !== storageRoot\n ) {\n throw errorUtils.getInternalError(\n 'package-specific path is not under the configured storage directory'\n );\n }\n\n debug('storage absolute path %o', packageStoragePath);\n\n return new LocalDriver(packageStoragePath, this.logger);\n }\n\n public async clean(): Promise<void> {\n await this._sync();\n }\n\n private _getCustomPackageLocalStorages(): Map<string, string> {\n const storages = new Map<string, string>();\n const { packages } = this.config;\n\n if (packages) {\n Object.keys(packages || {}).map((pkg) => {\n const { storage } = packages[pkg];\n if (typeof storage === 'string') {\n const storagePath = path.join(this.getStoragePath(), storage);\n debug('add custom storage for %s on %s', storage, storagePath);\n storages.set(storage, storagePath);\n }\n });\n }\n\n return storages;\n }\n\n private async _sync(): Promise<null> {\n debug('sync database started');\n\n if (this.locked) {\n this.logger.error(ERROR_DB_LOCKED);\n throw new Error(ERROR_DB_LOCKED);\n }\n // Uses sync to prevent ugly race condition\n try {\n const folderName = path.dirname(this.path);\n debug('creating folder %o', folderName);\n await mkdirPromise(folderName, { recursive: true });\n debug('creating folder %o created succeed', folderName);\n } catch (err) {\n this.logger.error({ err }, 'sync create folder has failed with error: @{err}');\n throw err;\n }\n\n try {\n await writeFilePromise(this.path, JSON.stringify(this.data));\n debug('sync write succeeded');\n\n return null;\n } catch (err: any) {\n this.logger.error({ err }, 'sync database file failed: @{err}');\n throw err;\n }\n }\n\n private _getLocalStoragePath(storage: string | void): string {\n const globalConfigStorage = this.getStoragePath();\n if (_.isNil(globalConfigStorage)) {\n this.logger.error('property storage in config.yaml is required for using this plugin');\n throw new Error('property storage in config.yaml is required for using this plugin');\n } else {\n if (typeof storage === 'string') {\n return path.join(globalConfigStorage as string, storage as string);\n }\n\n return globalConfigStorage as string;\n }\n }\n\n private async fetchLocalPackages(): Promise<LocalStorage> {\n try {\n return await loadPrivatePackages(this.path, this.logger);\n } catch (err: any) {\n // readFileSync is platform specific, macOS, Linux and Windows thrown an error\n // Only recreate if file not found to prevent data loss\n this.logger.info(\n { path: this.path },\n 'no private database found, recreating new one on @{path}'\n );\n if (err.code !== noSuchFile) {\n this.locked = true;\n this.logger.error(\n 'Failed to read package database file, please check the error printed below:\\n',\n `File Path: ${this.path}\\n\\n ${err.message}`\n );\n }\n // if no database is found we set empty placeholders\n return { list: [], secret: '' };\n }\n }\n\n private async getTokenDb(): Promise<low.LowdbAsync<any>> {\n if (!this.tokenDb) {\n debug('token database is not defined');\n let adapter;\n if (process.env.NODE_ENV === 'test') {\n debug('token memory adapter');\n adapter = new FileMemory('');\n } else {\n debug('token async adapter');\n const pathDb = _dbGenPath(TOKEN_DB_NAME, this.config);\n adapter = new FileAsync(pathDb);\n }\n debug('token bd generated');\n this.tokenDb = await low(adapter);\n }\n\n return this.tokenDb;\n }\n\n public async saveToken(token: Token): Promise<void> {\n debug('token key %o', token.key);\n const db = await this.getTokenDb();\n const userData = await db.get(token.user).value();\n debug('user data %o', userData);\n if (_.isNil(userData)) {\n await db.set(token.user, [token]).write();\n debug('token user %o new database', token.user);\n } else {\n // types does not match with valid implementation\n // @ts-ignore\n await db.get(token.user).push(token).write();\n }\n debug('data %o', await db.getState());\n debug('token saved %o', token.user);\n }\n\n public async deleteToken(user: string, tokenKey: string): Promise<void> {\n const db = await this.getTokenDb();\n const userTokens = await db.get(user).value();\n if (_.isNil(userTokens)) {\n throw new Error('user not found');\n }\n debug('tokens %o - %o', userTokens, userTokens.length);\n const remainingTokens = userTokens.filter(({ key }) => {\n debug('key %o', key);\n return key !== tokenKey;\n });\n await db.set(user, remainingTokens).write();\n debug('removed tokens key %o', tokenKey);\n }\n\n public async readTokens(filter: TokenFilter): Promise<Token[]> {\n const { user } = filter;\n debug('read tokens with %o', user);\n const db = await this.getTokenDb();\n const tokens = await db.get(user).value();\n return tokens || [];\n }\n}\n\nexport default LocalDatabase;\n"],"mappings":"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"}
1
+ {"version":3,"file":"local-database.js","names":[],"sources":["../src/local-database.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { escapeRegExp, isNil } from 'lodash-es';\nimport { Low, Memory } from 'lowdb';\nimport { JSONFile } from 'lowdb/node';\nimport path from 'node:path';\n\nimport type { searchUtils } from '@verdaccio/core';\nimport { authUtils, errorUtils, fileUtils, pluginUtils } from '@verdaccio/core';\nimport type { Config, Logger, Token, TokenFilter } from '@verdaccio/types';\n\nimport { searchOnStorage } from './dir-utils';\nimport { mkdirPromise, writeFilePromise } from './fs';\nimport LocalDriver, { noSuchFile } from './local-fs';\nimport type { LocalStorage } from './pkg-utils';\nimport { loadPrivatePackages } from './pkg-utils';\nimport { _dbGenPath } from './utils';\n\nconst TOKEN_DB_NAME = '.token-db.json';\nconst DB_NAME = process.env.VERDACCIO_STORAGE_NAME ?? fileUtils.Files.DatabaseName;\n\nconst debug = buildDebug('verdaccio:plugin:local-storage');\n\nexport const ERROR_DB_LOCKED =\n 'Database is locked, please check error message printed during startup to prevent data loss';\n\ntype Storage = pluginUtils.Storage<{}>;\n\nclass LocalDatabase extends pluginUtils.Plugin<{}> implements Storage {\n private readonly path: string;\n private readonly logger: Logger;\n public readonly config: Config;\n public readonly storages: Map<string, string>;\n public data: LocalStorage | undefined;\n public locked: boolean;\n public tokenDb: Low<Record<string, Token[]>> | null;\n\n public constructor(config: Config, logger: Logger) {\n // TODO: fix double config\n super(config, { config, logger });\n this.tokenDb = null;\n this.config = config;\n this.logger = logger;\n this.locked = false;\n this.data = undefined;\n debug('config path %o', config.configPath);\n this.path = _dbGenPath(DB_NAME, config);\n this.storages = this._getCustomPackageLocalStorages();\n this.logger.info({ path: this.path }, 'local storage path: @{path}');\n debug('plugin storage path %o', this.path);\n }\n\n public async init(): Promise<void> {\n debug('plugin init');\n this.data = await this.fetchLocalPackages();\n debug('local packages loaded');\n await this._sync();\n }\n\n public async getSecret(): Promise<string> {\n if (typeof this.data === 'undefined') {\n throw Error('no data secret available');\n }\n\n return Promise.resolve(this.data.secret);\n }\n\n public async setSecret(secret: string): Promise<void> {\n if (typeof this.data === 'undefined') {\n throw Error('no data secret available');\n } else {\n this.data.secret = secret;\n }\n\n await this._sync();\n }\n\n public async add(name: string): Promise<void> {\n if (typeof this.data === 'undefined') {\n throw Error('no data secret available');\n }\n\n if (this.data.list.indexOf(name) === -1) {\n this.data.list.push(name);\n debug('the private package %s has been added', name);\n await this._sync();\n } else {\n debug('the private package %s already exist on database', name);\n return Promise.resolve();\n }\n }\n\n /**\n * The field storage could be absolute or relative.\n * If relative, it will be resolved against the config path.\n * If absolute, it will be returned as is.\n **/\n private getStoragePath() {\n const { storage } = this.config;\n if (typeof storage !== 'string') {\n throw new TypeError('storage field is mandatory');\n }\n\n const storagePath = path.isAbsolute(storage)\n ? storage\n : path.normalize(path.join(this.getBaseConfigPath(), storage));\n debug('storage path %o', storagePath);\n return storagePath;\n }\n\n private getBaseConfigPath(): string {\n return path.dirname(this.config.configPath);\n }\n\n /**\n * Filter and only match those values that the query define.\n **/\n public async filterByQuery(results: searchUtils.SearchItemPkg[], query: searchUtils.SearchQuery) {\n // FUTURE: apply new filters, keyword, version, ...\n return results.filter((item: searchUtils.SearchItemPkg) => {\n // Sanitize user input\n const safeText = escapeRegExp(query.text);\n return item?.name?.match(safeText) !== null;\n }) as searchUtils.SearchItemPkg[];\n }\n\n public async getScore(_pkg: searchUtils.SearchItemPkg): Promise<searchUtils.Score> {\n // TODO: there is no particular reason to predefined scores\n // could be improved by using\n return Promise.resolve({\n final: 1,\n detail: {\n maintenance: 0,\n popularity: 1,\n quality: 1,\n },\n });\n }\n\n /**\n *\n * @param query\n * @returns\n */\n public async search(query: searchUtils.SearchQuery): Promise<searchUtils.SearchItem[]> {\n debug('search query to %o', query.text);\n const results: searchUtils.SearchItem[] = [];\n const storagePath = this.getStoragePath();\n const localResults = await searchOnStorage(storagePath, this.storages);\n const packagesOnStorage = await this.filterByQuery(localResults, query);\n debug('packages found %o', packagesOnStorage.length);\n for (const storage of packagesOnStorage) {\n // check if package is listed on the cache private database\n const isPrivate = (this.data as LocalStorage).list.includes(storage.name);\n const score = await this.getScore(storage);\n results.push({\n package: storage,\n verdaccioPrivate: isPrivate,\n verdaccioPkgCached: !isPrivate,\n score,\n });\n }\n return results;\n }\n\n public async remove(name: string): Promise<void> {\n try {\n if (typeof this.data === 'undefined') {\n throw Error('no data secret available');\n }\n\n const data = await this.get();\n\n const pkgName = data.indexOf(name);\n if (pkgName !== -1) {\n this.data.list.splice(pkgName, 1);\n debug('package %o has been removed', name);\n }\n await this._sync();\n } catch (err) {\n this.logger.error({ err }, 'remove the private package has failed @{err}');\n throw errorUtils.getInternalError('error remove private package');\n }\n }\n\n public async get(): Promise<any> {\n if (typeof this.data === 'undefined') {\n throw Error('no data secret available');\n }\n\n const { list } = this.data;\n const totalItems = list?.length;\n debug('get full list of packages (%o) has been fetched', totalItems);\n return Promise.resolve(list);\n }\n\n public getPackageStorage(packageName: string): pluginUtils.StorageHandler {\n const packageAccess = authUtils.getMatchedPackagesSpec(packageName, this.config.packages);\n\n const packagePath: string = this._getLocalStoragePath(\n packageAccess ? packageAccess.storage : undefined\n );\n debug('storage path selected %o', packagePath);\n if (typeof packagePath !== 'string') {\n debug('the package %o has no storage defined ', packageName);\n throw errorUtils.getInternalError('storage not found or implemented');\n }\n\n const packageStoragePath = path.resolve(path.join(packagePath, packageName));\n\n // Verify that the file path is under the storage root directory\n // to avoid \"uncontrolled data used in path expression\" issues\n const storageRoot = path.resolve(this.getStoragePath());\n\n debug('packageStoragePath %o, storageRoot %o', packageStoragePath, storageRoot);\n\n if (\n !packageStoragePath.startsWith(storageRoot + path.sep) &&\n packageStoragePath !== storageRoot\n ) {\n throw errorUtils.getInternalError(\n 'package-specific path is not under the configured storage directory'\n );\n }\n\n debug('storage absolute path %o', packageStoragePath);\n\n return new LocalDriver(packageStoragePath, this.logger);\n }\n\n public async clean(): Promise<void> {\n await this._sync();\n }\n\n private _getCustomPackageLocalStorages(): Map<string, string> {\n const storages = new Map<string, string>();\n const { packages } = this.config;\n\n if (packages) {\n Object.keys(packages || {}).map((pkg) => {\n const { storage } = packages[pkg];\n if (typeof storage === 'string') {\n const storagePath = path.join(this.getStoragePath(), storage);\n debug('add custom storage for %s on %s', storage, storagePath);\n storages.set(storage, storagePath);\n }\n });\n }\n\n return storages;\n }\n\n private async _sync(): Promise<null> {\n debug('sync database started');\n\n if (this.locked) {\n this.logger.error(ERROR_DB_LOCKED);\n throw new Error(ERROR_DB_LOCKED);\n }\n // Uses sync to prevent ugly race condition\n try {\n const folderName = path.dirname(this.path);\n debug('creating folder %o', folderName);\n await mkdirPromise(folderName, { recursive: true });\n debug('creating folder %o created succeed', folderName);\n } catch (err) {\n this.logger.error({ err }, 'sync create folder has failed with error: @{err}');\n throw err;\n }\n\n try {\n await writeFilePromise(this.path, JSON.stringify(this.data));\n debug('sync write succeeded');\n\n return null;\n } catch (err: any) {\n this.logger.error({ err }, 'sync database file failed: @{err}');\n throw err;\n }\n }\n\n private _getLocalStoragePath(storage: string | void): string {\n const globalConfigStorage = this.getStoragePath();\n if (isNil(globalConfigStorage)) {\n this.logger.error('property storage in config.yaml is required for using this plugin');\n throw new Error('property storage in config.yaml is required for using this plugin');\n } else {\n if (typeof storage === 'string') {\n return path.join(globalConfigStorage as string, storage as string);\n }\n\n return globalConfigStorage as string;\n }\n }\n\n private async fetchLocalPackages(): Promise<LocalStorage> {\n try {\n return await loadPrivatePackages(this.path, this.logger);\n } catch (err: any) {\n // readFileSync is platform specific, macOS, Linux and Windows thrown an error\n // Only recreate if file not found to prevent data loss\n this.logger.info(\n { path: this.path },\n 'no private database found, recreating new one on @{path}'\n );\n if (err.code !== noSuchFile) {\n this.locked = true;\n this.logger.error(\n 'Failed to read package database file, please check the error printed below:\\n',\n `File Path: ${this.path}\\n\\n ${err.message}`\n );\n }\n // if no database is found we set empty placeholders\n return { list: [], secret: '' };\n }\n }\n\n private async getTokenDb(): Promise<Low<Record<string, Token[]>>> {\n if (!this.tokenDb) {\n debug('token database is not defined');\n let adapter;\n if (process.env.NODE_ENV === 'test') {\n debug('token memory adapter');\n adapter = new Memory<Record<string, Token[]>>();\n } else {\n debug('token async adapter');\n const pathDb = _dbGenPath(TOKEN_DB_NAME, this.config);\n adapter = new JSONFile<Record<string, Token[]>>(pathDb);\n }\n debug('token db generated');\n this.tokenDb = new Low<Record<string, Token[]>>(adapter, {});\n await this.tokenDb.read();\n }\n\n return this.tokenDb;\n }\n\n public async saveToken(token: Token): Promise<void> {\n debug('token key %o', token.key);\n const db = await this.getTokenDb();\n const userData = db.data[token.user];\n debug('user data %o', userData);\n if (isNil(userData)) {\n db.data[token.user] = [token];\n debug('token user %o new database', token.user);\n } else {\n db.data[token.user].push(token);\n }\n await db.write();\n debug('data %o', db.data);\n debug('token saved %o', token.user);\n }\n\n public async deleteToken(user: string, tokenKey: string): Promise<void> {\n const db = await this.getTokenDb();\n const userTokens = db.data[user];\n if (isNil(userTokens)) {\n throw new Error('user not found');\n }\n debug('tokens %o - %o', userTokens, userTokens.length);\n db.data[user] = userTokens.filter(({ key }) => {\n debug('key %o', key);\n return key !== tokenKey;\n });\n await db.write();\n debug('removed tokens key %o', tokenKey);\n }\n\n public async readTokens(filter: TokenFilter): Promise<Token[]> {\n const { user } = filter;\n debug('read tokens with %o', user);\n const db = await this.getTokenDb();\n const tokens = db.data[user];\n return tokens || [];\n }\n}\n\nexport default LocalDatabase;\n"],"mappings":"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"}
@@ -4,10 +4,9 @@ import LocalFS from "./local-fs.mjs";
4
4
  import { loadPrivatePackages } from "./pkg-utils.mjs";
5
5
  import { _dbGenPath } from "./utils.mjs";
6
6
  import buildDebug from "debug";
7
- import _ from "lodash";
8
- import low from "lowdb";
9
- import FileAsync from "lowdb/adapters/FileAsync";
10
- import FileMemory from "lowdb/adapters/Memory";
7
+ import { escapeRegExp, isNil } from "lodash-es";
8
+ import { Low, Memory } from "lowdb";
9
+ import { JSONFile } from "lowdb/node";
11
10
  import path from "node:path";
12
11
  import { authUtils, errorUtils, fileUtils, pluginUtils } from "@verdaccio/core";
13
12
  //#region src/local-database.ts
@@ -85,7 +84,7 @@ var LocalDatabase = class extends pluginUtils.Plugin {
85
84
  **/
86
85
  async filterByQuery(results, query) {
87
86
  return results.filter((item) => {
88
- const safeText = _.escapeRegExp(query.text);
87
+ const safeText = escapeRegExp(query.text);
89
88
  return item?.name?.match(safeText) !== null;
90
89
  });
91
90
  }
@@ -147,7 +146,7 @@ var LocalDatabase = class extends pluginUtils.Plugin {
147
146
  const packageAccess = authUtils.getMatchedPackagesSpec(packageName, this.config.packages);
148
147
  const packagePath = this._getLocalStoragePath(packageAccess ? packageAccess.storage : void 0);
149
148
  debug("storage path selected %o", packagePath);
150
- if (_.isString(packagePath) === false) {
149
+ if (typeof packagePath !== "string") {
151
150
  debug("the package %o has no storage defined ", packageName);
152
151
  throw errorUtils.getInternalError("storage not found or implemented");
153
152
  }
@@ -200,7 +199,7 @@ var LocalDatabase = class extends pluginUtils.Plugin {
200
199
  }
201
200
  _getLocalStoragePath(storage) {
202
201
  const globalConfigStorage = this.getStoragePath();
203
- if (_.isNil(globalConfigStorage)) {
202
+ if (isNil(globalConfigStorage)) {
204
203
  this.logger.error("property storage in config.yaml is required for using this plugin");
205
204
  throw new Error("property storage in config.yaml is required for using this plugin");
206
205
  } else {
@@ -229,44 +228,46 @@ var LocalDatabase = class extends pluginUtils.Plugin {
229
228
  let adapter;
230
229
  if (process.env.NODE_ENV === "test") {
231
230
  debug("token memory adapter");
232
- adapter = new FileMemory("");
231
+ adapter = new Memory();
233
232
  } else {
234
233
  debug("token async adapter");
235
- adapter = new FileAsync(_dbGenPath(TOKEN_DB_NAME, this.config));
234
+ adapter = new JSONFile(_dbGenPath(TOKEN_DB_NAME, this.config));
236
235
  }
237
- debug("token bd generated");
238
- this.tokenDb = await low(adapter);
236
+ debug("token db generated");
237
+ this.tokenDb = new Low(adapter, {});
238
+ await this.tokenDb.read();
239
239
  }
240
240
  return this.tokenDb;
241
241
  }
242
242
  async saveToken(token) {
243
243
  debug("token key %o", token.key);
244
244
  const db = await this.getTokenDb();
245
- const userData = await db.get(token.user).value();
245
+ const userData = db.data[token.user];
246
246
  debug("user data %o", userData);
247
- if (_.isNil(userData)) {
248
- await db.set(token.user, [token]).write();
247
+ if (isNil(userData)) {
248
+ db.data[token.user] = [token];
249
249
  debug("token user %o new database", token.user);
250
- } else await db.get(token.user).push(token).write();
251
- debug("data %o", await db.getState());
250
+ } else db.data[token.user].push(token);
251
+ await db.write();
252
+ debug("data %o", db.data);
252
253
  debug("token saved %o", token.user);
253
254
  }
254
255
  async deleteToken(user, tokenKey) {
255
256
  const db = await this.getTokenDb();
256
- const userTokens = await db.get(user).value();
257
- if (_.isNil(userTokens)) throw new Error("user not found");
257
+ const userTokens = db.data[user];
258
+ if (isNil(userTokens)) throw new Error("user not found");
258
259
  debug("tokens %o - %o", userTokens, userTokens.length);
259
- const remainingTokens = userTokens.filter(({ key }) => {
260
+ db.data[user] = userTokens.filter(({ key }) => {
260
261
  debug("key %o", key);
261
262
  return key !== tokenKey;
262
263
  });
263
- await db.set(user, remainingTokens).write();
264
+ await db.write();
264
265
  debug("removed tokens key %o", tokenKey);
265
266
  }
266
267
  async readTokens(filter) {
267
268
  const { user } = filter;
268
269
  debug("read tokens with %o", user);
269
- return await (await this.getTokenDb()).get(user).value() || [];
270
+ return (await this.getTokenDb()).data[user] || [];
270
271
  }
271
272
  };
272
273
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"local-database.mjs","names":[],"sources":["../src/local-database.ts"],"sourcesContent":["// import LRU from 'lru-cache';\nimport buildDebug from 'debug';\nimport _ from 'lodash';\nimport low from 'lowdb';\nimport FileAsync from 'lowdb/adapters/FileAsync';\nimport FileMemory from 'lowdb/adapters/Memory';\nimport path from 'node:path';\n\nimport type { searchUtils } from '@verdaccio/core';\nimport { authUtils, errorUtils, fileUtils, pluginUtils } from '@verdaccio/core';\nimport type { Config, Logger, Token, TokenFilter } from '@verdaccio/types';\n\nimport { searchOnStorage } from './dir-utils';\nimport { mkdirPromise, writeFilePromise } from './fs';\nimport LocalDriver, { noSuchFile } from './local-fs';\nimport type { LocalStorage } from './pkg-utils';\nimport { loadPrivatePackages } from './pkg-utils';\nimport { _dbGenPath } from './utils';\n\nconst TOKEN_DB_NAME = '.token-db.json';\nconst DB_NAME = process.env.VERDACCIO_STORAGE_NAME ?? fileUtils.Files.DatabaseName;\n\nconst debug = buildDebug('verdaccio:plugin:local-storage');\n\nexport const ERROR_DB_LOCKED =\n 'Database is locked, please check error message printed during startup to prevent data loss';\n\ntype Storage = pluginUtils.Storage<{}>;\n\nclass LocalDatabase extends pluginUtils.Plugin<{}> implements Storage {\n private readonly path: string;\n private readonly logger: Logger;\n public readonly config: Config;\n public readonly storages: Map<string, string>;\n public data: LocalStorage | undefined;\n public locked: boolean;\n public tokenDb: low.LowdbAsync<any> | null;\n\n public constructor(config: Config, logger: Logger) {\n // TODO: fix double config\n super(config, { config, logger });\n this.tokenDb = null;\n this.config = config;\n this.logger = logger;\n this.locked = false;\n this.data = undefined;\n debug('config path %o', config.configPath);\n this.path = _dbGenPath(DB_NAME, config);\n this.storages = this._getCustomPackageLocalStorages();\n this.logger.info({ path: this.path }, 'local storage path: @{path}');\n debug('plugin storage path %o', this.path);\n }\n\n public async init(): Promise<void> {\n debug('plugin init');\n this.data = await this.fetchLocalPackages();\n debug('local packages loaded');\n await this._sync();\n }\n\n public async getSecret(): Promise<string> {\n if (typeof this.data === 'undefined') {\n throw Error('no data secret available');\n }\n\n return Promise.resolve(this.data.secret);\n }\n\n public async setSecret(secret: string): Promise<void> {\n if (typeof this.data === 'undefined') {\n throw Error('no data secret available');\n } else {\n this.data.secret = secret;\n }\n\n await this._sync();\n }\n\n public async add(name: string): Promise<void> {\n if (typeof this.data === 'undefined') {\n throw Error('no data secret available');\n }\n\n if (this.data.list.indexOf(name) === -1) {\n this.data.list.push(name);\n debug('the private package %s has been added', name);\n await this._sync();\n } else {\n debug('the private package %s already exist on database', name);\n return Promise.resolve();\n }\n }\n\n /**\n * The field storage could be absolute or relative.\n * If relative, it will be resolved against the config path.\n * If absolute, it will be returned as is.\n **/\n private getStoragePath() {\n const { storage } = this.config;\n if (typeof storage !== 'string') {\n throw new TypeError('storage field is mandatory');\n }\n\n const storagePath = path.isAbsolute(storage)\n ? storage\n : path.normalize(path.join(this.getBaseConfigPath(), storage));\n debug('storage path %o', storagePath);\n return storagePath;\n }\n\n private getBaseConfigPath(): string {\n return path.dirname(this.config.configPath);\n }\n\n /**\n * Filter and only match those values that the query define.\n **/\n public async filterByQuery(results: searchUtils.SearchItemPkg[], query: searchUtils.SearchQuery) {\n // FUTURE: apply new filters, keyword, version, ...\n return results.filter((item: searchUtils.SearchItemPkg) => {\n // Sanitize user input\n const safeText = _.escapeRegExp(query.text);\n return item?.name?.match(safeText) !== null;\n }) as searchUtils.SearchItemPkg[];\n }\n\n public async getScore(_pkg: searchUtils.SearchItemPkg): Promise<searchUtils.Score> {\n // TODO: there is no particular reason to predefined scores\n // could be improved by using\n return Promise.resolve({\n final: 1,\n detail: {\n maintenance: 0,\n popularity: 1,\n quality: 1,\n },\n });\n }\n\n /**\n *\n * @param query\n * @returns\n */\n public async search(query: searchUtils.SearchQuery): Promise<searchUtils.SearchItem[]> {\n debug('search query to %o', query.text);\n const results: searchUtils.SearchItem[] = [];\n const storagePath = this.getStoragePath();\n const localResults = await searchOnStorage(storagePath, this.storages);\n const packagesOnStorage = await this.filterByQuery(localResults, query);\n debug('packages found %o', packagesOnStorage.length);\n for (const storage of packagesOnStorage) {\n // check if package is listed on the cache private database\n const isPrivate = (this.data as LocalStorage).list.includes(storage.name);\n const score = await this.getScore(storage);\n results.push({\n package: storage,\n verdaccioPrivate: isPrivate,\n verdaccioPkgCached: !isPrivate,\n score,\n });\n }\n return results;\n }\n\n public async remove(name: string): Promise<void> {\n try {\n if (typeof this.data === 'undefined') {\n throw Error('no data secret available');\n }\n\n const data = await this.get();\n\n const pkgName = data.indexOf(name);\n if (pkgName !== -1) {\n this.data.list.splice(pkgName, 1);\n debug('package %o has been removed', name);\n }\n await this._sync();\n } catch (err) {\n this.logger.error({ err }, 'remove the private package has failed @{err}');\n throw errorUtils.getInternalError('error remove private package');\n }\n }\n\n public async get(): Promise<any> {\n if (typeof this.data === 'undefined') {\n throw Error('no data secret available');\n }\n\n const { list } = this.data;\n const totalItems = list?.length;\n debug('get full list of packages (%o) has been fetched', totalItems);\n return Promise.resolve(list);\n }\n\n public getPackageStorage(packageName: string): pluginUtils.StorageHandler {\n const packageAccess = authUtils.getMatchedPackagesSpec(packageName, this.config.packages);\n\n const packagePath: string = this._getLocalStoragePath(\n packageAccess ? packageAccess.storage : undefined\n );\n debug('storage path selected %o', packagePath);\n if (_.isString(packagePath) === false) {\n debug('the package %o has no storage defined ', packageName);\n throw errorUtils.getInternalError('storage not found or implemented');\n }\n\n const packageStoragePath = path.resolve(path.join(packagePath, packageName));\n\n // Verify that the file path is under the storage root directory\n // to avoid \"uncontrolled data used in path expression\" issues\n const storageRoot = path.resolve(this.getStoragePath());\n\n debug('packageStoragePath %o, storageRoot %o', packageStoragePath, storageRoot);\n\n if (\n !packageStoragePath.startsWith(storageRoot + path.sep) &&\n packageStoragePath !== storageRoot\n ) {\n throw errorUtils.getInternalError(\n 'package-specific path is not under the configured storage directory'\n );\n }\n\n debug('storage absolute path %o', packageStoragePath);\n\n return new LocalDriver(packageStoragePath, this.logger);\n }\n\n public async clean(): Promise<void> {\n await this._sync();\n }\n\n private _getCustomPackageLocalStorages(): Map<string, string> {\n const storages = new Map<string, string>();\n const { packages } = this.config;\n\n if (packages) {\n Object.keys(packages || {}).map((pkg) => {\n const { storage } = packages[pkg];\n if (typeof storage === 'string') {\n const storagePath = path.join(this.getStoragePath(), storage);\n debug('add custom storage for %s on %s', storage, storagePath);\n storages.set(storage, storagePath);\n }\n });\n }\n\n return storages;\n }\n\n private async _sync(): Promise<null> {\n debug('sync database started');\n\n if (this.locked) {\n this.logger.error(ERROR_DB_LOCKED);\n throw new Error(ERROR_DB_LOCKED);\n }\n // Uses sync to prevent ugly race condition\n try {\n const folderName = path.dirname(this.path);\n debug('creating folder %o', folderName);\n await mkdirPromise(folderName, { recursive: true });\n debug('creating folder %o created succeed', folderName);\n } catch (err) {\n this.logger.error({ err }, 'sync create folder has failed with error: @{err}');\n throw err;\n }\n\n try {\n await writeFilePromise(this.path, JSON.stringify(this.data));\n debug('sync write succeeded');\n\n return null;\n } catch (err: any) {\n this.logger.error({ err }, 'sync database file failed: @{err}');\n throw err;\n }\n }\n\n private _getLocalStoragePath(storage: string | void): string {\n const globalConfigStorage = this.getStoragePath();\n if (_.isNil(globalConfigStorage)) {\n this.logger.error('property storage in config.yaml is required for using this plugin');\n throw new Error('property storage in config.yaml is required for using this plugin');\n } else {\n if (typeof storage === 'string') {\n return path.join(globalConfigStorage as string, storage as string);\n }\n\n return globalConfigStorage as string;\n }\n }\n\n private async fetchLocalPackages(): Promise<LocalStorage> {\n try {\n return await loadPrivatePackages(this.path, this.logger);\n } catch (err: any) {\n // readFileSync is platform specific, macOS, Linux and Windows thrown an error\n // Only recreate if file not found to prevent data loss\n this.logger.info(\n { path: this.path },\n 'no private database found, recreating new one on @{path}'\n );\n if (err.code !== noSuchFile) {\n this.locked = true;\n this.logger.error(\n 'Failed to read package database file, please check the error printed below:\\n',\n `File Path: ${this.path}\\n\\n ${err.message}`\n );\n }\n // if no database is found we set empty placeholders\n return { list: [], secret: '' };\n }\n }\n\n private async getTokenDb(): Promise<low.LowdbAsync<any>> {\n if (!this.tokenDb) {\n debug('token database is not defined');\n let adapter;\n if (process.env.NODE_ENV === 'test') {\n debug('token memory adapter');\n adapter = new FileMemory('');\n } else {\n debug('token async adapter');\n const pathDb = _dbGenPath(TOKEN_DB_NAME, this.config);\n adapter = new FileAsync(pathDb);\n }\n debug('token bd generated');\n this.tokenDb = await low(adapter);\n }\n\n return this.tokenDb;\n }\n\n public async saveToken(token: Token): Promise<void> {\n debug('token key %o', token.key);\n const db = await this.getTokenDb();\n const userData = await db.get(token.user).value();\n debug('user data %o', userData);\n if (_.isNil(userData)) {\n await db.set(token.user, [token]).write();\n debug('token user %o new database', token.user);\n } else {\n // types does not match with valid implementation\n // @ts-ignore\n await db.get(token.user).push(token).write();\n }\n debug('data %o', await db.getState());\n debug('token saved %o', token.user);\n }\n\n public async deleteToken(user: string, tokenKey: string): Promise<void> {\n const db = await this.getTokenDb();\n const userTokens = await db.get(user).value();\n if (_.isNil(userTokens)) {\n throw new Error('user not found');\n }\n debug('tokens %o - %o', userTokens, userTokens.length);\n const remainingTokens = userTokens.filter(({ key }) => {\n debug('key %o', key);\n return key !== tokenKey;\n });\n await db.set(user, remainingTokens).write();\n debug('removed tokens key %o', tokenKey);\n }\n\n public async readTokens(filter: TokenFilter): Promise<Token[]> {\n const { user } = filter;\n debug('read tokens with %o', user);\n const db = await this.getTokenDb();\n const tokens = await db.get(user).value();\n return tokens || [];\n }\n}\n\nexport default LocalDatabase;\n"],"mappings":";;;;;;;;;;;;;AAmBA,IAAM,gBAAgB;AACtB,IAAM,UAAU,QAAQ,IAAI,0BAA0B,UAAU,MAAM;AAEtE,IAAM,QAAQ,WAAW,iCAAiC;AAE1D,IAAa,kBACX;AAIF,IAAM,gBAAN,cAA4B,YAAY,OAA8B;CACpE;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,YAAmB,QAAgB,QAAgB;AAEjD,QAAM,QAAQ;GAAE;GAAQ;GAAQ,CAAC;AACjC,OAAK,UAAU;AACf,OAAK,SAAS;AACd,OAAK,SAAS;AACd,OAAK,SAAS;AACd,OAAK,OAAO,KAAA;AACZ,QAAM,kBAAkB,OAAO,WAAW;AAC1C,OAAK,OAAO,WAAW,SAAS,OAAO;AACvC,OAAK,WAAW,KAAK,gCAAgC;AACrD,OAAK,OAAO,KAAK,EAAE,MAAM,KAAK,MAAM,EAAE,8BAA8B;AACpE,QAAM,0BAA0B,KAAK,KAAK;;CAG5C,MAAa,OAAsB;AACjC,QAAM,cAAc;AACpB,OAAK,OAAO,MAAM,KAAK,oBAAoB;AAC3C,QAAM,wBAAwB;AAC9B,QAAM,KAAK,OAAO;;CAGpB,MAAa,YAA6B;AACxC,MAAI,OAAO,KAAK,SAAS,YACvB,OAAM,MAAM,2BAA2B;AAGzC,SAAO,QAAQ,QAAQ,KAAK,KAAK,OAAO;;CAG1C,MAAa,UAAU,QAA+B;AACpD,MAAI,OAAO,KAAK,SAAS,YACvB,OAAM,MAAM,2BAA2B;MAEvC,MAAK,KAAK,SAAS;AAGrB,QAAM,KAAK,OAAO;;CAGpB,MAAa,IAAI,MAA6B;AAC5C,MAAI,OAAO,KAAK,SAAS,YACvB,OAAM,MAAM,2BAA2B;AAGzC,MAAI,KAAK,KAAK,KAAK,QAAQ,KAAK,KAAK,IAAI;AACvC,QAAK,KAAK,KAAK,KAAK,KAAK;AACzB,SAAM,yCAAyC,KAAK;AACpD,SAAM,KAAK,OAAO;SACb;AACL,SAAM,oDAAoD,KAAK;AAC/D,UAAO,QAAQ,SAAS;;;;;;;;CAS5B,iBAAyB;EACvB,MAAM,EAAE,YAAY,KAAK;AACzB,MAAI,OAAO,YAAY,SACrB,OAAM,IAAI,UAAU,6BAA6B;EAGnD,MAAM,cAAc,KAAK,WAAW,QAAQ,GACxC,UACA,KAAK,UAAU,KAAK,KAAK,KAAK,mBAAmB,EAAE,QAAQ,CAAC;AAChE,QAAM,mBAAmB,YAAY;AACrC,SAAO;;CAGT,oBAAoC;AAClC,SAAO,KAAK,QAAQ,KAAK,OAAO,WAAW;;;;;CAM7C,MAAa,cAAc,SAAsC,OAAgC;AAE/F,SAAO,QAAQ,QAAQ,SAAoC;GAEzD,MAAM,WAAW,EAAE,aAAa,MAAM,KAAK;AAC3C,UAAO,MAAM,MAAM,MAAM,SAAS,KAAK;IACvC;;CAGJ,MAAa,SAAS,MAA6D;AAGjF,SAAO,QAAQ,QAAQ;GACrB,OAAO;GACP,QAAQ;IACN,aAAa;IACb,YAAY;IACZ,SAAS;IACV;GACF,CAAC;;;;;;;CAQJ,MAAa,OAAO,OAAmE;AACrF,QAAM,sBAAsB,MAAM,KAAK;EACvC,MAAM,UAAoC,EAAE;EAE5C,MAAM,eAAe,MAAM,gBADP,KAAK,gBAAgB,EACe,KAAK,SAAS;EACtE,MAAM,oBAAoB,MAAM,KAAK,cAAc,cAAc,MAAM;AACvE,QAAM,qBAAqB,kBAAkB,OAAO;AACpD,OAAK,MAAM,WAAW,mBAAmB;GAEvC,MAAM,YAAa,KAAK,KAAsB,KAAK,SAAS,QAAQ,KAAK;GACzE,MAAM,QAAQ,MAAM,KAAK,SAAS,QAAQ;AAC1C,WAAQ,KAAK;IACX,SAAS;IACT,kBAAkB;IAClB,oBAAoB,CAAC;IACrB;IACD,CAAC;;AAEJ,SAAO;;CAGT,MAAa,OAAO,MAA6B;AAC/C,MAAI;AACF,OAAI,OAAO,KAAK,SAAS,YACvB,OAAM,MAAM,2BAA2B;GAKzC,MAAM,WAFO,MAAM,KAAK,KAAK,EAER,QAAQ,KAAK;AAClC,OAAI,YAAY,IAAI;AAClB,SAAK,KAAK,KAAK,OAAO,SAAS,EAAE;AACjC,UAAM,+BAA+B,KAAK;;AAE5C,SAAM,KAAK,OAAO;WACX,KAAK;AACZ,QAAK,OAAO,MAAM,EAAE,KAAK,EAAE,+CAA+C;AAC1E,SAAM,WAAW,iBAAiB,+BAA+B;;;CAIrE,MAAa,MAAoB;AAC/B,MAAI,OAAO,KAAK,SAAS,YACvB,OAAM,MAAM,2BAA2B;EAGzC,MAAM,EAAE,SAAS,KAAK;EACtB,MAAM,aAAa,MAAM;AACzB,QAAM,mDAAmD,WAAW;AACpE,SAAO,QAAQ,QAAQ,KAAK;;CAG9B,kBAAyB,aAAiD;EACxE,MAAM,gBAAgB,UAAU,uBAAuB,aAAa,KAAK,OAAO,SAAS;EAEzF,MAAM,cAAsB,KAAK,qBAC/B,gBAAgB,cAAc,UAAU,KAAA,EACzC;AACD,QAAM,4BAA4B,YAAY;AAC9C,MAAI,EAAE,SAAS,YAAY,KAAK,OAAO;AACrC,SAAM,0CAA0C,YAAY;AAC5D,SAAM,WAAW,iBAAiB,mCAAmC;;EAGvE,MAAM,qBAAqB,KAAK,QAAQ,KAAK,KAAK,aAAa,YAAY,CAAC;EAI5E,MAAM,cAAc,KAAK,QAAQ,KAAK,gBAAgB,CAAC;AAEvD,QAAM,yCAAyC,oBAAoB,YAAY;AAE/E,MACE,CAAC,mBAAmB,WAAW,cAAc,KAAK,IAAI,IACtD,uBAAuB,YAEvB,OAAM,WAAW,iBACf,sEACD;AAGH,QAAM,4BAA4B,mBAAmB;AAErD,SAAO,IAAI,QAAY,oBAAoB,KAAK,OAAO;;CAGzD,MAAa,QAAuB;AAClC,QAAM,KAAK,OAAO;;CAGpB,iCAA8D;EAC5D,MAAM,2BAAW,IAAI,KAAqB;EAC1C,MAAM,EAAE,aAAa,KAAK;AAE1B,MAAI,SACF,QAAO,KAAK,YAAY,EAAE,CAAC,CAAC,KAAK,QAAQ;GACvC,MAAM,EAAE,YAAY,SAAS;AAC7B,OAAI,OAAO,YAAY,UAAU;IAC/B,MAAM,cAAc,KAAK,KAAK,KAAK,gBAAgB,EAAE,QAAQ;AAC7D,UAAM,mCAAmC,SAAS,YAAY;AAC9D,aAAS,IAAI,SAAS,YAAY;;IAEpC;AAGJ,SAAO;;CAGT,MAAc,QAAuB;AACnC,QAAM,wBAAwB;AAE9B,MAAI,KAAK,QAAQ;AACf,QAAK,OAAO,MAAM,gBAAgB;AAClC,SAAM,IAAI,MAAM,gBAAgB;;AAGlC,MAAI;GACF,MAAM,aAAa,KAAK,QAAQ,KAAK,KAAK;AAC1C,SAAM,sBAAsB,WAAW;AACvC,SAAM,aAAa,YAAY,EAAE,WAAW,MAAM,CAAC;AACnD,SAAM,sCAAsC,WAAW;WAChD,KAAK;AACZ,QAAK,OAAO,MAAM,EAAE,KAAK,EAAE,mDAAmD;AAC9E,SAAM;;AAGR,MAAI;AACF,SAAM,iBAAiB,KAAK,MAAM,KAAK,UAAU,KAAK,KAAK,CAAC;AAC5D,SAAM,uBAAuB;AAE7B,UAAO;WACA,KAAU;AACjB,QAAK,OAAO,MAAM,EAAE,KAAK,EAAE,oCAAoC;AAC/D,SAAM;;;CAIV,qBAA6B,SAAgC;EAC3D,MAAM,sBAAsB,KAAK,gBAAgB;AACjD,MAAI,EAAE,MAAM,oBAAoB,EAAE;AAChC,QAAK,OAAO,MAAM,oEAAoE;AACtF,SAAM,IAAI,MAAM,oEAAoE;SAC/E;AACL,OAAI,OAAO,YAAY,SACrB,QAAO,KAAK,KAAK,qBAA+B,QAAkB;AAGpE,UAAO;;;CAIX,MAAc,qBAA4C;AACxD,MAAI;AACF,UAAO,MAAM,oBAAoB,KAAK,MAAM,KAAK,OAAO;WACjD,KAAU;AAGjB,QAAK,OAAO,KACV,EAAE,MAAM,KAAK,MAAM,EACnB,2DACD;AACD,OAAI,IAAI,SAAA,UAAqB;AAC3B,SAAK,SAAS;AACd,SAAK,OAAO,MACV,iFACA,cAAc,KAAK,KAAK,OAAO,IAAI,UACpC;;AAGH,UAAO;IAAE,MAAM,EAAE;IAAE,QAAQ;IAAI;;;CAInC,MAAc,aAA2C;AACvD,MAAI,CAAC,KAAK,SAAS;AACjB,SAAM,gCAAgC;GACtC,IAAI;AACJ,OAAA,QAAA,IAAA,aAA6B,QAAQ;AACnC,UAAM,uBAAuB;AAC7B,cAAU,IAAI,WAAW,GAAG;UACvB;AACL,UAAM,sBAAsB;AAE5B,cAAU,IAAI,UADC,WAAW,eAAe,KAAK,OAAO,CACtB;;AAEjC,SAAM,qBAAqB;AAC3B,QAAK,UAAU,MAAM,IAAI,QAAQ;;AAGnC,SAAO,KAAK;;CAGd,MAAa,UAAU,OAA6B;AAClD,QAAM,gBAAgB,MAAM,IAAI;EAChC,MAAM,KAAK,MAAM,KAAK,YAAY;EAClC,MAAM,WAAW,MAAM,GAAG,IAAI,MAAM,KAAK,CAAC,OAAO;AACjD,QAAM,gBAAgB,SAAS;AAC/B,MAAI,EAAE,MAAM,SAAS,EAAE;AACrB,SAAM,GAAG,IAAI,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO;AACzC,SAAM,8BAA8B,MAAM,KAAK;QAI/C,OAAM,GAAG,IAAI,MAAM,KAAK,CAAC,KAAK,MAAM,CAAC,OAAO;AAE9C,QAAM,WAAW,MAAM,GAAG,UAAU,CAAC;AACrC,QAAM,kBAAkB,MAAM,KAAK;;CAGrC,MAAa,YAAY,MAAc,UAAiC;EACtE,MAAM,KAAK,MAAM,KAAK,YAAY;EAClC,MAAM,aAAa,MAAM,GAAG,IAAI,KAAK,CAAC,OAAO;AAC7C,MAAI,EAAE,MAAM,WAAW,CACrB,OAAM,IAAI,MAAM,iBAAiB;AAEnC,QAAM,kBAAkB,YAAY,WAAW,OAAO;EACtD,MAAM,kBAAkB,WAAW,QAAQ,EAAE,UAAU;AACrD,SAAM,UAAU,IAAI;AACpB,UAAO,QAAQ;IACf;AACF,QAAM,GAAG,IAAI,MAAM,gBAAgB,CAAC,OAAO;AAC3C,QAAM,yBAAyB,SAAS;;CAG1C,MAAa,WAAW,QAAuC;EAC7D,MAAM,EAAE,SAAS;AACjB,QAAM,uBAAuB,KAAK;AAGlC,SADe,OADJ,MAAM,KAAK,YAAY,EACV,IAAI,KAAK,CAAC,OAAO,IACxB,EAAE"}
1
+ {"version":3,"file":"local-database.mjs","names":[],"sources":["../src/local-database.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { escapeRegExp, isNil } from 'lodash-es';\nimport { Low, Memory } from 'lowdb';\nimport { JSONFile } from 'lowdb/node';\nimport path from 'node:path';\n\nimport type { searchUtils } from '@verdaccio/core';\nimport { authUtils, errorUtils, fileUtils, pluginUtils } from '@verdaccio/core';\nimport type { Config, Logger, Token, TokenFilter } from '@verdaccio/types';\n\nimport { searchOnStorage } from './dir-utils';\nimport { mkdirPromise, writeFilePromise } from './fs';\nimport LocalDriver, { noSuchFile } from './local-fs';\nimport type { LocalStorage } from './pkg-utils';\nimport { loadPrivatePackages } from './pkg-utils';\nimport { _dbGenPath } from './utils';\n\nconst TOKEN_DB_NAME = '.token-db.json';\nconst DB_NAME = process.env.VERDACCIO_STORAGE_NAME ?? fileUtils.Files.DatabaseName;\n\nconst debug = buildDebug('verdaccio:plugin:local-storage');\n\nexport const ERROR_DB_LOCKED =\n 'Database is locked, please check error message printed during startup to prevent data loss';\n\ntype Storage = pluginUtils.Storage<{}>;\n\nclass LocalDatabase extends pluginUtils.Plugin<{}> implements Storage {\n private readonly path: string;\n private readonly logger: Logger;\n public readonly config: Config;\n public readonly storages: Map<string, string>;\n public data: LocalStorage | undefined;\n public locked: boolean;\n public tokenDb: Low<Record<string, Token[]>> | null;\n\n public constructor(config: Config, logger: Logger) {\n // TODO: fix double config\n super(config, { config, logger });\n this.tokenDb = null;\n this.config = config;\n this.logger = logger;\n this.locked = false;\n this.data = undefined;\n debug('config path %o', config.configPath);\n this.path = _dbGenPath(DB_NAME, config);\n this.storages = this._getCustomPackageLocalStorages();\n this.logger.info({ path: this.path }, 'local storage path: @{path}');\n debug('plugin storage path %o', this.path);\n }\n\n public async init(): Promise<void> {\n debug('plugin init');\n this.data = await this.fetchLocalPackages();\n debug('local packages loaded');\n await this._sync();\n }\n\n public async getSecret(): Promise<string> {\n if (typeof this.data === 'undefined') {\n throw Error('no data secret available');\n }\n\n return Promise.resolve(this.data.secret);\n }\n\n public async setSecret(secret: string): Promise<void> {\n if (typeof this.data === 'undefined') {\n throw Error('no data secret available');\n } else {\n this.data.secret = secret;\n }\n\n await this._sync();\n }\n\n public async add(name: string): Promise<void> {\n if (typeof this.data === 'undefined') {\n throw Error('no data secret available');\n }\n\n if (this.data.list.indexOf(name) === -1) {\n this.data.list.push(name);\n debug('the private package %s has been added', name);\n await this._sync();\n } else {\n debug('the private package %s already exist on database', name);\n return Promise.resolve();\n }\n }\n\n /**\n * The field storage could be absolute or relative.\n * If relative, it will be resolved against the config path.\n * If absolute, it will be returned as is.\n **/\n private getStoragePath() {\n const { storage } = this.config;\n if (typeof storage !== 'string') {\n throw new TypeError('storage field is mandatory');\n }\n\n const storagePath = path.isAbsolute(storage)\n ? storage\n : path.normalize(path.join(this.getBaseConfigPath(), storage));\n debug('storage path %o', storagePath);\n return storagePath;\n }\n\n private getBaseConfigPath(): string {\n return path.dirname(this.config.configPath);\n }\n\n /**\n * Filter and only match those values that the query define.\n **/\n public async filterByQuery(results: searchUtils.SearchItemPkg[], query: searchUtils.SearchQuery) {\n // FUTURE: apply new filters, keyword, version, ...\n return results.filter((item: searchUtils.SearchItemPkg) => {\n // Sanitize user input\n const safeText = escapeRegExp(query.text);\n return item?.name?.match(safeText) !== null;\n }) as searchUtils.SearchItemPkg[];\n }\n\n public async getScore(_pkg: searchUtils.SearchItemPkg): Promise<searchUtils.Score> {\n // TODO: there is no particular reason to predefined scores\n // could be improved by using\n return Promise.resolve({\n final: 1,\n detail: {\n maintenance: 0,\n popularity: 1,\n quality: 1,\n },\n });\n }\n\n /**\n *\n * @param query\n * @returns\n */\n public async search(query: searchUtils.SearchQuery): Promise<searchUtils.SearchItem[]> {\n debug('search query to %o', query.text);\n const results: searchUtils.SearchItem[] = [];\n const storagePath = this.getStoragePath();\n const localResults = await searchOnStorage(storagePath, this.storages);\n const packagesOnStorage = await this.filterByQuery(localResults, query);\n debug('packages found %o', packagesOnStorage.length);\n for (const storage of packagesOnStorage) {\n // check if package is listed on the cache private database\n const isPrivate = (this.data as LocalStorage).list.includes(storage.name);\n const score = await this.getScore(storage);\n results.push({\n package: storage,\n verdaccioPrivate: isPrivate,\n verdaccioPkgCached: !isPrivate,\n score,\n });\n }\n return results;\n }\n\n public async remove(name: string): Promise<void> {\n try {\n if (typeof this.data === 'undefined') {\n throw Error('no data secret available');\n }\n\n const data = await this.get();\n\n const pkgName = data.indexOf(name);\n if (pkgName !== -1) {\n this.data.list.splice(pkgName, 1);\n debug('package %o has been removed', name);\n }\n await this._sync();\n } catch (err) {\n this.logger.error({ err }, 'remove the private package has failed @{err}');\n throw errorUtils.getInternalError('error remove private package');\n }\n }\n\n public async get(): Promise<any> {\n if (typeof this.data === 'undefined') {\n throw Error('no data secret available');\n }\n\n const { list } = this.data;\n const totalItems = list?.length;\n debug('get full list of packages (%o) has been fetched', totalItems);\n return Promise.resolve(list);\n }\n\n public getPackageStorage(packageName: string): pluginUtils.StorageHandler {\n const packageAccess = authUtils.getMatchedPackagesSpec(packageName, this.config.packages);\n\n const packagePath: string = this._getLocalStoragePath(\n packageAccess ? packageAccess.storage : undefined\n );\n debug('storage path selected %o', packagePath);\n if (typeof packagePath !== 'string') {\n debug('the package %o has no storage defined ', packageName);\n throw errorUtils.getInternalError('storage not found or implemented');\n }\n\n const packageStoragePath = path.resolve(path.join(packagePath, packageName));\n\n // Verify that the file path is under the storage root directory\n // to avoid \"uncontrolled data used in path expression\" issues\n const storageRoot = path.resolve(this.getStoragePath());\n\n debug('packageStoragePath %o, storageRoot %o', packageStoragePath, storageRoot);\n\n if (\n !packageStoragePath.startsWith(storageRoot + path.sep) &&\n packageStoragePath !== storageRoot\n ) {\n throw errorUtils.getInternalError(\n 'package-specific path is not under the configured storage directory'\n );\n }\n\n debug('storage absolute path %o', packageStoragePath);\n\n return new LocalDriver(packageStoragePath, this.logger);\n }\n\n public async clean(): Promise<void> {\n await this._sync();\n }\n\n private _getCustomPackageLocalStorages(): Map<string, string> {\n const storages = new Map<string, string>();\n const { packages } = this.config;\n\n if (packages) {\n Object.keys(packages || {}).map((pkg) => {\n const { storage } = packages[pkg];\n if (typeof storage === 'string') {\n const storagePath = path.join(this.getStoragePath(), storage);\n debug('add custom storage for %s on %s', storage, storagePath);\n storages.set(storage, storagePath);\n }\n });\n }\n\n return storages;\n }\n\n private async _sync(): Promise<null> {\n debug('sync database started');\n\n if (this.locked) {\n this.logger.error(ERROR_DB_LOCKED);\n throw new Error(ERROR_DB_LOCKED);\n }\n // Uses sync to prevent ugly race condition\n try {\n const folderName = path.dirname(this.path);\n debug('creating folder %o', folderName);\n await mkdirPromise(folderName, { recursive: true });\n debug('creating folder %o created succeed', folderName);\n } catch (err) {\n this.logger.error({ err }, 'sync create folder has failed with error: @{err}');\n throw err;\n }\n\n try {\n await writeFilePromise(this.path, JSON.stringify(this.data));\n debug('sync write succeeded');\n\n return null;\n } catch (err: any) {\n this.logger.error({ err }, 'sync database file failed: @{err}');\n throw err;\n }\n }\n\n private _getLocalStoragePath(storage: string | void): string {\n const globalConfigStorage = this.getStoragePath();\n if (isNil(globalConfigStorage)) {\n this.logger.error('property storage in config.yaml is required for using this plugin');\n throw new Error('property storage in config.yaml is required for using this plugin');\n } else {\n if (typeof storage === 'string') {\n return path.join(globalConfigStorage as string, storage as string);\n }\n\n return globalConfigStorage as string;\n }\n }\n\n private async fetchLocalPackages(): Promise<LocalStorage> {\n try {\n return await loadPrivatePackages(this.path, this.logger);\n } catch (err: any) {\n // readFileSync is platform specific, macOS, Linux and Windows thrown an error\n // Only recreate if file not found to prevent data loss\n this.logger.info(\n { path: this.path },\n 'no private database found, recreating new one on @{path}'\n );\n if (err.code !== noSuchFile) {\n this.locked = true;\n this.logger.error(\n 'Failed to read package database file, please check the error printed below:\\n',\n `File Path: ${this.path}\\n\\n ${err.message}`\n );\n }\n // if no database is found we set empty placeholders\n return { list: [], secret: '' };\n }\n }\n\n private async getTokenDb(): Promise<Low<Record<string, Token[]>>> {\n if (!this.tokenDb) {\n debug('token database is not defined');\n let adapter;\n if (process.env.NODE_ENV === 'test') {\n debug('token memory adapter');\n adapter = new Memory<Record<string, Token[]>>();\n } else {\n debug('token async adapter');\n const pathDb = _dbGenPath(TOKEN_DB_NAME, this.config);\n adapter = new JSONFile<Record<string, Token[]>>(pathDb);\n }\n debug('token db generated');\n this.tokenDb = new Low<Record<string, Token[]>>(adapter, {});\n await this.tokenDb.read();\n }\n\n return this.tokenDb;\n }\n\n public async saveToken(token: Token): Promise<void> {\n debug('token key %o', token.key);\n const db = await this.getTokenDb();\n const userData = db.data[token.user];\n debug('user data %o', userData);\n if (isNil(userData)) {\n db.data[token.user] = [token];\n debug('token user %o new database', token.user);\n } else {\n db.data[token.user].push(token);\n }\n await db.write();\n debug('data %o', db.data);\n debug('token saved %o', token.user);\n }\n\n public async deleteToken(user: string, tokenKey: string): Promise<void> {\n const db = await this.getTokenDb();\n const userTokens = db.data[user];\n if (isNil(userTokens)) {\n throw new Error('user not found');\n }\n debug('tokens %o - %o', userTokens, userTokens.length);\n db.data[user] = userTokens.filter(({ key }) => {\n debug('key %o', key);\n return key !== tokenKey;\n });\n await db.write();\n debug('removed tokens key %o', tokenKey);\n }\n\n public async readTokens(filter: TokenFilter): Promise<Token[]> {\n const { user } = filter;\n debug('read tokens with %o', user);\n const db = await this.getTokenDb();\n const tokens = db.data[user];\n return tokens || [];\n }\n}\n\nexport default LocalDatabase;\n"],"mappings":"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"}
@@ -1,7 +1,6 @@
1
- const require_runtime = require("./_virtual/_rolldown/runtime.js");
1
+ require("./_virtual/_rolldown/runtime.js");
2
2
  const require_fs = require("./fs.js");
3
- let lodash = require("lodash");
4
- lodash = require_runtime.__toESM(lodash);
3
+ let lodash_es = require("lodash-es");
5
4
  //#region src/pkg-utils.ts
6
5
  async function loadPrivatePackages(path, logger) {
7
6
  const emptyDatabase = {
@@ -9,7 +8,7 @@ async function loadPrivatePackages(path, logger) {
9
8
  secret: ""
10
9
  };
11
10
  const data = await require_fs.readFilePromise(path);
12
- if (lodash.default.isNil(data)) return emptyDatabase;
11
+ if ((0, lodash_es.isNil)(data)) return emptyDatabase;
13
12
  let db;
14
13
  try {
15
14
  db = JSON.parse(data);
@@ -20,7 +19,7 @@ async function loadPrivatePackages(path, logger) {
20
19
  }, `Package database file corrupted (invalid JSON) @{err.message}, please check the error printed below.File Path: @{path}`);
21
20
  throw Error("Package database file corrupted (invalid JSON)");
22
21
  }
23
- if (lodash.default.isEmpty(db)) return emptyDatabase;
22
+ if ((0, lodash_es.isEmpty)(db)) return emptyDatabase;
24
23
  return db;
25
24
  }
26
25
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"pkg-utils.js","names":[],"sources":["../src/pkg-utils.ts"],"sourcesContent":["import _ from 'lodash';\n\nimport type { Logger, StorageList } from '@verdaccio/types';\n\nimport { readFilePromise } from './fs';\n\nexport type LocalStorage = {\n list: any;\n secret: string;\n};\n\nexport async function loadPrivatePackages(path: string, logger: Logger): Promise<LocalStorage> {\n const list: StorageList = [];\n const emptyDatabase = { list, secret: '' };\n const data = await readFilePromise(path);\n if (_.isNil(data)) {\n // readFilePromise is platform specific, FreeBSD might return null\n return emptyDatabase;\n }\n\n let db;\n try {\n db = JSON.parse(data);\n } catch (err: any) {\n logger.error(\n {\n err,\n path,\n },\n `Package database file corrupted (invalid JSON) @{err.message}, please check the error printed below.File Path: @{path}`\n );\n throw Error('Package database file corrupted (invalid JSON)');\n }\n\n if (_.isEmpty(db)) {\n return emptyDatabase;\n }\n\n return db;\n}\n"],"mappings":";;;;;AAWA,eAAsB,oBAAoB,MAAc,QAAuC;CAE7F,MAAM,gBAAgB;EAAE,MADE,EAAE;EACE,QAAQ;EAAI;CAC1C,MAAM,OAAO,MAAM,WAAA,gBAAgB,KAAK;AACxC,KAAI,OAAA,QAAE,MAAM,KAAK,CAEf,QAAO;CAGT,IAAI;AACJ,KAAI;AACF,OAAK,KAAK,MAAM,KAAK;UACd,KAAU;AACjB,SAAO,MACL;GACE;GACA;GACD,EACD,yHACD;AACD,QAAM,MAAM,iDAAiD;;AAG/D,KAAI,OAAA,QAAE,QAAQ,GAAG,CACf,QAAO;AAGT,QAAO"}
1
+ {"version":3,"file":"pkg-utils.js","names":[],"sources":["../src/pkg-utils.ts"],"sourcesContent":["import { isEmpty, isNil } from 'lodash-es';\n\nimport type { Logger, StorageList } from '@verdaccio/types';\n\nimport { readFilePromise } from './fs';\n\nexport type LocalStorage = {\n list: any;\n secret: string;\n};\n\nexport async function loadPrivatePackages(path: string, logger: Logger): Promise<LocalStorage> {\n const list: StorageList = [];\n const emptyDatabase = { list, secret: '' };\n const data = await readFilePromise(path);\n if (isNil(data)) {\n // readFilePromise is platform specific, FreeBSD might return null\n return emptyDatabase;\n }\n\n let db;\n try {\n db = JSON.parse(data);\n } catch (err: any) {\n logger.error(\n {\n err,\n path,\n },\n `Package database file corrupted (invalid JSON) @{err.message}, please check the error printed below.File Path: @{path}`\n );\n throw Error('Package database file corrupted (invalid JSON)');\n }\n\n if (isEmpty(db)) {\n return emptyDatabase;\n }\n\n return db;\n}\n"],"mappings":";;;;AAWA,eAAsB,oBAAoB,MAAc,QAAuC;CAE7F,MAAM,gBAAgB;EAAE,MADE,EAAE;EACE,QAAQ;EAAI;CAC1C,MAAM,OAAO,MAAM,WAAA,gBAAgB,KAAK;AACxC,MAAA,GAAA,UAAA,OAAU,KAAK,CAEb,QAAO;CAGT,IAAI;AACJ,KAAI;AACF,OAAK,KAAK,MAAM,KAAK;UACd,KAAU;AACjB,SAAO,MACL;GACE;GACA;GACD,EACD,yHACD;AACD,QAAM,MAAM,iDAAiD;;AAG/D,MAAA,GAAA,UAAA,SAAY,GAAG,CACb,QAAO;AAGT,QAAO"}
@@ -1,5 +1,5 @@
1
1
  import { readFilePromise } from "./fs.mjs";
2
- import _ from "lodash";
2
+ import { isEmpty, isNil } from "lodash-es";
3
3
  //#region src/pkg-utils.ts
4
4
  async function loadPrivatePackages(path, logger) {
5
5
  const emptyDatabase = {
@@ -7,7 +7,7 @@ async function loadPrivatePackages(path, logger) {
7
7
  secret: ""
8
8
  };
9
9
  const data = await readFilePromise(path);
10
- if (_.isNil(data)) return emptyDatabase;
10
+ if (isNil(data)) return emptyDatabase;
11
11
  let db;
12
12
  try {
13
13
  db = JSON.parse(data);
@@ -18,7 +18,7 @@ async function loadPrivatePackages(path, logger) {
18
18
  }, `Package database file corrupted (invalid JSON) @{err.message}, please check the error printed below.File Path: @{path}`);
19
19
  throw Error("Package database file corrupted (invalid JSON)");
20
20
  }
21
- if (_.isEmpty(db)) return emptyDatabase;
21
+ if (isEmpty(db)) return emptyDatabase;
22
22
  return db;
23
23
  }
24
24
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"pkg-utils.mjs","names":[],"sources":["../src/pkg-utils.ts"],"sourcesContent":["import _ from 'lodash';\n\nimport type { Logger, StorageList } from '@verdaccio/types';\n\nimport { readFilePromise } from './fs';\n\nexport type LocalStorage = {\n list: any;\n secret: string;\n};\n\nexport async function loadPrivatePackages(path: string, logger: Logger): Promise<LocalStorage> {\n const list: StorageList = [];\n const emptyDatabase = { list, secret: '' };\n const data = await readFilePromise(path);\n if (_.isNil(data)) {\n // readFilePromise is platform specific, FreeBSD might return null\n return emptyDatabase;\n }\n\n let db;\n try {\n db = JSON.parse(data);\n } catch (err: any) {\n logger.error(\n {\n err,\n path,\n },\n `Package database file corrupted (invalid JSON) @{err.message}, please check the error printed below.File Path: @{path}`\n );\n throw Error('Package database file corrupted (invalid JSON)');\n }\n\n if (_.isEmpty(db)) {\n return emptyDatabase;\n }\n\n return db;\n}\n"],"mappings":";;;AAWA,eAAsB,oBAAoB,MAAc,QAAuC;CAE7F,MAAM,gBAAgB;EAAE,MADE,EAAE;EACE,QAAQ;EAAI;CAC1C,MAAM,OAAO,MAAM,gBAAgB,KAAK;AACxC,KAAI,EAAE,MAAM,KAAK,CAEf,QAAO;CAGT,IAAI;AACJ,KAAI;AACF,OAAK,KAAK,MAAM,KAAK;UACd,KAAU;AACjB,SAAO,MACL;GACE;GACA;GACD,EACD,yHACD;AACD,QAAM,MAAM,iDAAiD;;AAG/D,KAAI,EAAE,QAAQ,GAAG,CACf,QAAO;AAGT,QAAO"}
1
+ {"version":3,"file":"pkg-utils.mjs","names":[],"sources":["../src/pkg-utils.ts"],"sourcesContent":["import { isEmpty, isNil } from 'lodash-es';\n\nimport type { Logger, StorageList } from '@verdaccio/types';\n\nimport { readFilePromise } from './fs';\n\nexport type LocalStorage = {\n list: any;\n secret: string;\n};\n\nexport async function loadPrivatePackages(path: string, logger: Logger): Promise<LocalStorage> {\n const list: StorageList = [];\n const emptyDatabase = { list, secret: '' };\n const data = await readFilePromise(path);\n if (isNil(data)) {\n // readFilePromise is platform specific, FreeBSD might return null\n return emptyDatabase;\n }\n\n let db;\n try {\n db = JSON.parse(data);\n } catch (err: any) {\n logger.error(\n {\n err,\n path,\n },\n `Package database file corrupted (invalid JSON) @{err.message}, please check the error printed below.File Path: @{path}`\n );\n throw Error('Package database file corrupted (invalid JSON)');\n }\n\n if (isEmpty(db)) {\n return emptyDatabase;\n }\n\n return db;\n}\n"],"mappings":";;;AAWA,eAAsB,oBAAoB,MAAc,QAAuC;CAE7F,MAAM,gBAAgB;EAAE,MADE,EAAE;EACE,QAAQ;EAAI;CAC1C,MAAM,OAAO,MAAM,gBAAgB,KAAK;AACxC,KAAI,MAAM,KAAK,CAEb,QAAO;CAGT,IAAI;AACJ,KAAI;AACF,OAAK,KAAK,MAAM,KAAK;UACd,KAAU;AACjB,SAAO,MACL;GACE;GACA;GACD,EACD,yHACD;AACD,QAAM,MAAM,iDAAiD;;AAG/D,KAAI,QAAQ,GAAG,CACb,QAAO;AAGT,QAAO"}
package/build/utils.js CHANGED
@@ -1,6 +1,5 @@
1
1
  const require_runtime = require("./_virtual/_rolldown/runtime.js");
2
- let lodash = require("lodash");
3
- lodash = require_runtime.__toESM(lodash);
2
+ require("lodash-es");
4
3
  let node_path = require("node:path");
5
4
  node_path = require_runtime.__toESM(node_path);
6
5
  let node_fs = require("node:fs");
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","names":[],"sources":["../src/utils.ts"],"sourcesContent":["import _ from 'lodash';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport type { Config } from '@verdaccio/types';\n\nexport function getFileStats(packagePath: string): Promise<fs.Stats> {\n return new Promise((resolve, reject): void => {\n fs.stat(packagePath, (err, stats) => {\n if (_.isNil(err) === false) {\n return reject(err);\n }\n resolve(stats);\n });\n });\n}\n\nexport function readDirectory(packagePath: string): Promise<string[]> {\n return new Promise((resolve, reject): void => {\n fs.readdir(packagePath, (err, scopedPackages) => {\n if (_.isNil(err) === false) {\n return reject(err);\n }\n\n resolve(scopedPackages);\n });\n });\n}\n\nfunction hasScope(file: string): boolean {\n return file.match(/^@/) !== null;\n}\n\n/* eslint-disable no-async-promise-executor */\nexport async function findPackages(\n storagePath: string,\n validationHandler: Function\n): Promise<{ name: string; path: string }[]> {\n const listPackages: { name: string; path: string }[] = [];\n return new Promise(async (resolve, reject) => {\n try {\n const scopePath = path.resolve(storagePath);\n const storageDirs = await readDirectory(scopePath);\n for (const directory of storageDirs) {\n // we check whether has 2nd level\n if (hasScope(directory)) {\n // we read directory multiple\n const scopeDirectory = path.resolve(storagePath, directory);\n const scopedPackages = await readDirectory(scopeDirectory);\n for (const scopedDirName of scopedPackages) {\n if (validationHandler(scopedDirName)) {\n // we build the complete scope path\n const scopePath = path.resolve(storagePath, directory, scopedDirName);\n // list content of such directory\n listPackages.push({\n name: `${directory}/${scopedDirName}`,\n path: scopePath,\n });\n }\n }\n } else {\n // otherwise we read as single level\n if (validationHandler(directory)) {\n const scopePath = path.resolve(storagePath, directory);\n listPackages.push({\n name: directory,\n path: scopePath,\n });\n }\n }\n }\n } catch (error: any) {\n reject(error);\n }\n\n resolve(listPackages);\n });\n}\n\nexport function _dbGenPath(dbName: string, config: Pick<Config, 'configPath' | 'storage'>): string {\n return path.join(\n path.resolve(path.dirname(config.configPath || ''), config.storage as string, dbName)\n );\n}\n"],"mappings":";;;;;;;;AA+EA,SAAgB,WAAW,QAAgB,QAAwD;AACjG,QAAO,UAAA,QAAK,KACV,UAAA,QAAK,QAAQ,UAAA,QAAK,QAAQ,OAAO,cAAc,GAAG,EAAE,OAAO,SAAmB,OAAO,CACtF"}
1
+ {"version":3,"file":"utils.js","names":[],"sources":["../src/utils.ts"],"sourcesContent":["import { isNil } from 'lodash-es';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport type { Config } from '@verdaccio/types';\n\nexport function getFileStats(packagePath: string): Promise<fs.Stats> {\n return new Promise((resolve, reject): void => {\n fs.stat(packagePath, (err, stats) => {\n if (isNil(err) === false) {\n return reject(err);\n }\n resolve(stats);\n });\n });\n}\n\nexport function readDirectory(packagePath: string): Promise<string[]> {\n return new Promise((resolve, reject): void => {\n fs.readdir(packagePath, (err, scopedPackages) => {\n if (isNil(err) === false) {\n return reject(err);\n }\n\n resolve(scopedPackages);\n });\n });\n}\n\nfunction hasScope(file: string): boolean {\n return file.match(/^@/) !== null;\n}\n\n/* eslint-disable no-async-promise-executor */\nexport async function findPackages(\n storagePath: string,\n validationHandler: Function\n): Promise<{ name: string; path: string }[]> {\n const listPackages: { name: string; path: string }[] = [];\n return new Promise(async (resolve, reject) => {\n try {\n const scopePath = path.resolve(storagePath);\n const storageDirs = await readDirectory(scopePath);\n for (const directory of storageDirs) {\n // we check whether has 2nd level\n if (hasScope(directory)) {\n // we read directory multiple\n const scopeDirectory = path.resolve(storagePath, directory);\n const scopedPackages = await readDirectory(scopeDirectory);\n for (const scopedDirName of scopedPackages) {\n if (validationHandler(scopedDirName)) {\n // we build the complete scope path\n const scopePath = path.resolve(storagePath, directory, scopedDirName);\n // list content of such directory\n listPackages.push({\n name: `${directory}/${scopedDirName}`,\n path: scopePath,\n });\n }\n }\n } else {\n // otherwise we read as single level\n if (validationHandler(directory)) {\n const scopePath = path.resolve(storagePath, directory);\n listPackages.push({\n name: directory,\n path: scopePath,\n });\n }\n }\n }\n } catch (error: any) {\n reject(error);\n }\n\n resolve(listPackages);\n });\n}\n\nexport function _dbGenPath(dbName: string, config: Pick<Config, 'configPath' | 'storage'>): string {\n return path.join(\n path.resolve(path.dirname(config.configPath || ''), config.storage as string, dbName)\n );\n}\n"],"mappings":";;;;;;;AA+EA,SAAgB,WAAW,QAAgB,QAAwD;AACjG,QAAO,UAAA,QAAK,KACV,UAAA,QAAK,QAAQ,UAAA,QAAK,QAAQ,OAAO,cAAc,GAAG,EAAE,OAAO,SAAmB,OAAO,CACtF"}
package/build/utils.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import "lodash";
1
+ import "lodash-es";
2
2
  import path from "node:path";
3
3
  import "node:fs";
4
4
  //#region src/utils.ts
@@ -1 +1 @@
1
- {"version":3,"file":"utils.mjs","names":[],"sources":["../src/utils.ts"],"sourcesContent":["import _ from 'lodash';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport type { Config } from '@verdaccio/types';\n\nexport function getFileStats(packagePath: string): Promise<fs.Stats> {\n return new Promise((resolve, reject): void => {\n fs.stat(packagePath, (err, stats) => {\n if (_.isNil(err) === false) {\n return reject(err);\n }\n resolve(stats);\n });\n });\n}\n\nexport function readDirectory(packagePath: string): Promise<string[]> {\n return new Promise((resolve, reject): void => {\n fs.readdir(packagePath, (err, scopedPackages) => {\n if (_.isNil(err) === false) {\n return reject(err);\n }\n\n resolve(scopedPackages);\n });\n });\n}\n\nfunction hasScope(file: string): boolean {\n return file.match(/^@/) !== null;\n}\n\n/* eslint-disable no-async-promise-executor */\nexport async function findPackages(\n storagePath: string,\n validationHandler: Function\n): Promise<{ name: string; path: string }[]> {\n const listPackages: { name: string; path: string }[] = [];\n return new Promise(async (resolve, reject) => {\n try {\n const scopePath = path.resolve(storagePath);\n const storageDirs = await readDirectory(scopePath);\n for (const directory of storageDirs) {\n // we check whether has 2nd level\n if (hasScope(directory)) {\n // we read directory multiple\n const scopeDirectory = path.resolve(storagePath, directory);\n const scopedPackages = await readDirectory(scopeDirectory);\n for (const scopedDirName of scopedPackages) {\n if (validationHandler(scopedDirName)) {\n // we build the complete scope path\n const scopePath = path.resolve(storagePath, directory, scopedDirName);\n // list content of such directory\n listPackages.push({\n name: `${directory}/${scopedDirName}`,\n path: scopePath,\n });\n }\n }\n } else {\n // otherwise we read as single level\n if (validationHandler(directory)) {\n const scopePath = path.resolve(storagePath, directory);\n listPackages.push({\n name: directory,\n path: scopePath,\n });\n }\n }\n }\n } catch (error: any) {\n reject(error);\n }\n\n resolve(listPackages);\n });\n}\n\nexport function _dbGenPath(dbName: string, config: Pick<Config, 'configPath' | 'storage'>): string {\n return path.join(\n path.resolve(path.dirname(config.configPath || ''), config.storage as string, dbName)\n );\n}\n"],"mappings":";;;;AA+EA,SAAgB,WAAW,QAAgB,QAAwD;AACjG,QAAO,KAAK,KACV,KAAK,QAAQ,KAAK,QAAQ,OAAO,cAAc,GAAG,EAAE,OAAO,SAAmB,OAAO,CACtF"}
1
+ {"version":3,"file":"utils.mjs","names":[],"sources":["../src/utils.ts"],"sourcesContent":["import { isNil } from 'lodash-es';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport type { Config } from '@verdaccio/types';\n\nexport function getFileStats(packagePath: string): Promise<fs.Stats> {\n return new Promise((resolve, reject): void => {\n fs.stat(packagePath, (err, stats) => {\n if (isNil(err) === false) {\n return reject(err);\n }\n resolve(stats);\n });\n });\n}\n\nexport function readDirectory(packagePath: string): Promise<string[]> {\n return new Promise((resolve, reject): void => {\n fs.readdir(packagePath, (err, scopedPackages) => {\n if (isNil(err) === false) {\n return reject(err);\n }\n\n resolve(scopedPackages);\n });\n });\n}\n\nfunction hasScope(file: string): boolean {\n return file.match(/^@/) !== null;\n}\n\n/* eslint-disable no-async-promise-executor */\nexport async function findPackages(\n storagePath: string,\n validationHandler: Function\n): Promise<{ name: string; path: string }[]> {\n const listPackages: { name: string; path: string }[] = [];\n return new Promise(async (resolve, reject) => {\n try {\n const scopePath = path.resolve(storagePath);\n const storageDirs = await readDirectory(scopePath);\n for (const directory of storageDirs) {\n // we check whether has 2nd level\n if (hasScope(directory)) {\n // we read directory multiple\n const scopeDirectory = path.resolve(storagePath, directory);\n const scopedPackages = await readDirectory(scopeDirectory);\n for (const scopedDirName of scopedPackages) {\n if (validationHandler(scopedDirName)) {\n // we build the complete scope path\n const scopePath = path.resolve(storagePath, directory, scopedDirName);\n // list content of such directory\n listPackages.push({\n name: `${directory}/${scopedDirName}`,\n path: scopePath,\n });\n }\n }\n } else {\n // otherwise we read as single level\n if (validationHandler(directory)) {\n const scopePath = path.resolve(storagePath, directory);\n listPackages.push({\n name: directory,\n path: scopePath,\n });\n }\n }\n }\n } catch (error: any) {\n reject(error);\n }\n\n resolve(listPackages);\n });\n}\n\nexport function _dbGenPath(dbName: string, config: Pick<Config, 'configPath' | 'storage'>): string {\n return path.join(\n path.resolve(path.dirname(config.configPath || ''), config.storage as string, dbName)\n );\n}\n"],"mappings":";;;;AA+EA,SAAgB,WAAW,QAAgB,QAAwD;AACjG,QAAO,KAAK,KACV,KAAK,QAAQ,KAAK,QAAQ,OAAO,cAAc,GAAG,EAAE,OAAO,SAAmB,OAAO,CACtF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@verdaccio/local-storage",
3
- "version": "14.0.0-next-9.6",
3
+ "version": "14.0.0-next-9.8",
4
4
  "description": "Verdaccio Local Storage Plugin",
5
5
  "keywords": [
6
6
  "private",
@@ -36,18 +36,18 @@
36
36
  "node": ">=24"
37
37
  },
38
38
  "dependencies": {
39
- "@verdaccio/core": "9.0.0-next-9.6",
39
+ "@verdaccio/core": "9.0.0-next-9.8",
40
40
  "@verdaccio/file-locking": "14.0.0-next-9.2",
41
- "@verdaccio/utils": "9.0.0-next-9.6",
42
41
  "debug": "4.4.3",
43
42
  "globby": "11.1.0",
44
- "lodash": "4.17.23",
45
- "lowdb": "1.0.0",
43
+ "lodash-es": "4.17.23",
44
+ "lowdb": "7.0.1",
46
45
  "sanitize-filename": "1.6.3"
47
46
  },
48
47
  "devDependencies": {
49
- "@verdaccio/logger": "9.0.0-next-9.6",
50
- "@verdaccio/types": "14.0.0-next-9.3",
48
+ "@verdaccio/logger": "9.0.0-next-9.8",
49
+ "@verdaccio/types": "14.0.0-next-9.4",
50
+ "@types/lodash-es": "4.17.12",
51
51
  "vitest": "4.1.0"
52
52
  },
53
53
  "funding": {