{"id":17457779,"url":"https://github.com/stackblitz/alien-signals","last_synced_at":"2025-05-14T15:06:39.447Z","repository":{"id":202932312,"uuid":"707495991","full_name":"stackblitz/alien-signals","owner":"stackblitz","description":"👾 The lightest signal library","archived":false,"fork":false,"pushed_at":"2025-03-31T05:39:27.000Z","size":1603,"stargazers_count":1886,"open_issues_count":1,"forks_count":67,"subscribers_count":15,"default_branch":"master","last_synced_at":"2025-04-03T05:08:46.866Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://npmjs.com/package/alien-signals","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/stackblitz.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":"2023-10-20T03:03:20.000Z","updated_at":"2025-04-03T01:49:54.000Z","dependencies_parsed_at":null,"dependency_job_id":"b58fb61c-eda8-44c5-b63c-c71492df2085","html_url":"https://github.com/stackblitz/alien-signals","commit_stats":null,"previous_names":["johnsoncodehk/computeds","johnsoncodehk/native-signals","johnsoncodehk/signals"],"tags_count":38,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackblitz%2Falien-signals","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackblitz%2Falien-signals/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackblitz%2Falien-signals/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackblitz%2Falien-signals/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stackblitz","download_url":"https://codeload.github.com/stackblitz/alien-signals/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248197463,"owners_count":21063619,"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":[],"created_at":"2024-10-18T03:01:39.309Z","updated_at":"2025-05-14T15:06:39.435Z","avatar_url":"https://github.com/stackblitz.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n\t\u003cimg src=\"assets/logo.png\" width=\"250\"\u003e\u003cbr\u003e\n\u003cp\u003e\n\n\u003cp align=\"center\"\u003e\n\t\u003ca href=\"https://npmjs.com/package/alien-signals\"\u003e\u003cimg src=\"https://badgen.net/npm/v/alien-signals\" alt=\"npm package\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n# alien-signals\n\nThis project explores a push-pull based signal algorithm. Its current implementation 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 function recursion) to ensure performance. We found that under these conditions, maintaining algorithmic simplicity offers more significant improvements than complex scheduling strategies.\n\nEven though Vue 3.4 is already optimized, alien-signals is still noticeably faster. (I wrote code for both, and since they share similar algorithms, they’re quite comparable.)\n\n\u003cimg width=\"1210\" alt=\"Image\" src=\"https://github.com/user-attachments/assets/88448f6d-4034-4389-89aa-9edf3da77254\" /\u003e\n\n\u003e Benchmark repo: https://github.com/transitive-bullshit/js-reactivity-benchmark\n\n## Background\n\nI spent considerable time [optimizing Vue 3.4’s reactivity system](https://github.com/vuejs/core/pull/5912), gaining experience along the way. Since Vue 3.5 [switched to a pull-based algorithm similar to Preact](https://github.com/vuejs/core/pull/10397), I decided to continue researching a push-pull based implementation in a separate project. Our end goal is to implement fully incremental AST parsing and virtual code generation in Vue language tools, based on alien-signals.\n\n## Other Language Implementations\n\n- **Dart:** [medz/alien-signals-dart](https://github.com/medz/alien-signals-dart) \u003csup\u003e2.0.4\u003c/sup\u003e\n- **Luau:** [Nicell/alien-signals-luau](https://github.com/Nicell/alien-signals-luau) \u003csup\u003e1.0.13\u003c/sup\u003e\n- **Java:** [CTRL-Neo-Studios/java-alien-signals](https://github.com/CTRL-Neo-Studios/java-alien-signals) \u003csup\u003e1.0.13\u003c/sup\u003e\n- **C#:** [CTRL-Neo-Studios/csharp-alien-signals](https://github.com/CTRL-Neo-Studios/csharp-alien-signals) \u003csup\u003e1.0.13\u003c/sup\u003e\n- **Go:** [delaneyj/alien-signals-go](https://github.com/delaneyj/alien-signals-go) \u003csup\u003e1.0.7\u003c/sup\u003e\n- **Lua:** [YanqingXu/alien-signals-in-lua](https://github.com/YanqingXu/alien-signals-in-lua) \u003csup\u003e0.6.0\u003c/sup\u003e\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\n## Adoption\n\n- [vuejs/core](https://github.com/vuejs/core): The core algorithm has been ported to v3.6 (PR: https://github.com/vuejs/core/pull/12349)\n- [statelyai/xstate](https://github.com/statelyai/xstate): The core algorithm has been ported to implement the atom architecture (PR: https://github.com/statelyai/xstate/pull/5250)\n- [vuejs/language-tools](https://github.com/vuejs/language-tools): Used in the language-core package for virtual code generation\n\n## Usage\n\n#### Basic APIs\n\n```ts\nimport { signal, computed, effect } from 'alien-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 'alien-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 build your own signal API. For implementation examples, see:\n\n- [Starter template](https://github.com/johnsoncodehk/alien-signals-starter) (implements  `.get()` \u0026 `.set()` methods like the [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 last link node of the previous loop in `propagate` and `checkDirty` functions, and implement the rollback logic to return to this node.\n\nThis results in code that is difficult to understand, and you don't necessarily get the same performance improvements in other languages, so we record the original 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%2Fstackblitz%2Falien-signals","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstackblitz%2Falien-signals","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstackblitz%2Falien-signals/lists"}