{"id":31376518,"url":"https://github.com/substrate-system/signals","last_synced_at":"2026-01-20T16:33:16.718Z","repository":{"id":300325023,"uuid":"1005740522","full_name":"substrate-system/signals","owner":"substrate-system","description":"Signals","archived":false,"fork":false,"pushed_at":"2025-06-21T04:25:49.000Z","size":1709,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"fork","last_synced_at":"2025-10-28T16:10:25.741Z","etag":null,"topics":["signals"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/substrate-system.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,"zenodo":null}},"created_at":"2025-06-20T18:22:50.000Z","updated_at":"2025-06-21T04:25:52.000Z","dependencies_parsed_at":"2025-06-21T03:52:01.509Z","dependency_job_id":"fdf03fb2-2a04-410e-bd18-26259f59a071","html_url":"https://github.com/substrate-system/signals","commit_stats":null,"previous_names":["substrate-system/signals"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/substrate-system/signals","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/substrate-system%2Fsignals","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/substrate-system%2Fsignals/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/substrate-system%2Fsignals/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/substrate-system%2Fsignals/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/substrate-system","download_url":"https://codeload.github.com/substrate-system/signals/tar.gz/refs/heads/fork","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/substrate-system%2Fsignals/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28607284,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-20T16:10:39.856Z","status":"ssl_error","status_checked_at":"2026-01-20T16:10:39.493Z","response_time":117,"last_error":"SSL_read: 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":["signals"],"created_at":"2025-09-28T03:53:48.287Z","updated_at":"2026-01-20T16:33:16.697Z","avatar_url":"https://github.com/substrate-system.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# signals\n[![tests](https://img.shields.io/github/actions/workflow/status/substrate-system/signals/nodejs.yml?style=flat-square)](https://github.com/substrate-system/signals/actions/workflows/nodejs.yml)\n[![types](https://img.shields.io/npm/types/@substrate-system/signals?style=flat-square)](README.md)\n[![module](https://img.shields.io/badge/module-ESM%2FCJS-blue?style=flat-square)](README.md)\n[![semantic versioning](https://img.shields.io/badge/semver-2.0.0-blue?logo=semver\u0026style=flat-square)](https://semver.org/)\n[![Common Changelog](https://nichoth.github.io/badge/common-changelog.svg)](./CHANGELOG.md)\n[![install size](https://flat.badgen.net/packagephobia/install/@substrate-system/signals)](https://packagephobia.com/result?p=@substrate-system/signals)\n[![dependencies](https://img.shields.io/badge/dependencies-zero-brightgreen.svg?style=flat-square)](package.json)\n[![license](https://img.shields.io/badge/license-Big_Time-blue?style=flat-square)](LICENSE)\n\n\nThis project explores a push-pull based signal algorithm. Its current\nimplementation is similar to or related to certain other frontend projects:\n\n- Propagation algorithm of Vue 3\n- Preact’s double-linked-list approach (https://preactjs.com/blog/signal-boosting/)\n- Inner effects scheduling of Svelte\n- Graph-coloring approach of Reactively (https://milomg.dev/2022-12-01/reactivity)\n\nWe impose some constraints (such as not using Array/Set/Map and disallowing\nfunction recursion) to ensure performance. We found that under these conditions,\nmaintaining algorithmic simplicity offers more significant improvements than\ncomplex scheduling strategies.  Even though Vue 3.4 is already optimized,\nalien-signals is still noticeably faster. (I wrote code for both, and since they\nshare similar algorithms, they’re quite comparable.)\n\n\u003e Benchmark repo: https://github.com/transitive-bullshit/js-reactivity-benchmark\n\n## fork\n\nThis is a fork of [stackblitz/alien-signals](https://github.com/stackblitz/alien-signals).\n\n\u003cdetails\u003e\u003csummary\u003e\u003ch2\u003eContents\u003c/h2\u003e\u003c/summary\u003e\n\n\u003c!-- toc --\u003e\n\n- [Install](#install)\n- [Derived Projects](#derived-projects)\n- [Adoption](#adoption)\n- [Usage](#usage)\n  * [Basic APIs](#basic-apis)\n  * [Effect Scope](#effect-scope)\n  * [Creating Your Own Surface API](#creating-your-own-surface-api)\n- [About `propagate` and `checkDirty` functions](#about-propagate-and-checkdirty-functions)\n  * [`propagate`](#propagate)\n  * [`checkDirty`](#checkdirty)\n\n\u003c!-- tocstop --\u003e\n\n\u003c/details\u003e\n\n## Install\n\n```sh\nnpm i -S @susbtrate-system/signals\n```\n\n\n## Derived Projects\n\n- [Rajaniraiyn/react-alien-signals](https://github.com/Rajaniraiyn/react-alien-signals): React bindings for the alien-signals API\n- [CCherry07/alien-deepsignals](https://github.com/CCherry07/alien-deepsignals): Use alien-signals with the interface of a plain JavaScript object\n- [hunghg255/reactjs-signal](https://github.com/hunghg255/reactjs-signal): Share Store State with Signal Pattern\n- [gn8-ai/universe-alien-signals](https://github.com/gn8-ai/universe-alien-signals): Enables simple use of the Alien Signals state management system in modern frontend frameworks\n- [WebReflection/alien-signals](https://github.com/WebReflection/alien-signals): Preact signals like API and a class based approach for easy brand check\n- [@lift-html/alien](https://github.com/JLarky/lift-html/tree/main/packages/alien): Integrating alien-signals into lift-html\n\n## Adoption\n\n- [vuejs/core](https://github.com/vuejs/core): The core algorithm has been\n  ported to v3.6 (PR: https://github.com/vuejs/core/pull/12349)\n- [statelyai/xstate](https://github.com/statelyai/xstate): The core algorithm\n  has been ported to implement the atom architecture\n  (PR: https://github.com/statelyai/xstate/pull/5250)\n- [flamrdevs/xignal](https://github.com/flamrdevs/xignal): Infrastructure\n  for the reactive system\n- [vuejs/language-tools](https://github.com/vuejs/language-tools): Used\n  in the language-core package for virtual code generation\n\n## Usage\n\n### Basic APIs\n\n```ts\nimport { signal, computed, effect } from '@substrate-system/signals';\n\nconst count = signal(1);\nconst doubleCount = computed(() =\u003e count() * 2);\n\neffect(() =\u003e {\n  console.log(`Count is: ${count()}`);\n});  // Console: Count is: 1\n\nconsole.log(doubleCount()); // 2\n\ncount(2);  // Console: Count is: 2\n\nconsole.log(doubleCount());  // 4\n```\n\n### Effect Scope\n\n```ts\nimport { signal, effect, effectScope } from '@substrate-system/signals';\n\nconst count = signal(1);\n\nconst stopScope = effectScope(() =\u003e {\n  effect(() =\u003e {\n    console.log(`Count in scope: ${count()}`);\n  });  // Console: Count in scope: 1\n});\n\ncount(2);  // Console: Count in scope: 2\n\nstopScope();\n\ncount(3);  // No console output\n```\n\n### Creating Your Own Surface API\n\nYou can reuse alien-signals’ core algorithm via `createReactiveSystem()` to\nbuild your own signal API. For implementation examples, see:\n\n- [Starter template](https://github.com/johnsoncodehk/alien-signals-starter)\n  (implements  `.get()` \u0026 `.set()` methods like the\n  [Signals proposal](https://github.com/tc39/proposal-signals))\n- [stackblitz/alien-signals/src/index.ts](https://github.com/stackblitz/alien-signals/blob/master/src/index.ts)\n- [proposal-signals/signal-polyfill#44](https://github.com/proposal-signals/signal-polyfill/pull/44)\n\n\n## About `propagate` and `checkDirty` functions\n\nIn order to eliminate recursive calls and improve performance, we record the\nlast link node of the previous loop in `propagate` and `checkDirty` functions,\nand implement the rollback logic to return to this node.\n\nThis results in code that is difficult to understand, and you don't necessarily\nget the same performance improvements in other languages, so we record the\noriginal implementation without eliminating recursive calls here for reference.\n\n### `propagate`\n\n```ts\nfunction propagate(link: Link): void {\n\tdo {\n\t\tconst sub = link.sub;\n\n\t\tlet flags = sub.flags;\n\n\t\tif (flags \u0026 (ReactiveFlags.Mutable | ReactiveFlags.Watching)) {\n\t\t\tif (!(flags \u0026 (ReactiveFlags.RecursedCheck | ReactiveFlags.Recursed | ReactiveFlags.Dirty | ReactiveFlags.Pending))) {\n\t\t\t\tsub.flags = flags | ReactiveFlags.Pending;\n\t\t\t} else if (!(flags \u0026 (ReactiveFlags.RecursedCheck | ReactiveFlags.Recursed))) {\n\t\t\t\tflags = ReactiveFlags.None;\n\t\t\t} else if (!(flags \u0026 ReactiveFlags.RecursedCheck)) {\n\t\t\t\tsub.flags = (flags \u0026 ~ReactiveFlags.Recursed) | ReactiveFlags.Pending;\n\t\t\t} else if (!(flags \u0026 (ReactiveFlags.Dirty | ReactiveFlags.Pending)) \u0026\u0026 isValidLink(link, sub)) {\n\t\t\t\tsub.flags = flags | ReactiveFlags.Recursed | ReactiveFlags.Pending;\n\t\t\t\tflags \u0026= ReactiveFlags.Mutable;\n\t\t\t} else {\n\t\t\t\tflags = ReactiveFlags.None;\n\t\t\t}\n\n\t\t\tif (flags \u0026 ReactiveFlags.Watching) {\n\t\t\t\tnotify(sub);\n\t\t\t}\n\n\t\t\tif (flags \u0026 ReactiveFlags.Mutable) {\n\t\t\t\tconst subSubs = sub.subs;\n\t\t\t\tif (subSubs !== undefined) {\n\t\t\t\t\tpropagate(subSubs);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlink = link.nextSub!;\n\t} while (link !== undefined);\n}\n```\n\n### `checkDirty`\n\n```ts\nfunction checkDirty(link: Link, sub: ReactiveNode): boolean {\n\tdo {\n\t\tconst dep = link.dep;\n\t\tconst depFlags = dep.flags;\n\n\t\tif (sub.flags \u0026 ReactiveFlags.Dirty) {\n\t\t\treturn true;\n\t\t} else if ((depFlags \u0026 (ReactiveFlags.Mutable | ReactiveFlags.Dirty)) === (ReactiveFlags.Mutable | ReactiveFlags.Dirty)) {\n\t\t\tif (update(dep)) {\n\t\t\t\tconst subs = dep.subs!;\n\t\t\t\tif (subs.nextSub !== undefined) {\n\t\t\t\t\tshallowPropagate(subs);\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} else if ((depFlags \u0026 (ReactiveFlags.Mutable | ReactiveFlags.Pending)) === (ReactiveFlags.Mutable | ReactiveFlags.Pending)) {\n\t\t\tif (checkDirty(dep.deps!, dep)) {\n\t\t\t\tif (update(dep)) {\n\t\t\t\t\tconst subs = dep.subs!;\n\t\t\t\t\tif (subs.nextSub !== undefined) {\n\t\t\t\t\t\tshallowPropagate(subs);\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tdep.flags = depFlags \u0026 ~ReactiveFlags.Pending;\n\t\t\t}\n\t\t}\n\n\t\tlink = link.nextDep!;\n\t} while (link !== undefined);\n\n\treturn false;\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsubstrate-system%2Fsignals","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsubstrate-system%2Fsignals","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsubstrate-system%2Fsignals/lists"}