{"id":19040531,"url":"https://github.com/vitaly-rudenko/dependency-registry","last_synced_at":"2026-01-27T08:07:17.790Z","repository":{"id":52105106,"uuid":"297322430","full_name":"vitaly-rudenko/dependency-registry","owner":"vitaly-rudenko","description":"Tiny JavaScript dependency registry.","archived":false,"fork":false,"pushed_at":"2024-11-30T09:44:50.000Z","size":301,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-09-07T02:41:16.163Z","etag":null,"topics":["dependency-injection","javascript","package"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vitaly-rudenko.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-09-21T11:44:35.000Z","updated_at":"2024-11-30T09:44:54.000Z","dependencies_parsed_at":"2025-09-07T02:33:56.315Z","dependency_job_id":null,"html_url":"https://github.com/vitaly-rudenko/dependency-registry","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/vitaly-rudenko/dependency-registry","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitaly-rudenko%2Fdependency-registry","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitaly-rudenko%2Fdependency-registry/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitaly-rudenko%2Fdependency-registry/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitaly-rudenko%2Fdependency-registry/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vitaly-rudenko","download_url":"https://codeload.github.com/vitaly-rudenko/dependency-registry/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitaly-rudenko%2Fdependency-registry/sbom","scorecard":{"id":924020,"data":{"date":"2025-08-11","repo":{"name":"github.com/vitaly-rudenko/dependency-registry","commit":"89d544edbfb34a0732c3d6796c1d743424d6301e"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.3,"checks":[{"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":"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":"Code-Review","score":0,"reason":"Found 0/19 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":"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":"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":"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":"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":"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":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"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 'master'"],"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"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 12 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"18 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-9j5q-479x-43g2","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-pfrx-2q88-qq97","Warn: Project is vulnerable to: GHSA-rc47-6667-2j5j","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-82v2-mx6x-wq7q","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-px4h-xg32-q955","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-52f5-9888-hmc6","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-25T08:50:27.453Z","repository_id":52105106,"created_at":"2025-08-25T08:50:27.454Z","updated_at":"2025-08-25T08:50:27.454Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28809345,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-27T07:41:26.337Z","status":"ssl_error","status_checked_at":"2026-01-27T07:41:08.776Z","response_time":168,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: 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":["dependency-injection","javascript","package"],"created_at":"2024-11-08T22:23:07.805Z","updated_at":"2026-01-27T08:07:17.746Z","avatar_url":"https://github.com/vitaly-rudenko.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @vitalyrudenko/dependency-registry\n\n## Usage\n\n### Installation\n\n```\nnpm i @vitalyrudenko/dependency-registry\n```\n\n### Initialize the registry\n\n```js\nconst { DependencyRegistry } = require('@vitalyrudenko/dependency-registry');\n\nconst registry = new DependencyRegistry();\n```\n\n#### Values\n\n```js\nregistry.registerValue('externalApiKey', config.external.apiKey);\n\nconst { externalApiKey } = registry.export();\n```\n\n#### Instances\n\n##### Class instances\n\n\u003e The name of the instance is automatically generated from the class name.\n\n```js\nregistry.registerInstance(new House());\n\nconst { house } = registry.export();\n```\n\n##### Named instances\n```js\nregistry.registerInstance('myHouse', new House());\nregistry.registerInstance('pgPool', pg.createPool());\n\nconst { myHouse, pgPool } = registry.export();\n```\n\n#### Factories\n\n##### Simple class factories\n\n\u003e The name of the factory is automatically generated from the class name.\n\n```js\nclass MyClass {\n    constructor(a, b, c) {\n        console.log('test:', a, b, c);\n    }\n}\n\nregistry.registerFactory(MyClass);\n\nconst { myClassFactory } = registry.export();\n\nmyClassFactory.create(123, 'hello', true); // logs \"test: 123 hello true\"\n```\n\n##### Dependent class factories\n\n\u003e The name of the factory is automatically generated from the class name.\n\n```js\nclass Transformer {\n    transform(value) {\n        return '(' + value + ')';\n    }\n}\n\nclass MyClass {\n    constructor(a, b, c, { transformer }) {\n        console.log(\n            'test:',\n            transformer.transform(a),\n            transformer.transform(b),\n            transformer.transform(c)\n        );\n    }\n}\n\nregistry.registerInstance(new Transformer());\nregistry.registerFactory(MyClass, (deps, ...args) =\u003e new MyClass(...args, deps));\n\nconst { myClassFactory } = registry.export();\n\nmyClassFactory.create(123, 'hello', true); // logs \"test: (123) (hello) (true)\"\n```\n\n##### Named factories\n\n```js\nregistry.registerInstance('translator', {\n    translate(key, variables) {\n        if (key === 'greeting') {\n            return `Hello, ${variables.name}!`;\n        }\n\n        throw new Error('Unknown key: ' + key);\n    }\n})\n\nregistry.registerFactory(\n    'greetingFactory',\n    ({ translator }, name) =\u003e translator.translate('greeting', { name })\n);\n\nconst { greetingFactory } = registry.export();\n\nconsole.log(greetingFactory.create('John Doe')); // logs \"Hello, John Doe!\"\n```\n\n#### Importing registries\n\n```js\nregistry1.registerValue('appName', 'My App');\n\nregistry2.registerFactory('welcomeFactory', ({ appName }) =\u003e `Welcome to ${appName}!`);\nregistry2.import(registry1);\n\nconst { welcomeFactory } = registry2.export();\n\nconsole.log(welcomeFactory.create()); // logs \"Welcome to My App!\"\n```\n\n\u003e Imported factories can use dependencies of the new registry\n\n## The problem\n\nWhen using dependency injection or writing unit tests, you might notice that your dependency hierarchy slowly becomes bloated and hard to maintain.\n\nLet's say we have a simple `HouseBuilder` class, which creates `House` instances:\n```js\nclass HouseBuilder {\n    constructor({ windowCount, hasDoor }) {\n        this._windowCount = windowCount;\n        this._hasDoor = hasDoor;\n    }\n\n    build(owner) {\n        return new House({\n            owner,\n            windows: Array.from(\n                new Array(this._windowCount),\n                () =\u003e new Window()\n            ),\n            door: this._hasDoor ? new Door() : null\n        });\n    }\n}\n```\n\nNow let's say that `House` requires to have its own security system, and it's gonna be an external security system (which \"implements\" `Security` interface), which requires an `apiKey`:\n\n```js\nconst config = require('../config');\n\nclass House {\n    constructor({ owner, windows, door }) {\n        this._security = new ProvidedSecurity({\n            windows,\n            door\n        }, {\n            apiKey: config.apiKey,\n        });\n    }\n\n    toggleLock() {\n        if (this._security.isLocked()) {\n            this._security.unlock();\n        } else {\n            this._security.lock();\n        }\n    }\n}\n```\n\nAnd on top of that, let's add new dependency to the `Window`:\n\n```js\nclass Window {\n    constructor() {\n        this._windowHandle = new WindowHandle();\n    }\n\n    toggle() {\n        if (this._windowHandle.isOpened) {\n            this._windowHandle.close();\n        } else {\n            this._windowHandle.open();\n        }\n    }\n}\n```\n\nFinally, let's add a `WindowsUtils` dependency to the `WindowHandle`:\n```js\nclass WindowHandle {\n    constructor({ windowUtils }) {\n        this._windowUtils = windowUtils;\n        this._isOpened = false;\n    }\n\n    measure() {\n        return this._windowUtils.measureDimensions(/* ... */);\n    }\n\n    open() {\n        this._isOpened = true;\n    }\n\n    close() {\n        this._isOpened = false;\n    }\n\n    get isOpened() {\n        return this._isOpened;\n    }\n}\n\n```\n\nBut wait, how are we going to pass `windowUtils` to the `WindowHandle`?\n\nWe'll need to pass it to `HouseBuilder`, then to `Window`:\n\n```js\nclass HouseBuilder {\n    constructor({ windowCount, hasDoor }, { windowUtils }) {\n        this._windowCount = windowCount;\n        this._hasDoor = hasDoor;\n        this._windowUtils = windowUtils;\n    }\n\n    build(owner) {\n        return new House({\n            owner,\n            windows: Array.from(\n                new Array(this._windowCount),\n                () =\u003e new Window({ windowUtils })\n            ),\n            door: this._hasDoor ? new Door() : null\n        });\n    }\n}\n```\n\n```js\nclass Window {\n    constructor({ windowUtils }) {\n        this._windowHandle = new WindowHandle({ windowUtils });\n    }\n\n    // ...\n}\n```\n\nThere are three main problems with the code above:\n- This code is almost impossible to unit test, because it's very hard to mock creation of external dependencies (like `new ProvidedSecurity()` or `new WindowHandle()`)\n- The code knows about stuff it shouldn't know about (like `ProvidedSecurity` implementation and `config`)\n- We need to pass all necessary dependencies from top to bottom of the dependency hierarchy (like with `WindowsUtils`: `HouseBuilder -\u003e Window -\u003e WindowHandle`), which means top-level code should know how are low-level instances are created and which dependencies they use. It's also hard to add new dependencies in low-level code, because we now need to pass it to every single \"parent\".\n\nThis tiny dependency registry tries to solve these issues by flattening the dependency tree and creating factories for classes.\n\nIn order to do that, we need to specify how to instantiate every class:\n```js\n// app.js\nconst registry = new DependencyRegistry();\n\nregistry.registerFactory(House, (deps, attributes) =\u003e new House(attributes, deps));\nregistry.registerFactory(\n    'securityFactory',\n    (deps, attributes) =\u003e new ProvidedSecurity(attributes, { apiKey: config.apiKey })\n);\n\nregistry.registerFactory(Window, (deps, attributes) =\u003e new Window(deps));\nregistry.registerFactory(WindowHandle, (deps, attributes) =\u003e new WindowHandle(deps));\nregistry.registerInstance(new WindowUtils());\n\nregistry.registerFactory(Door);\n\nconst houseBuilder = new HouseBuilder(\n    { windowCount: 2, hasDoor: true },\n    registry.export()\n);\n\nconst house = houseBuilder.build(new Person('John Doe'));\n```\n\nNow we need to update our classes to use the factories to create instances:\n```js\nclass HouseBuilder {\n    constructor(\n        { windowCount, hasDoor },\n        { houseFactory, windowFactory, doorFactory }\n    ) {\n        this._windowCount = windowCount;\n        this._hasDoor = hasDoor;\n\n        this._houseFactory = houseFactory;\n        this._windowFactory = windowFactory;\n        this._doorFactory = doorFactory;\n    }\n\n    build(owner) {\n        return this._houseFactory.create({\n            owner,\n            windows: Array.from(\n                new Array(this._windowCount),\n                () =\u003e this._windowFactory.create()\n            ),\n            door: this._hasDoor ? this._doorFactory.create() : null\n        });\n    }\n}\n```\n```js\nclass House {\n    constructor({ owner, windows, door }, { securityFactory }) {\n        this._security = securityFactory.create({\n            windows,\n            door\n        });\n    }\n\n    toggleLock() {\n        if (this._security.isLocked()) {\n            this._security.unlock();\n        } else {\n            this._security.lock();\n        }\n    }\n}\n```\n```js\nclass Window {\n    constructor({ windowHandleFactory }) {\n        this._windowHandle = windowHandleFactory.create();\n    }\n\n    toggle() {\n        if (this._windowHandle.isOpened) {\n            this._windowHandle.close();\n        } else {\n            this._windowHandle.open();\n        }\n    }\n}\n```\n\nAs you can see:\n- Code is now perfectly unit testable, because we can create mock factories and completely isolate the module when testing it\n- All classes only know about dependencies they actually use, they don't know about dependencies of the \"children\" classes (for example, `House` doesn't know about `WindowHandle` and `Window` doesn't know about `WindowUtils`). Now it's much easier to change dependencies of low-level classes, because we don't need to change anything it in \"parent\" classes\n- Code doesn't know about stuff it shouldn't know about (for example, `House` doesn't know about `ProvidedSecurity` class and `config`, it's only concerned about `Security` interface and not the details)\n\n\u003e It's important that your application code (like business logic) doesn't know about `DependencyRegistry`'s existence.\n\u003e \n\u003e You only really need it in `main` part of your application, which initializes the application, configures routes, creates connections to databases, etc. (like `app.js`, routers and controllers).\n\u003e\n\u003e In terms of your business logic, it should behave as if those factories do actually exist in your code.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvitaly-rudenko%2Fdependency-registry","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvitaly-rudenko%2Fdependency-registry","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvitaly-rudenko%2Fdependency-registry/lists"}