{"id":"nsawill1405/signalx","name":"signalx","scope":"nsawill1405","platform":"roblox","description":"SignalX is a structured, memory-safe Roblox event and messaging system.","version":"0.1.0","latest":"0.1.0","versions":["0.1.0"],"license":"MIT","licenseRating":"safe","licenseCaveats":[],"licenseVerified":true,"dependencies":{},"integrity":"4b1d7f63f459049f5788761af758bcc8b5c5083ebd268a7ce4b7ef2847998b5e","likes":0,"downloads":0,"install":"forest install nsawill1405/signalx","url":"https://forest.dev/p/roblox/nsawill1405/signalx","files":"https://api.forest.dev/ai/package/roblox/nsawill1405/signalx/files","readme":"![SignalX Logo](logo.png)\n\n# SignalX\n\nSignalX is an advanced event and messaging system for Roblox focused on developer experience, memory safety, and production debugging.\n\nIt improves on native signals by adding scoped cleanup, listener priorities, middleware, tags, replay-on-connect, and strong safety defaults.\n\n## Overview\n\nSignalX is designed as a structured messaging layer:\n\n- Better API ergonomics for large codebases\n- Automatic cleanup patterns to prevent leaks\n- Built-in debug visibility\n- Extensible middleware pipeline\n\nDesign priorities:\n\n1. Developer experience\n2. Safety\n3. Debug visibility\n4. Performance\n\n## Features\n\n- Priority listeners (`Connect(fn, 100)`)\n- One-shot listeners (`Once`)\n- Safe execution (`pcall` per listener)\n- Middleware chain (`Use`)\n- Tagged listeners and bulk disconnect (`DisconnectTag`)\n- Scope-based cleanup (`Signal.Scope.new()`)\n- Replay last payload on connect (`Connect(fn, { replay = true })`)\n- Auto disconnect when instance is destroyed (`AutoDisconnect`)\n- Deferred firing (`FireDeferred`)\n- Listener warning threshold (`SetMaxListeners`)\n- Debug logging (`EnableDebug`)\n\n## Installation\n\nInstall with Wally:\n\n```toml\n[dependencies]\nSignalX = \"signalx/signalx@0.1.0\"\n```\n\nThen run:\n\n```bash\nwally install\n```\n\nUse in Roblox:\n\n```lua\nlocal Packages = game:GetService(\"ReplicatedStorage\").Packages\nlocal Signal = require(Packages.SignalX)\n```\n\n## Examples\n\n### Basic usage\n\n```lua\nlocal Signal = require(Packages.SignalX)\n\nlocal playerHit = Signal.new({ name = \"PlayerHit\" })\nplayerHit:Connect(function(player, damage)\n\tprint(player.Name, damage)\nend)\n\nplayerHit:Fire(game.Players:GetPlayers()[1], 15)\n```\n\n### Priority + once\n\n```lua\nlocal sig = Signal.new()\n\nsig:Connect(function() print(\"Low\") end, 0)\nsig:Connect(function() print(\"High\") end, 100)\nsig:Once(function() print(\"Only once\") end)\n\nsig:Fire()\nsig:Fire()\n```\n\n### Scope cleanup\n\n```lua\nlocal roundScope = Signal.Scope.new()\nlocal roundEnded = Signal.new()\n\nroundScope:Connect(roundEnded, function()\n\tprint(\"Round ended\")\nend)\n\nroundScope:Destroy() -- disconnects all tracked connections\n```\n\n### Middleware\n\n```lua\nlocal sig = Signal.new()\n\nsig:Use(function(next, ...)\n\tprint(\"Before\")\n\tnext(...)\n\tprint(\"After\")\nend)\n\nsig:Connect(function(message)\n\tprint(\"Message:\", message)\nend)\n\nsig:Fire(\"hello\")\n```\n\n### Tagged listeners\n\n```lua\nlocal sig = Signal.new()\n\nsig:Connect(function() end):Tag(\"UI\")\nsig:Connect(function() end):Tag(\"Combat\")\n\nsig:DisconnectTag(\"UI\")\n```\n\n### Replay last fire\n\n```lua\nlocal sig = Signal.new()\nsig:Fire(\"cached payload\")\n\nsig:Connect(function(value)\n\tprint(\"Replayed:\", value)\nend, { replay = true })\n```\n\n## Benchmarks\n\nSignalX includes an example benchmark script at:\n\n- `examples/Benchmark.server.lua`\n\nBenchmark guidance:\n\n1. Run benchmark in an empty place.\n2. Compare native `BindableEvent` and SignalX on your target device profile.\n3. Measure connect cost, fire cost, and disconnect-all cost.\n\nSignalX performance design choices:\n\n- Array-based listener storage\n- Dirty-sort only when needed\n- Cached listener count\n- Deferred compaction during active fire loops\n\n## Why SignalX?\n\nSignalX is not trying to be the largest feature set. It is focused on predictable, safe behavior in real Roblox projects where many systems communicate concurrently.\n\nIt gives teams a better default event layer by making cleanup easy, failures isolated, and runtime behavior observable.\n\n## API Reference\n\n### `Signal.new(config?)`\n\nCreate a signal.\n\n- `config.name: string?`\n- `config.maxListeners: number?`\n\n### `signal:Connect(fn, priorityOrOptions?)`\n\nConnect a listener.\n\n- `priorityOrOptions` can be:\n  - `number` (priority)\n  - `{ priority, replay, once, tags, autoDisconnect }`\n\nReturns a `Connection`.\n\n### `signal:Once(fn, priorityOrOptions?)`\n\nConnect and auto-disconnect after first execution.\n\n### `signal:Fire(...args)`\n\nRuns middleware and listeners immediately.\n\n### `signal:FireDeferred(...args)`\n\nDefers execution with `task.defer`.\n\n### `signal:Wait()`\n\nYields current coroutine until next fire and returns payload.\n\n### `signal:WaitAsync()`\n\nReturns a wait handle with:\n\n- `:andThen(callback)`\n- `:await()`\n- `:cancel()`\n\n### `signal:DisconnectAll()`\n\nDisconnect every listener.\n\n### `signal:DisconnectTag(tag)`\n\nDisconnect listeners that have `tag`.\n\n### `signal:Use(middlewareFn)`\n\nAdd middleware (`function(next, ...args)`).\n\n### `signal:EnableDebug(enabled?)`\n\nEnable debug logging.\n\n### `signal:SetName(name)`\n\nSet signal display name in debug output.\n\n### `signal:SetMaxListeners(count)`\n\nConfigure warning threshold for listener count.\n\n### `signal:GetListenerCount()`\n\nReturns the active listener count.\n\n### `Connection`\n\n- `connection:Disconnect()`\n- `connection:Tag(tag)`\n- `connection:HasTag(tag)`\n- `connection:GetTag()`\n- `connection:GetTags()`\n- `connection:AutoDisconnect(instance)`\n- `connection:IsConnected()`\n- `connection:GetPriority()`\n\n### `Scope`\n\n- `local scope = Signal.Scope.new()`\n- `scope:Track(connection)`\n- `scope:Connect(signal, fn, priorityOrOptions?)`\n- `scope:Destroy()`\n\n## Testing\n\nScripts included:\n\n- `test/Signal.unit.lua`\n- `test/Signal.stress.lua`\n\nThe unit tests cover:\n\n- Connect / Fire\n- Once behavior\n- Priority ordering\n- Disconnect logic\n- Scope cleanup\n- Replay behavior\n\nThe stress test covers:\n\n- 1000+ listeners\n- Rapid fire loop\n- Listener cleanup validation\n","readmeTruncated":false}