{"id":13822994,"url":"https://github.com/davideicardi/live-plugin-manager","last_synced_at":"2025-04-12T16:35:57.582Z","repository":{"id":19930732,"uuid":"87232028","full_name":"davideicardi/live-plugin-manager","owner":"davideicardi","description":"Plugin manager and installer for Node.JS","archived":false,"fork":false,"pushed_at":"2024-06-11T21:27:58.000Z","size":605,"stargazers_count":244,"open_issues_count":37,"forks_count":44,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-04-03T15:12:43.335Z","etag":null,"topics":["extensions-manager","node-js","npm","plugin-manager","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/davideicardi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["davideicardi"]}},"created_at":"2017-04-04T20:31:10.000Z","updated_at":"2025-02-26T07:19:51.000Z","dependencies_parsed_at":"2024-12-13T09:02:05.636Z","dependency_job_id":"a6452fdc-133d-40da-8b82-c4f120597a27","html_url":"https://github.com/davideicardi/live-plugin-manager","commit_stats":{"total_commits":138,"total_committers":7,"mean_commits":"19.714285714285715","dds":"0.050724637681159424","last_synced_commit":"79d057ff95af8d466ce363e5a95a5c00c2d7dbd6"},"previous_names":[],"tags_count":46,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davideicardi%2Flive-plugin-manager","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davideicardi%2Flive-plugin-manager/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davideicardi%2Flive-plugin-manager/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davideicardi%2Flive-plugin-manager/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/davideicardi","download_url":"https://codeload.github.com/davideicardi/live-plugin-manager/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248597209,"owners_count":21130833,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["extensions-manager","node-js","npm","plugin-manager","typescript"],"created_at":"2024-08-04T08:02:28.197Z","updated_at":"2025-04-12T16:35:57.562Z","avatar_url":"https://github.com/davideicardi.png","language":"TypeScript","funding_links":["https://github.com/sponsors/davideicardi"],"categories":["TypeScript"],"sub_categories":[],"readme":"# live-plugin-manager\n\n[![npm version](https://badge.fury.io/js/live-plugin-manager.svg)](https://www.npmjs.com/package/live-plugin-manager)\n[![Build Status](https://travis-ci.org/davideicardi/live-plugin-manager.svg?branch=master)](https://travis-ci.org/davideicardi/live-plugin-manager)\n\n`live-plugin-manager` is a Node.js module that allows you to \ninstall, uninstall and load any node package at runtime from npm registry.\n\nMy main goal is to allow any application to be easily extensible by \ninstalling and running any node package at runtime, without deployments or server customization. You can basically allow your users to decide what plugin/extension to install.\n\nMain features are:\n\n- Install and run Node packages at runtime (no deployment)\n- Install from npm registry (private or public)\n- Install from filesystem\n- Install from github (branch, commit or tag)\n- Update/uninstall packages\n- Any Node.js packages can be installed\n  - No special configuration is required\n- Installed packages can have dependencies \n  - dependencies are automatically installed\n  - when updating a dependencies all dependents are reloaded\n- Support for concurrency operation on filesystem (cloud/web farm scenario where file system is shared)\n  - A filesystem lock is used to prevent multiple instances to work on the same filesystem in the same moment\n- Each package run in an semi isolated environment (VM sandbox)\n- Set different environment variables for each package\n- Implemented in TypeScript\n- Fully tested (mocha tests)\n\nThere are some **known limitations**, see section at the end.\n\n## Installation\n\n```\nnpm install live-plugin-manager\n```\n\n## Usage\n\n```js\nimport {PluginManager} from \"live-plugin-manager\";\n\nconst manager = new PluginManager();\n\nasync function run() {\n  await manager.install(\"moment\");\n\n  const moment = manager.require(\"moment\");\n  console.log(moment().format());\n\n  await manager.uninstall(\"moment\");\n}\n\nrun();  \n```\n\nIn the above code I install `moment` package at runtime, load and execute it.\n\nPlugins are installed inside the directory specified in the `PluginManager` constructor or in the `plugin_packages` directory if not specified.\n\nEach time your application start you should reinstall any packages that you need; already downloaded packages are not automatically installed, but installation is faster because no new file is downloaded. Typically I suggest to put the list of the installed packages in a database or any other central repository.\n\nHere another more complex scenario where I install `express` with all it's dependencies, just to demonstrate how many possibilities you can have:\n\n```js\n\nimport {PluginManager} from \"live-plugin-manager\";\n\nconst manager = new PluginManager();\n\nasync function run() {\n  await manager.install(\"express\");\n  const express = manager.require(\"express\");\n\n  const app = express();\n  app.get(\"/\", function(req: any, res: any) {\n    res.send(\"Hello World!\");\n  });\n\n  const server = app.listen(3000, function() {\n  });\n}\n\nrun();\n```\n\n\n## Load plugins\n\n`live-plugin-manager` doesn't have any special code or convention to load plugins.\nWhen you require a plugin it just load the main file (taken from `package.json`) and execute it, exactly like standard node.js `require`.\nOften when working with plugins you need some extension point or convention to actually integrate your plugins inside your host application. Here are some possible solutions:\n\n- [c9/architect](https://github.com/c9/architect)\n- [easeway/js-plugins](https://github.com/easeway/js-plugins)\n\nAnother solution is to load your plugins inside a Dependency Injection container. \n\nI'm working also on [shelf-dependency](https://www.npmjs.com/package/shelf-dependency), a simple dependency injection/inversion of control container that can be used to load plugins.\n\n## Samples\n\n- See `./samples` and `./test` directories\n- Sample of an extensible web application: [lpm-admin](https://github.com/davideicardi/lpm-admin)\n\n## Reference\n\n### PluginManager.constructor(options?: Partial\\\u003cPluginManagerOptions\\\u003e)\n\nCreate a new instance of `PluginManager`. Takes an optional `options` parameter with the following properties:\n\n- `cwd`: current working directory (default to `process.cwd()`)\n- `pluginsPath`: plugins installation directory (default to `.\\plugin_packages`, see `cwd`). Directory is created if not exists\n- `versionsPath`: directory containing the individual versions of the plugins (default to `./plugin_packages/.versions`). Directory is created if not exists\n- `npmRegistryUrl`: npm registry to use (default to https://registry.npmjs.org)\n- `npmRegistryConfig`: npm registry configuration see [npm-registry-client config](https://github.com/npm/npm-registry-client)\n- `ignoredDependencies`: array of string or RegExp with the list of dependencies to ignore, default to `@types/*`\n- `staticDependencies`: object with an optional list of static dependencies that can be used to force a dependencies to be ignored (not installed when a plugin depend on it) and loaded from this list\n- `githubAuthentication`: Github authentication configuration (optional). See `installFromGithub` or [Github api authentication](https://github.com/octokit/node-github#authentication) for more info.\n- `lockWait`: A number of milliseconds to wait for locks when installing modules to expire before giving up. (default 2 min)\n- `lockStale`: A number of milliseconds before installations locks are considered to have expired. (default 3 min)\n\n### pluginManager.install(name: string, version?: string): Promise\\\u003cIPluginInfo\\\u003e\n\nInstall the specified package from npm registry or directly from github repository. \n`version` parameter can be a version like `1.0.3` or a github repository in the format `owner/repository_name#ref` (like `expressjs/express#351396f`).\nIf a version is specified package is downloaded from NPM.\n\nDependencies are automatically installed (devDependencies are ignored).\n\n### pluginManager.installFromNpm(name: string, version = \"latest\"): Promise\\\u003cIPluginInfo\\\u003e\n\nInstall the specified package from npm registry. Dependencies are automatically installed (devDependencies are ignored).\nBy default is the package is already available in the download folder then and version is compatible then it is not requested again from npm. Change this behavior by setting `npmInstallMode: \"noCache\"` options.\n\nTo setup authentication for private npm registry use:\n\n```js\nconst manager = new PluginManager({\n  npmRegistryUrl: \"http://your-private-registry\",\n  npmRegistryConfig: {\n    auth: {\n      token: \"your-token\"\n    }\n  }\n});\n```\n\n\n### pluginManager.installFromGithub(repository: string): Promise\\\u003cIPluginInfo\\\u003e\n\nInstall the specified package from github. `repository` is specified in the format `owner/repository_name#ref` (like `expressjs/express#351396f`).\nDependencies are automatically installed (devDependencies are ignored).\n\nNote: Github has an API rate limit of 60 calls if not authenticated. To authenticate calls just set the `githubAuthentication` property in the options:\n\n```js\nmanager = new PluginManager({\n  githubAuthentication: {\n    \"type\": \"basic\",\n    \"username\": \"YOUR_USER\",\n    \"password\": \"YOUR_PERSONAL_TOKEN\"\n  }\n});\n```\n\nSee [Github api authentication](https://github.com/octokit/node-github#authentication) for more info.\n\n### installFromPath(location: string, options: {force: boolean} = {}): Promise\\\u003cIPluginInfo\\\u003e\n\nInstall the specified package from a filesystem location. Dependencies are automatically installed from npm. `node_modules` folder is excluded from source location.\n\n### installFromCode(name: string, code: string, version?: string): Promise\\\u003cIPluginInfo\\\u003e\n\nInstall a package by specifying code directly. If no version is specified it will be always reinstalled.\n\n### uninstall(name: string): Promise\\\u003cvoid\\\u003e\n\nUninstall the package. Dependencies are not uninstalled automatically.\n\n### uninstallAll(): Promise\\\u003cvoid\\\u003e\n\nUninstall all installed packages.\n\n### list(): Promise\\\u003cIPluginInfo[]\\\u003e\n\nGet the list of installed packages.\n\n### require(name: string): any\n\nLoad and get the instance of the plugin. Node.js `require` rules are used to load modules.\nCalling require multiple times gives always the same instance until plugins changes.\n\n### getInfo(name: string): IPluginInfo | undefined\n\nGet information about an installed package.\n\n### runScript(code: string): any\n\nRun the specified Node.js javascript code with the same context of plugins. Script are executed using `vm` as with each plugin.\n\n### async queryPackage(name: string, version?: string): Promise\u003cPackageInfo\u003e\n\nGet package/module info from npm registry or github depending of the version format.\n\n### async queryPackageFromNpm(name: string, version = \"latest\"): Promise\u003cPackageInfo\u003e\n\nGet package/module info from npm registry.\n\n### async queryPackageFromGithub(repository: string): Promise\u003cPackageInfo\u003e\n\nGet package/module info from github registry.\n\n## Security\n\nOften is a bad idea for security to allow installation and execution of any node.js package inside your application.\nWhen installing a package it's code is executed with the same permissions of your host application and can potentially damage your entire server.\nI suggest usually to allow to install only a limited sets of plugins or only allow trusted administrator to install plugins.\n\n## Sandbox\n\nFor advanced scenarios you can customize the sandbox where each plugin run. Sandbox defined NodeJS `global` object.\nA typical scenario is to customize the environment variables `global.process.env`. To customize the sandbox you can set a single\nsandbox for all plugins using `options.sandbox` in the `PluginManager` constructor or set a different sandbox for each plugin, see\n`PluginManager.setSandboxTemplate` and `PluginManager.getSandboxTemplate` functions.\n\n## Under to hood\n\nThis project use the following dependencies to do it's job:\n\n- [vm](https://nodejs.org/api/vm.html): compiling and running plugin code within V8 Virtual Machine contexts\n- [lockfile](https://github.com/npm/lockfile): file system locking to prevent concurrent operations (see below)\n- [tar.gz](https://github.com/alanhoff/node-tar.gz): extract package file\n- [fs-extra](https://github.com/jprichardson/node-fs-extra): file system operations\n- [debug](https://github.com/visionmedia/debug): debug information\n- (removed for now because I have problems with getArchiveLink api, 302 ...) [github](https://www.npmjs.com/package/github)\n- (removed for now because for some dependencies issues) [npm-registry-client](https://github.com/npm/npm-registry-client): npm registry handling\n\nWhile I have tried to mimic the standard Node.js module and package architecture there are some differences.\nFirst of all is the fact that plugins by definition are installed at runtime in contrast with a standard Node.js application where modules are installed before executing the node.js process.\nModules can be loaded one or more times, instead in standard Node.js they are loaded only the first time that you `require` it.\nOnly one version of a plugin can be installed, also for dependencies, while in Node.js multiple version can be installed (each module can have it's own `node_modules`).\n\n### Locking\n\nA file system locking is implemented using [lockfile](https://github.com/npm/lockfile) library to allow multiple app instances to share the file system. \nThis is common in some cloud scenario (for example Azure) where file system is shared between instances.\n\nLocking is used to ensure that only one instance is installing/uninstalling plugins in a given moment.\n\nYou can configure `lockWait` inside constructor to configure lock timeout, and `lockStale` to consider a file lock no more valid after that period.\n\n## Git integration\n\nRemember to git ignore the directory where packages are installed, see `pluginsPath`.\nUsually you should add this to your `.gitignore` file:\n\n```\nplugin_packages\n```\n\n## Known limitations\n\nThere are some known limitations when installing a package:\n\n- `process.on` has been stubbed, so plugins that use these events may not work as expected. \n- No `pre/post-install` scripts are executed (some packages use this scripts to build assets or for platform specific installation, so for this reason some packages are not supported)\n- C/C++ packages (`.node`) are not supported\n- Plugin dependencies can be specified only as NPM dependencies (version number) or github dependencies (owner/repo), url or other kind of dependencies are not supported\n\nIf you find other problems please open an issue.\n\n## Development\n\nCompile typescript using:\n\n```\nnpm run src-build\n```\n\nRun tests using:\n\n```\nnpm run test\n```\n\nDue to some github rate limits, github related tests can fail if you don't specify a github token. To specify it use:\n\n```\ngithub_auth_username=YOURUSER github_auth_token=YOUR_TOKEN npm test\n```\n\nThe token must only have public access permission.\n\n## License (MIT)\n\nMIT License\n\nCopyright (c) 2021 Davide Icardi\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavideicardi%2Flive-plugin-manager","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdavideicardi%2Flive-plugin-manager","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavideicardi%2Flive-plugin-manager/lists"}