{"id":48894672,"url":"https://github.com/mpazik/linki","last_synced_at":"2026-04-16T10:06:23.087Z","repository":{"id":57399441,"uuid":"363287674","full_name":"mpazik/linki","owner":"mpazik","description":"A simple library that provides utilities to compose your application from function-based components","archived":false,"fork":false,"pushed_at":"2022-12-25T00:15:19.000Z","size":638,"stargazers_count":9,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-09-30T19:41:46.802Z","etag":null,"topics":["api","callbacks","dataflow","framework","functional-reactive-programming","library","linki","reactive-programming","typescript"],"latest_commit_sha":null,"homepage":"","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/mpazik.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-05-01T00:07:47.000Z","updated_at":"2024-11-16T04:49:11.000Z","dependencies_parsed_at":"2023-01-30T21:30:28.137Z","dependency_job_id":null,"html_url":"https://github.com/mpazik/linki","commit_stats":null,"previous_names":["mpazik/data-pipes"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mpazik/linki","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mpazik%2Flinki","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mpazik%2Flinki/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mpazik%2Flinki/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mpazik%2Flinki/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mpazik","download_url":"https://codeload.github.com/mpazik/linki/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mpazik%2Flinki/sbom","scorecard":{"id":662672,"data":{"date":"2025-08-11","repo":{"name":"github.com/mpazik/linki","commit":"5de47d3b2bf74be043b9cb45af545745aa1771bc"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.7,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/30 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":"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":"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":"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":"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":"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":"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":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 'main'"],"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":"Vulnerabilities","score":0,"reason":"12 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-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7","Warn: Project is vulnerable to: GHSA-3h5v-q93c-6h6q"],"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-21T16:40:51.591Z","repository_id":57399441,"created_at":"2025-08-21T16:40:51.591Z","updated_at":"2025-08-21T16:40:51.591Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31880935,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-16T09:23:21.276Z","status":"ssl_error","status_checked_at":"2026-04-16T09:23:15.028Z","response_time":69,"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":["api","callbacks","dataflow","framework","functional-reactive-programming","library","linki","reactive-programming","typescript"],"created_at":"2026-04-16T10:06:22.964Z","updated_at":"2026-04-16T10:06:23.079Z","avatar_url":"https://github.com/mpazik.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Linki - Framework-less reactive programming\nA simple library that provides utilities to compose your application from function-based components.\n\nLinki's connections could be mapped into a diagram.\nIt is technically possible to autogenerate diagrams from the code or the code from the diagrams or even visualize a working application to better understand the app and easier debugging.\n\n## No code framework\nLinki provides a frame for your application but consists only of typed patterns and small, common utilities function.\n\nIt prioritizes simplicity, so you know exactly what you are doing.\nAs it is extremely simple it is very performant as well, has minimal footprint, easy to learn, maintain and extend.\n\n## Example\nA simple component that counts click events, and when the enter key is pressed, it sends the total number to save.\n\n```typescript\nexport const example: Component\u003c\n  { onClick: void; onKeyPress: string },\n  { saveNumber: number; displayTotal: number }\n  \u003e = ({ saveNumber, displayTotal }) =\u003e {\n  const [triggerSave, setupForSave] = link(withState(0), saveNumber);\n\n  return {\n    onClick: link(\n      throttle(1000),\n      reduce(count, 0),\n      fork(displayTotal, setupForSave)\n    ),\n    onKeyPress: link(\n      filter(isKey(\"Enter\")),\n      ignoreParam(),\n      triggerSave\n    ),\n  };\n};\n```\n\n- throttle - enforces a maximum number of times it passes the event downstream; time in this example no more than once per 1000 milliseconds\n- reduce - returns its states on each event. It takes the reducer function. In this example, it just counts the number of incoming events.\n- filter - pass only an element that matches the setup predicate. In this example, it only passes keyboard event when the \"enter\" key was pressed\n- ignoreParam - ignores the input data but propagates the signal as an output\n- withState - a processor that allows to set up state, on trigger the state is propagated to the output, in this example when enter key is pressed it passes the number of clicks on the output\n\n#### Diagram\n![diagram](./examples/basic/diagram.png)\n\n## How does it work\nThe library uses three types of primitives: transformers, processors, and components.\nEach primitive type is based on javascript functions, and callbacks and each type get more complex.\nThe idea is to use the least complex primitive possible to simplify application and avoid overhead.\n\n### Transformers\nThey are used as the name suggests for data transformation. They can be easily composed using the `pipe` operator.\nFor an input item, they immediately(synchronously) return the corresponding output item.\n\nTransformers are used to creating data pipelines.\n\n### Processors\nProcessor is a function that accepts a callback which is its output and returns a callback which is its input.\n\nProcessors are used to creating complex data flows.\n\nHaving a callback as an argument gives processors the ability to return an item on output regardless of the input.\nProcessors can: pass some of the items, none of the items, propagate them later, return multiple items, produce new items regardless without receiving anything on input.  \nThey also can have state and multiple inputs and outputs.\n\n(They are kind of similar to recently popular [transducers](https://raganwald.com/2017/04/30/transducers.html), but simpler)\n\nWe have few types of processors.\n\n#### Synchronous processors\nThe simplest type of processor. It is synchronous and stateless.\nFor a single input, item processor can\n- return a single output item - map\n- return or not a single output item - filter\n- return multiple output item - expand\n\n#### Asynchronous processors\nFor each input, returns an output item with some delay.\nCould have a second input to signal to cancel of the currently processing operation.\n\nExamples: promise handler, debounce, throttle\n\n#### Stateful processor\nOn each input item, they modify their internal state, which is pass to the output.\n\nExamples: reduce, state machine\n\n#### Providers\nKeep returning elements without requiring any input item. They can have an input to listen to the close signal.\n\n#### Consumers\nDo not have any outputs; only consume items.\n\n#### Multi output processors\nPropagate input value to a single or all of the outputs.\n\nExamples: switch, select\n\n#### Multi input processors\nCombines input events that come from multiple sources and pass the result into a single output.\n\n### Components\nProcesses can be composed into a black box component. The composing function can have additional logic and accept different parameters so that it could wrap other processors.\n\n\nCan be used to implement backpressure, lazy loading even separately deployable part of the application.\n\n## How it is different from functional reactive programming libraries\nReactive programming libraries use a stream abstraction with a protocol of communication between nodes.\nThat protocol allows passing extra information from upstream to downstream, like if a stream has completed or if an error occurred;\nand also pass messages from downstream to upstream, like if more data is required.\n\nHowever, that also introduces some cognitive and computation overhead.\nAdditionally, that protocol is not trivial to extend, and it is often limited to building pipelines, not networks consisting of nodes with multiple inputs and outputs.\n\nLinki is designed to promote the use of the simples required tool for the task. \nIt provides low level functions that do the job well as also more sophisticated patters for more complex cases.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmpazik%2Flinki","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmpazik%2Flinki","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmpazik%2Flinki/lists"}