{"id":"wutwere/drop","name":"drop","scope":"wutwere","platform":"roblox","description":"A datastore library built on ACID principles.","version":"1.1.4","latest":"1.1.4","versions":["1.1.0","1.1.1","1.1.2","1.1.3-rc.1","1.1.3-rc.2","1.1.3-rc.3","1.1.3","1.1.4-rc.1","1.1.4-rc.2","1.1.4","1.2.0-rc.1"],"license":"MIT","licenseRating":"safe","licenseCaveats":["The package archive does not include its license text; the license is declared in its manifest metadata."],"licenseVerified":false,"dependencies":{},"integrity":"ec6f2f78e3671b750f85e83088e9153f7ddf014d85e89785e570420c4092e919","likes":0,"downloads":0,"install":"forest install wutwere/drop","url":"https://forest.dev/p/roblox/wutwere/drop","files":"https://api.forest.dev/ai/package/roblox/wutwere/drop/files","readme":"# Drop\n\nA datastore library.\n\n## Quick start\n\nDrop has two main types: schemas and stores. Let's make both.\n\n```luau\nlocal drop = require(\"@drop\")\n\nlocal schema = drop.schema({\n\tcoins = 100,\n\titems = {} :: { [string]: true },\n})\n\nlocal store = drop.store({\n\tname = \"players\",\n\tschema = schema,\n})\n```\n\nFor Studio development and integration tests, you can swap in a mock datastore service instead of Roblox's live datastores.\n\n```luau\nlocal mock = drop.mockdatastoreservice.new()\n\nlocal store = drop.store({\n\tname = \"players\",\n\tschema = schema,\n\tdatastoreservice = mock,\n})\n```\n\nIf you just want an isolated in-memory backend for a store, you can also pass `usemock = true`.\n\nLet's view jack's data. To view the data of any key, call `drop.viewasync`. This function yields and loads data directly from storage. If the data cannot be loaded for any reason, then this function will error.\n\n```luau\nlocal data = drop.viewasync(store, \"jack\")\n```\n\nJack purchased a developer product for 1000 coins, so let's give that to him. To update data use `drop.updateasync`. This function yields while it attempts to apply the update directly to storage. If the update fails for any reason, then the function will error. This should be the function you use when you need to know if an update was successful.\n\n```luau\nlocal success = pcall(drop.updateasync, store, \"jack\", function(data)\n\treturn {\n\t\tcoins = data.coins + 1000,\n\t\titems = data.items,\n\t}\nend)\n\nif success then\n\treturn Enum.ProductPurchaseDecision.PurchaseGranted\nelse\n\treturn Enum.ProductPurchaseDecision.NotProcessedYet\nend\n```\n\n> [!TIP]\n> `drop.updateasync` implements its own internal retry logic. Don't repeatedly call `drop.updateasync` if it errors.\n\nWe don't want to worry about catching errors, and we want updates to apply immediately. That means we're going to need to open a session. To start a session, call `drop.startsession`. This function does not yield, but the session will not be immediately available. To wait for the key's session to be available, call `drop.waitforsession`.\n\n```luau\ndrop.startsession(store, \"jack\")\ndrop.waitforsession(store, \"jack\")\n```\n\nWhen your server is shutting down, call `drop.closeasync(store)` yourself during your shutdown sequence to flush and stop all active sessions for that store.\n\n```luau\ngame:BindToClose(function()\n\tdrop.closeasync(store)\nend)\n```\n\nThe session has been started, we can now view and update the data in the same way without yielding. Let's make a purchase item function. All updates passed to drop should be atomic and pure. Update functions may be called any number of times. To cancel an update, return `nil`.\n\n```luau\nlocal function purchase(key: string, item: string, cost: number)\n\tdrop.update(store, key, function(data)\n\t\tif data.coins >= cost and not data.items[item] then\n\t\t\tlocal items = table.clone(data.items)\n\t\t\titems[item] = true\n\t\t\t\n\t\t\treturn {\n\t\t\t\tcoins = data.coins - cost,\n\t\t\t\titems = items,\n\t\t\t}\n\t\telse\n\t\t\treturn nil\n\t\tend\n\tend)\nend\n```\n\nUpdates from `drop.update` apply immediately, but we want to see this data changing. For that, we can make an observer. Observers take functions that get called every time data updates. The data may be the same, or it may be different.\n\n```luau\ndrop.observe(store, function(key, data)\n\tprint(`{key} has {data.coins} coins!`)\nend)\n```\n\nMarcus and Jack want to trade items. When an update needs to apply to multiple keys, transactions should be used.\n\n```luau\ndrop.txasync(function(tx)\n\ttx(store, \"jack\", function(data)\n\t\tif not data.items[\"sword\"] then\n\t\t\treturn nil\n\t\tend\n\n\t\tlocal items = table.clone(data.items)\n\t\titems[\"sword\"] = nil\n\t\titems[\"horn\"] = true\n\n\t\treturn {\n\t\t\tcoins = data.coins,\n\t\t\titems = items,\n\t\t}\n\tend)\n\n\ttx(store, \"marcus\", function(data)\n\t\tif not data.items[\"horn\"] then\n\t\t\treturn nil\n\t\tend\n\n\t\tlocal items = table.clone(data.items)\n\t\titems[\"sword\"] = true\n\t\titems[\"horn\"] = nil\n\n\t\treturn {\n\t\t\tcoins = data.coins,\n\t\t\titems = items,\n\t\t}\n\tend)\nend)\n```\n\n## Help\n\nYou can ask questions and talk to maintainers either here on github, or in the [Roblox OSS discord](https://quenty.org/oss/conduct).\n","readmeTruncated":false}