{"id":13485213,"url":"https://github.com/frouriojs/velona","last_synced_at":"2025-04-09T18:16:56.128Z","repository":{"id":40573250,"uuid":"282428430","full_name":"frouriojs/velona","owner":"frouriojs","description":"TypeScript DI helper for functional programming","archived":false,"fork":false,"pushed_at":"2023-08-05T16:35:22.000Z","size":298,"stargazers_count":106,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-09T18:16:51.235Z","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/frouriojs.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-03-28T14:06:23.000Z","dependencies_parsed_at":"2024-01-16T07:37:18.298Z","dependency_job_id":null,"html_url":"https://github.com/frouriojs/velona","commit_stats":{"total_commits":44,"total_committers":4,"mean_commits":11.0,"dds":"0.20454545454545459","last_synced_commit":"db78354ce61d2fc505fe82cb41936433213fc9e9"},"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frouriojs%2Fvelona","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frouriojs%2Fvelona/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frouriojs%2Fvelona/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frouriojs%2Fvelona/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/frouriojs","download_url":"https://codeload.github.com/frouriojs/velona/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248085323,"owners_count":21045139,"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":["typescript"],"created_at":"2024-07-31T17:01:51.275Z","updated_at":"2025-04-09T18:16:56.108Z","avatar_url":"https://github.com/frouriojs.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%2Ffrouriojs%2Fvelona","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffrouriojs%2Fvelona","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrouriojs%2Fvelona/lists"}