{"id":17491790,"url":"https://github.com/darky/ts-fp-di","last_synced_at":"2025-06-25T10:38:22.983Z","repository":{"id":57380734,"uuid":"415416603","full_name":"darky/ts-fp-di","owner":"darky","description":"Tiny TypeScript functional dependency injection, based on AsyncLocalStorage. Supports Node.js, Deno","archived":false,"fork":false,"pushed_at":"2025-05-23T12:53:51.000Z","size":298,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-20T04:50:08.711Z","etag":null,"topics":["async","asynclocalstorage","deno","dependency","di","fp","functional","injection","local","nodejs","storage","ts","typescript"],"latest_commit_sha":null,"homepage":"https://darky.github.io/ts-fp-di/","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/darky.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,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-10-09T20:36:15.000Z","updated_at":"2025-05-23T12:53:55.000Z","dependencies_parsed_at":"2022-09-09T13:00:42.665Z","dependency_job_id":"70d8d729-0bf2-4274-bd13-722b7558932f","html_url":"https://github.com/darky/ts-fp-di","commit_stats":{"total_commits":171,"total_committers":1,"mean_commits":171.0,"dds":0.0,"last_synced_commit":"cab5a43d30231834b8269df2479c1029bde20488"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/darky/ts-fp-di","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darky%2Fts-fp-di","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darky%2Fts-fp-di/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darky%2Fts-fp-di/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darky%2Fts-fp-di/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/darky","download_url":"https://codeload.github.com/darky/ts-fp-di/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darky%2Fts-fp-di/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261462016,"owners_count":23161871,"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":["async","asynclocalstorage","deno","dependency","di","fp","functional","injection","local","nodejs","storage","ts","typescript"],"created_at":"2024-10-19T08:05:01.285Z","updated_at":"2025-06-25T10:38:22.947Z","avatar_url":"https://github.com/darky.png","language":"TypeScript","funding_links":[],"categories":["Libraries"],"sub_categories":[],"readme":"# ts-fp-di\nTiny TypeScript functional dependency injection, based on AsyncLocalStorage. Supports Node.js, Deno\n\n## Get started\nFirstly, need init DI container for each life cycle of your backend application (each HTTP request/response, handle MQ message, ...).\n\nExample of middleware for typical Koa application, where on each HTTP request will be created particular DI container:\n\n```typescript\napp.use(async (ctx, next) =\u003e {\n  await diInit(async () =\u003e return await next());\n});\n```\n\nFurther, simply use **ts-fp-di** API \"as is\" in code, it will consider particular DI scope.\n\n## Examples\n\n#### Basic\n\n```typescript\nconst fn = di(() =\u003e 1);\nfn() // call `fn` function inside DI scope, it's return 1\n```\n\n#### Override dependency (method 1)\n\n```typescript\nconst fn = di(() =\u003e 1);\ndiSet(fn, () =\u003e 2); // Override `fn` function inside DI scope. Useful for unit tests.\nfn() // returns 2, because it rewriten.\n```\n\n#### Override dependency (method 2)\n\n```typescript\nconst fn = () =\u003e 1;\ndiSet(fn, () =\u003e 2); // Override `fn` function inside DI scope. Useful for unit tests.\ndiDep(fn)() // returns 2, because it rewriten.\n```\n\n#### Dependency by string key\n\n```typescript\ndiSet('user', {login: 'xxx'}); // Useful to setup current user in DI scope\ndiDep\u003cUser\u003e('user') // Extract current user from anywhere\n```\n\n#### State managment in DI scope\n\n```typescript\n// setup Redux like state with reducer in DI scope\nconst inc = dis((sum, n: number) =\u003e sum + n, 0); \ninc(1); // mutate state\ninc(); // 1, \"inc\" without argument returns current state\n\nconst num = div\u003cnumber\u003e(); // alias to dis((sum, n: number) =\u003e n, void 0)\nnum(5); // mutate state\nnum(); // 5\n```\n\n#### State managment in global scope\n\n```typescript\n// setup Redux like state with reducer in global scope (pass true as isGlobal flag)\nconst inc = dis((sum, n: number) =\u003e sum + n, 0, true); \ninc(1); // mutate state\ninc(); // 1, \"inc\" without argument returns current state\n\nclearGlobalState(); // you can clear global state (useful in tests)\ninc() // 0, \"inc\" returns default value now\n```\n\n#### Singleton for DI scope\n\n```typescript\nlet i = 0;\nconst fn = diOnce(() =\u003e { // \u003c- setup Singleton function for DI scope\n  i += 1;\n  return i;\n});\n\nfn(); // 1\nfn(); // also 1, because fn is singleton for DI scope\n```\n\n#### Singleton constant for DI scope\n\n```typescript\nconst cache = dic\u003cnumber\u003e()\n\ncache(1)\ncache() // 1\n```\n\n#### Override Singleton for DI scope\n\n```typescript\nconst fn = diOnce((n: number) =\u003e { // \u003c- setup Singleton function for DI scope\n  return n + 1;\n});\n\ndiOnceSet(fn, -1); // Override diOnceSet. For example, use this in your unit tests\nfn(4) // -1 instead 5, because -1 set on prev line\n```\n\n#### Check that runtime in DI scope\n\n```typescript\ndiExists() // false\n\ndiInit(() =\u003e {\n  diExists() // true\n});\n```\n\n#### Share DI context\n\n```typescript\nconst ctx = diContext()\n\ndiInit(() =\u003e {\n  // ctx will be considered here\n}, ctx)\n\ndiInit(() =\u003e {\n  // same ctx will be considered here too\n}, ctx)\n```\n\n#### DI Scope (OOP incapsulation alternative)\n\n```typescript\nconst inc = dis((resp: number, n: number) =\u003e resp + n, 0)\n\nconst scope = diScope({ inc }, () =\u003e {\n  // optional \"constructor\" function\n  // some `diSet` can be used here\n})\n\nscope.inc(5) // this mutation occur only inside this scope\nscope.inc() // 5 \n```\n\n#### Functional reactive programming, mapping\n\n```typescript\nconst cacheNumber = dic\u003cnumber\u003e()\nconst calcString = diMap(n =\u003e `string - ${n}`, cacheNumber)\n\ncacheNumber(5)\ncalcString() // \"string - 5\"\n\nconst onceNumber = diOnce((n: number) =\u003e {\n  return n;\n});\nconst calcString = diMap(n =\u003e `string - ${n}`, onceNumber)\n\nonceNumber(5)\ncalcString() // \"string - 5\"\n\nconst inc = dis((sum, n: number) =\u003e sum + n, 0);\nconst calcString = diMap(s =\u003e `string - ${s}`, inc)\ninc(1);\ninc(4);\ncalcString() // \"string - 5\"\n\ncalcString.raw(1) // direct call of function, useful for unit tests\n\nconst cacheNumber = dic\u003cnumber\u003e()\nlet i = 0\nconst onceString = diMapOnce(n =\u003e ((i = i + 1), `string - ${n}`), cacheNumber)\n\ncacheNumber(5)\nonceString() // \"string - 5\"\nonceString() // i = 1, because onceString is singleton for DI scope\n```\n\n#### Attach async effect to State\n\n```typescript\nconst numberState = dic\u003cnumber\u003e()\nnumberState(5)\nconst stringState = diMap(n =\u003e `string - ${n}`, numberState)\nconst seState = div\u003cstring\u003e() // will be populated via side effect\nconst se = dise(\n  async (n, s) =\u003e `${n} ${s}`, // side effect async function\n  seState, // this state will be populated via async response\n  numberState, // optional arg1 for effect function\n  stringState) // optional arg2 for effect function\n\nawait se()\n\nseState() // \"5 string - 5\"\n\n// dise function can be overriden for unit tests\ndiseSet(se, async (n, s) =\u003e n + parseInt(s.match(/\\d/)))\n\nawait se()\n\nseState() // 10\n```\n\n## Plugins\n\nInternal AsyncLocalStorage instance exposed as `als` property. You can implement your own plugin around it.\n\n‼️ If you use ts-fp-di with plugins on your project, please consider, that you have only one ts-fp-di *node_module*\u003cbr/\u003e\nFor example you can freeze as singleton you dependency via *package.json* `overrides`\n\n```json\n{\n  \"overrides\": {\n    \"ts-fp-di\": \"^x.x.x\"\n  }\n}\n\n\n```\n\n* [ts-fp-di-mikroorm](https://github.com/darky/ts-fp-di-mikroorm) - Use MikroORM Entities inside ts-fp-di State and achieve auto persistence in DB\n* [ts-fp-di-rxjs](https://github.com/darky/ts-fp-di-rxjs) - Utils for RxJS for working with ts-fp-di\n* [rxjs-wait-next](https://github.com/darky/rxjs-wait-next) - Wait RxJS Subject.next emition for all subscribers\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdarky%2Fts-fp-di","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdarky%2Fts-fp-di","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdarky%2Fts-fp-di/lists"}