{"id":13991718,"url":"https://github.com/jamsocket/aper","last_synced_at":"2025-04-12T06:21:32.560Z","repository":{"id":41399424,"uuid":"337900656","full_name":"jamsocket/aper","owner":"jamsocket","description":"Synchronizable structs for Rust.","archived":false,"fork":false,"pushed_at":"2024-10-05T16:49:39.000Z","size":22499,"stargazers_count":312,"open_issues_count":6,"forks_count":12,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-04-05T22:42:48.027Z","etag":null,"topics":["rust","state","state-machine"],"latest_commit_sha":null,"homepage":"https://aper.dev","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jamsocket.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-02-11T01:24:54.000Z","updated_at":"2025-04-03T21:43:50.000Z","dependencies_parsed_at":"2024-01-15T15:47:27.809Z","dependency_job_id":"7f0bcde3-fc16-4996-bb7f-5683fb270b8f","html_url":"https://github.com/jamsocket/aper","commit_stats":{"total_commits":347,"total_committers":7,"mean_commits":49.57142857142857,"dds":"0.32564841498559083","last_synced_commit":"647577186b7314a2786b03c64eab425151c5faf1"},"previous_names":["jamsocket/aper"],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamsocket%2Faper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamsocket%2Faper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamsocket%2Faper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamsocket%2Faper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jamsocket","download_url":"https://codeload.github.com/jamsocket/aper/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248525773,"owners_count":21118750,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["rust","state","state-machine"],"created_at":"2024-08-09T14:01:32.947Z","updated_at":"2025-04-12T06:21:32.520Z","avatar_url":"https://github.com/jamsocket.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# [Aper](https://aper.dev)\n\n[![GitHub Repo stars](https://img.shields.io/github/stars/drifting-in-space/aper?style=social)](https://github.com/drifting-in-space/aper)\n[![crates.io](https://img.shields.io/crates/v/aper.svg)](https://crates.io/crates/aper)\n[![docs.rs](https://img.shields.io/badge/docs-release-brightgreen)](https://docs.rs/aper/)\n[![wokflow state](https://github.com/drifting-in-space/aper/workflows/build/badge.svg)](https://github.com/drifting-in-space/aper/actions/workflows/rust.yml)\n\nAper is a Rust library for data synchronization over a network.\n\nAper supports optimistic updates and arbitrary business logic, making it useful for real-time collabrative and agentic use cases.\n\n## Introduction\n\n**(Aper is mid-refactor. Docs and examples may be out of date.)**\n\nTypes marked with the `AperSync` trait can be stored in the `Store`, Aper's synchronizable data store.\nAper includes several data structures that implement `AperSync` in the `aper::data_structures` module, which\ncan be used as building blocks to build your own synchronizable types.\n\nYou can use these, along with the `AperSync` derive macro, to compose structs that also implement `AperSync`.\n\n```rust\nuse aper::{AperSync, data_structures::{Atom, Map}};\nuse uuid::Uuid;\n\n#[derive(AperSync)]\nstruct ToDoItem {\n   pub done: Atom\u003cbool\u003e,\n   pub name: Atom\u003cString\u003e,\n}\n\n#[derive(AperSync)]\nstruct ToDoList {\n   pub items: Map\u003cUuid, ToDoItem\u003e,\n}\n```\n\nTo synchronize from the server to clients, Aper replicates changes to the `Store` when it receives them. To synchronize\nfrom clients to servers, we instead send *intents* to the server.\n\nIntents are represented as a serializable `enum` representing every possible action a user might take on the data.\nFor example, in our to-do list, that represents creating a task, renaming a task, marking a task as (not) done, or\nremoving completed items.\n\n```rust\nuse aper::Aper;\n\n#[derive(Serialize, Deserialize, Clone, std::cmp::PartialEq)]\nenum ToDoIntent {\n    CreateTask {\n        id: Uuid,\n        name: String,\n    },\n    RenameTask {\n        id: Uuid,\n        name: String,\n    },\n    MarkDone {\n        id: Uuid,\n        done: bool,\n    },\n    RemoveCompleted,\n}\n\nimpl Aper for ToDoList {\n    type Intent = ToDoIntent;\n    type Error = ();\n\n    fn apply(\u0026mut self, intent: \u0026ToDoIntent) -\u003e Result\u003c(), ()\u003e {\n        match intent {\n            ToDoIntent::CreateTask { id, name } =\u003e {\n                let mut item = self.items.get_or_create(id);\n                item.name.set(name.to_string());\n                item.done.set(false);\n            },\n            ToDoIntent::RenameTask { id, name } =\u003e {\n                // Unlike CreateTask, we bail early with an `Err` if\n                // the item doesn't exist. Most likely, the server has\n                // seen a `RemoveCompleted` that removed the item, but\n                // a client attempted to rename it before the removal\n                // was synced to it.\n                let mut item = self.items.get(id).ok_or(())?;\n                item.name.set(name.to_string());\n            }\n            ToDoIntent::MarkDone { id, done } =\u003e {\n                let mut item = self.items.get(id).ok_or(())?;\n                item.done.set(*done);\n            }\n            ToDoIntent::RemoveCompleted =\u003e {\n                // TODO: need to implement .iter() on Map first.\n            }\n        }\n\n        Ok(())\n    }\n}\n```\n\n---\n\n**Aper is rapidly evolving. Consider this a technology preview.** See the [list of issues outstanding for version 1.0](https://github.com/drifting-in-space/aper/labels/v1-milestone)\n\n- [Documentation](https://docs.rs/aper/)\n- [Examples](https://github.com/drifting-in-space/aper/tree/main/examples)\n- [Talk on Aper for Rust Berlin (20 minute video)](https://www.youtube.com/watch?v=HNzeouj0eKc\u0026t=1852s)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjamsocket%2Faper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjamsocket%2Faper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjamsocket%2Faper/lists"}