{"id":"felixcodestech/miniknit","name":"miniknit","scope":"felixcodestech","platform":"roblox","description":"A small, predictable Roblox framework: service/controller loader, Signal, Trove, observers.","version":"0.2.0","latest":"0.2.0","versions":["0.1.0","0.2.0"],"license":"MIT","licenseRating":"safe","licenseCaveats":[],"licenseVerified":true,"dependencies":{},"integrity":"591dfaf7d2e85fd668dc9ab3a4dec5fbaea25c427d252ee23f5fa33435fa1c34","likes":0,"downloads":0,"install":"forest install felixcodestech/miniknit","url":"https://forest.dev/p/roblox/felixcodestech/miniknit","files":"https://api.forest.dev/ai/package/roblox/felixcodestech/miniknit/files","readme":"# MiniKnit\n\nA small, predictable Roblox framework: a service/controller loader, a Signal, a Trove and two observers. No dependencies.\n\n## Install\n\n```toml\n[dependencies]\nMiniKnit = \"felix/miniknit@0.1.0\"\n```\n\n## Boot\n\nOne entry script per side, and nothing else in it.\n\n```lua\nlocal MiniKnit = require(ReplicatedStorage.Packages.MiniKnit)\n\nMiniKnit.Start(ServerScriptService.Services)\n```\n\nOn the server every ModuleScript under the container whose name ends in `Service` is loaded, on the client every one ending in `Controller`. Everything else is ignored. Modules are required, registered, `Init`ed one at a time, then `Start`ed in their own thread. `Init` must not yield and may only resolve dependencies; a `Start` that errors is warned about and leaves the others running.\n\n## API\n\n| Call | Does |\n| --- | --- |\n| `MiniKnit.Start(container)` | Boots every service or controller under `container`. Once only. |\n| `MiniKnit.OnStarted()` | Yields until every `Init` has finished and every `Start` has been spawned. |\n| `MiniKnit.IsStarted()` | Whether the boot has got that far. |\n| `MiniKnit.Services` / `MiniKnit.Controllers` | Read-only registry, keyed by module name. |\n| `MiniKnit.Signal` | `new`, `Connect`, `Once`, `Fire`, `Wait`, `DisconnectAll`, `Destroy`. |\n| `MiniKnit.Trove` | `new`, `Add`, `Connect`, `Clean`, `Destroy`. |\n| `MiniKnit.ObservePlayers(fn)` | Runs `fn(player)` for everyone here and everyone later, returns `stop()`. |\n| `MiniKnit.ObserveTag(tag, fn, ancestors?)` | Same, for tagged instances under `ancestors` (default `{ workspace }`). |\n\nEach observed function may return a cleanup function, which runs when the player leaves or the instance loses its tag, is destroyed, streams out or moves out of the allowed ancestors.\n\n## A service\n\n```lua\n-- Requires\nlocal MiniKnit = require(ReplicatedStorage.Packages.MiniKnit)\n\n-- Dependencies\nlocal DataService: typeof(require(script.Parent.DataService))\n\n-- Module\nlocal ShopService = {}\nShopService.ItemPurchased = MiniKnit.Signal.new()\n\n-- Lifecycle\nfunction ShopService.Init()\n\tDataService = MiniKnit.Services.DataService\nend\n\nfunction ShopService.Start()\n\ttrove:Connect(ShopService.ItemPurchased, onItemPurchased)\nend\n\nreturn ShopService\n```\n\n`test/Scenarios/Boot/Example` holds the full version, with every section a service may have, and is the reference to copy from.\n\n## Tests\n\n`test.project.json` builds a place that runs every scenario in a single playtest: the ones under `Scenarios/Fail` each make the boot fail in one specific way, the ones under `Scenarios/Boot` boot together. Each check prints a `[Test] PASS` or `[Test] FAIL` line, and each side prints a summary.\n\n```\nrojo serve test.project.json\n```\n","readmeTruncated":false}