{"_id":"@aminnairi/facts","_rev":"8-064d7cb926487f2dc76041294285e8ad","name":"@aminnairi/facts","dist-tags":{"latest":"3.0.1"},"versions":{"0.1.0":{"name":"@aminnairi/facts","version":"0.1.0","keywords":["event","sourcing","sqlite","fact","query","store"],"author":{"url":"https://github.com/aminnairi","name":"Amin NAIRI"},"license":"MIT","_id":"@aminnairi/facts@0.1.0","maintainers":[{"name":"aminnairi","email":"nairi.amin@gmail.com"}],"homepage":"https://github.com/aminnairi/facts#readme","bugs":{"url":"https://github.com/aminnairi/facts/issues"},"dist":{"shasum":"71c5facdc779896327e41a5f7d2a5a9de5d784df","tarball":"https://registry.npmjs.org/@aminnairi/facts/-/facts-0.1.0.tgz","fileCount":6,"integrity":"sha512-zd2IJM4gvWkezyLODozBuSo1NctfiYsQL251RP+vMmfr2GEVaUw5RWZcaq2KKUPlNjVSSrApCjmZjldyVN/F/A==","signatures":[{"sig":"MEUCIEiklGqNCiPUnURXQOaA9g4rYLZTYcTXIDN/h6XH3gWdAiEAvnJ93aZx0aDsF9Z8p3aWqEWAA1k2CnDhlDv/eWdpPCQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":26164},"engines":{"node":">=22.5.1"},"gitHead":"3c13b8ec4c7fe3a3db3afd8076732e265c6d1190","scripts":{"test":"vitest run --coverage"},"_npmUser":{"name":"aminnairi","email":"nairi.amin@gmail.com"},"repository":{"url":"git+https://github.com/aminnairi/facts.git","type":"git"},"_npmVersion":"11.6.2","description":"Database agnostic implementation of the Event Sourcing & CQRS design pattern","directories":{},"_nodeVersion":"25.2.1","dependencies":{"vitest":"^4.0.9"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.20.6","@types/node":"^24.10.1","@vitest/coverage-v8":"^4.0.9"},"_npmOperationalInternal":{"tmp":"tmp/facts_0.1.0_1763580727795_0.007752539986256135","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"@aminnairi/facts","version":"1.0.0","keywords":["event","sourcing","sqlite","fact","query","store"],"author":{"url":"https://github.com/aminnairi","name":"Amin NAIRI"},"license":"MIT","_id":"@aminnairi/facts@1.0.0","maintainers":[{"name":"aminnairi","email":"nairi.amin@gmail.com"}],"homepage":"https://github.com/aminnairi/facts#readme","bugs":{"url":"https://github.com/aminnairi/facts/issues"},"dist":{"shasum":"5f12ab6833694fb52f950c380c76cfbc512084d2","tarball":"https://registry.npmjs.org/@aminnairi/facts/-/facts-1.0.0.tgz","fileCount":7,"integrity":"sha512-lddOlRZtlsyemsUQDvbm+hyZWFaxu49jEW2JLSVJra97LaI9Z0iU9iW7Ljg9/oZrnnTGniO9W14U7bk7U4SjsQ==","signatures":[{"sig":"MEUCIQDbj8jYKz41aXX5bOz5YqMNUPFvY7Byo/xtF6koGGoXWAIgGX+l8dmQkh7CS+PHAO40NSPk52zMyiDpDUZroS81XKM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":40671},"engines":{"node":">=22.5.1"},"gitHead":"a08ca3cd8018e68a423efb9fc2fafd3b6e0ffa4e","scripts":{"test":"vitest run --coverage"},"_npmUser":{"name":"aminnairi","email":"nairi.amin@gmail.com"},"repository":{"url":"git+https://github.com/aminnairi/facts.git","type":"git"},"_npmVersion":"11.6.2","description":"Database agnostic implementation of the Event Sourcing & CQRS design pattern","directories":{},"_nodeVersion":"25.2.1","dependencies":{"vitest":"^4.0.9"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.20.6","zod":"^4.1.12","@types/node":"^24.10.1","@vitest/coverage-v8":"^4.0.9"},"_npmOperationalInternal":{"tmp":"tmp/facts_1.0.0_1763827824979_0.8148172064383863","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@aminnairi/facts","version":"1.0.1","keywords":["event","sourcing","sqlite","fact","query","store"],"author":{"url":"https://github.com/aminnairi","name":"Amin NAIRI"},"license":"MIT","_id":"@aminnairi/facts@1.0.1","maintainers":[{"name":"aminnairi","email":"nairi.amin@gmail.com"}],"homepage":"https://github.com/aminnairi/facts#readme","bugs":{"url":"https://github.com/aminnairi/facts/issues"},"dist":{"shasum":"4d6025e2947758a9d166b6412875e3e23698065d","tarball":"https://registry.npmjs.org/@aminnairi/facts/-/facts-1.0.1.tgz","fileCount":7,"integrity":"sha512-H8UST75jaUSKs+d5xu2xRlZSSx31OuWTnTstjDqGuJCQe1rQe4VJa+e2uiPLt9oXBkHKHo6YksFANytdqLet3A==","signatures":[{"sig":"MEQCIFXlxqRsgx4Vq1pWjetrVwAxOx3h1wrbaj6jVQsk9hD4AiAoaxbY6L3oNxMNjKjH0UaZp5cXEsFXzzRqwopIuj0b/w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":40699},"engines":{"node":">=22.5.1"},"gitHead":"39783e5f372296496e925aa777c9f0bafd2a5fab","scripts":{"test":"vitest run --coverage"},"_npmUser":{"name":"aminnairi","email":"nairi.amin@gmail.com"},"repository":{"url":"git+https://github.com/aminnairi/facts.git","type":"git"},"_npmVersion":"11.6.2","description":"Database agnostic implementation of the Event Sourcing & CQRS design pattern","directories":{},"_nodeVersion":"25.2.1","dependencies":{"vitest":"^4.0.9"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.20.6","zod":"^4.1.12","@types/node":"^24.10.1","@vitest/coverage-v8":"^4.0.9"},"_npmOperationalInternal":{"tmp":"tmp/facts_1.0.1_1763828307980_0.4648562039361852","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@aminnairi/facts","version":"1.1.0","keywords":["event","sourcing","sqlite","fact","query","store"],"author":{"url":"https://github.com/aminnairi","name":"Amin NAIRI"},"license":"MIT","_id":"@aminnairi/facts@1.1.0","maintainers":[{"name":"aminnairi","email":"nairi.amin@gmail.com"}],"homepage":"https://github.com/aminnairi/facts#readme","bugs":{"url":"https://github.com/aminnairi/facts/issues"},"dist":{"shasum":"4178e79d9bdafcd117abb2a4ad56557d41c5a3f1","tarball":"https://registry.npmjs.org/@aminnairi/facts/-/facts-1.1.0.tgz","fileCount":7,"integrity":"sha512-9EWfHelb3nqUF9yUyn8en+hie5sUfFb1yt+H+gEqIEABzXdnXwC/iWfl+CB9Ph2Q5M60aHXNc7W7DM+qvi5CZg==","signatures":[{"sig":"MEUCIQDS6FES4seOFqPDoIIA8wuGhvJ7NE1rpvzDgaknbj/IKgIgHLpYBOiG6sQOWlmksAzeJvul4vBcLQudlZI/9o45OGs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":46286},"type":"module","engines":{"node":">=22.5.1"},"gitHead":"afe9ac3822b8930671189a56750b40986877a8e9","scripts":{"test":"vitest run --coverage"},"_npmUser":{"name":"aminnairi","email":"nairi.amin@gmail.com"},"repository":{"url":"git+https://github.com/aminnairi/facts.git","type":"git"},"_npmVersion":"11.6.2","description":"Database agnostic implementation of the Event Sourcing & CQRS design pattern","directories":{},"_nodeVersion":"25.2.1","dependencies":{"vitest":"^4.0.9"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.20.6","zod":"^4.1.12","@types/node":"^24.10.1","@vitest/coverage-v8":"^4.0.9"},"_npmOperationalInternal":{"tmp":"tmp/facts_1.1.0_1763923895866_0.9124720962473893","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"@aminnairi/facts","version":"2.0.0","keywords":["event","sourcing","sqlite","fact","query","store"],"author":{"url":"https://github.com/aminnairi","name":"Amin NAIRI"},"license":"MIT","_id":"@aminnairi/facts@2.0.0","maintainers":[{"name":"aminnairi","email":"nairi.amin@gmail.com"}],"homepage":"https://github.com/aminnairi/facts#readme","bugs":{"url":"https://github.com/aminnairi/facts/issues"},"dist":{"shasum":"18a4a7ceb3b732d7e5d0ac48d038ba527b66b916","tarball":"https://registry.npmjs.org/@aminnairi/facts/-/facts-2.0.0.tgz","fileCount":8,"integrity":"sha512-jxVtrk5EjDuxIu3nsrQoTaiC6C2wrC+Q7W03r27XuOL2SA6sSmHBVsTJgnziTcCDX9lq4srNMmi4wkBZcoCdnQ==","signatures":[{"sig":"MEUCIHzyXw3mlAxn24HtFEYCgwiv9tK1joxva16PwXBflIqOAiEAqabZeQMgfSIhMgMewWnbxE7UcB1sgyQvOpT4RnqVFlA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":50407},"type":"module","engines":{"node":">=22.5.1"},"gitHead":"f18d0dc244892aa1341a4ebb7e9657d22c7eaa16","scripts":{"test":"vitest run --coverage"},"_npmUser":{"name":"aminnairi","email":"nairi.amin@gmail.com"},"repository":{"url":"git+https://github.com/aminnairi/facts.git","type":"git"},"_npmVersion":"11.6.2","description":"Database agnostic implementation of the Event Sourcing & CQRS design pattern","directories":{},"_nodeVersion":"25.2.1","dependencies":{"vitest":"^4.0.9"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.20.6","zod":"^4.1.12","@types/node":"^24.10.1","@vitest/coverage-v8":"^4.0.9"},"_npmOperationalInternal":{"tmp":"tmp/facts_2.0.0_1764271609071_0.8258383065076353","host":"s3://npm-registry-packages-npm-production"}},"2.1.0":{"name":"@aminnairi/facts","version":"2.1.0","keywords":["event","sourcing","sqlite","fact","query","store"],"author":{"url":"https://github.com/aminnairi","name":"Amin NAIRI"},"license":"MIT","_id":"@aminnairi/facts@2.1.0","maintainers":[{"name":"aminnairi","email":"nairi.amin@gmail.com"}],"homepage":"https://github.com/aminnairi/facts#readme","bugs":{"url":"https://github.com/aminnairi/facts/issues"},"dist":{"shasum":"b6236f304cb4558aebcebb17f6b1bda5318937f4","tarball":"https://registry.npmjs.org/@aminnairi/facts/-/facts-2.1.0.tgz","fileCount":4,"integrity":"sha512-SNM4y0+roYMze+11ONIW9SyxATkHLYYtjIcwkxHDGMvzYHfHU1ZmNUvfBzPySrpxK0c8brC8Bil+AHi7S9zDOw==","signatures":[{"sig":"MEUCIDmP9BObWF49NN+5bzaoefg0lVrDowtNskGV1XXc66KlAiEA3Xo/SmdYg3fxI/vuQEpq+i8O2BA9/KaJLFdh0GGDnDc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":26855},"type":"module","engines":{"node":">=22.5.1"},"gitHead":"34d0c84d2ceea5a6cedb78189abde3beee5ed004","scripts":{"lint":"tsc --noEmit","test":"vitest run --coverage"},"_npmUser":{"name":"aminnairi","email":"nairi.amin@gmail.com"},"repository":{"url":"git+https://github.com/aminnairi/facts.git","type":"git"},"_npmVersion":"11.10.0","description":"Database agnostic implementation of the Event Sourcing & CQRS design pattern","directories":{},"_nodeVersion":"25.6.1","dependencies":{"vitest":"^4.0.9"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.20.6","zod":"^4.1.12","exhaustive":"^1.1.2","typescript":"^5.9.3","@types/node":"^24.10.1","@vitest/coverage-v8":"^4.0.9"},"_npmOperationalInternal":{"tmp":"tmp/facts_2.1.0_1771944602736_0.2649573935788472","host":"s3://npm-registry-packages-npm-production"}},"3.0.0":{"name":"@aminnairi/facts","version":"3.0.0","keywords":["event","sourcing","sqlite","fact","query","store"],"author":{"url":"https://github.com/aminnairi","name":"Amin NAIRI"},"license":"MIT","_id":"@aminnairi/facts@3.0.0","maintainers":[{"name":"aminnairi","email":"nairi.amin@gmail.com"}],"homepage":"https://github.com/aminnairi/facts#readme","bugs":{"url":"https://github.com/aminnairi/facts/issues"},"dist":{"shasum":"4df38ba30d1ab47c02fb299e29478de4511518b1","tarball":"https://registry.npmjs.org/@aminnairi/facts/-/facts-3.0.0.tgz","fileCount":4,"integrity":"sha512-yUZH+4Q5clOTJfJqF0qMPws5rO+m7uaufiowj3KIUz/ZddWJZGcEwhoP8Xx7aCwjaQUZ5IbQnVscn0+iawDrrQ==","signatures":[{"sig":"MEYCIQCckpB+FXcWg+r5HSGCg3C7XOJckdovX5HGOBl1nSjWJwIhANmM3O8aWjiMJtwMXLNiODF1jfr3uBrMsMa3CRGRVCZn","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":26742},"type":"module","engines":{"node":">=22.5.1"},"gitHead":"49991f8bbab932b9879dd0af151f70ccbc98034c","scripts":{"lint":"tsc --noEmit","test":"vitest run --coverage"},"_npmUser":{"name":"aminnairi","email":"nairi.amin@gmail.com"},"repository":{"url":"git+https://github.com/aminnairi/facts.git","type":"git"},"_npmVersion":"11.10.1","description":"Database agnostic implementation of the Event Sourcing & CQRS design pattern","directories":{},"_nodeVersion":"25.7.0","dependencies":{"vitest":"^4.0.9"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.20.6","zod":"^4.1.12","exhaustive":"^1.1.2","typescript":"^5.9.3","@types/node":"^24.10.1","@vitest/coverage-v8":"^4.0.9"},"_npmOperationalInternal":{"tmp":"tmp/facts_3.0.0_1772487246641_0.5484279788388038","host":"s3://npm-registry-packages-npm-production"}},"3.0.1":{"type":"module","name":"@aminnairi/facts","description":"Database agnostic implementation of the Event Sourcing & CQRS design pattern","version":"3.0.1","homepage":"https://github.com/aminnairi/facts#readme","license":"MIT","scripts":{"lint":"tsc --noEmit","test":"vitest run --coverage"},"devDependencies":{"@types/node":"^24.10.1","@vitest/coverage-v8":"^4.0.9","exhaustive":"^1.1.2","tsx":"^4.20.6","typescript":"^5.9.3","vitest":"^4.0.9","zod":"^4.1.12"},"bugs":{"url":"https://github.com/aminnairi/facts/issues"},"author":{"name":"Amin NAIRI","url":"https://github.com/aminnairi"},"repository":{"type":"git","url":"git+https://github.com/aminnairi/facts.git"},"engines":{"node":">=22.5.1"},"keywords":["event","sourcing","sqlite","fact","query","store"],"gitHead":"60645305f06d11253b06d52d78e48f5fd5730f97","_id":"@aminnairi/facts@3.0.1","_nodeVersion":"25.7.0","_npmVersion":"11.10.1","dist":{"integrity":"sha512-FABA/dfR6j3cHC896y3oF8PqerrQVYeDmubO4+9co3Agru08uzS1zpX7qY9S3ROPILLrzf+2KIyItgexEa8ZCA==","shasum":"4f6ff624463f21c9b55178ad146d0305c87a6baa","tarball":"https://registry.npmjs.org/@aminnairi/facts/-/facts-3.0.1.tgz","fileCount":4,"unpackedSize":26718,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDyq+FH7kClIwEmOLf3PFcckwORLUt1YrQ0BsW7ITbJnAIhAOyAWqWMAB60bCmeknld4XwW71fGjEiY2ClrMUCGRrjX"}]},"_npmUser":{"name":"aminnairi","email":"nairi.amin@gmail.com"},"directories":{},"maintainers":[{"name":"aminnairi","email":"nairi.amin@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/facts_3.0.1_1772699511980_0.9516431790586852"},"_hasShrinkwrap":false}},"time":{"created":"2025-11-19T19:32:07.682Z","modified":"2026-03-05T08:31:52.236Z","0.1.0":"2025-11-19T19:32:08.009Z","1.0.0":"2025-11-22T16:10:25.209Z","1.0.1":"2025-11-22T16:18:28.213Z","1.1.0":"2025-11-23T18:51:36.089Z","2.0.0":"2025-11-27T19:26:49.275Z","2.1.0":"2026-02-24T14:50:02.867Z","3.0.0":"2026-03-02T21:34:06.783Z","3.0.1":"2026-03-05T08:31:52.120Z"},"bugs":{"url":"https://github.com/aminnairi/facts/issues"},"author":{"name":"Amin NAIRI","url":"https://github.com/aminnairi"},"license":"MIT","homepage":"https://github.com/aminnairi/facts#readme","keywords":["event","sourcing","sqlite","fact","query","store"],"repository":{"type":"git","url":"git+https://github.com/aminnairi/facts.git"},"description":"Database agnostic implementation of the Event Sourcing & CQRS design pattern","maintainers":[{"name":"aminnairi","email":"nairi.amin@gmail.com"}],"readme":"# @aminnairi/facts\n\n[![GitHub License](https://img.shields.io/github/license/aminnairi/facts)](/LICENSE) [![NPM Version](https://img.shields.io/npm/v/%40aminnairi%2Ffacts)](https://www.npmjs.com/package/@aminnairi/facts) [![Test](https://github.com/aminnairi/facts/actions/workflows/test.yml/badge.svg?branch=production)](https://github.com/aminnairi/facts/actions/workflows/test.yml) [![Codecov (with branch)](https://img.shields.io/codecov/c/github/aminnairi/facts)](https://app.codecov.io/github/aminnairi/facts)\n\nDatabase agnostic implementation of the Event Sourcing & CQRS design pattern\n\n## 🤔 Presentation\n\nSome businesses have a legal obligation to store data as a never-ending stream of\nfacts generated from application usage.\n\nOthers need to empower their analytics teams by storing every fact that has ever\nhappened in their application.\n\nHowever, it can be challenging to model your data, especially if you are used\nto relational databases.\n\nThis package was created to help you leverage the benefits of the Event\nSourcing & CQRS design patterns while reducing the friction of implementation.\n\n## ✨ Features\n\n- Default memory implementation for testing & easy adoption\n- SQLite implementation for persistence\n- 100% TypeScript source-code & functional programming in mind\n- Ready for deployment in clusters thanks to optimistic locking\n- Database agnostic, use files, SQL, NoSQL, IndexedDB, LocalStorage, etc...\n- Event Sourcing inspired to prevent data loss and enable smarter analytics\n- Query implementation for CQRS applications\n- Easy initialization of Queries from past events useful after application restart\n- No migration script required, evolve your data model as your project evolve\n\n## 💻 Usage\n\n### Install the requirements\n\n- [Node.js](https://nodejs.org)\n- [NPM](https://npmjs.com)\n\n### Install the packages\n\n```bash\nnpm install tsx @aminnairi/facts\n```\n\n### Create the source file\n\n```bash\ntouch index.ts\n```\n\n### Define facts\n\nFirst, define the structure of your facts using TypeScript interfaces.\n\n```typescript\nimport { FactShape } from \"@aminnairi/facts\";\n\ninterface TodoAddedV1Fact extends FactShape {\n  name: \"todo-added\";\n  version: 1;\n  streamName: \"todo\";\n  streamIdentifier: string;\n  payload: {\n    name: string;\n    done: boolean;\n  };\n}\n\ninterface TodoRemovedV1Fact extends FactShape {\n  name: \"todo-removed\";\n  version: 1;\n  streamName: \"todo\";\n  streamIdentifier: string;\n  payload: null;\n}\n\ntype TodoFact = TodoAddedV1Fact | TodoRemovedV1Fact;\n```\n\n### Initialize the store\n\nYou can use the in-memory store for development and testing, or the SQLite store for production.\n\n```typescript\nimport { FactShape, MemoryFactStore } from \"@aminnairi/facts\";\n\nconst factStore = new MemoryFactStore<TodoFact>();\n```\n\n### Define a query\n\nQueries are used to build read models from your facts.\n\n```typescript\nimport { Query, match } from \"@aminnairi/facts\";\n\ninterface Todo {\n  identifier: string;\n  name: string;\n  done: boolean;\n}\n\nclass MemoryTodosQuery implements Query<TodoFact> {\n  public constructor(private readonly todos: Map<string, Todo> = new Map()) {}\n\n  public async handle(fact: TodoFact): Promise<void> {\n    match(fact, {\n      \"todo-added\": (todoAddedFact) => {\n        this.todos.set(todoAddedFact.streamIdentifier, {\n          identifier: todoAddedFact.streamIdentifier,\n          name: todoAddedFact.payload.name,\n          done: todoAddedFact.payload.done,\n        });\n      },\n      \"todo-removed\": (todoRemovedFact) => {\n        this.todos.delete(todoRemovedFact.streamIdentifier);\n      },\n    });\n  }\n\n  public async getTodos(): Promise<Todo[]> {\n    return Array.from(this.todos.values());\n  }\n}\n\nconst todosQuery = new MemoryTodosQuery();\n```\n\n### Register queries\n\n```typescript\nfactStore.registerQuery(todosQuery);\n\nconst error = await factStore.initialize();\n\nif (error instanceof Error) {\n  console.error(\"Failed to initialize queries:\", error);\n}\n```\n\n### Save facts\n\nSave facts to the store using dependency injection. The `position` property is used for optimistic locking.\n\n```typescript\nimport { randomUUID } from \"crypto\";\n\nconst streamIdentifier = randomUUID();\n\nawait factStore.save({\n  identifier: randomUUID(),\n  name: \"todo-added\",\n  version: 1,\n  date: new Date(),\n  streamName: \"todo\",\n  streamIdentifier: streamIdentifier,\n  position: 0,\n  payload: {\n    name: \"Do the dishes\",\n    done: false,\n  },\n});\n\nawait factStore.save({\n  identifier: randomUUID(),\n  name: \"todo-removed\",\n  version: 1,\n  date: new Date(),\n  streamName: \"todo\",\n  streamIdentifier: streamIdentifier,\n  position: 1,\n  payload: null,\n});\n```\n\n### List facts\n\nYou can retrieve facts from the store using `find` and `findFromLast`.\n\n```typescript\nconst result = await factStore.find((fact) => {\n  return fact.streamIdentifier === streamIdentifier;\n});\n\nif (result instanceof Error) {\n  console.error(\"Failed to find facts:\", result);\n  process.exit(1);\n}\n\nfor (const fact of result) {\n  console.log(fact.name, fact.payload);\n}\n```\n\n### Fetch data\n\nFetch the read model from your query using your custom method.\n\n```typescript\nconst todos = await todosQuery.getTodos();\n\nfor (const todo of todos) {\n  console.log(todo.name);\n}\n```\n\n### Commands\n\nThe `FactStore` acts as the Command side in CQRS. Define your commands as classes that depend on the store using dependency injection:\n\n```typescript\nclass AddTodoCommand {\n  constructor(private readonly store: FactStore<TodoFact>) {}\n\n  async execute(name: string, done: boolean) {\n    await this.store.save({\n      identifier: randomUUID(),\n      name: \"todo-added\",\n      version: 1,\n      date: new Date(),\n      streamName: \"todo\",\n      streamIdentifier: randomUUID(),\n      position: 0,\n      payload: { name, done },\n    });\n  }\n}\n\nconst addTodoCommand = new AddTodoCommand(factStore);\nawait addTodoCommand.execute(\"Buy milk\", false);\n```\n\nThis approach makes your commands testable by allowing dependency injection.\n\n## Run the script\n\n```bash\nnpx tsx index.ts\n```\n\n## ✍️ Examples\n\n- [Fact store using the provided SQLite implementation](packages/examples/sqlite/store.ts)\n- [Fact store using the provided SQLite implementation with snapshotting](packages/examples/sqlite/snapshot.ts)\n- [Fact store using the provided RAM implementation](packages/examples/memory/store.ts)\n- [Query implemented using RAM](packages/examples/memory/query.ts)\n- [Query initialization using RAM](packages/examples/memory/initialization.ts)\n\n## API\n\n### FactShape\n\nThis is the base interface for any fact. It defines the common properties that\nevery fact must have.\n\n```typescript\ninterface FactShape {\n  identifier: string;\n  name: string;\n  version: number;\n  position: number;\n  date: Date;\n  streamName: string;\n  streamIdentifier: string;\n  payload: unknown;\n}\n```\n\n### ConcurrencyError\n\nThis is a custom error class that is thrown when there is a position conflict\nwhen saving a fact, which is part of the optimistic locking mechanism.\n\n```typescript\nclass ConcurrencyError extends Error {\n  public override readonly name = \"ConcurrencyError\";\n}\n```\n\n### UnexpectedError\n\nThis is a custom error class that is thrown for unexpected errors during fact\nretrieval, usually wrapping a database or filesystem error.\n\n```typescript\nclass UnexpectedError extends Error {\n  public override readonly name = \"UnexpectedError\";\n\n  public constructor(public readonly error: unknown) {\n    super();\n  }\n}\n```\n\n### ParseError\n\nThis is a custom error class that is thrown when a fact cannot be parsed\ncorrectly from the database.\n\n```typescript\nclass ParseError extends Error {\n  public override readonly name = \"ParseError\";\n}\n```\n\n### QueryInitializeError\n\nThis is a custom error class that is thrown when the query's `initialize` method\nfails.\n\n```typescript\nclass QueryInitializeError extends Error {\n  public override readonly name = \"QueryInitializeError\";\n}\n```\n\n### FactStore\n\nThis is an interface that defines the contract for a fact store.\n\n```typescript\ninterface FactStore<Fact extends FactShape> {\n  save(fact: Fact): Promise<void | ConcurrencyError>;\n  find<DiscriminatedFact extends Fact>(\n    accept: (fact: Fact) => fact is DiscriminatedFact,\n  ): Promise<DiscriminatedFact[] | UnexpectedError | ParseError>;\n  find(\n    accept?: (fact: Fact) => boolean,\n  ): Promise<Fact[] | UnexpectedError | ParseError>;\n  findFromLast<DiscriminatedFact extends Fact>(\n    stop: (fact: Fact) => fact is DiscriminatedFact,\n  ): Promise<DiscriminatedFact[] | UnexpectedError | ParseError>;\n  findFromLast(\n    stop: (fact: Fact) => boolean,\n  ): Promise<Fact[] | UnexpectedError | ParseError>;\n  registerQuery(listener: Query<Fact>): void;\n  initialize(): Promise<void | ParseError | UnexpectedError | QueryInitializeError>;\n}\n```\n\nWhen calling `store.initialize()`, the `initialize` method of each registered query will be called.\nThis allows queries to set up their data model (e.g., create tables in SQLite).\n\n### Query\n\nThis interface defines the contract for a query that can handle facts and can\nbe used to build read models.\n\n```typescript\ninterface Query<Fact extends FactShape> {\n  handle(fact: Fact): Promise<void>;\n  initialize?: () => Promise<void | QueryInitializeError>;\n}\n```\n\nThe `initialize` method is optional and is called once when `store.initialize()` is called.\nThis is useful for databases like SQLite or PostgreSQL that need to create tables or\nset up the data model before handling facts. It is not necessary for in-memory queries.\n\nDefine your own method to fetch data from your query:\n\n```typescript\nclass MyQuery implements Query<MyFact> {\n  async getData(): Promise<MyData> {\n    // your implementation\n  }\n}\n```\n\n### until\n\nThis is a utility function that takes an array and a stop condition, and\nreturns a new array with all the elements until the stop condition is met. It's\nused by `findFromLast`.\n\n```typescript\nfunction until<Value>(\n  values: Value[],\n  stop: (value: Value) => boolean,\n): Value[];\n```\n\n### match\n\nThis is a utility function that provides a way to do pattern matching on a\nfact's `name` property.\n\n```typescript\nfunction match<Output, Fact extends FactShape>(\n  fact: Fact,\n  options: {\n    [Key in Fact[\"name\"]]: (fact: Extract<Fact, { name: Key }>) => Output;\n  },\n): Output;\n```\n\n### MemoryFactStore\n\nCreate a store for saving facts in RAM. This should not be used in a\nproduction environment.\n\n```typescript\nclass MemoryFactStore<Fact extends FactShape> implements FactStore<Fact>\n```\n\n### SqliteFactStore\n\nCreate a store for saving facts in a SQLite database. This implementation is\nsuitable for production environments. It is constructed using the `for` static method.\n\n```typescript\nclass SqliteFactStore<Fact extends FactShape> {\n  public static for<Fact extends FactShape>(\n    path: string,\n    options: { parser: (fact: unknown) => Fact | ParseError },\n  ): SqliteFactStore<Fact>;\n\n  public close(): void;\n}\n```\n\n## Contributing\n\nSee [`CONTRIBUTING.md`](/CONTRIBUTING.md).\n\n## Security\n\nSee [`SECURITY.md`](/SECURITY.md).\n\n## License\n\nSee [`LICENSE`](./LICENSE).\n","readmeFilename":"README.md"}