Skip to main content

Storage Plugin

What's a Storage Plugin?​

Verdaccio by default uses a file system storage plugin local-storage. The default storage can be easily replaced, either using a community plugin or creating one by your own.

API​

Storage is the one plugin type whose contract changed: it moved from callbacks to promises. Which one you implement depends on the Verdaccio you target.

VerdaccioNative contractA callback plugin
6.xcallbacksworks — it is the native one
7.xpromisesworks, wrapped by a compatibility adapter (since 7.0.0-next-7.28)
9.xpromisesnot supported

New plugins should implement the promise contract. It is the only one 9.x accepts, and 7.x runs it natively rather than through the adapter.

Two interfaces, both from pluginUtils in @verdaccio/core. Storage handles the local database of private packages:

import { pluginUtils } from '@verdaccio/core';

interface Storage<PluginConfig> extends Plugin<PluginConfig> {
add(packageName: string): Promise<void>;
remove(packageName: string): Promise<void>;
get(): Promise<string[]>;
init(): Promise<void>;
getSecret(): Promise<string>;
setSecret(secret: string): Promise<any>;
getPackageStorage(packageName: string): StorageHandler;
search(query: searchUtils.SearchQuery): Promise<searchUtils.SearchItem[]>;
saveToken(token: Token): Promise<any>;
deleteToken(user: string, tokenKey: string): Promise<any>;
readTokens(filter: TokenFilter): Promise<Token[]>;
}

StorageHandler is returned by getPackageStorage and does the I/O for one package's manifest and tarballs:

interface StorageHandler {
logger: Logger;
createPackage(packageName: string, manifest: Manifest): Promise<void>;
readPackage(packageName: string): Promise<Manifest>;
savePackage(packageName: string, manifest: Manifest): Promise<void>;
deletePackage(fileName: string): Promise<void>;
removePackage(packageName: string): Promise<void>;
updatePackage(
packageName: string,
handleUpdate: (manifest: Manifest) => Promise<Manifest>
): Promise<Manifest>;
readTarball(fileName: string, { signal }: { signal: AbortSignal }): Promise<Readable>;
writeTarball(fileName: string, { signal }: { signal: AbortSignal }): Promise<Writable>;
hasTarball(fileName: string): Promise<boolean>;
hasPackage(packageName: string): Promise<boolean>;
}

Note that readTarball and writeTarball return real Node streams and receive an AbortSignal: a plugin is expected to stop the transfer when the client disconnects.

How Verdaccio tells them apart​

7.x decides by arity, not by configuration: a get that takes an argument and an add that takes two are read as the callback contract, and the plugin is wrapped. There is nothing to declare — but it also means a promise-based get() must take no parameters.

Generate a storage plugin​

Run yo verdaccio-plugin and pick storage when asked for the plugin type; the plugin generator page covers installation and the prompts. The scaffold implements the promise contract against the current @verdaccio/core.