{"id":21615827,"url":"https://github.com/valango/verdant","last_synced_at":"2026-05-20T09:09:01.991Z","repository":{"id":57253943,"uuid":"328120038","full_name":"valango/verdant","owner":"valango","description":"Code modules live updater for Continuous Development.","archived":false,"fork":false,"pushed_at":"2021-01-14T07:28:09.000Z","size":20,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"dev","last_synced_at":"2025-10-05T18:54:53.145Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/valango.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-01-09T09:34:13.000Z","updated_at":"2021-01-14T07:28:12.000Z","dependencies_parsed_at":"2022-08-31T09:01:52.121Z","dependency_job_id":null,"html_url":"https://github.com/valango/verdant","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/valango/verdant","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/valango%2Fverdant","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/valango%2Fverdant/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/valango%2Fverdant/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/valango%2Fverdant/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/valango","download_url":"https://codeload.github.com/valango/verdant/tar.gz/refs/heads/dev","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/valango%2Fverdant/sbom","scorecard":{"id":914883,"data":{"date":"2025-08-11","repo":{"name":"github.com/valango/verdant","commit":"1482752a4c4b9377cfd49d57d3796162891854c2"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Code-Review","score":0,"reason":"Found 0/12 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'dev'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-24T20:36:24.566Z","repository_id":57253943,"created_at":"2025-08-24T20:36:24.566Z","updated_at":"2025-08-24T20:36:24.566Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33253161,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-20T04:48:54.280Z","status":"ssl_error","status_checked_at":"2026-05-20T04:48:10.851Z","response_time":356,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":[],"created_at":"2024-11-24T22:12:56.922Z","updated_at":"2026-05-20T09:09:01.972Z","avatar_url":"https://github.com/valango.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# greenium [![Build Status](https://travis-ci.org/valango/verdant.svg?branch=master)](https://travis-ci.org/valango/verdant) [![Code coverage](https://codecov.io/gh/valango/verdant/branch/master/graph/badge.svg)](https://codecov.io/gh/valango/verdant)\n\nThis package makes your Node.js app evergreen by\nbecoming _**Continuous Deployment** capable_.\n\nIt uses a very simple API and has only few limitations for your code.\nThe main goal of writing _this package_ was to avoid the demanding micro-services stuff\nin early stages of application life cycle, while still having _Continuous Deployment_ on\nfrom _the day one_.\n\nUnlike some other similar packages, _`greenium`_ does not modify any of Node.js\noriginal behavior.\n\n* [Usage](#usage)\n* [API](#api)\n* [Concept](#concept)\n* [Restrictions](#restrictions)\n* [Compatibility](#compatibility)\n\n## Usage\nOf course, it should be installed:\n\n```\nyarn add greenium   # npm i -S greenium\n```\n...before it can be used in your code:\n\n```javascript\nconst express = require('express')\nconst app = express()\n// Commented out:   const shopAPI = require('./shop')\nconst verdant = require('greenium')(['./shop']) \nconst shopAPI = verdant.attach(app.locals)\nprocess.on('SIGPIPE', () =\u003e verdant.reload())     //  Admin pressing the \"red button\".\n// The rest of the application code remains unchanged.\n\napp.use('/shop/hello', shopAPI.hello)\n...\n```\n\nIn the above example, a new namespace for an e-shop API is created and every time\nthe app receives the `SIGPIPE`, it re-loads its (hopefully fixed) source code again - w/o breaking anything 😎! \n\nThe bad news is, that instead of having just line #3, you ended up adding _two extra lines_ 😖.\n\n**NOTE:** the _`greenium`_ package _does not_ depend on _express.js_ or on any particular\nprotocol, except the \n[Node.js `module API`](https://nodejs.org/dist/latest-v14.x/docs/api/modules.html#modules_the_module_scope).\n\n## API\n**NOTE:** The _asynchronous_ attach / reload will probably not supported in next releases.\nThe real-world experience has shown that those fancy features create more problems than\nthey can solve.\n\n### Package exports\n   1. _**`Verdant`**_: the object class / constructor - useful for derived classes;\n   1. **_`verdant`_**: the factory function, also the default export.\n   \n### Verdant class\n\n**`Verdant`**`( [ options : object | string[] ] )` constructor.\u003cbr /\u003e\nThe valid _`options`_ object keys are:\n   - **_`async`_** `: boolean `- if set, forces either fully async or fully sync operation;\n   - **_`attacher`_** `: string `- [attach](#attaching) hook name, def: `'attach'`;\n   - **_`detacher`_** `: string `- [detach](#detaching) hook name, def: `'detach'`;\n   - **_`dirPath`_** `: string `- def: _`__dirpath`_ of the parent module;\n   - **_`paths`_** `: string[] `- paths for initial [loadables](#concept);\n   - **_`strict`_** `: boolean `- enables strict error checking (`true` in production mode).\n\nIf the argument is an array of strings, then it is assumed to present the _`paths`_ option.\n\n**`add`**`(path: string) : Verdant` instance method.\u003cbr /\u003e\nLoads the module or package by the path. It resolves the path the same way as _`require()`_\ndoes, except that it uses the _`dirPath`_ option value instead of Node.js `__dirpath`.\nAn alternative way is to use the _`paths`_ option.\n\n**`api`**` : Proxy\u003cObject\u003e` r/o instance property.\u003cbr /\u003e\nExposes the (aggregated) API from modules loaded by this instance. This value\nremains the same.\n\n**`attach`**`([context : object]) : Proxy | Promise\u003cProxy\u003e` instance method.\u003cbr /\u003e\n[Attaches](#attaching) (initializes) all loaded loadables, not initialized yet and\nreturns the API proxy. Returns a promise, if at least one detach hook was asynchronous,\nof if the _`asyncAttach`_ option is on.\u003cbr /\u003e\nThe returned / resolved value is always the api instance property value and does not change.\n\n\u003e Repeated calls with the same context and w/o anything new being loaded in between,\nhave no effect. ❓\n\n**`reload`**`([filter : string|RegExp]) : Promise\u003cVerdant\u003e`\ninstance method.\u003cbr /\u003e\nReloads all matching loadables, detaching their old instances and repeating the recently\napplied attach sequence to each of them.\nThe optional filter argument applies to loadables paths and may be:\n   - a string: must be an exact match or if it contains meta symbols,\n   then it will be turned to a RegExp instance;\n   - a RegExp: only the loadables with matching paths will be processed.\n   \nIf there were no matches, an error will be thrown, if _`strict`_ option is on.\nRepeated calls while reloading in progress, will have no effect.\n\n**`reloadSync`**`([filter : string|RegExp]) : Verdant` instance method.\u003cbr /\u003e\nThe same as above, but will throw an exception, if any of attach hooks returns a promise\nor if the _`asyncAttach`_ option is on.\n\n**`revoke`**`()` instance method.\u003cbr /\u003e\nThis is analogous to Javascript _`Proxy.prototype.revoke()`_, except that it calls\n_detach hooks_ of all loaded modules, before rendering the _Verdant_ instance\nand its exposed API inactive. Call this method before exiting the application.\nCalling this method will interrupt any reloading or attaching in progress.\n\n### Factory function\n**`verdant`**`( dirPath : string [, options : object] ) : Verdant`\u003cbr /\u003e\nis just a wrapper around the constructor call. This is also the default export\nof the _package_.\n\n## Concept\nHere: [life cycle of a loadable module](#life-cycle-of-a-loadable) and \n[what `verdant` does](#what-verdant-does).\n\nAn application may contain a set of modules (_**loadables**_), each providing its\nown _**sub-API**_.\nOften, a _sub-API_ needs a special initialization with a proper context\n(e.g. services or shared namespaces), in order to become functional.\n\nA _`Verdant` instance_ represents an aggregated API from its _loadables_,\nsharing the same _initialization **context**_. For example, common utility functions\nmay need no context at all, or may depend on some command-line options;\nwhereas business logic might need ready-to-use services running, and lower-level\nAPI-s ready to be used.\n\nA _`Verdant` instance_ can fetch a _loadable_ at any time, but it would be unwise\nto do this from HTTP request handler, for example. Because of this, initial loading\nof _loadables_, their initialization and possible further re-loading are separate operations.\n\n### Life cycle of a loadable\n#### Attaching\nIf loadable exports a function, it is called with the _context_ as\nits argument and its return value is used as a _sub-API_. Otherwise, the exported\nobject is a _sub-API_. Next, if the _sub-API_ contains a specific **_attach hook_**\nfunction, it is called with the _context_. The optional _attach hook_\ncan be synchronous or asynchronous function with meaningless return value.\n\n#### Operation\nLeaving few [restrictions](#restrictions) aside, a loadable can do anything it needs to. \n\n#### Detaching\nOn reloading a loadable, the old instance may need to be detached first.\nThis being a case, its _sub-API_ must have a specific **_detach hook_** function.\nThis optional function is called w/o arguments, and it must be synchronous.\nTypically, this function removes event listeners or object references from\nthe outside world, so the instance can be _garbage-collected_. Do not\ntrash any resources needed for servicing a possibly pending asynchronous request, however!\n\nAfter all the requests targeting this instance, are gone\nfrom [Node.js event queue](https://nodejs.dev/learn/the-nodejs-event-loop), \nthe _garbage collector_ should kick in automatically.\n\n### What greenium does\nAfter loading a source code module, _`Verdant`_ instance exposes the module API\nvia its _`api`_ instance property, which is actually a proxy. This way, the rest of\napplication code will not see actual data references - so when the module\nneeds to be reloaded, its old instance can be garbage collected.\n\nThe loadable code may run asynchronous operations, though - but this will not\nspoil the party here. The old instance will be kept in memory, until all its\nasynchronous ops are finished. After this, it will be likely garbage collected.\nEven if this fails (for some sort of memory leak bug), the old instance will\nstill be un-accessible from the moment the new one gets loaded.\n\nBeyond the API proxying and `require.cache` manipulations, there is not much\nleft for a _`Verdant`_ instance to take care of.\n\n## Restrictions\nThe loadable modules code must be _**stateless**_ - everything, except database connections,\nevent handlers and similar (being set up by _attach hook_ and released by _detach hook_),\nmust live in database records, Node.js event loop or HTTP request/response instances.\nThis is how the most of the server code should be written anyway.\n\nThe loadable main module _should not expose a **class** (constructor)_ via its default export.\nThe default export may be a synchronous wrapper function only.\n\nAll parts of loadable modules exported _API will be **read-only**_ and mutating\nany part of it would likely result in an exception thrown. For altering any options,\nuse the wrapper- or _attach hook_ function instead.\n\nNames in loadable API-s should not overlap - if any part of API collides with an item from\nsome other module, an exception will be thrown.\n\n## Compatibility\nThis package requires Node.js v10.15.1 or higher.\n\n_The package_ may fail, if Node.js _`require`_ subsystem is modified in any way.\nFor this particular reason, good 'ol _**mocha**_, not the magnificent _**jest**_, \nwas used for testing here.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvalango%2Fverdant","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvalango%2Fverdant","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvalango%2Fverdant/lists"}