{"id":"kartzrbx/dataservicev2","name":"dataservicev2","scope":"kartzrbx","platform":"roblox","description":"KeepData — typed player stores for Roblox (formerly DataServiceV2). Features: - CreateStore / Server Init with ProfileStore persistence and QuickNet replication - Typed path tokens (Store.Paths.Currencies.Coins) — PathOf, Patch, Watch on handles - Multi-store client replication (Client:Init StoreNames, GetStore) - OrderedDataStore leaderboards with delta/full sync and client rank signals - Migrations (TemplateVersion), Policy (rate limit, ACL, audit), session-only data - Transient overlay API fo","version":"3.2.0","latest":"3.2.0","versions":["0.1.0","0.1.1","1.0.0","1.0.1","2.2.0","2.2.1","2.2.2","2.3.0","2.3.1","2.3.2","2.3.3","1.1.0","2.0.1","2.0.2","2.0.3","2.0.4","2.0.5","2.0.6","2.0.7","2.0.8","2.0.9","2.1.0","2.1.1","2.1.4","2.1.6","2.1.7","2.1.8","2.1.9","2.1.10","2.1.11","2.1.12","2.1.13","2.1.14","2.1.15","3.2.0"],"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":"7bd0ae41e3394112b21d92fd1f541eeda56c2a7ce7037b59978b616ba972b20a","likes":0,"downloads":0,"install":"forest install kartzrbx/dataservicev2","url":"https://forest.dev/p/roblox/kartzrbx/dataservicev2","files":"https://api.forest.dev/ai/package/roblox/kartzrbx/dataservicev2/files","readme":"# DataServiceV2\n\nHigh-performance Roblox data service with automatic server/client replication, embedded ProfileStore persistence, typed path tokens, QuickNet transport, and Janitor-based connection lifecycle management.\n\n## Instalação\n\n`wally.toml`:\n\n```toml\n[dependencies]\nDataServiceV2 = \"kartzrbx/dataservicev2@2.0.6\"\n```\n\n```bash\nwally install\n```\n\n## Uso\n\n### Exemplo completo (Server + Client)\n\nUse este exemplo como base de produção.\n\n**ServerScriptService/DataBootstrap.server.lua**\n\n```lua\nconst Players = game:GetService(\"Players\")\nconst ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\nconst DataService = require(ReplicatedStorage.Packages.DataServiceV2).Server\n\nconst TEMPLATE = {\n    Currencies = {\n        Coins = 0,\n        Gems = 0,\n    },\n    Inventory = {},\n    Stats = {\n        Level = 1,\n        XP = 0,\n    },\n}\n\nDataService:Init({\n    Template = TEMPLATE,\n    StoreName = \"PlayerDataV2\",\n    StrictPaths = true,\n    AutoCreateMissingTables = false,\n})\n\nPlayers.PlayerAdded:Connect(function(player)\n    const data = DataService:WaitFor(player)\n    if not data then\n        return\n    end\n\n    -- Path token (recomendado)\n    data:Update(data.Currencies.Coins, function(current)\n        return (current or 0) + 100\n    end)\n\n    data:Update(data.Stats.XP, function(current)\n        return (current or 0) + 25\n    end)\n\n    data:GetChangedSignal(data.Currencies.Coins):Connect(function(newValue, oldValue)\n        print((\"[Server] %s Coins: %s -> %s\"):format(player.Name, tostring(oldValue), tostring(newValue)))\n    end)\n\n    -- Compatibilidade legada (continua suportado)\n    data:Set({ \"Currencies\", \"Gems\" }, 10)\nend)\n\nPlayers.PlayerRemoving:Connect(function(player)\n    const stats = DataService:GetBufferStats(player)\n    if stats then\n        print((\"[Server] Buffer %s | bytes=%d messages=%d\"):format(player.Name, stats.Bytes, stats.Messages))\n    end\nend)\n```\n\n**StarterPlayerScripts/DataClient.client.lua**\n\n```lua\nconst ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\nconst DataService = require(ReplicatedStorage.Packages.DataServiceV2).Client\n\nconst data = DataService:Init()\n\nprint(\"[Client] Coins initial:\", data:Get(data.Currencies.Coins))\n\ndata:GetChangedSignal(data.Currencies.Coins):Connect(function(newValue, oldValue)\n    print((\"[Client] Coins changed: %s -> %s\"):format(tostring(oldValue), tostring(newValue)))\nend)\n\ndata:GetChangedSignal(data.Stats.XP):Connect(function(newValue)\n    print(\"[Client] XP changed:\", newValue)\nend)\n\ntask.spawn(function()\n    while task.wait(10) do\n        const buffer = DataService:GetBufferStats()\n        if buffer then\n            print((\"[Client] Buffer | bytes=%d messages=%d utilization=%.2f\"):format(\n                buffer.Bytes or 0,\n                buffer.Messages or 0,\n                buffer.LastUtilization or 0\n            ))\n        end\n    end\nend)\n```\n\n## Why DataServiceV2 is Powerful\n\n- Built for scale: QuickNet-based replication reduces CPU and bandwidth overhead compared to plain RemoteEvent flows.\n- Safer writes: strict path validation prevents silent corruption caused by invalid dynamic paths.\n- Developer speed: chainable path tokens (`data.Currencies.Coins`) improve readability and reduce human error.\n- Full compatibility: legacy array paths keep working, so migration can be incremental.\n- Operational visibility: built-in buffer utilization stats help inspect replication pressure in real time.\n- Lifecycle safety: Janitor cleanup prevents connection leaks in both server and client runtime.\n\n### Servidor\n\n```lua\nlocal DataService = require(game.ReplicatedStorage.Packages.DataServiceV2).Server\n\nlocal TEMPLATE = {\n    Coins = 0,\n    Inventory = {},\n}\n\nDataService:Init({\n    Template = TEMPLATE,\n    StoreName = \"PlayerData\", -- opcional, default \"PlayerData\"\n    UseMock = false,           -- opcional, true em Studio sem API services\n    Exclude = { { \"Secret\" } },-- caminhos que não replicam ao cliente\n    StrictPaths = true,        -- opcional, default true (evita paths inválidos)\n    AutoCreateMissingTables = false, -- opcional, default false (modo legado = true)\n})\n\ngame.Players.PlayerAdded:Connect(function(player)\n    local data = DataService:WaitFor(player)\n    -- Novo (recomendado): path token com autocomplete\n    data:Set(data.Coins, 100)\n\n    -- Compatibilidade legada (continua funcionando)\n    data:Set({ \"Coins\" }, 100)\nend)\n```\n\n### Cliente\n\n```lua\nlocal DataService = require(game.ReplicatedStorage.Packages.DataServiceV2).Client\n\nlocal data = DataService:Init() -- yields até receber snapshot inicial\nprint(data:Get(data.Coins))\n\ndata:GetChangedSignal(data.Coins):Connect(function(new, old)\n    print(\"Coins:\", old, \"->\", new)\nend)\n```\n\n## API\n\n### Server\n\n- `Init(options)` — `{ Template, StoreName?, UseMock?, KeyPrefix?, Exclude?, StrictPaths?, AutoCreateMissingTables? }`\n- `WaitFor(player)` → Data\n- `Get(player)` → Data?\n- `HasData(player)` → boolean\n- `GetProfile(player)` → ProfileStore profile\n- `GetBufferStats(player?)` → métricas de buffer/uso de rede da replicação\n- `Destroy()` → limpa conexões e listeners do serviço\n- `Paths` → token raiz de caminhos (disponível após `Init`)\n\n### Client\n\n- `Init()` → Data (yields)\n- `WaitForData()` → Data\n- `Get()` → Data?\n- `GetBufferStats()` → últimas métricas de buffer enviadas pelo servidor\n- `Destroy()` → limpa conexões/listeners do cliente\n\n### Data\n\n- `Get(path?)` — lê (caminho vazio = root), aceita token ou array legado\n- `Typed<T>(path)` — força tipo do caminho para inferência forte em sinais e Get/Set/Update\n- `Set(path, value)` — server-only, aceita token ou array legado\n- `Update(path, fn)` — server-only, aceita token ou array legado\n- `ArrayInsert(path, value, index?)` — server-only, aceita token ou array legado\n- `ArrayRemove(path, index)` — server-only, aceita token ou array legado\n- `GetChangedSignal(path)` → Signal(new, old), aceita token ou array legado\n- `GetIndexChangedSignal(path)` → Signal(key, new, old), aceita token ou array legado\n- `GetArrayInsertedSignal(path)` → Signal(index, value), aceita token ou array legado\n- `GetArrayRemovedSignal(path)` → Signal(index, removed), aceita token ou array legado\n\n### Caminhos tipados / seguros (novo)\n\nAgora você pode usar acesso encadeado para montar caminhos sem tabelas manuais:\n\n```lua\ndata:Set(data.Currencies.Coins, 100)\nlocal coins = data:Get(data.Currencies.Coins)\n\ndata:GetChangedSignal(data.Currencies.Coins):Connect(function(new, old)\n    print(old, new)\nend)\n\nlocal coinsPath = data:Typed<number>(data.Currencies.Coins)\ndata:GetPathChangedSignal(coinsPath):Connect(function(newValue, oldValue)\n    -- newValue e oldValue inferidos como number\n    print(newValue, oldValue)\nend)\n```\n\nIsso reduz erros de digitação em paths (`{\"Currencies\",\"Coins\"}`), mantendo compatibilidade com o formato antigo.\n\n### Compatibilidade e modo legado\n\n- Compatível com chamadas antigas via array: `data:Set({ \"Currencies\", \"Coins\" }, 100)`\n- `StrictPaths = true` (default): bloqueia escrita em caminho inválido\n- `AutoCreateMissingTables = false` (default): não cria estruturas faltantes automaticamente\n- Para comportamento antigo (mais permissivo), use:\n\n```lua\nDataService:Init({\n    Template = TEMPLATE,\n    StrictPaths = false,\n    AutoCreateMissingTables = true,\n})\n```\n\n## Dependências\n\n- `sleitnick/signal@2.0.3` (instalado automaticamente pelo Wally)\n- `breadboardengineer1234/quicknet@0.3.0` (replicação otimizada)\n- `1foreverhd/janitor@1.18.15` (gerenciamento de conexões/recursos)\n- ProfileStore — embutido, sem necessidade de instalação separada\n","readmeBorrowedFrom":{"version":"2.1.4","source":"wally"},"readmeTruncated":false}