{"id":"xoifaii/ledger","name":"ledger","scope":"xoifaii","platform":"roblox","description":"Lock-free event-sourced player data for Roblox. State is a fold over validated ops, no session locks.","version":"7.0.2","latest":"7.0.2","versions":["1.1.0","1.1.1","2.0.0","2.0.1","2.0.2","2.0.3","2.1.0","2.2.0","3.0.0","3.0.1","3.1.1","3.1.2","3.2.0","3.4.0","4.0.0","4.0.1","4.0.2","4.0.3","4.0.4","4.0.5","4.0.6","4.1.0","4.1.1","4.3.0","4.3.1","4.4.0","4.4.1","4.5.0","5.0.0","5.0.1","5.1.1","5.2.0","5.2.1","5.2.2","5.3.0","5.4.0","5.4.1","6.0.0","6.0.1","6.1.0","6.2.0","7.0.0","7.0.1","7.0.2"],"license":"MIT","licenseRating":"safe","licenseCaveats":[],"licenseVerified":true,"dependencies":{},"integrity":"4330a5c29105070b1feee2a0aef1ea73a8904d55434d70126f6a10280461e5a9","likes":0,"downloads":1,"install":"forest install xoifaii/ledger","url":"https://forest.dev/p/roblox/xoifaii/ledger","files":"https://api.forest.dev/ai/package/roblox/xoifaii/ledger/files","readme":"<div align=\"center\">\n\n# Ledger\n[![GitHub](https://img.shields.io/badge/GitHub-Ledger-181717?style=for-the-badge&logo=github&logoColor=white)](https://github.com/XoifaiI/Ledger) [![Docs](https://img.shields.io/badge/Docs-Read-8B5CF6?style=for-the-badge)](https://xoifaii.github.io/LedgerDocs/)\n\n</div>\n\n## What is this?\n\nA datastore library for Roblox with no session locks. You never write state. You write down\nthe change you want, a function you own decides whether it is valid, and Ledger saves each\nchange exactly once.\n\n```luau\ntype PlayerData = {\n\tGold: number,\n\tItems: { [string]: number },\n}\n\ntype PlayerOps = {\n\tSpendGold: { Amount: number },\n}\n\nlocal PlayerStore = Ledger.New<<PlayerData, PlayerOps>>({\n\tName = \"PlayerData\",\n\tKeys = \"Player\",\n\tDefault = { Gold = 100, Items = {} },\n\tMustExist = false,\n\tErasable = true,\n\tReducer = function(Data, Op)\n\t\tif Op.Kind == \"SpendGold\" then\n\t\t\tif Op.Amount > Data.Gold then\n\t\t\t\treturn nil -- refused, on every server\n\t\t\tend\n\t\t\tlocal New = table.clone(Data)\n\t\t\tNew.Gold -= Op.Amount\n\t\t\treturn New\n\t\tend\n\t\treturn nil\n\tend,\n})\n\nlocal Session = PlayerStore:Load(Player)\nif Session then\n\tlocal Ok, Why = Session:Commit({ Kind = \"SpendGold\", Amount = 25 })\n\tif not Ok then\n\t\twarn(\"Couldn't spend gold:\", Why)\n\tend\nend\n```\n\nTwo servers spend the same 100 gold at once: the reducer accepts one and refuses the other.\nEvery server agrees, every time, and every call says what happened.\n\nA session lock serializes writers. A validating fold makes the invalid state unreachable,\nwhich is a stronger guarantee that also costs nothing when a server crashes: no lease to wait\nout, no locked player join stall, no side channel to touch someone offline or on another\nserver. Changes are ops with names, and a reducer validates them.\n\n## Features\n\n- **Lock free** | every server, same result, no locks\n- **Cross server** | write to any player, even offline\n- **Entity stores** |  clans, listings, world records\n- **Transactions** | up to 29 players or keys, all move or none do\n- **Balances** | gold keeps moving while a trade is in progress\n- **Limited items** | sold from every server, never oversold\n- **Global counters** | every server adds, reading is free\n- **Migrations** | old servers can't corrupt new data\n- **Idempotent** | a write retried applies one time\n- **Typed ops** | name them once, every write is checked\n- **Loud misuse** | bad code throws, immediately\n\n## Installing\n\n**Wally**\n\n```toml\n[dependencies]\nLedger = \"xoifaii/ledger@7.0.2\"\n```\n\n**Model file**: insert the [Ledger](https://github.com/XoifaiI/Ledger/releases) module anywhere server side.\n\n**roblox-ts**\n\n```\nnpm install @xoifail/ledger\n```\nThe same types work for TypeScript: see [Types](https://xoifaii.github.io/LedgerDocs/docs/reference/types).\n\n## License\n\nThis project has the [MIT License](https://github.com/XoifaiI/Ledger/blob/main/LICENSE).\n","readmeTruncated":false}