{"id":30713684,"url":"https://github.com/frourios/velona","last_synced_at":"2025-09-03T04:03:39.842Z","repository":{"id":40573250,"uuid":"282428430","full_name":"frourios/velona","owner":"frourios","description":"TypeScript DI helper for functional programming","archived":false,"fork":false,"pushed_at":"2023-08-05T16:35:22.000Z","size":298,"stargazers_count":108,"open_issues_count":0,"forks_count":4,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-09-01T03:44:22.302Z","etag":null,"topics":["typescript"],"latest_commit_sha":null,"homepage":"https://frourio.io/docs/dependency-injection","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/frourios.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,"governance":null,"roadmap":null,"authors":null}},"created_at":"2020-07-25T11:16:25.000Z","updated_at":"2025-07-07T07:09:53.000Z","dependencies_parsed_at":"2024-01-16T07:37:18.298Z","dependency_job_id":null,"html_url":"https://github.com/frourios/velona","commit_stats":{"total_commits":44,"total_committers":4,"mean_commits":11.0,"dds":"0.20454545454545459","last_synced_commit":"db78354ce61d2fc505fe82cb41936433213fc9e9"},"previous_names":["frourios/velona"],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/frourios/velona","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frourios%2Fvelona","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frourios%2Fvelona/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frourios%2Fvelona/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frourios%2Fvelona/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/frourios","download_url":"https://codeload.github.com/frourios/velona/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frourios%2Fvelona/sbom","scorecard":{"id":412323,"data":{"date":"2025-08-11","repo":{"name":"github.com/frouriojs/velona","commit":"88ec7fbecc42c83b3251865424b66bf2acb06173"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.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":"Code-Review","score":1,"reason":"Found 2/18 approved changesets -- score normalized to 1","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":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/frouriojs/velona/nodejs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:19: update your workflow using https://app.stepsecurity.io/secureworkflow/frouriojs/velona/nodejs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/frouriojs/velona/nodejs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:40: update your workflow using https://app.stepsecurity.io/secureworkflow/frouriojs/velona/nodejs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:42: update your workflow using https://app.stepsecurity.io/secureworkflow/frouriojs/velona/nodejs.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:46: update your workflow using https://app.stepsecurity.io/secureworkflow/frouriojs/velona/nodejs.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/release-drafter-main.yml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/frouriojs/velona/release-drafter-main.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/release-drafter.yml:11: update your workflow using https://app.stepsecurity.io/secureworkflow/frouriojs/velona/release-drafter.yml/main?enable=pin","Warn: npmCommand not pinned by hash: .github/workflows/nodejs.yml:28","Warn: npmCommand not pinned by hash: .github/workflows/nodejs.yml:52","Info:   0 out of   6 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   2 third-party GitHubAction dependencies pinned","Info:   0 out of   2 npmCommand dependencies pinned"],"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":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/nodejs.yml:1","Warn: no topLevel permission defined: .github/workflows/release-drafter-main.yml:1","Warn: no topLevel permission defined: .github/workflows/release-drafter.yml:1","Info: no jobLevel write permissions found"],"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":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","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":"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":"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":"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":"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":"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":"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":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"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":"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":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 14 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":4,"reason":"6 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-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-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-18T23:02:01.541Z","repository_id":40573250,"created_at":"2025-08-18T23:02:01.541Z","updated_at":"2025-08-18T23:02:01.541Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273386865,"owners_count":25096250,"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","status":"online","status_checked_at":"2025-09-03T02:00:09.631Z","response_time":76,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["typescript"],"created_at":"2025-09-03T04:02:05.986Z","updated_at":"2025-09-03T04:03:39.830Z","avatar_url":"https://github.com/frourios.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"\u003cbr /\u003e\n\u003cimg src=\"https://frouriojs.github.io/velona/assets/images/ogp.png\" width=\"1280\" alt=\"velona\" /\u003e\n\n\u003cdiv align=\"center\"\u003e\n  \u003ca href=\"https://www.npmjs.com/package/velona\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/v/velona\" alt=\"npm version\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/velona\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/dm/velona\" alt=\"npm download\" /\u003e\n  \u003c/a\u003e\n\u003c/div\u003e\n\n\u003cp align=\"center\"\u003eVelona is TypeScript DI helper for functional programming.\u003c/p\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\n## Table of Contents\n\n- [Installation](#Installation)\n- [Usage](#Usage)\n- [DI to browser API callback](#browser)\n- [Comparison with no DI](#Comparison)\n- [Usage with fs](#fs)\n- [Usage with prisma](#prisma)\n- [Integration test](#integration)\n\n## Installation\n\n- Using [npm](https://www.npmjs.com/):\n\n  ```sh\n  $ npm install velona\n  ```\n\n- Using [Yarn](https://yarnpkg.com/):\n\n  ```sh\n  $ yarn add velona\n  ```\n\n## Usage\n\n`index.ts`\n\n```ts\nimport { depend } from \"velona\";\n\nconst add = (a: number, b: number) =\u003e a + b;\n\nexport const basicFn = depend({ add }, ({ add }, a: number, b: number, c: number) =\u003e add(a, b) * c);\n```\n\n`sample.ts`\n\n```ts\nimport { basicFn } from \"./\";\n\nconsole.log(basicFn(2, 3, 4)); // 20\n```\n\n`index.spec.ts`\n\n```ts\nimport { basicFn } from \"./\";\n\nconst injectedFn = basicFn.inject({ add: (a, b) =\u003e a * b });\n\nexpect(injectedFn(2, 3, 4)).toBe(2 * 3 * 4); // pass\nexpect(basicFn(2, 3, 4)).toBe((2 + 3) * 4); // pass\n```\n\n\u003ca id=\"browser\"\u003e\u003c/a\u003e\n\n## DI to browser API callback\n\n`handler.ts`\n\n```ts\nimport { depend } from \"velona\";\n\nexport const handler = depend(\n  { print: (text: string) =\u003e alert(text) },\n  ({ print }, e: Pick\u003cMouseEvent, \"type\" | \"x\" | \"y\"\u003e) =\u003e\n    print(`type: ${e.type}, x: ${e.x}, y: ${e.y}`)\n);\n```\n\n`index.ts`\n\n```ts\nimport { handler } from \"./handler\";\n\ndocument.body.addEventListener(\"click\", handler, false);\ndocument.body.click(); // alert('type: click, x: 0, y: 0')\n```\n\n`index.spec.ts`\n\n```ts\nimport { handler } from \"./handler\";\n\nconst event = { type: \"click\", x: 1, y: 2 };\n\nexpect(() =\u003e handler(event)).toThrow(); // ReferenceError: alert is not defined (on Node.js)\n\nconst injectedHandler = handler.inject({ print: text =\u003e text });\n\nexpect(injectedHandler(event)).toBe(`type: ${event.type}, x: ${event.x}, y: ${event.y}`); // pass\n```\n\n\u003ca id=\"Comparison\"\u003e\u003c/a\u003e\n\n## Comparison with no DI\n\n`add.ts`\n\n```ts\nexport const add = (a: number, b: number) =\u003e a + b;\n```\n\n`noDI.ts`\n\n```ts\nimport { add } from \"./add\";\n\nexport const noDIFn = (a: number, b: number, c: number) =\u003e add(a, b) * c;\n```\n\n`index.ts`\n\n```ts\nimport { depend } from \"velona\";\nimport { add } from \"./add\";\n\nexport const basicFn = depend({ add }, ({ add }, a: number, b: number, c: number) =\u003e add(a, b) * c);\n```\n\n`sample.ts`\n\n```ts\nimport { basicFn } from \"./\";\nimport { noDIFn } from \"./noDI\";\n\nconsole.log(basicFn(2, 3, 4)); // 20\nconsole.log(noDIFn(2, 3, 4)); // 20\n```\n\n`index.spec.ts`\n\n```ts\nimport { basicFn } from \"./\";\nimport { noDIFn } from \"./noDI\";\n\nconst injectedFn = basicFn.inject({ add: (a, b) =\u003e a * b });\n\nexpect(injectedFn(2, 3, 4)).toBe(2 * 3 * 4); // pass\nexpect(basicFn(2, 3, 4)).toBe((2 + 3) * 4); // pass\nexpect(noDIFn(2, 3, 4)).toBe((2 + 3) * 4); // pass\n```\n\n\u003ca id=\"fs\"\u003e\u003c/a\u003e\n\n## Usage with fs\n\n`index.ts`\n\n```ts\nimport fs from \"fs\";\nimport { depend } from \"velona\";\n\ntype FS = {\n  readFile(path: string, option: \"utf8\"): Promise\u003cstring\u003e;\n  writeFile(path: string, text: string, option: \"utf8\"): Promise\u003cvoid\u003e;\n};\n\nexport const basicFn = depend(\n  fs.promises as FS, // downcast for injection\n  async (dependencies, path: string, text: string) =\u003e {\n    await dependencies.writeFile(path, text, \"utf8\");\n    return dependencies.readFile(path, \"utf8\");\n  }\n);\n```\n\n`sample.ts`\n\n```ts\nimport { basicFn } from \"./\";\n\nconst text = await basicFn(\"sample.txt\", \"Hello world!\"); // create sample.txt\nconsole.log(text); // 'Hello world!'\n```\n\n`index.spec.ts`\n\n```ts\nimport { basicFn } from \"./\";\n\nconst data: Record\u003cstring, string\u003e = {};\nconst injectedFn = basicFn.inject({\n  readFile: path =\u003e Promise.resolve(data[path]),\n  writeFile: (path, text) =\u003e {\n    data[path] = text;\n    return Promise.resolve();\n  },\n});\n\nconst text = \"Hello world!\";\nawait expect(injectedFn(\"test.txt\", text)).resolves.toBe(text);\n```\n\n\u003ca id=\"prisma\"\u003e\u003c/a\u003e\n\n## Usage with prisma\n\n`tasks.ts`\n\n```ts\nimport { depend } from \"velona\";\nimport { PrismaClient } from \"@prisma/client\";\n\ntype Task = {\n  id: number;\n  label: string;\n  done: boolean;\n};\n\nconst prisma = new PrismaClient();\n\nexport const getTasks = depend(\n  { prisma: prisma as { task: { findMany(): Promise\u003cTask[]\u003e } } }, // inject prisma\n  ({ prisma }) =\u003e prisma.task.findMany() // prisma is injected object\n);\n```\n\n`tasks.spec.ts`\n\n```ts\nimport { getTasks } from \"$/service/tasks\";\n\nconst injectedGetTasks = getTasks.inject({\n  prisma: {\n    task: {\n      findMany: () =\u003e\n        Promise.resolve([\n          { id: 0, label: \"task1\", done: false },\n          { id: 1, label: \"task2\", done: false },\n          { id: 2, label: \"task3\", done: true },\n          { id: 3, label: \"task4\", done: true },\n          { id: 4, label: \"task5\", done: false },\n        ]),\n    },\n  },\n});\n\nawait expect(injectedGetTasks()).resolves.toHaveLength(5);\n```\n\n\u003ca id=\"integration\"\u003e\u003c/a\u003e\n\n## Integration test\n\n`add.ts`\n\n```ts\nexport const add = (a: number, b: number) =\u003e a + b;\n```\n\n`grandchild.ts`\n\n```ts\nimport { depend } from \"velona\";\nimport { add } from \"./add\";\n\nexport const grandchild = depend({ add }, ({ add }, a: number, b: number) =\u003e add(a, b));\n```\n\n`child.ts`\n\n```ts\nimport { depend } from \"velona\";\nimport { grandchild } from \"./grandchild\";\n\nexport const child = depend(\n  { grandchild },\n  ({ grandchild }, a: number, b: number, c: number) =\u003e grandchild(a, b) * c\n);\n```\n\n`parentFn.ts`\n\n```ts\nimport { depend } from \"velona\";\nimport { child } from \"./child\";\n\nexport const parentFn = depend(\n  { child, print: (data: number) =\u003e alert(data) },\n  ({ child, print }, a: number, b: number, c: number) =\u003e print(child(a, b, c))\n);\n```\n\n`index.ts`\n\n```ts\nimport { parentFn } from \"./parentFn\";\n\nparentFn(2, 3, 4); // alert(20)\n```\n\n`parentFn.spec.ts`\n\n```ts\nimport { parentFn } from \"./parentFn\";\n\nconst injectedFn = parentFn.inject(parentDeps =\u003e ({\n  child: parentDeps.child.inject(childDeps =\u003e ({\n    grandchild: clildDeps.grandchild.inject({\n      add: (a, b) =\u003e a * b,\n    }),\n  })),\n  print: data =\u003e data,\n}));\n\nexpect(injectedFn(2, 3, 4)).toBe(2 * 3 * 4); // pass\n```\n\n## License\n\nVelona is licensed under a [MIT License](https://github.com/frouriojs/velona/blob/master/LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrourios%2Fvelona","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffrourios%2Fvelona","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrourios%2Fvelona/lists"}