{"_id":"@emmett-community/emmett-google-services-bootstrap","_rev":"3-32e625b59e8c0f3e1a17f16c697375a5","name":"@emmett-community/emmett-google-services-bootstrap","dist-tags":{"latest":"0.3.0"},"versions":{"0.1.0":{"name":"@emmett-community/emmett-google-services-bootstrap","version":"0.1.0","keywords":["emmett","event-driven","bootstrap","google-cloud","firebase","firestore","pubsub","realtime-db"],"author":{"name":"Andre X Costa"},"license":"MIT","_id":"@emmett-community/emmett-google-services-bootstrap@0.1.0","maintainers":[{"name":"axcosta","email":"axcosta@gmail.com"}],"homepage":"https://github.com/emmett-community/emmett-google-services-bootstrap#readme","bugs":{"url":"https://github.com/emmett-community/emmett-google-services-bootstrap/issues"},"dist":{"shasum":"ab4ee3619fd60828b04076d1c2c486bab554ced3","tarball":"https://registry.npmjs.org/@emmett-community/emmett-google-services-bootstrap/-/emmett-google-services-bootstrap-0.1.0.tgz","fileCount":8,"integrity":"sha512-+h3D18Ted+VBrxCpFoIpawizYUUPHnhzRnyiKRF49eiAkKwPXd8yq7/5kXnifhnzAl7hESkobg7Pk3jq1uSDDA==","signatures":[{"sig":"MEYCIQC8D9aJHhc5wBvSpyaCquJ2IyTY0dAmQy9vj2LHGGLatgIhAPNhEokLd0W3P5HpVYl0wssdJFL6wIoTwIbCe2zBLv5b","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":209191},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"0a493e02f15189eae53ff59e17581adf9ccc4d84","scripts":{"test":"run-s test:unit test:int test:e2e","build":"tsup","build:ts":"tsc","test:e2e":"glob -c \"node --import tsx --import tsx/cjs --test\" \"test/e2e/**/*.e2e.spec.ts\"","test:int":"glob -c \"node --import tsx --import tsx/cjs --test\" \"test/integration/**/*.int.spec.ts\"","test:unit":"glob -c \"node --import tsx --import tsx/cjs --test\" \"test/unit/**/*.unit.spec.ts\"","typecheck":"tsc --noEmit","test:watch":"node --import tsx --import tsx/cjs --test --watch","build:ts:watch":"tsc -b --watch","test:e2e:watch":"glob -c \"node --import tsx --import tsx/cjs --test --watch\" \"test/e2e/**/*.e2e.spec.ts\"","test:int:watch":"glob -c \"node --import tsx --import tsx/cjs --test --watch\" \"test/integration/**/*.int.spec.ts\"","test:unit:watch":"glob -c \"node --import tsx --import tsx/cjs --test --watch\" \"test/unit/**/*.unit.spec.ts\""},"_npmUser":{"name":"axcosta","email":"axcosta@gmail.com"},"repository":{"url":"git+https://github.com/emmett-community/emmett-google-services-bootstrap.git","type":"git"},"_npmVersion":"11.6.2","description":"Service bootstrap for event-driven apps using Google Cloud (Firebase, PubSub)","directories":{},"sideEffects":false,"_nodeVersion":"24.12.0","dependencies":{"express":"^4.19.2","pino-http":"^9.0.0","firebase-admin":"^13.6.0","@google-cloud/pubsub":"^4.9.0","express-async-errors":"^3.1.1","http-problem-details":"^0.1.7","@event-driven-io/emmett":"^0.39.0","express-openapi-validator":"^5.6.0","@my-f-startup/firebase-auth-express":"^0.1.0","@emmett-community/emmett-google-pubsub":"^0.3.0","@emmett-community/emmett-observability":"^0.3.0","@emmett-community/emmett-google-firestore":"^0.4.0","@emmett-community/emmett-google-realtime-db":"^0.5.0","@emmett-community/emmett-expressjs-with-openapi":"^0.7.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","glob":"^11.0.0","tsup":"^8.5.0","typescript":"^5.6.0","@types/node":"^22.0.0","npm-run-all2":"^7.0.0","@types/express":"^4.17.21","testcontainers":"^10.28.0"},"_npmOperationalInternal":{"tmp":"tmp/emmett-google-services-bootstrap_0.1.0_1767699145148_0.9216019515538445","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@emmett-community/emmett-google-services-bootstrap","version":"0.2.0","keywords":["emmett","event-driven","bootstrap","google-cloud","firebase","firestore","pubsub","realtime-db"],"author":{"name":"Andre X Costa"},"license":"MIT","_id":"@emmett-community/emmett-google-services-bootstrap@0.2.0","maintainers":[{"name":"axcosta","email":"axcosta@gmail.com"}],"homepage":"https://github.com/emmett-community/emmett-google-services-bootstrap#readme","bugs":{"url":"https://github.com/emmett-community/emmett-google-services-bootstrap/issues"},"dist":{"shasum":"d3d87f0039842223f68d835f21175533cf430087","tarball":"https://registry.npmjs.org/@emmett-community/emmett-google-services-bootstrap/-/emmett-google-services-bootstrap-0.2.0.tgz","fileCount":8,"integrity":"sha512-Mtp+FL0WrXtsRFK7zGL263iNN2NxsFwTapxOa1LeM4BCL8AP0nG46a2CRQSmbwgm7eBnCYPN/SERBs3vNnOZlQ==","signatures":[{"sig":"MEYCIQDUdDeoEgs9Jv7LIVNM6ee8kGRQ+qFn/xMQGi9jaWzECwIhAIFJDq7aU2q9pDNGZFAPSHwICvhgWgjkym2YVTQgV6VD","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@emmett-community%2femmett-google-services-bootstrap@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":209235},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"3bcad1d85ea02eb498edfc6fa073308e26b4f20e","scripts":{"test":"run-s test:unit test:int test:e2e","build":"tsup","build:ts":"tsc","test:e2e":"glob -c \"node --import tsx --import tsx/cjs --test\" \"test/e2e/**/*.e2e.spec.ts\"","test:int":"glob -c \"node --import tsx --import tsx/cjs --test\" \"test/integration/**/*.int.spec.ts\"","test:unit":"glob -c \"node --import tsx --import tsx/cjs --test\" \"test/unit/**/*.unit.spec.ts\"","typecheck":"tsc --noEmit","test:watch":"node --import tsx --import tsx/cjs --test --watch","build:ts:watch":"tsc -b --watch","test:e2e:watch":"glob -c \"node --import tsx --import tsx/cjs --test --watch\" \"test/e2e/**/*.e2e.spec.ts\"","test:int:watch":"glob -c \"node --import tsx --import tsx/cjs --test --watch\" \"test/integration/**/*.int.spec.ts\"","test:unit:watch":"glob -c \"node --import tsx --import tsx/cjs --test --watch\" \"test/unit/**/*.unit.spec.ts\""},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:13842a6c-f186-46ff-a2b1-d07dcceacee2"}},"repository":{"url":"git+https://github.com/emmett-community/emmett-google-services-bootstrap.git","type":"git"},"_npmVersion":"11.6.2","description":"Service bootstrap for event-driven apps using Google Cloud (Firestore, Realtime DB, PubSub)","directories":{},"sideEffects":false,"_nodeVersion":"24.12.0","dependencies":{"express":"^4.19.2","pino-http":"^9.0.0","pino-pretty":"^13.1.3","firebase-admin":"^13.6.0","@google-cloud/pubsub":"^4.9.0","express-async-errors":"^3.1.1","http-problem-details":"^0.1.7","@event-driven-io/emmett":"^0.39.0","express-openapi-validator":"^5.6.0","@my-f-startup/firebase-auth-express":"^0.1.0","@emmett-community/emmett-google-pubsub":"^0.4.0","@emmett-community/emmett-observability":"^0.3.0","@emmett-community/emmett-google-firestore":"^0.5.0","@emmett-community/emmett-google-realtime-db":"^0.7.0","@emmett-community/emmett-expressjs-with-openapi":"^0.8.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","glob":"^11.0.0","tsup":"^8.5.0","typescript":"^5.6.0","@types/node":"^22.0.0","npm-run-all2":"^7.0.0","@types/express":"^4.17.21","testcontainers":"^10.28.0"},"_npmOperationalInternal":{"tmp":"tmp/emmett-google-services-bootstrap_0.2.0_1768565138184_0.4571163910028231","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@emmett-community/emmett-google-services-bootstrap","version":"0.3.0","license":"MIT","type":"module","sideEffects":false,"description":"Service bootstrap for event-driven apps using Google Cloud (Firestore, Realtime DB, PubSub)","scripts":{"build":"tsup","build:ts":"tsc","build:ts:watch":"tsc -b --watch","test":"run-s test:unit test:int test:e2e","test:unit":"glob -c \"node --import tsx --import tsx/cjs --test\" \"test/unit/**/*.unit.spec.ts\"","test:int":"glob -c \"node --import tsx --import tsx/cjs --test\" \"test/integration/**/*.int.spec.ts\"","test:e2e":"glob -c \"node --import tsx --import tsx/cjs --test\" \"test/e2e/**/*.e2e.spec.ts\"","test:watch":"node --import tsx --import tsx/cjs --test --watch","test:unit:watch":"glob -c \"node --import tsx --import tsx/cjs --test --watch\" \"test/unit/**/*.unit.spec.ts\"","test:int:watch":"glob -c \"node --import tsx --import tsx/cjs --test --watch\" \"test/integration/**/*.int.spec.ts\"","test:e2e:watch":"glob -c \"node --import tsx --import tsx/cjs --test --watch\" \"test/e2e/**/*.e2e.spec.ts\"","typecheck":"tsc --noEmit"},"repository":{"type":"git","url":"git+https://github.com/emmett-community/emmett-google-services-bootstrap.git"},"keywords":["emmett","event-driven","bootstrap","google-cloud","firebase","firestore","pubsub","realtime-db"],"author":{"name":"Andre X Costa"},"bugs":{"url":"https://github.com/emmett-community/emmett-google-services-bootstrap/issues"},"homepage":"https://github.com/emmett-community/emmett-google-services-bootstrap#readme","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","dependencies":{"@emmett-community/emmett-expressjs-with-openapi":"^0.8.0","@emmett-community/emmett-google-firestore":"^0.5.0","@emmett-community/emmett-google-pubsub":"^0.4.0","@emmett-community/emmett-google-realtime-db":"^0.7.0","@emmett-community/emmett-observability":"^0.3.0","@event-driven-io/emmett":"^0.39.0","@google-cloud/pubsub":"^4.9.0","@my-f-startup/firebase-auth-express":"^0.1.0","express":"^4.19.2","express-async-errors":"^3.1.1","express-openapi-validator":"^5.6.0","firebase-admin":"^13.6.0","http-problem-details":"^0.1.7","pino-http":"^9.0.0","pino-pretty":"^13.1.3"},"devDependencies":{"@types/express":"^4.17.21","@types/node":"^22.0.0","glob":"^11.0.0","npm-run-all2":"^7.0.0","testcontainers":"^10.28.0","tsup":"^8.5.0","tsx":"^4.19.0","typescript":"^5.6.0"},"publishConfig":{"access":"public"},"gitHead":"b8cd3f39959a54852c6c57f9bb094fa0736dfdc2","_id":"@emmett-community/emmett-google-services-bootstrap@0.3.0","_nodeVersion":"24.12.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-Nw3nQ9HWmc/R3cFpfAvAhgnIe5sgC0i8EaZ4vnUrr5ufuYhL1PBYFPVnQcQMGDrrCP9GHOGIRAmUeQ48CH4tnQ==","shasum":"4821305f0bea2c39d265d06b8ae95585f38f3b08","tarball":"https://registry.npmjs.org/@emmett-community/emmett-google-services-bootstrap/-/emmett-google-services-bootstrap-0.3.0.tgz","fileCount":8,"unpackedSize":211442,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@emmett-community%2femmett-google-services-bootstrap@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIBf5N4w3b7jWzmJ9J+e7b3GP7O+HdelyA586EZbeRptgAiAsVAq51OcCm5I7zdXqa1eHo3aX5R+RNTCY345o1/M8rg=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:13842a6c-f186-46ff-a2b1-d07dcceacee2"}},"directories":{},"maintainers":[{"name":"axcosta","email":"axcosta@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/emmett-google-services-bootstrap_0.3.0_1768571741349_0.4054341011967464"},"_hasShrinkwrap":false}},"time":{"created":"2026-01-06T11:32:25.057Z","modified":"2026-01-16T13:55:41.894Z","0.1.0":"2026-01-06T11:32:25.313Z","0.2.0":"2026-01-16T12:05:38.313Z","0.3.0":"2026-01-16T13:55:41.555Z"},"bugs":{"url":"https://github.com/emmett-community/emmett-google-services-bootstrap/issues"},"author":{"name":"Andre X Costa"},"license":"MIT","homepage":"https://github.com/emmett-community/emmett-google-services-bootstrap#readme","keywords":["emmett","event-driven","bootstrap","google-cloud","firebase","firestore","pubsub","realtime-db"],"repository":{"type":"git","url":"git+https://github.com/emmett-community/emmett-google-services-bootstrap.git"},"description":"Service bootstrap for event-driven apps using Google Cloud (Firestore, Realtime DB, PubSub)","maintainers":[{"name":"axcosta","email":"axcosta@gmail.com"}],"readme":"# @emmett-community/emmett-google-services-bootstrap\n\nService bootstrap for event-driven apps using Google Cloud. This package wires Firebase Admin, Firestore event store, Realtime DB projections, PubSub message bus, and lifecycle utilities so services can start with minimal boilerplate.\n\n[![npm version](https://img.shields.io/npm/v/@emmett-community/emmett-google-services-bootstrap.svg)](https://www.npmjs.com/package/@emmett-community/emmett-google-services-bootstrap) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Build and test](https://github.com/emmett-community/emmett-google-services-bootstrap/actions/workflows/build_and_test.yml/badge.svg)](https://github.com/emmett-community/emmett-google-services-bootstrap/actions/workflows/build_and_test.yml)\n\n## Features\n\n- ✅ One orchestrator to initialize Firebase, PubSub, and event store wiring\n- ✅ Realtime DB projections hooked automatically\n- ✅ Built-in dependency checks and graceful shutdown\n- ✅ OpenAPI-ready app creation with sensible defaults\n- ✅ Auth security handlers available without extra service deps\n- ✅ No side effects on import\n\n## Why this package exists\n\nMost Emmett services repeat the same bootstrap work: Firebase Admin init, Firestore event store setup, PubSub wiring, and shutdown logic. This package centralizes that boilerplate into a single, explicit orchestrator.\n\n## How it relates to other emmett-community packages\n\n- It is **for application entrypoints** (service `index.ts`).\n- It composes existing packages (firestore, pubsub, realtime-db, observability, expressjs-with-openapi).\n- It keeps domain code untouched and wiring centralized.\n\n## Installation\n\n```bash\nnpm install @emmett-community/emmett-google-services-bootstrap\n```\n\nMinimal service dependencies:\n\n```json\n{\n  \"dependencies\": {\n    \"@emmett-community/emmett-google-services-bootstrap\": \"^0.1.0\"\n  },\n  \"devDependencies\": {\n    \"@types/express\": \"^4.17.21\"\n  }\n}\n```\n\n## Quick start\n\n```typescript\nimport { ServiceBootstrap } from '@emmett-community/emmett-google-services-bootstrap';\nimport { userDetailsProjection } from './projections/userDetailsProjection';\nimport { setupUserDataRemovalSubscriber } from './subscriptions/userDataRemovalSubscriber';\n\nconst bootstrap = new ServiceBootstrap({\n  serviceName: 'user-service',\n  projections: [userDetailsProjection],\n});\n\nawait bootstrap.startApi({\n  port: Number(process.env.PORT ?? 3000),\n  openApiPath: './openapi.yml',\n  handlersPath: './handlers',\n  initializeHandlers: (handlers, ctx) => {\n    handlers.users.initializeHandlers(\n      ctx.eventStore,\n      ctx.database,\n      ctx.messageBus,\n      ctx.getCurrentTime,\n    );\n  },\n  beforeStart: (ctx) => {\n    setupUserDataRemovalSubscriber({\n      eventStore: ctx.eventStore,\n      messageBus: ctx.messageBus,\n      auth: ctx.auth,\n      getCurrentTime: ctx.getCurrentTime,\n    });\n  },\n});\n```\n\n## Configuration\n\n### Environment variables\n\n- `FIRESTORE_PROJECT_ID`: Firebase project id (fallback to `GCLOUD_PROJECT` / `GOOGLE_CLOUD_PROJECT`)\n- `demo-project` fallback: if project id is missing and any emulator host is configured, defaults to `demo-project` (also used for PubSub)\n- `FIRESTORE_EMULATOR_HOST`: Firestore emulator host\n- `FIREBASE_DATABASE_EMULATOR_HOST`: Realtime DB emulator host\n- `PUBSUB_PROJECT_ID`: PubSub project id\n- `PUBSUB_EMULATOR_HOST`: PubSub emulator host\n- `LOG_LEVEL`: default log level for built-in logger\n- `NODE_ENV`: forwarded to logging helpers\n\n### Options\n\n```typescript\nimport type { ServiceBootstrapConfig } from '@emmett-community/emmett-google-services-bootstrap';\n\nconst config: ServiceBootstrapConfig = {\n  serviceName: 'user-service',\n  firebase: {\n    projectId: 'demo-project',\n    databaseURL: 'https://demo-project-default-rtdb.firebaseio.com',\n  },\n  pubsub: {\n    projectId: 'demo-project',\n    topicPrefix: 'user-service',\n    autoCreateResources: true,\n  },\n  eventStore: {\n    collections: {\n      streams: 'user-service-streams',\n      counters: '_user-service-counters',\n    },\n  },\n  projections: [],\n  observability: {\n    createLogger: true,\n    logLevel: 'info',\n  },\n  includeDefaultDependencyChecks: true,\n  autoStartMessageBus: true,\n  shutdownOnDependencyFailure: true,\n};\n```\n\n### Event store collection names\n\n- `eventStore.collections.streams`: Firestore collection that stores stream metadata (defaults to `\"streams\"`).\n- `eventStore.collections.counters`: Firestore collection for shared counters (defaults to `\"_counters\"`).\n\n## API\n\n### ServiceBootstrap\n\n- `initialize()` initializes Firebase, PubSub, and the event store.\n- `start()` runs dependency checks and returns the same context as `initialize()`.\n- `createApp()` returns an Express app wired with OpenAPI validation.\n- `shutdown(reason, exitCode?)` runs graceful shutdown tasks.\n- `registerSignalHandlers()` installs SIGINT/SIGTERM handlers.\n- `startApi()` initializes, runs checks, creates the app, and starts the HTTP server.\n\n### Lifecycle utilities\n\n- `DependencyChecker` and `startDependency()` for health checks.\n- `GracefulShutdown` for ordered shutdown tasks.\n- `registerSignalHandlers()` to attach process signals.\n\n## What this package intentionally does NOT do\n\n- Hide application infrastructure choices behind magic defaults\n- Create domain handlers or business rules\n- Start HTTP servers automatically (use `startApi()` if you want the helper)\n- Require any specific deployment model\n\n## Testing\n\n```bash\nnpm run test:unit\nnpm run test:int\nnpm run test:e2e\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---\n\nMade with ❤️ by the Emmett Community\n","readmeFilename":"README.md"}