{"id":20262092,"url":"https://github.com/saber-notes/abstract_sync","last_synced_at":"2026-02-11T16:02:08.764Z","repository":{"id":245751630,"uuid":"817008028","full_name":"saber-notes/abstract_sync","owner":"saber-notes","description":null,"archived":false,"fork":false,"pushed_at":"2024-12-14T01:00:31.000Z","size":40,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-08-05T13:08:23.083Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Dart","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/saber-notes.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"2024-06-18T20:39:21.000Z","updated_at":"2025-02-26T14:45:04.000Z","dependencies_parsed_at":"2024-11-14T11:30:04.551Z","dependency_job_id":"d8b8bfa5-0b18-4dfc-9a91-5dcfda9789a9","html_url":"https://github.com/saber-notes/abstract_sync","commit_stats":null,"previous_names":["saber-notes/abstract_sync"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/saber-notes/abstract_sync","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saber-notes%2Fabstract_sync","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saber-notes%2Fabstract_sync/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saber-notes%2Fabstract_sync/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saber-notes%2Fabstract_sync/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/saber-notes","download_url":"https://codeload.github.com/saber-notes/abstract_sync/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saber-notes%2Fabstract_sync/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29337001,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-11T16:00:30.228Z","status":"ssl_error","status_checked_at":"2026-02-11T16:00:25.398Z","response_time":97,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":[],"created_at":"2024-11-14T11:28:22.502Z","updated_at":"2026-02-11T16:02:08.749Z","avatar_url":"https://github.com/saber-notes.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"# abstract_sync\n\nA framework for synchronizing files to and from anything.\n\nI'm building and maintaining this package for my open source notetaking app,\n[Saber](https://github.com/saber-notes/saber).\n\n## Getting started\n\nThere is a bit of setup required for this package due to its abstract nature.\nYou can do all of the following in a single file (e.g. `my_syncer.dart`)\nor organize it however you like.\n\nYou can view a simple example of this in the\n[example](example/main.dart) file,\nor see a real-world example in\n[Saber's `saber_syncer.dart`](https://github.com/saber-notes/saber/blob/main/lib/data/nextcloud/saber_syncer.dart),\nwhich has added functionality for encryption and caching.\n\n1. Decide on your local and remote file classes.\n   E.g. Saber uses `File` for local files and `WebDavFile` for remote files.\n   I will refer to these as `MyLocalFile` and `MyRemoteFile` respectively,\n   but they can be anything you want.\n\n2. Extend the `AbstractSyncFile` class to use the correct generic types.\n\n   The `AbstractSyncFile` class is intended to help you store more information\n   other than just the local and remote files,\n   such as their path on the remote server, so add any additional fields you need.\n\n   ```dart\n   class MySyncFile extends AbstractSyncFile\u003cMyLocalFile, MyRemoteFile\u003e {\n     MySyncFile({\n       required super.remoteFile,\n       required super.localFile,\n     });\n\n     // Use your IDE to help you implement the necessary methods\n   }\n   ```\n\n3. Extend the `AbstractSyncInterface` with the correct generic types.\n\n   The `AbstractSyncInterface` class is how this package can interact with\n   your local and remote files.\n\n   Your class has to have a `const` constructor which means you can't use\n   variables that aren't final. However, you can use `static` variables\n   if you need to save state.\n\n   ```dart\n   class MySyncInterface extends AbstractSyncInterface\u003cMySyncFile, MyLocalFile, MyRemoteFile\u003e {\n     const MySyncInterface();\n\n     // Use your IDE to help you implement the necessary methods\n   }\n   ```\n\n4. Finally, we can create a `Syncer` object.\n\n   The `Syncer` object is what you will interact with outside of this file.\n\n   ```dart\n   final syncer = Syncer\u003cMySyncInterface, MySyncFile, MyLocalFile, MyRemoteFile\u003e(\n     const MySyncInterface(),\n   );\n   ```\n\n## Usage examples\n\n### Add a file to the upload queue\n```dart\nsyncer.uploader.enqueue(\n  // Provide any of syncFile, localFile, or remoteFile\n  localFile: MyLocalFile(...),\n);\n```\n\n### Add a file to the download queue\n```dart\nsyncer.downloader.enqueue(\n  // Provide any of syncFile, localFile, or remoteFile\n  remoteFile: MyRemoteFile(...),\n);\n```\n\n(The uploader and downloader have an identical interface.)\n\n### Check the server for changes (and download them)\n```dart\nawait syncer.downloader.refresh();\n```\n\n### Monitor the progress of uploads\n\n```dart\nif (syncer.downloader.isRefreshing) {\n  print('Still checking the server for changes');\n}\n\nfinal subscription = syncer.uploader.transferStream.listen((syncFile) {\n  print('Uploaded file: $syncFile');\n});\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaber-notes%2Fabstract_sync","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsaber-notes%2Fabstract_sync","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaber-notes%2Fabstract_sync/lists"}