[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-81236":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":9,"language":10,"languages":9,"totalLinesOfCode":9,"stars":11,"forks":12,"watchers":13,"openIssues":14,"contributorsCount":14,"subscribersCount":14,"size":14,"stars1d":14,"stars7d":12,"stars30d":15,"stars90d":14,"forks30d":14,"starsTrendScore":14,"compositeScore":16,"rankGlobal":9,"rankLanguage":9,"license":17,"archived":18,"fork":18,"defaultBranch":19,"hasWiki":20,"hasPages":18,"topics":21,"createdAt":9,"pushedAt":9,"updatedAt":22,"readmeContent":23,"aiSummary":24,"trendingCount":14,"starSnapshotCount":14,"syncStatus":12,"lastSyncTime":25,"discoverSource":26},81236,"signals","WebReflection\u002Fsignals","WebReflection","A minimalistic Preact-like signals implementation.",null,"Python",31,2,23,0,8,43.23,"MIT License",false,"main",true,[],"2026-06-12 04:01:32","# @webreflection\u002Fsignals\n\n\u003Csup>**Social Media Photo by [Carlos Alberto Gómez Iñiguez](https:\u002F\u002Funsplash.com\u002F@iniguez) on [Unsplash](https:\u002F\u002Funsplash.com\u002F)**\u003C\u002Fsup>\n\n[![Coverage Status](https:\u002F\u002Fcoveralls.io\u002Frepos\u002Fgithub\u002FWebReflection\u002Fsignals\u002Fbadge.svg?branch=main)](https:\u002F\u002Fcoveralls.io\u002Fgithub\u002FWebReflection\u002Fsignals?branch=main)\n\nA minimalistic [Preact-like signals](https:\u002F\u002Fgithub.com\u002Fpreactjs\u002Fsignals\u002Fblob\u002Fmain\u002Fpackages\u002Fcore\u002FREADME.md) implementation.\n\nOnce minified and compressed, this module is actually [0.5KB](https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002F@webreflection\u002Fsignals\u002Fdist\u002Fsignals.js).\n\n### core\n```js\n\u002F\u002F basic core features\nimport {\n  Signal,     \u002F\u002F class for brand check\n  Computed,   \u002F\u002F extends Signal: brand check\n  batch,      \u002F\u002F Preact-like API\n  computed,   \u002F\u002F Preact-like API\n  effect,     \u002F\u002F Preact-like API\n  signal,     \u002F\u002F Preact-like API\n  untracked,  \u002F\u002F Preact-like API\n} from '@webreflection\u002Fsignals';\n```\n\n### disposable\n\nExposes a Preact-like [createModel](https:\u002F\u002Fgithub.com\u002Fpreactjs\u002Fsignals\u002Fblob\u002Fmain\u002Fpackages\u002Fcore\u002FREADME.md#createmodelfn) utility with a `disposable` export.\n\n```js\n\u002F\u002F extra core features\nimport {\n  \u002F\u002F extra:\n  disposable, \u002F\u002F equivalent of createModel(fn)\n  \u002F\u002F all other exports from core\n  ...core\n} from '@webreflection\u002Fsignals\u002Fdisposable';\n```\n\n\n### In Depth\n\n  * simply (swapped) stack-based, maybe not the best approach, but one that can guarantee reasonable performance with minimal code size.\n  * only `signal` and `computed` subscribe while reading values, unless `sig_or_comp.peek()` is used.\n  * any `effect` updates synchronously but then runs only in isolation. Every effect is disposed of if the outer effect is running, meaning stacked effects work out of the box and always™ do the right thing.\n  * `disposable` uses the very same `effect` logic to dispose itself when not needed anymore.\n  * `batch` piles up subscribers and filters at the end for those that didn't get trashed in between (not perfect, yet fast).\n  * `untracked` temporarily disables subscription in both read (for `symbol` and `computed`) and write (for `symbol` only).\n\n\n#### Background\n\nYou know, nowadays it's hard to find libraries that are still 100% under control, minimalistic, not bloated, yet correct, and this one would like to be one of those 😇\n\n\n#### Benchmark\n\n![benchmark](https:\u002F\u002Fraw.githubusercontent.com\u002FWebReflection\u002Fusignal\u002Fmain\u002Ftest\u002Fbenchmark.png)\n\n\n## Architecture\n\nFine-tuned signals are a piece of art:\n\n  * fastest possible feedback\n  * linked graphs with bitwise flags deciding what to do and\u002For when (alien-signals)\n  * transpiled and understood ahead of time to produce the best possible outcome (Svelte, SolidJS)\n  * ad-hoc or coupled DOM manipulation\n  * specific to JSX syntax (Preact or alternatives)\n  * ... other attempts\u002Fvariants out there\n\nThat is all good and fine, yet the graph behind *signals* is, *imho*, pretty simple in theory (clearly hard in practice) ... and I will tell you how this module keeps that *simple* concept in mind.\n\n\u003Cdetails>\n  \u003Csummary>\u003Cstrong>Signals\u003C\u002Fstrong>\u003C\u002Fsummary>\n  \u003Cdiv>\n\nThese are just a *value wrapper*: you reach that *value*? You are subscribing to it. You change that *value*? You are triggering anything listening to that *signal* reference after subscribing.\n\nThat's it, that's the contract!\n\nHere, there is a `.peek()` method to avoid subscribing, but any time you access a `signal.value`, you are subscribing to it if you are either a *computed* reference or an *event* one.\n\n  \u003C\u002Fdiv>\n\u003C\u002Fdetails>\n\n\u003Cdetails>\n  \u003Csummary>\u003Cstrong>Computed\u003C\u002Fstrong>\u003C\u002Fsummary>\n  \u003Cdiv>\n\nIt's a `signal` by all means, because once you reach its `value`, it subscribes to any *subscriber*, just like any *signal* would do.\n\nThe main difference between *computed* and *signal* is that *computed* is a **read-only** contract, and it expects a callback as an argument that will \"*brand*\" that computed *type* from then on.\n\nEverything else is the same: you cannot `computed.value = anything` but you can always retrieve `computed.value` to subscribe to that computed.\n\nWhen a dependency changes, a *computed* is not immediately executed again: it is simply marked as *invalid*. That invalid state is also a guard, because once a *computed* is already known to be stale there is no reason to notify its subscribers again for every other signal change happening in the same flow, or while that *computed* is already resolving itself. The next `.value` or `.peek()` access refreshes it once, producing the latest result from the current state.\n\n  \u003C\u002Fdiv>\n\u003C\u002Fdetails>\n\n\u003Cdetails>\n  \u003Csummary>\u003Cstrong>Effect\u003C\u002Fstrong>\u003C\u002Fsummary>\n  \u003Cdiv>\n\nThis is the whole orchestration around *signals* or *computed* that makes anything **reactive**, but because it's a *bottom-up* situation we're dealing with, things might feel overly complicated. In theory, that's not the case.\n\n```js\nconst num = signal(0);\n\nconst dispose = effect(() => {\n  \u002F\u002F subscribe to this signal state\n  const value = num.value;\n\n  \u002F\u002F make this effect able to dispose itself\n  if (2 \u003C= value) {\n    \u002F\u002F no further changes to num will be observed\n    dispose();\n  }\n  else {\n    console.log({ value });\n  }\n});\n\n\u002F\u002F logs: { value: 0 }\n\n\u002F\u002F increment by 1\nnum.value++;\n\u002F\u002F logs: { value: 1 }\n\n\u002F\u002F increment by 1\nnum.value++;\n\u002F\u002F logs nothing!\n\n\u002F\u002F drop reactivity explicitly!\ndispose();\n\n\u002F\u002F increment by 1\nnum.value++;\n\u002F\u002F also logs nothing!\n```\n\nIn this example, the *effect* subscribes to `num` changes, but it seppukus itself once its `value` reaches the number `2` or above (\"*how is that possible?*\" ... you'll learn that in a bit!).\n\nThe architecture in this example is also easy to explain: any *signal* or *computed* value that is reached will consider its outer *effect* a potential subscriber!\n\nThe important difference in this module is that *effect* has no notion of what it subscribed to. It's the *signal* or *computed* that retains that information, not the consumer, and that's simply because invoking any foreign *callback* doesn't mean you know what happens within that *callback*. Not knowing what happens is indeed a great way to understand this architecture.\n\nLong story short, any *callback* executing within an *effect* is registered as a consumer of the current *signal* or *computed* reference that is required to provide a `.value` while the *callback* is happening, so the relation is from *signal* or *computed* to any running *callback*, if **any**.\n\n\n#### Effect in details\n\nNo *effect*? No reactivity! This is the *signals* contract, but there is a *catch*:\n\n  * what if an *effect* is within another *effect*?\n  * how can **conditional** operators ditch what *sub-effect* should run and what shouldn't?\n\nGreat questions. Here are the details about why that's never a concern:\n\n  * *effect* never add subscribers to itself, like signals or computeds do, it just registers itself as an *observer* (*subscriber*)\n  * *effect* never runs if it knows outer *effects* are queued to resolve the latest change or changes are happening while it's running\n  * the previous point means if `signal.value` is registered both at the *inner* effect level and at the *outer* one, the *outer* one will dictate the execution because ...\n  * only the top-most subscribed effects will eventually execute, and ...\n  * any *effect* previously registered for its outer *effect* will be **disposed** and never react to anything again!\n\nI am not sure you are still following, but because *effect* is a bottom-up problem, top-down is clearly the solution, and that's granted by the registration **stack**, where the outer *effect* runs before the *inner effect*. That solves everything!\n\nThe same *invalid* guard used by *computed* values applies to *effects* too: when multiple signals ask the same *effect* to refresh, only the first invalidation matters until that *effect* has actually run again. This avoids repeated work, avoids repeated notifications, and prevents an *effect* from recursively refreshing itself while its own callback is still on the stack.\n\n  \u003C\u002Fdiv>\n\u003C\u002Fdetails>\n\n\u003Cdetails>\n  \u003Csummary>\u003Cstrong>Batch\u003C\u002Fstrong>\u003C\u002Fsummary>\n  \u003Cdiv>\n\nIf you followed everything else I've explained around this architecture, `batch(callback)` simply represents a running *callback* with no instant reactivity, it simply accumulates changes and trigger after all changes happend for whatever effect was involved.\n\n  \u003C\u002Fdiv>\n\u003C\u002Fdetails>\n\n\u003Cdetails>\n  \u003Csummary>\u003Cstrong>Untracked\u003C\u002Fstrong>\u003C\u002Fsummary>\n  \u003Cdiv>\n\nThis utility basically runs updates and whatnot like *batch*, but it will never register itself while doing that execution, resulting in a safe hook for foreign functions that wouldn't otherwise belong to our logic. They are just interested in our data instead, and that's fine!\n\n  \u003C\u002Fdiv>\n\u003C\u002Fdetails>\n\n\n## Differences from Preact Signals\n\nThis module aims to be *Preact-like*, not a byte-for-byte or behavior-for-behavior clone of Preact Signals. The goal is to keep the core small, predictable, and easy to reason about, while still exposing `Signal` and `Computed` classes for projects that want to specialize any behavior.\n\nThe following differences are intentional tradeoffs:\n\n  * assigning the same value still notifies subscribers. If you want distinct-value semantics, extend `Signal` and provide your own factory:\n\n```js\nimport { Signal as WRSignal } from '@webreflection\u002Fsignals';\n\nclass Signal extends WRSignal {\n  get value() {\n    return super.value;\n  }\n\n  set value(value) {\n    if (!Object.is(this.peek(), value)) {\n      super.value = value;\n    }\n  }\n}\n\nexport const signal = value => new Signal(value);\n```\n\n  * conditional dependencies are not pruned after every run. Instead, subscribers are kept by the signal or computed reference and are pruned lazily when that source notifies again, skipping disposed effects along the way. This might look \"leaky\" at first glance, but every `Signal` and `Computed` implements native `Symbol.dispose`, so the latest JavaScript `using` syntax can deterministically clear retained subscribers when a scope is done:\n\n```js\n{\n  using count = signal(0);\n  using doubled = computed(() => count.value * 2);\n\n  effect(() => console.log(doubled.value));\n}\n\u002F\u002F count and doubled are disposed here\n```\n\nIf a source is no longer referenced, normal garbage collection takes care of it; if it is still referenced, the next notification keeps the final value valid and fast enough to retrieve without maintaining a more complex dependency graph.\n\n  * mixed direct signal reads and derived computed reads can observe an intermediate stale computed value in the same effect, because there is no graph ordering involved. For example, reading both `count.value` and `doubled.value` where `doubled` is computed from `count` can briefly see the updated `count` with the previous `doubled` before the computed invalidation catches up. This is the little extra cost paid by a not-so-common scenario to keep the library as small as it is.\n\n  * `batch(callback)` coalesces execution, but it does not perform Preact's final-value reconciliation. This follows from the first point: signals do not compare old and new values, so `signal.value = signal.value` is still a notification. That can be a desirable pattern in some cases; for every other \"don't do that\" case, extend `Signal` as shown above.\n\n  * `effect(callback)` callbacks must return either `void` or a cleanup function, as specified by the TypeScript contract. Returning any other value is unsupported and will throw later if that value is invoked as cleanup.\n\n  * extra Preact APIs such as `subscribe`, `valueOf`, `toJSON`, and `action` are outside the minimal core. You can extend the exported classes to add the hooks that make sense for your project. Preact's `createModel` API surface is covered as much as this module needs by the explicit `@webreflection\u002Fsignals\u002Fdisposable` entry point and its `disposable` exported utility, so model-style disposal stays available when imported on purpose without being embedded into the default signals primitive.\n\n#### Please Note\n\nBecause the public shape intentionally follows the Preact Signals API, this module can also be replaced by the real Preact Signals package whenever a project needs its full graph, helpers, or framework integrations. In that sense, it is meant to make the API familiar without forcing anyone to keep using this smaller implementation, or its intentional constraints, forever.\n","WebReflection\u002Fsignals 是一个极简的类似 Preact 信号实现。该项目提供了一系列核心功能，包括信号（Signal）、计算属性（Computed）、批处理（batch）、计算（computed）、效应（effect）和非跟踪（untracked），这些功能均模仿了 Preact 的 API 设计。其设计目标是保持代码体积尽可能小，压缩后仅0.5KB，同时确保性能合理。此外，它还提供了可丢弃模型创建工具（disposable），用于自动管理资源。适用于需要轻量级状态管理和响应式编程的场景，特别适合那些对库大小敏感但又希望获得现代前端框架响应式特性的项目。","2026-06-11 04:04:01","CREATED_QUERY"]