{"id":21206210,"url":"https://github.com/rtmigo/squirrel_dart","last_synced_at":"2026-04-29T13:34:58.730Z","repository":{"id":41448532,"uuid":"448618463","full_name":"rtmigo/squirrel_dart","owner":"rtmigo","description":"Accumulates structured log entries in a file-based local storage before sending them to the server","archived":false,"fork":false,"pushed_at":"2022-11-11T00:49:33.000Z","size":89,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"staging","last_synced_at":"2025-06-07T22:41:03.088Z","etag":null,"topics":["database","hive","log","logging"],"latest_commit_sha":null,"homepage":"","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/rtmigo.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}},"created_at":"2022-01-16T17:04:23.000Z","updated_at":"2022-01-18T23:43:58.000Z","dependencies_parsed_at":"2023-01-21T08:39:18.676Z","dependency_job_id":null,"html_url":"https://github.com/rtmigo/squirrel_dart","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rtmigo/squirrel_dart","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rtmigo%2Fsquirrel_dart","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rtmigo%2Fsquirrel_dart/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rtmigo%2Fsquirrel_dart/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rtmigo%2Fsquirrel_dart/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rtmigo","download_url":"https://codeload.github.com/rtmigo/squirrel_dart/tar.gz/refs/heads/staging","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rtmigo%2Fsquirrel_dart/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32427738,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-29T13:34:34.882Z","status":"ssl_error","status_checked_at":"2026-04-29T13:34:29.830Z","response_time":110,"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":["database","hive","log","logging"],"created_at":"2024-11-20T20:54:49.337Z","updated_at":"2026-04-29T13:34:58.703Z","avatar_url":"https://github.com/rtmigo.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![stability-experimental](https://img.shields.io/badge/stability-experimental-orange.svg)](https://github.com/mkenney/software-guides/blob/master/STABILITY-BADGES.md#experimental)\n\n# [squirrel](https://github.com/rtmigo/squirrel_dart)\n\n`Squirrel` is a FIFO queue. The queue is stored in the file system and persists\nacross application restarts.\n\nQueue elements are any JSON-compatible objects (`Map`, `List`, `Set`, `String`,\n`int`, `double`, `null`) wrapped to\n[`JsonNode`](https://pub.dev/packages/jsontree). Also, each element of the queue\nhas a unique identifier, a timestamp, and an optional reference to the parent\nelement.\n\n`Squirrel` is intended to be a temporary buffer for storing log data before\nsending it to the server. Upon request, the object returns its elements grouped\ninto chunks. Iterators allow both simple reading of elements, and reading with\nautomatic deletion after sending.\n\n## Adding entries\n\n```dart\nimport 'package:squirrel/squirrel.dart';\n\nvoid main() {\n  final squirrel = await Squirrel.open(File(\"/path/to/db\"));\n\n  // The argument to addEvent is arbitrary data. It is only \n  // important that it can be converted to JSON.\n\n  await squirrel.add('Text entry');\n\n  await squirrel.add(['List entry'.jsonNode,\n    1.jsonNode, 2.jsonNode, 3.jsonNode].jsonNode);\n\n  await squirrel.add({'type': 'info'.jsonNode,\n    'text': 'Structured entry'.jsonNode,\n    'more': [1.jsonNode, 2.jsonNode, 3.jsonNode]\n        .jsonNode});\n}\n```\n\n## Adding child entries\n\nEach entry can have a child entry.\n\n```dart\nimport 'package:squirrel/squirrel.dart';\n\nvoid main() {\n  final squirrel = await Squirrel.open(File(\"/path/to/db\"));\n\n  final parentA = await squirrel.add({'game': 'chess'.jsonNode}.jsonNode);\n  await parentA.add({'move': 'E2-E4'.jsonNode}.jsonNode);\n  await parentA.add({'move': 'E7-E5'.jsonNode}.jsonNode);\n\n  final parentB = await squirrel.add({'game': 'chess'.jsonNode}.jsonNode);\n  await parentB.add({'move': 'D2-D4'.jsonNode}.jsonNode);\n  await parentB.add({'move': 'D7-D5'.jsonNode}.jsonNode);\n}\n```\n\nChild nodes can be nested. You can create a tree-like structure of any depth.\n\n## Data format\n\n### Entries as JSON data\n\n```dart\nstorage.add({'mydata': 123.jsonNode}.\njsonNode);\n```\n\nThe data generated by such calls will be something like this:\n\n```dart\n{\n'I': 'U_sWAEJnR4epXu-TK0FCYA', // unique Slugid for each entry\n'T': 194942647293470, // time in microseconds since epoch UTC\n'D': {'mydata': 123}, // the data from argument\n'P': null // parent context; null for root\n}\n```\n\nWe will essentially send a list of such records converted to JSON to the server.\n\nWhen adding child entries, the child data gets a link to the parent entry.\n\n```dart\n\nfinal parent = await\nsquirrel.add({'mydata': 123.jsonNode}.\njsonNode);\nawait\nparent.add('\nchild A\n'\n.\njsonNode);\nawait\nparent.add('\nchild B\n'\n.\njsonNode);\n```\n\n```dart\n// the parent is an ordinary entry\n{\n'I': 'U_sWAEJnR4epXu-TK0FCYA', // note this id\n'T': 194942647293470,\n'D': {'mydata': 123},\n'P': null\n},\n\n// child entries contain a reference to the parent \n    {\n'I': 'Fum-zBhASyO50rg3mtQcD',\n'T': 194942647223453,\n'D': 'child A',\n'P': 'U_sWAEJnR4epXu-TK0FCYA' // link to the parent id\n},\n\n{\n'I': 'a8_YezW8T7e1jLxG7evy-A',\n'T': 194942647254428,\n'D': 'child B',\n'P': 'U_sWAEJnR4epXu-TK0FCYA' // link to the parent id\n},\n```\n\n## SquirrelSender\n\n`SquirrelSender` automates sending logs to an abstract server when the number of\nrecords in the database exceeds a given limit (100 entries by default). It\nhandes to the`onModified` and `onSendingTrigger` events.\n\n```dart\nimport 'package:squirrel/squirrel.dart';\n\nFuture\u003cvoid\u003e sendToServer(List\u003cdynamic\u003e chunk) async {\n  // this function will receive data to be sent.\n  // Each item of the list is convertible to JSON.\n\n  // If the data cannot be sent (for example, due to connection errors),\n  // the function should throw an exception.\n}\n\nvoid main() {\n  final squirrel = SquirrelSender.create(sendToServer);\n  // ...\n  // use Squirrel as usual\n}\n```\n\n## License\n\nCopyright © 2022 [Artёm iG](https://github.com/rtmigo). Released under\nthe [MIT License](LICENSE).","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frtmigo%2Fsquirrel_dart","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frtmigo%2Fsquirrel_dart","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frtmigo%2Fsquirrel_dart/lists"}