{"id":"molyidev/signal","name":"signal","scope":"molyidev","platform":"roblox","description":"Signal library.","version":"1.0.3","latest":"1.0.3","versions":["1.0.0","1.0.1","1.0.2","1.0.3"],"license":"MIT","licenseRating":"safe","licenseCaveats":[],"licenseVerified":true,"dependencies":{},"integrity":"514ae8b40fa772fb868229ee33a8f42972d629a2d977625184a073b62703e4a3","likes":0,"downloads":0,"install":"forest install molyidev/signal","url":"https://forest.dev/p/roblox/molyidev/signal","files":"https://api.forest.dev/ai/package/roblox/molyidev/signal/files","readme":"# Signal\n\n## Installation\n\n### Pesde\n\n```bash\npesde add molyidev/signal\n```\n\n### Wally\n\n```toml\nSignal = \"molyidev/signal@^1.0.3\"\n```\n\n## Why use Signal?\n\n* **Fully typed:** Designed to work with the new Luau type solver.\n* **Priority:** Includes a `PrioritySignal` variant, allowing you to control the exact execution order of your connections.\n* **Performance:** Uses a **Doubly Linked List** for O(1) removal and **LIFO** cleanup structure.\n* **GC Friendly:** Designed to have minimal memory allocation.\n\n## Usage\n\n### Basics\n\n```lua\nlocal Signal = require(path.to.Signal)\n\n-- Create a standard signal\nlocal signal = Signal.new()\n\n-- Connect to the signal\nlocal connection = signal:Connect(function(msg)\n    print(\"Received:\", msg)\nend)\n\n-- Fire the signal\nsignal:Fire(\"Hello World!\") \n\n-- Clean up\nconnection:Disconnect()\n```\n\n### Priority Signal\n\n```lua\nlocal Signal = require(path.to.Signal)\n\nlocal prioritySignal = Signal.newPriority()\n\n-- Priority 10 (Runs second)\nprioritySignal:ConnectPriority(10, function()\n    print(\"Second\")\nend)\n\n-- Priority 0 (Runs first)\nprioritySignal:ConnectPriority(0, function()\n    print(\"First\")\nend)\n\nprioritySignal:Fire()\n-- Output:\n-- First\n-- Second\n```\n\n### Type Checking\n\n```lua\nlocal Signal = require(path.to.Signal)\n\n-- Define a signal that accepts a number and a string\nlocal signal = Signal.new() :: Signal.Signal<number, string>\n\nsignal:Connect(function(num, text)\n    -- 'num' is inferred as number\n    -- 'text' is inferred as string\n    print(num, text)\nend)\n\nsignal:Fire(10, \"str\") -- Valid\nsignal:Fire(\"str\", 10) -- Type Error\n```\n\n### Synchronous Firing\n\nBy default, `Fire` runs listeners in separate threads. Use `FireSync` to run them immediately and yield until they finish.\n\n```lua\nsignal:FireSync(\"Yields current thread until all listeners finish\")\n```\n\n### Bound Args\n\nYou can pass arguments to `:Connect()` that will be bound to that specific connection. These arguments are passed to your callback **first**, followed by any arguments passed to `:Fire()`.\n\n```lua\nlocal Signal = require(path.to.Signal)\n\nlocal signal = Signal.new()\n\n-- Bind '1000' and '\"context\"' to this specific connection\nsignal:Connect(function(boundNum, boundStr, firedStr)\n    print(boundNum, boundStr, firedStr)\nend, 1000, \"context\")\n\n-- This connection has no bound arguments\nsignal:Connect(function(firedStr)\n    print(firedStr)\nend)\n\nsignal:Fire(\"strExample\")\n\n-- Output:\n-- 1000 context strExample\n-- strExample\n```\n\n\n## API\n\n### Static Signal\n\n| Method | Description |\n| --- | --- |\n| `.new()` | Creates a new standard Signal. |\n| `.newPriority()` | Creates a new PrioritySignal. |\n| `.IsSignal(obj)` | Returns `true` if `obj` is a `Signal` or `PrioritySignal`. |\n| `.IsConnection(obj)` | Returns `true` if `obj` is a `Connection`. |\n\n### Signal\n\n| Method | Description |\n| --- | --- |\n| `:Connect(fn, ...)` | Connects a function to the signal. Returns a `Connection`. |\n| `:Once(fn, ...)` | Connects a function that runs only once, then disconnects. Returns a `Connection`. |\n| `:Wait()` | Yields the current thread until the signal is fired. Returns the arguments fired. |\n| `:Fire(...)` | Fires the signal. Listeners run in separate threads. |\n| `:FireSync(...)` | Fires the signal synchronously. |\n| `:DisconnectAll()` | Disconnects all connections. |\n| `:Destroy()` | Disconnects all connections and leaves the signal unusable. |\n\n### PrioritySignal (Inherits `Signal`)\n\n| Method | Description |\n| --- | --- |\n| `:ConnectPriority(priority, fn, ...)` | Connects with a specific priority. Lower executes first. Returns a `Connection`. |\n| `:OncePriority(priority, fn, ...)` | Connects once with a specific priority. Returns a `Connection`. |\n| `:WaitPriority(priority)` | Yields until fired. The temporary connection uses the given priority. Returns the arguments fired. |\n\n> **Note:** `PrioritySignal` also has the standard methods (`Connect`, `Once`, etc.), which default to priority `0`.\n\n### Connection\n\n| Property/Method | Description |\n| --- | --- |\n| `.Connected` | Boolean. `true` if the connection is active. |\n| `:Disconnect()` | Disconnects the listener from the signal. |\n| `:Reconnect()` | Re-activates a disconnected listener (maintains original priority). |\n\n## Benchmarks\n\nBenchmarks are located in the `./public/` directory.\n\n### Speed - 5K Iterations\n\n![First Bench Image](./public/bench1.png)\n![Second Bench Image](./public/bench2.png)\n\n* **Signal** and **LemonSignal** have the same raw speed performance.\n* **Signal** is faster than **FastSignal**.\n* Since **LemonSignal** and **FastSignal** both outperform **GoodSignal**, **Signal** is consequently faster than **GoodSignal**.\n\n### Memory - 10K Iterations\n\nThe following table compares memory usage across different operations. **Signal** is designed to be GC-friendly with 0 allocations during firing.\n\n| Operation | Signal | LemonSignal | FastSignal |\n| --- | --- | --- | --- |\n| **New** | 782 KB | 782 KB | 1.07 MB |\n| **Connect** | 2.90 MB | 3.36 MB | 3.97 MB |\n| **Fire** | 0 KB | 0 KB | 11.14 MB |\n| **Fire (Bound Args)** | 0 KB | 938 KB | *N/A* |\n| **FireSync** | 0 KB | *N/A* | *N/A* |\n| **FireSync (Bound Args)** | 0 KB | *N/A* | *N/A* |\n\n* **vs LemonSignal:**\n    * Uses **~14% less memory** in `Connect`.\n    * Uses **100% less memory** in `Fire` with Bound Args (0 KB vs 938 KB).\n\n* **vs FastSignal:**\n    * Uses **~27% less memory** in `new`.\n    * Uses **~27% less memory** in `Connect`.\n    * Uses **100% less memory** in `Fire` (0 KB vs 11.14 MB).\n\n## Inspiration\n\n**Signal** takes inspiration from both **[LemonSignal](https://github.com/Data-Oriented-House/LemonSignal)** and **[FastSignal](https://github.com/RBLXUtils/FastSignal)**. These libraries are fantastic.\nIf you do not want to use **Signal** and would prefer another option, I highly recommend either of them. However, if I had to choose between the two, I would recommend **LemonSignal**.\n","readmeTruncated":false}