{"_id":"@emmett-community/emmett-google-firestore","_rev":"5-769e12e14aa115e14a56f11c8573048b","name":"@emmett-community/emmett-google-firestore","dist-tags":{"latest":"0.5.0"},"versions":{"0.1.0":{"name":"@emmett-community/emmett-google-firestore","version":"0.1.0","keywords":["emmett","event-sourcing","event-store","firestore","google-cloud-firestore","cqrs","event-driven","ddd"],"author":{"name":"Andre X Costa"},"license":"MIT","_id":"@emmett-community/emmett-google-firestore@0.1.0","maintainers":[{"name":"axcosta","email":"axcosta@gmail.com"}],"homepage":"https://github.com/emmett-community/emmett-google-firestore#readme","bugs":{"url":"https://github.com/emmett-community/emmett-google-firestore/issues"},"dist":{"shasum":"530146af0fdf1557ef91de23a74ca3f55ceeb0f5","tarball":"https://registry.npmjs.org/@emmett-community/emmett-google-firestore/-/emmett-google-firestore-0.1.0.tgz","fileCount":17,"integrity":"sha512-GdA585pYcLbMfbGXvFClqC2D1rbRY4dr1UZAWLXWbZkT2fNZZ4O6S0Vh3DDkCyHFsy0B2uPFFIvAwGYcM3VF/Q==","signatures":[{"sig":"MEYCIQDG6JDp9q8TE3EgiqQIklE/zjbD1klG0+Nh1rjWDlTtAgIhAOZinuN5tUZSBh4fPJTrPxX1/+whUbha1xOBkNdKl30+","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":180773},"main":"./dist/index.js","types":"./dist/index.d.ts","module":"./dist/index.mjs","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"./testing":{"import":{"types":"./dist/testing/index.d.mts","default":"./dist/testing/index.mjs"},"require":{"types":"./dist/testing/index.d.ts","default":"./dist/testing/index.js"}}},"gitHead":"9ee877f6f775dbe259e84bd8a54375707560097e","scripts":{"lint":"eslint . --ext .ts","test":"jest","build":"tsup","clean":"rm -rf dist","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","build:ts":"tsc --noEmit","lint:fix":"eslint . --ext .ts --fix","test:e2e":"jest test/e2e","test:unit":"jest test/unit","typecheck":"tsc --noEmit","test:watch":"jest --watch","format:check":"prettier --check \"src/**/*.ts\" \"test/**/*.ts\"","test:coverage":"jest --coverage","prepublishOnly":"npm run clean && npm run build","test:integration":"jest test/integration"},"_npmUser":{"name":"axcosta","email":"axcosta@gmail.com"},"repository":{"url":"git+https://github.com/emmett-community/emmett-google-firestore.git","type":"git"},"_npmVersion":"11.1.0","description":"Google Firestore event store implementation for Emmett","directories":{},"_nodeVersion":"22.11.0","dependencies":{"@google-cloud/firestore":"^7.10.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","tsup":"^8.3.5","eslint":"^9.16.0","ts-jest":"^29.2.5","ts-node":"^10.9.2","prettier":"^3.4.2","typescript":"^5.7.2","@types/jest":"^29.5.14","@types/node":"^22.10.1","eslint-config-prettier":"^9.1.0","eslint-plugin-prettier":"^5.2.1","@event-driven-io/emmett":"^0.39.1","@typescript-eslint/parser":"^8.17.0","@typescript-eslint/eslint-plugin":"^8.17.0"},"peerDependencies":{"@event-driven-io/emmett":"^0.39.0"},"_npmOperationalInternal":{"tmp":"tmp/emmett-google-firestore_0.1.0_1765728459962_0.5006106828712233","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@emmett-community/emmett-google-firestore","version":"0.2.0","keywords":["emmett","event-sourcing","event-store","firestore","google-cloud-firestore","cqrs","event-driven","ddd"],"author":{"name":"Andre X Costa"},"license":"MIT","_id":"@emmett-community/emmett-google-firestore@0.2.0","maintainers":[{"name":"axcosta","email":"axcosta@gmail.com"}],"homepage":"https://github.com/emmett-community/emmett-google-firestore#readme","bugs":{"url":"https://github.com/emmett-community/emmett-google-firestore/issues"},"dist":{"shasum":"3589bf550932c2e4c619b397255a13629451f2af","tarball":"https://registry.npmjs.org/@emmett-community/emmett-google-firestore/-/emmett-google-firestore-0.2.0.tgz","fileCount":9,"integrity":"sha512-nKZo8jrQGRU2COhYIG40TABWlnO3BuFkbFc+oifYyvs6RoWIyKwmkGthhPENqm2M78aflHoxNg7A5OHjOPRVbg==","signatures":[{"sig":"MEQCIERXWzRlDC6v8JFAfpVyyY0jSoGJWG15pXKGsIs2rKVyAiAH0Qu+ttxSfsrY4ky9T4gZI+9OeLR9Apq8VhSnQ7bT0g==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@emmett-community%2femmett-google-firestore@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":92494},"main":"./dist/index.js","types":"./dist/index.d.ts","module":"./dist/index.mjs","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.js"}}},"gitHead":"54e045d79a907d814839967bad4afac3f4709056","scripts":{"lint":"eslint . --ext .ts","test":"npm run test:unit && npm run test:int && npm run test:e2e","build":"tsup","clean":"rm -rf dist","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","build:ts":"tsc --noEmit","lint:fix":"eslint . --ext .ts --fix","test:e2e":"jest --testMatch \"**/*.e2e.spec.ts\" --watchman=false","test:int":"jest --testMatch \"**/*.int.spec.ts\" --watchman=false","test:unit":"jest --testMatch \"**/*.unit.spec.ts\" --watchman=false","typecheck":"tsc --noEmit","test:watch":"jest --watch --watchman=false","format:check":"prettier --check \"src/**/*.ts\" \"test/**/*.ts\"","test:coverage":"jest --coverage --watchman=false","prepublishOnly":"npm run clean && npm run build","test:integration":"npm run test:int"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:fd9b999a-a608-442e-9f5f-bfa3ded8efc0"}},"repository":{"url":"git+https://github.com/emmett-community/emmett-google-firestore.git","type":"git"},"_npmVersion":"11.6.2","description":"Google Firestore event store implementation for Emmett","directories":{},"_nodeVersion":"24.12.0","dependencies":{"@google-cloud/firestore":"^7.10.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","tsup":"^8.3.5","eslint":"^9.16.0","ts-jest":"^29.2.5","ts-node":"^10.9.2","prettier":"^3.4.2","typescript":"^5.7.2","@types/jest":"^29.5.14","@types/node":"^22.10.1","testcontainers":"^10.28.0","eslint-config-prettier":"^9.1.0","eslint-plugin-prettier":"^5.2.1","@event-driven-io/emmett":"^0.39.1","@typescript-eslint/parser":"^8.17.0","@typescript-eslint/eslint-plugin":"^8.17.0"},"peerDependencies":{"@event-driven-io/emmett":"^0.39.0"},"_npmOperationalInternal":{"tmp":"tmp/emmett-google-firestore_0.2.0_1766797213163_0.7057220709203498","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@emmett-community/emmett-google-firestore","version":"0.3.0","keywords":["emmett","event-sourcing","event-store","firestore","google-cloud-firestore","cqrs","event-driven","ddd"],"author":{"name":"Andre X Costa"},"license":"MIT","_id":"@emmett-community/emmett-google-firestore@0.3.0","maintainers":[{"name":"axcosta","email":"axcosta@gmail.com"}],"homepage":"https://github.com/emmett-community/emmett-google-firestore#readme","bugs":{"url":"https://github.com/emmett-community/emmett-google-firestore/issues"},"dist":{"shasum":"cc776c2b55fa89aab6cdf40f08d514676cf9b944","tarball":"https://registry.npmjs.org/@emmett-community/emmett-google-firestore/-/emmett-google-firestore-0.3.0.tgz","fileCount":9,"integrity":"sha512-VzN0ggAf4BhykEHQu/feDtPvIdC8Wg73joqYC222/lW6qhhy1rNyv5VV04NDIDqjFIM30M0ZDVJRzXfNqSdcPQ==","signatures":[{"sig":"MEUCIEGqBq+7A/c2WMNBXqp1OTOyVIvC0Ot0O/W2/rPuxEAbAiEAqh3JRFRyCfLuKmt3JCd0VispWKDnYVvZ+/DVNOLhcLg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@emmett-community%2femmett-google-firestore@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":116830},"main":"./dist/index.js","types":"./dist/index.d.ts","module":"./dist/index.mjs","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.js"}}},"gitHead":"a721ee30eea35ad526c7b5c36610c58d286f134e","scripts":{"lint":"eslint . --ext .ts","test":"npm run test:unit && npm run test:int && npm run test:e2e","build":"tsup","clean":"rm -rf dist","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","build:ts":"tsc --noEmit","lint:fix":"eslint . --ext .ts --fix","test:e2e":"jest --testMatch \"**/*.e2e.spec.ts\" --watchman=false","test:int":"jest --testMatch \"**/*.int.spec.ts\" --watchman=false","test:unit":"jest --testMatch \"**/*.unit.spec.ts\" --watchman=false","typecheck":"tsc --noEmit","test:watch":"jest --watch --watchman=false","format:check":"prettier --check \"src/**/*.ts\" \"test/**/*.ts\"","test:coverage":"jest --coverage --watchman=false","prepublishOnly":"npm run clean && npm run build","test:integration":"npm run test:int"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:fd9b999a-a608-442e-9f5f-bfa3ded8efc0"}},"repository":{"url":"git+https://github.com/emmett-community/emmett-google-firestore.git","type":"git"},"_npmVersion":"11.6.2","description":"Google Firestore event store implementation for Emmett","directories":{},"_nodeVersion":"24.12.0","dependencies":{"@google-cloud/firestore":"^7.10.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","tsup":"^8.3.5","eslint":"^9.16.0","ts-jest":"^29.2.5","ts-node":"^10.9.2","prettier":"^3.4.2","typescript":"^5.7.2","@types/jest":"^29.5.14","@types/node":"^22.10.1","testcontainers":"^10.28.0","@opentelemetry/api":"^1.9.0","eslint-config-prettier":"^9.1.0","eslint-plugin-prettier":"^5.2.1","@event-driven-io/emmett":"^0.39.1","@typescript-eslint/parser":"^8.17.0","@typescript-eslint/eslint-plugin":"^8.17.0"},"peerDependencies":{"@opentelemetry/api":"^1.0.0","@event-driven-io/emmett":"^0.39.0"},"peerDependenciesMeta":{"@opentelemetry/api":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/emmett-google-firestore_0.3.0_1767127436066_0.9161132800701168","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@emmett-community/emmett-google-firestore","version":"0.4.0","keywords":["emmett","event-sourcing","event-store","firestore","google-cloud-firestore","cqrs","event-driven","ddd"],"author":{"name":"Andre X Costa"},"license":"MIT","_id":"@emmett-community/emmett-google-firestore@0.4.0","maintainers":[{"name":"axcosta","email":"axcosta@gmail.com"}],"homepage":"https://github.com/emmett-community/emmett-google-firestore#readme","bugs":{"url":"https://github.com/emmett-community/emmett-google-firestore/issues"},"dist":{"shasum":"6713f9298d3998624b42e81196315b20116f1fef","tarball":"https://registry.npmjs.org/@emmett-community/emmett-google-firestore/-/emmett-google-firestore-0.4.0.tgz","fileCount":9,"integrity":"sha512-Z1opFDkDRvuSPtfcS2SHq4Hy5qgtnI8Fb5MA+dVLYteLvaR4hhUx3nBmIUXGsqu1nTI4Wip9ARXluwbwGLEInQ==","signatures":[{"sig":"MEQCIGOjK/CsM4DcPH1tfeWGpbIezqO8nnvlv9/0Y7TWzYwcAiAMEDFWIbhXsN2NJzXovXDcjg/0nRpIBqynlNIl990QdA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@emmett-community%2femmett-google-firestore@0.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":129932},"main":"./dist/index.js","types":"./dist/index.d.ts","module":"./dist/index.mjs","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.js"}}},"gitHead":"bac0e2f94702b2421e5cbc7faa9303ca94307cea","scripts":{"lint":"eslint . --ext .ts","test":"npm run test:unit && npm run test:int && npm run test:e2e","build":"tsup","clean":"rm -rf dist","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","build:ts":"tsc --noEmit","lint:fix":"eslint . --ext .ts --fix","test:e2e":"jest --testMatch \"**/*.e2e.spec.ts\" --watchman=false","test:int":"jest --testMatch \"**/*.int.spec.ts\" --watchman=false","test:unit":"jest --testMatch \"**/*.unit.spec.ts\" --watchman=false","typecheck":"tsc --noEmit","test:watch":"jest --watch --watchman=false","format:check":"prettier --check \"src/**/*.ts\" \"test/**/*.ts\"","test:coverage":"jest --coverage --watchman=false","prepublishOnly":"npm run clean && npm run build","test:integration":"npm run test:int"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:fd9b999a-a608-442e-9f5f-bfa3ded8efc0"}},"repository":{"url":"git+https://github.com/emmett-community/emmett-google-firestore.git","type":"git"},"_npmVersion":"11.6.2","description":"Google Firestore event store implementation for Emmett","directories":{},"_nodeVersion":"24.12.0","dependencies":{"@google-cloud/firestore":"^7.10.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","tsup":"^8.3.5","eslint":"^9.16.0","ts-jest":"^29.2.5","ts-node":"^10.9.2","prettier":"^3.4.2","typescript":"^5.7.2","@types/jest":"^29.5.14","@types/node":"^22.10.1","testcontainers":"^10.28.0","@opentelemetry/api":"^1.9.0","eslint-config-prettier":"^9.1.0","eslint-plugin-prettier":"^5.2.1","@event-driven-io/emmett":"^0.39.1","@typescript-eslint/parser":"^8.17.0","@typescript-eslint/eslint-plugin":"^8.17.0"},"peerDependencies":{"@opentelemetry/api":"^1.0.0","@event-driven-io/emmett":"^0.39.0"},"peerDependenciesMeta":{"@opentelemetry/api":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/emmett-google-firestore_0.4.0_1767230997061_0.9538030324799385","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@emmett-community/emmett-google-firestore","version":"0.5.0","description":"Google Firestore event store implementation for Emmett","main":"./dist/index.js","module":"./dist/index.mjs","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.js"}}},"scripts":{"build":"tsup","build:ts":"tsc --noEmit","test":"npm run test:unit && npm run test:int && npm run test:e2e","test:unit":"jest --testMatch \"**/*.unit.spec.ts\" --watchman=false","test:int":"jest --testMatch \"**/*.int.spec.ts\" --watchman=false","test:integration":"npm run test:int","test:e2e":"jest --testMatch \"**/*.e2e.spec.ts\" --watchman=false","test:watch":"jest --watch --watchman=false","test:coverage":"jest --coverage --watchman=false","lint":"eslint . --ext .ts","lint:fix":"eslint . --ext .ts --fix","format":"prettier --write \"src/**/*.ts\" \"test/**/*.ts\"","format:check":"prettier --check \"src/**/*.ts\" \"test/**/*.ts\"","typecheck":"tsc --noEmit","clean":"rm -rf dist","prepublishOnly":"npm run clean && npm run build"},"keywords":["emmett","event-sourcing","event-store","firestore","google-cloud-firestore","cqrs","event-driven","ddd"],"author":{"name":"Andre X Costa"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/emmett-community/emmett-google-firestore.git"},"bugs":{"url":"https://github.com/emmett-community/emmett-google-firestore/issues"},"homepage":"https://github.com/emmett-community/emmett-google-firestore#readme","peerDependencies":{"@event-driven-io/emmett":"^0.39.0","@opentelemetry/api":"^1.0.0"},"peerDependenciesMeta":{"@opentelemetry/api":{"optional":true}},"dependencies":{"@google-cloud/firestore":"^7.10.0"},"devDependencies":{"@event-driven-io/emmett":"^0.39.1","@opentelemetry/api":"^1.9.0","@types/jest":"^29.5.14","@types/node":"^22.10.1","@typescript-eslint/eslint-plugin":"^8.17.0","@typescript-eslint/parser":"^8.17.0","eslint":"^9.16.0","eslint-config-prettier":"^9.1.0","eslint-plugin-prettier":"^5.2.1","jest":"^29.7.0","prettier":"^3.4.2","testcontainers":"^10.28.0","ts-jest":"^29.2.5","ts-node":"^10.9.2","tsup":"^8.3.5","typescript":"^5.7.2"},"engines":{"node":">=18.0.0"},"publishConfig":{"access":"public"},"gitHead":"487229b1f2019af6418d8f8e30e716fa9bab662e","_id":"@emmett-community/emmett-google-firestore@0.5.0","_nodeVersion":"24.12.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-ojUoxGThiJ5KCQW9+qUxgZZ40DXFt1ITyZ+YRKppInwrE/yS59Lw14qH1JkE/81FlkPLwdlnHfdzEAUEYpNiWQ==","shasum":"0f5ab7f726cfdb7bd963a07c3e21545138d5aa40","tarball":"https://registry.npmjs.org/@emmett-community/emmett-google-firestore/-/emmett-google-firestore-0.5.0.tgz","fileCount":9,"unpackedSize":141429,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@emmett-community%2femmett-google-firestore@0.5.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQC6gx3EbvmtV/eqtCWeHby7mdWu31s5BO2D9kSENx/9YAIhAPER80UJttBRIsjC8v+UHbnCUkM515PrHgTmHUhwrhEW"}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:fd9b999a-a608-442e-9f5f-bfa3ded8efc0"}},"directories":{},"maintainers":[{"name":"axcosta","email":"axcosta@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/emmett-google-firestore_0.5.0_1768503801757_0.40676935797309355"},"_hasShrinkwrap":false}},"time":{"created":"2025-12-14T16:07:39.849Z","modified":"2026-01-15T19:03:22.273Z","0.1.0":"2025-12-14T16:07:40.128Z","0.2.0":"2025-12-27T01:00:13.344Z","0.3.0":"2025-12-30T20:43:56.236Z","0.4.0":"2026-01-01T01:29:57.238Z","0.5.0":"2026-01-15T19:03:21.946Z"},"bugs":{"url":"https://github.com/emmett-community/emmett-google-firestore/issues"},"author":{"name":"Andre X Costa"},"license":"MIT","homepage":"https://github.com/emmett-community/emmett-google-firestore#readme","keywords":["emmett","event-sourcing","event-store","firestore","google-cloud-firestore","cqrs","event-driven","ddd"],"repository":{"type":"git","url":"git+https://github.com/emmett-community/emmett-google-firestore.git"},"description":"Google Firestore event store implementation for Emmett","maintainers":[{"name":"axcosta","email":"axcosta@gmail.com"}],"readme":"# @emmett-community/emmett-google-firestore\n\nGoogle Firestore event store implementation for [Emmett](https://event-driven-io.github.io/emmett/), the Node.js event sourcing framework.\n\n[![npm version](https://img.shields.io/npm/v/@emmett-community/emmett-google-firestore.svg)](https://www.npmjs.com/package/@emmett-community/emmett-google-firestore)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n## Features\n\n- ✅ **Event Storage & Retrieval** - Store and read events from Google Firestore\n- ✅ **Optimistic Concurrency** - Built-in version conflict detection\n- ✅ **Type-Safe** - Full TypeScript support with comprehensive types\n- ✅ **Minimal Boilerplate** - Simple, intuitive API\n- ✅ **Subcollection-based** - Efficient Firestore-native structure (no size limits!)\n- ✅ **Global Event Ordering** - Maintain total ordering across all streams\n- ✅ **Testing Utilities** - Helper functions for easy testing\n- ✅ **Emmett Compatible** - Works seamlessly with the Emmett ecosystem\n\n## Installation\n\n```bash\nnpm install @emmett-community/emmett-google-firestore @google-cloud/firestore\n```\n\n## Quick Start\n\n```typescript\nimport { Firestore } from '@google-cloud/firestore';\nimport { getFirestoreEventStore } from '@emmett-community/emmett-google-firestore';\n\n// Initialize Firestore\nconst firestore = new Firestore({\n  projectId: 'your-project-id',\n  keyFilename: 'path/to/service-account.json',\n});\n\n// Create event store\nconst eventStore = getFirestoreEventStore(firestore);\n\n// Define your events\ntype UserRegistered = Event<'UserRegistered', { userId: string; email: string }>;\ntype UserEvent = UserRegistered | /* other events */;\n\n// Append events\nawait eventStore.appendToStream('User-123', [\n  {\n    type: 'UserRegistered',\n    data: { userId: '123', email: 'user@example.com' },\n  },\n]);\n\n// Read events\nconst events = await eventStore.readStream<UserEvent>('User-123');\n\n// Aggregate state\nconst state = await eventStore.aggregateStream(\n  'User-123',\n  evolve,\n  initialState,\n);\n```\n\n## How It Works\n\n### Firestore Structure\n\nEvents are stored using a **subcollection pattern** for optimal performance:\n\n```\n/streams/                              # Root collection\n  {streamName}/                        # Stream document (metadata)\n    version: number\n    createdAt: Timestamp\n    updatedAt: Timestamp\n\n    /events/                           # Subcollection (actual events)\n      0000000000: { type, data, ... }  # Zero-padded version IDs\n      0000000001: { type, data, ... }\n      0000000002: { type, data, ... }\n\n/_counters/                            # System collection\n  global_position/\n    value: number\n```\n\n**Benefits of this structure:**\n\n- ✅ No document size limits (Firestore 1MB limit doesn't apply to subcollections)\n- ✅ Natural isolation per stream\n- ✅ Automatic ordering (document IDs sort naturally)\n- ✅ No composite indexes needed\n- ✅ Efficient queries\n\n### Optimistic Concurrency\n\nThe event store uses **optimistic locking** to prevent conflicts:\n\n```typescript\n// Append with version check\nawait eventStore.appendToStream(\n  'User-123',\n  events,\n  { expectedStreamVersion: 5 }  // Will fail if version ≠ 5\n);\n\n// Or use special version markers\nimport { NO_STREAM, STREAM_EXISTS, ANY } from '@emmett-community/emmett-google-firestore';\n\n// Stream must not exist\nawait eventStore.appendToStream('User-123', events, {\n  expectedStreamVersion: NO_STREAM\n});\n\n// Stream must exist (any version)\nawait eventStore.appendToStream('User-123', events, {\n  expectedStreamVersion: STREAM_EXISTS\n});\n\n// No version check\nawait eventStore.appendToStream('User-123', events, {\n  expectedStreamVersion: ANY\n});\n```\n\n## API Reference\n\n### `getFirestoreEventStore(firestore, options?)`\n\nCreates a Firestore event store instance.\n\n**Parameters:**\n\n- `firestore`: Firestore instance\n- `options`: Optional configuration\n  - `collections`: Custom collection names\n    - `streams`: Stream collection name (default: `\"streams\"`)\n    - `counters`: Counter collection name (default: `\"_counters\"`)\n\n**Returns:** `FirestoreEventStore`\n\n```typescript\nconst eventStore = getFirestoreEventStore(firestore, {\n  collections: {\n    streams: 'my_streams',\n    counters: 'my_counters',\n  },\n});\n```\n\n### `asEventStore(firestoreEventStore)`\n\nWraps a `FirestoreEventStore` instance in Emmett's `EventStore` interface so it can be passed into helpers that expect the canonical contract (CommandHandlers, workflows, etc.).\n\n```typescript\nconst firestoreEventStore = getFirestoreEventStore(firestore);\nconst eventStore = asEventStore(firestoreEventStore);\n```\n\n### `eventStore.appendToStream(streamName, events, options?)`\n\nAppends events to a stream.\n\n**Parameters:**\n\n- `streamName`: Stream identifier (e.g., `\"User-123\"`)\n- `events`: Array of events to append\n- `options`: Optional append options\n  - `expectedStreamVersion`: Version constraint\n\n**Returns:** `Promise<AppendToStreamResult>`\n\n```typescript\nconst result = await eventStore.appendToStream(\n  'User-123',\n  [{ type: 'UserRegistered', data: {...} }],\n  { expectedStreamVersion: 0 }\n);\n\nconsole.log(result.nextExpectedStreamVersion); // 1\nconsole.log(result.createdNewStream); // true/false\n```\n\n### `eventStore.readStream(streamName, options?)`\n\nReads events from a stream.\n\n**Parameters:**\n\n- `streamName`: Stream identifier\n- `options`: Optional read options\n  - `from`: Start version (inclusive)\n  - `to`: End version (inclusive)\n  - `maxCount`: Maximum number of events to read\n\n**Returns:** `Promise<FirestoreReadEvent[]>`\n\n```typescript\n// Read all events\nconst events = await eventStore.readStream('User-123');\n\n// Read from version 10 onwards\nconst events = await eventStore.readStream('User-123', { from: 10n });\n\n// Read range\nconst events = await eventStore.readStream('User-123', {\n  from: 5n,\n  to: 10n\n});\n\n// Limit results\nconst events = await eventStore.readStream('User-123', {\n  maxCount: 100\n});\n```\n\n### `eventStore.aggregateStream(streamName, evolve, initialState, options?)`\n\nAggregates stream events into state.\n\n**Parameters:**\n\n- `streamName`: Stream identifier\n- `evolve`: Function to apply events to state\n- `initialState`: Function returning initial state\n- `options`: Optional read options (same as `readStream`)\n\n**Returns:** `Promise<State>`\n\n```typescript\nconst state = await eventStore.aggregateStream(\n  'User-123',\n  (state, event) => {\n    switch (event.type) {\n      case 'UserRegistered':\n        return { ...state, ...event.data };\n      default:\n        return state;\n    }\n  },\n  () => ({ status: 'empty' }),\n);\n```\n\n## Testing\n\n### Running Tests\n\n```bash\n# Unit tests\nnpm run test:unit\n\n# Integration tests (in-memory)\nnpm run test:int\n\n# E2E tests (Firestore Emulator via Testcontainers, requires Docker)\nnpm run test:e2e\n\n# All tests\nnpm test\n\n# Coverage\nnpm run test:coverage\n```\n\nTest files live in `test/` and are selected by filename suffix:\n\n- `*.unit.spec.ts` (unit tests, pure logic)\n- `*.int.spec.ts` (integration tests, in-memory Firestore)\n- `*.e2e.spec.ts` (E2E tests, Firestore emulator via Testcontainers)\n\nSupport fixtures live under `test/support` (including Firebase emulator configs in `test/support/firebase`).\n\n### Using Firestore Emulator\n\nFor local development and manual testing:\n\n```bash\n# Install Firebase CLI\nnpm install -g firebase-tools\n\n# Start emulator\nfirebase emulators:start --only firestore\n```\n\nSet environment variables:\n\n```bash\nexport FIRESTORE_PROJECT_ID=test-project\nexport FIRESTORE_EMULATOR_HOST=localhost:8080\n```\n\nE2E tests start the emulator automatically via Testcontainers.\n\n## Examples\n\n### Complete Shopping Cart Example\n\nSee [examples/shopping-cart](./examples/shopping-cart) for a full application including:\n\n- Event-sourced shopping cart\n- Express.js API with OpenAPI spec\n- Docker Compose setup\n- Unit, integration, and E2E tests\n\n```bash\ncd examples/shopping-cart\ndocker-compose up\n```\n\n### Basic Usage Example\n\n```typescript\nimport { Firestore } from '@google-cloud/firestore';\nimport { getFirestoreEventStore } from '@emmett-community/emmett-google-firestore';\nimport type { Event } from '@event-driven-io/emmett';\n\n// Define events\ntype AccountOpened = Event<'AccountOpened', {\n  accountId: string;\n  initialBalance: number;\n}>;\n\ntype MoneyDeposited = Event<'MoneyDeposited', {\n  accountId: string;\n  amount: number;\n}>;\n\ntype BankAccountEvent = AccountOpened | MoneyDeposited;\n\n// Define state\ntype BankAccount = {\n  accountId: string;\n  balance: number;\n  status: 'open' | 'closed';\n};\n\n// Evolve function\nconst evolve = (state: BankAccount, event: BankAccountEvent): BankAccount => {\n  switch (event.type) {\n    case 'AccountOpened':\n      return {\n        accountId: event.data.accountId,\n        balance: event.data.initialBalance,\n        status: 'open',\n      };\n    case 'MoneyDeposited':\n      return {\n        ...state,\n        balance: state.balance + event.data.amount,\n      };\n    default:\n      return state;\n  }\n};\n\nconst initialState = (): BankAccount => ({\n  accountId: '',\n  balance: 0,\n  status: 'closed',\n});\n\n// Usage\nconst firestore = new Firestore({ projectId: 'my-project' });\nconst eventStore = getFirestoreEventStore(firestore);\n\n// Open account\nawait eventStore.appendToStream('BankAccount-123', [\n  {\n    type: 'AccountOpened',\n    data: { accountId: '123', initialBalance: 100 }\n  },\n]);\n\n// Deposit money\nawait eventStore.appendToStream('BankAccount-123', [\n  {\n    type: 'MoneyDeposited',\n    data: { accountId: '123', amount: 50 }\n  },\n]);\n\n// Get current state\nconst account = await eventStore.aggregateStream(\n  'BankAccount-123',\n  evolve,\n  initialState,\n);\n\nconsole.log(account.balance); // 150\n```\n\n## Configuration\n\n### Custom Collection Names\n\n```typescript\nconst eventStore = getFirestoreEventStore(firestore, {\n  collections: {\n    streams: 'app_streams',\n    counters: 'app_counters',\n  },\n});\n```\n\n### Firestore Emulator (Development)\n\n```typescript\nconst firestore = new Firestore({\n  projectId: 'demo-project',\n  host: 'localhost:8080',\n  ssl: false,\n});\n```\n\n### Production Configuration\n\n```typescript\nconst firestore = new Firestore({\n  projectId: process.env.GCP_PROJECT_ID,\n  keyFilename: process.env.GCP_KEY_FILE,\n  // Optional: specify database\n  databaseId: '(default)',\n});\n```\n\n## Architecture\n\n### Event Sourcing Pattern\n\nThis package implements the **Event Sourcing** pattern:\n\n1. **Commands** → Validate and create events\n2. **Events** → Immutable facts that happened\n3. **State** → Rebuilt by replaying events\n\n```\nCommand → Decide → Events → Append to Firestore → Evolve → State\n```\n\n### Firestore Transaction Flow\n\nWhen appending events, the following happens atomically:\n\n1. Read current stream version\n2. Validate expected version\n3. Increment global position counter\n4. Append events to subcollection\n5. Update stream metadata\n6. Commit transaction\n\nIf any step fails or versions don't match, the entire transaction is rolled back.\n\n## Performance Considerations\n\n### Batch Size\n\nFirestore transactions are limited to 500 operations. When appending many events:\n\n```typescript\n// Good: Small batches\nawait eventStore.appendToStream('stream', events.slice(0, 100));\n\n// Avoid: Very large batches (>400 events)\n```\n\n### Query Optimization\n\n```typescript\n// Good: Use range queries\nconst recent = await eventStore.readStream('stream', {\n  from: lastKnownVersion,\n});\n\n// Good: Limit results\nconst events = await eventStore.readStream('stream', {\n  maxCount: 100\n});\n```\n\n### Firestore Costs\n\n- **Reads**: Each document read counts (events + metadata)\n- **Writes**: Each event appended counts\n- **Storage**: Charged per GB stored\n\nUse the emulator for development to avoid costs!\n\n## Error Handling\n\n```typescript\nimport { ExpectedVersionConflictError } from '@emmett-community/emmett-google-firestore';\n\ntry {\n  await eventStore.appendToStream('stream', events, {\n    expectedStreamVersion: 5\n  });\n} catch (error) {\n  if (error instanceof ExpectedVersionConflictError) {\n    console.log('Version conflict:', error.expected, 'vs', error.actual);\n    // Handle conflict (retry, merge, etc.)\n  }\n}\n```\n\n## Observability\n\nThe event store supports optional logging and OpenTelemetry tracing.\n\n### Logging\n\nPass an optional logger compatible with Pino:\n\n```typescript\nimport pino from 'pino';\n\nconst eventStore = getFirestoreEventStore(firestore, {\n  observability: {\n    logger: pino({ level: 'debug' }),\n  },\n});\n```\n\n**Logging points:**\n\n- `info`: Initialization\n- `debug`: I/O operations (queries, transactions)\n- `warn`: Version conflicts (recoverable)\n- `error`: Failures before rethrowing\n\n**Note:** Event payloads are never logged.\n\n### Tracing\n\nThe package uses `@opentelemetry/api` directly. Spans are created passively:\n\n- If your application initializes OpenTelemetry, spans will be recorded\n- If not, the tracing calls are no-ops with zero overhead\n\n**Span names:**\n\n- `emmett.firestore.read_stream`\n- `emmett.firestore.append_to_stream`\n\n**Attributes:**\n\n- `emmett.stream_name`\n- `emmett.event_count`\n- `emmett.new_version`\n- `emmett.created_new_stream`\n\nTo enable tracing, initialize OpenTelemetry in your application (this package never initializes tracing itself).\n\n## TypeScript Support\n\nThe package is written in TypeScript and includes full type definitions:\n\n```typescript\nimport type {\n  FirestoreEventStore,\n  FirestoreReadEvent,\n  AppendToStreamOptions,\n  ExpectedStreamVersion,\n} from '@emmett-community/emmett-google-firestore';\n```\n\n## Compatibility\n\n- **Node.js**: >= 18.0.0\n- **Emmett**: ^0.39.0\n- **Firestore**: ^7.10.0\n\n## Development\n\n```bash\n# Install dependencies\nnpm install\n\n# Build\nnpm run build\n\n# Run tests\nnpm test\n\n# Lint\nnpm run lint\n\n# Format\nnpm run format\n```\n\n## Contributing\n\nContributions are welcome! Please:\n\n1. Fork the repository\n2. Create a feature branch\n3. Add tests for new functionality\n4. Ensure all tests pass\n5. Submit a pull request\n\n## License\n\nMIT © Emmett Community\n\n## Resources\n\n- [Emmett Documentation](https://event-driven-io.github.io/emmett/)\n- [Event Sourcing Guide](https://event-driven.io/en/event_sourcing_basics/)\n- [Google Firestore Docs](https://cloud.google.com/firestore/docs)\n- [GitHub Repository](https://github.com/emmett-community/emmett-google-firestore)\n\n## Support\n\n- **Issues**: [GitHub Issues](https://github.com/emmett-community/emmett-google-firestore/issues)\n- **Discussions**: [GitHub Discussions](https://github.com/emmett-community/emmett-google-firestore/discussions)\n- **Emmett Discord**: [Join Discord](https://discord.gg/fTpqUTMmVa)\n\n## Acknowledgments\n\n- Built for the [Emmett](https://event-driven-io.github.io/emmett/) framework by [Oskar Dudycz](https://github.com/oskardudycz)\n- Inspired by [emmett-mongodb](https://github.com/event-driven-io/emmett/tree/main/src/packages/emmett-mongodb)\n- Part of the [Emmett Community](https://github.com/emmett-community)\n\n---\n\nMade with ❤️ by the Emmett Community","readmeFilename":"README.md"}