{"id":26800614,"url":"https://github.com/ktsu-dev/appdatastorage","last_synced_at":"2026-07-04T01:04:49.719Z","repository":{"id":213893928,"uuid":"733734663","full_name":"ktsu-dev/AppDataStorage","owner":"ktsu-dev","description":"A .NET library for persistent application data storage using JSON serialization.","archived":false,"fork":false,"pushed_at":"2026-06-28T00:36:39.000Z","size":3161,"stargazers_count":1,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-06-28T02:19:34.823Z","etag":null,"topics":["app-data","application-data","backup","c","configuration","csharp","debounce","dotnet","file-system","json","net","persistence","serialization","settings","singleton","storage","system-io-abstractions","system-text-json","testable","thread-safety"],"latest_commit_sha":null,"homepage":"https://ktsu.dev","language":"C#","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/ktsu-dev.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS.md","dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":"COPYRIGHT.md","agents":null,"dco":null,"cla":null}},"created_at":"2023-12-20T02:16:29.000Z","updated_at":"2026-06-28T00:34:37.000Z","dependencies_parsed_at":"2026-02-16T08:05:13.594Z","dependency_job_id":null,"html_url":"https://github.com/ktsu-dev/AppDataStorage","commit_stats":null,"previous_names":["ktsu-io/appdatastorage","ktsu-dev/appdatastorage"],"tags_count":257,"template":false,"template_full_name":null,"purl":"pkg:github/ktsu-dev/AppDataStorage","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ktsu-dev%2FAppDataStorage","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ktsu-dev%2FAppDataStorage/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ktsu-dev%2FAppDataStorage/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ktsu-dev%2FAppDataStorage/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ktsu-dev","download_url":"https://codeload.github.com/ktsu-dev/AppDataStorage/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ktsu-dev%2FAppDataStorage/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34890795,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-28T02:00:05.809Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["app-data","application-data","backup","c","configuration","csharp","debounce","dotnet","file-system","json","net","persistence","serialization","settings","singleton","storage","system-io-abstractions","system-text-json","testable","thread-safety"],"created_at":"2025-03-29T20:18:10.697Z","updated_at":"2026-06-28T14:00:36.348Z","avatar_url":"https://github.com/ktsu-dev.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ktsu.AppDataStorage\n\n\u003e A .NET library for simple, persistent application data management with JSON serialization.\n\n[![License](https://img.shields.io/github/license/ktsu-dev/AppDataStorage.svg?label=License\u0026logo=nuget)](LICENSE.md)\n[![NuGet Version](https://img.shields.io/nuget/v/ktsu.AppDataStorage?label=Stable\u0026logo=nuget)](https://nuget.org/packages/ktsu.AppDataStorage)\n[![NuGet Version](https://img.shields.io/nuget/vpre/ktsu.AppDataStorage?label=Latest\u0026logo=nuget)](https://nuget.org/packages/ktsu.AppDataStorage)\n[![NuGet Downloads](https://img.shields.io/nuget/dt/ktsu.AppDataStorage?label=Downloads\u0026logo=nuget)](https://nuget.org/packages/ktsu.AppDataStorage)\n[![GitHub commit activity](https://img.shields.io/github/commit-activity/m/ktsu-dev/AppDataStorage?label=Commits\u0026logo=github)](https://github.com/ktsu-dev/AppDataStorage/commits/main)\n[![GitHub contributors](https://img.shields.io/github/contributors/ktsu-dev/AppDataStorage?label=Contributors\u0026logo=github)](https://github.com/ktsu-dev/AppDataStorage/graphs/contributors)\n[![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/ktsu-dev/AppDataStorage/dotnet.yml?label=Build\u0026logo=github)](https://github.com/ktsu-dev/AppDataStorage/actions)\n\n## Introduction\n\n`ktsu.AppDataStorage` is a .NET library designed to simplify the process of persisting application data. It stores configuration or state data as JSON files in the user's application data folder, with built-in safety mechanisms like automatic backups, debounced saves, and thread-safe operations. The library provides a singleton-like access pattern and supports custom subdirectories and file names for organizing data.\n\n## Features\n\n- **Easy-to-use API**: Inherit from `AppData\u003cT\u003e` and get automatic JSON persistence with `LoadOrCreate()`, `Save()`, and `Get()`.\n- **Automatic Backup**: Creates backup files before overwriting to prevent data loss, with timestamped collision handling.\n- **Safe Write Pattern**: Writes to a temporary file first, then atomically replaces the original to avoid corruption.\n- **Debounced Saves**: `QueueSave()` and `SaveIfRequired()` prevent frequent file writes with a 3-second debounce window.\n- **Thread-Safe Operations**: All file operations are synchronized with lock objects (uses `Lock` type on .NET 9+, `object` on earlier versions).\n- **Singleton Access**: `Get()` provides lazy-initialized, singleton-like access to your app data instance.\n- **Custom Storage Locations**: Support for custom subdirectories and file names via `LoadOrCreate()` overloads.\n- **File System Abstraction**: Uses `System.IO.Abstractions` for easy unit testing with mock file systems.\n- **Corrupt File Recovery**: Automatically falls back to backup files when the main data file is corrupt or missing.\n- **Dispose-on-Exit**: Registers for process exit to ensure queued saves are flushed before the application terminates.\n\n## Installation\n\n### Package Manager Console\n\n```powershell\nInstall-Package ktsu.AppDataStorage\n```\n\n### .NET CLI\n\n```bash\ndotnet add package ktsu.AppDataStorage\n```\n\n### Package Reference\n\n```xml\n\u003cPackageReference Include=\"ktsu.AppDataStorage\" Version=\"x.y.z\" /\u003e\n```\n\n## Usage Examples\n\n### Basic Example\n\nCreate a class that inherits from `AppData\u003cT\u003e`, where `T` is your custom data type.\n\n```csharp\nusing ktsu.AppDataStorage;\n\npublic class MySettings : AppData\u003cMySettings\u003e\n{\n    public string Theme { get; set; } = \"light\";\n    public int FontSize { get; set; } = 14;\n    public bool AutoSave { get; set; } = true;\n}\n\n// Load existing data or create a new instance\nvar settings = MySettings.LoadOrCreate();\nConsole.WriteLine(settings.Theme);    // \"light\"\nConsole.WriteLine(settings.FontSize); // 14\n```\n\n### Accessing the Singleton Instance\n\nThe `Get()` method provides a lazy-initialized singleton instance, automatically calling `LoadOrCreate()` on first access.\n\n```csharp\nusing ktsu.AppDataStorage;\n\n// Access the singleton from anywhere in your application\nvar settings = MySettings.Get();\nsettings.Theme = \"dark\";\nsettings.Save();\n\n// Same instance returned every time\nvar sameSettings = MySettings.Get();\nConsole.WriteLine(sameSettings.Theme); // \"dark\"\n```\n\n### Saving Data\n\nModify properties and call `Save()` to persist changes immediately.\n\n```csharp\nusing ktsu.AppDataStorage;\n\nvar settings = MySettings.Get();\nsettings.Theme = \"dark\";\nsettings.FontSize = 16;\nsettings.Save();\n```\n\n### Custom Storage Location\n\nUse overloads of `LoadOrCreate()` to store data in subdirectories or with custom file names.\n\n```csharp\nusing ktsu.AppDataStorage;\nusing ktsu.Semantics.Paths;\n\n// Store in a subdirectory\nvar profileData = MySettings.LoadOrCreate(RelativeDirectoryPath.Create(\"profiles\"));\n\n// Store with a custom file name\nvar customData = MySettings.LoadOrCreate(FileName.Create(\"user_preferences.json\"));\n\n// Both subdirectory and custom file name\nvar specificData = MySettings.LoadOrCreate(\n    RelativeDirectoryPath.Create(\"profiles\"),\n    FileName.Create(\"admin_settings.json\"));\n```\n\n## Advanced Usage\n\n### Queued and Debounced Saving\n\nFor scenarios with frequent updates (e.g., UI-driven changes), use `QueueSave()` to schedule a save that is debounced with a 3-second threshold. Call `SaveIfRequired()` periodically (e.g., in a game loop or timer) to flush queued saves.\n\n```csharp\nusing ktsu.AppDataStorage;\n\nvar settings = MySettings.Get();\nsettings.Theme = \"dark\";\nsettings.QueueSave();  // Schedules a save\n\n// Later, in your update loop or timer:\nsettings.SaveIfRequired();  // Saves only if 3+ seconds have elapsed since QueueSave\n\n// Or use the static convenience methods:\nMySettings.QueueSave();\nMySettings.SaveIfRequired();\n```\n\nQueued saves are also automatically flushed when the `AppData\u003cT\u003e` instance is disposed or when the process exits.\n\n### Testing with Mock File Systems\n\nThe library supports `System.IO.Abstractions` for testability. Configure a mock file system in your tests:\n\n```csharp\nusing System.IO.Abstractions.TestingHelpers;\nusing ktsu.AppDataStorage;\n\n// In test setup - each thread gets its own isolated instance\nAppData.ConfigureForTesting(() =\u003e new MockFileSystem());\n\n// Run your tests...\nvar data = MySettings.LoadOrCreate();\ndata.Theme = \"test\";\ndata.Save();\n\n// In test teardown\nAppData.ResetFileSystem();\n```\n\n### Directory and File Paths\n\nData is stored in a directory unique to the current application domain under the user's `%APPDATA%` folder. File names are derived from the class name in snake_case.\n\n```csharp\nusing ktsu.AppDataStorage;\n\n// View the storage path\nConsole.WriteLine(AppData.Path);\n// e.g., C:\\Users\\{user}\\AppData\\Roaming\\{AppDomainName}\n\n// File name is automatically generated from the class name\n// MySettings -\u003e my_settings.json\n```\n\n## API Reference\n\n### `AppData` Static Class\n\nProvides static helper methods and properties for managing application data storage.\n\n#### Properties\n\n| Name | Type | Description |\n| --- | --- | --- |\n| `Path` | `AbsoluteDirectoryPath` | The path where persistent data is stored for this application |\n\n#### Methods\n\n| Name | Return Type | Description |\n| --- | --- | --- |\n| `WriteText\u003cT\u003e(T appData, string text)` | `void` | Writes text to an app data file using a safe write pattern |\n| `ReadText\u003cT\u003e(T appData)` | `string` | Reads text from an app data file, falling back to backup if missing |\n| `QueueSave\u003cT\u003e(this T appData)` | `void` | Extension method that queues a debounced save operation |\n| `SaveIfRequired\u003cT\u003e(this T appData)` | `void` | Extension method that saves if the debounce threshold has elapsed |\n| `ConfigureForTesting(Func\u003cIFileSystem\u003e)` | `void` | Configures a mock file system factory for unit testing |\n| `ResetFileSystem()` | `void` | Resets the file system to the default implementation after testing |\n\n### `AppData\u003cT\u003e` Generic Abstract Class\n\nBase class for app data storage. Inherit from this class to create persistable data types.\n\n#### Type Constraints\n\n`T : AppData\u003cT\u003e, IDisposable, new()`\n\n#### Instance and Static Methods\n\n| Name | Return Type | Description |\n| --- | --- | --- |\n| `Get()` | `T` | Gets the lazy-initialized singleton instance of the app data |\n| `LoadOrCreate()` | `T` | Loads app data from file or creates a new instance if none exists |\n| `LoadOrCreate(RelativeDirectoryPath?)` | `T` | Loads or creates with a custom subdirectory |\n| `LoadOrCreate(FileName?)` | `T` | Loads or creates with a custom file name |\n| `LoadOrCreate(RelativeDirectoryPath?, FileName?)` | `T` | Loads or creates with both custom subdirectory and file name |\n| `Save()` | `void` | Serializes and saves the app data to its JSON file |\n| `QueueSave()` | `void` | Queues a debounced save for the singleton instance |\n| `SaveIfRequired()` | `void` | Saves the singleton instance if the debounce threshold has elapsed |\n| `Dispose()` | `void` | Disposes the instance, flushing any queued saves |\n\n## Contributing\n\nContributions are welcome! Feel free to open issues or submit pull requests.\n\n## License\n\nThis project is licensed under the MIT License. See the [LICENSE.md](LICENSE.md) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fktsu-dev%2Fappdatastorage","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fktsu-dev%2Fappdatastorage","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fktsu-dev%2Fappdatastorage/lists"}