{"_id":"@cameronlr/watchtower","_rev":"2-19520488bbc642d488a57468edd91b40","name":"@cameronlr/watchtower","dist-tags":{"beta":"0.2.0","latest":"0.2.0"},"versions":{"0.2.0":{"name":"@cameronlr/watchtower","version":"0.2.0","keywords":["healthcheck","monitoring","status","uptime","observability","last-rev"],"license":"MIT","_id":"@cameronlr/watchtower@0.2.0","maintainers":[{"name":"cameronlr","email":"cameron@lastrev.com"}],"homepage":"https://github.com/last-rev-llc/watchtower#readme","bugs":{"url":"https://github.com/last-rev-llc/watchtower/issues"},"dist":{"shasum":"18d8dd1388b8a99eb9ec97155e84d4765dbefe9e","tarball":"https://registry.npmjs.org/@cameronlr/watchtower/-/watchtower-0.2.0.tgz","fileCount":48,"integrity":"sha512-vW5f+EE2y8qRDVLeOyBOChFP17vN/SToTx9jMHvyDmi/0MhoBu71R25/wyUOcWY1iqxSWKu5Wn9SLARUl//uug==","signatures":[{"sig":"MEUCIQCRcmBtw7ERXpCA3WezYkvnB63CLPy4E+AM+AIl5HAHNAIgcI13wgRpZsErjdVpATclj1+3GK1/I4xOfCIDQP4otvE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":467305},"main":"dist/index.cjs","types":"dist/index.d.ts","module":"dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./next":{"types":"./dist/adapters/next.d.ts","import":"./dist/adapters/next.js","require":"./dist/adapters/next.cjs"}},"gitHead":"f8a8e53e82b678fac2cc80be638381cc2c06dff1","private":false,"scripts":{"dev":"tsup --watch","lint":"eslint . --ext .ts","test":"vitest run","build":"tsup","release":"changeset publish","changeset":"changeset","test:watch":"vitest","publish:beta":"pnpm build && npm publish --tag beta","version:beta":"changeset version --snapshot beta","prepublishOnly":"pnpm build","publish:stable":"pnpm build && npm publish","version:stable":"changeset version","publish:dry-run":"pnpm build && npm publish --dry-run","version-packages":"changeset version && pnpm i --lockfile-only"},"_npmUser":{"name":"cameronlr","email":"cameron@lastrev.com"},"repository":{"url":"git+https://github.com/last-rev-llc/watchtower.git","type":"git"},"_npmVersion":"11.6.0","description":"A unified healthcheck and status toolkit for web applications — Watchtower keeps a vigilant eye on your site's heartbeat, ensuring every critical path and service stays up.","directories":{},"sideEffects":false,"_nodeVersion":"22.19.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.1.0","eslint":"^9.12.0","vitest":"^2.1.0","typescript":"^5.6.3","@types/node":"^20.11.30","@changesets/cli":"^2.27.8"},"peerDependencies":{"next":"^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0","algoliasearch":"^5.0.0"},"peerDependenciesMeta":{"next":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/watchtower_0.2.0_1761767352169_0.09834955313566351","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."}},"time":{"created":"2025-10-29T19:49:12.077Z","modified":"2025-11-12T23:45:46.219Z","0.2.0":"2025-10-29T19:49:12.403Z"},"bugs":{"url":"https://github.com/last-rev-llc/watchtower/issues"},"license":"MIT","homepage":"https://github.com/last-rev-llc/watchtower#readme","keywords":["healthcheck","monitoring","status","uptime","observability","last-rev"],"repository":{"url":"git+https://github.com/last-rev-llc/watchtower.git","type":"git"},"description":"A unified healthcheck and status toolkit for web applications — Watchtower keeps a vigilant eye on your site's heartbeat, ensuring every critical path and service stays up.","maintainers":[{"name":"cameronlr","email":"cameron@lastrev.com"}],"readme":"# @last-rev/watchtower\n\nA unified healthcheck and status toolkit for web applications — Watchtower keeps a vigilant eye on your site's heartbeat, ensuring every critical path and service stays up.\n\n[![npm version](https://badge.fury.io/js/%40last-rev%2Fwatchtower.svg)](https://badge.fury.io/js/%40last-rev%2Fwatchtower)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n## Features\n\n- 🏥 **Comprehensive Health Checks** - Monitor Algolia search, web pages, HTTP endpoints, and build integrity\n- ⚡ **High Performance** - Parallel execution with configurable timeouts and caching\n- 🔒 **Security First** - Multiple sanitization strategies for production environments\n- 🔧 **Framework Agnostic** - Next.js adapter included, Express support coming soon\n- 📦 **Zero Runtime Dependencies** - Lightweight package with peer dependencies only\n- 🎯 **TypeScript Ready** - Full TypeScript support with comprehensive type definitions\n\n## Installation\n\n```bash\n# npm\nnpm install @last-rev/watchtower\n\n# pnpm\npnpm add @last-rev/watchtower\n\n# yarn\nyarn add @last-rev/watchtower\n```\n\n**Peer Dependencies:**\n- `algoliasearch@^5.0.0` (required for Algolia checks)\n- `next@^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0` (required for Next.js adapter)\n\n## Examples\n\nReady-to-use examples are available in the [`examples/`](./examples/) directory:\n\n- **[Next.js Pages Router](./examples/nextjs-pages-router/)** - Complete setup with Contentful and Algolia\n- **[Next.js App Router](./examples/nextjs-app-router/)** - Modern Next.js 13+ App Router integration\n- **[Manual Configuration](./examples/manual-config/)** - Custom configuration without templates\n- **[Minimal Setup](./examples/minimal/)** - Simple configuration with essential checks only\n\nSee the [examples README](./examples/README.md) for detailed setup instructions.\n\n## Quick Start\n\n### Next.js Setup\n\n1. **Create your healthcheck configuration:**\n\n```typescript\n// healthcheck.config.ts\nimport {\n  createAlgoliaCheck,\n  createPagesCheck,\n  createBuildCheck,\n  type RunnerConfig\n} from '@last-rev/watchtower';\n\nconst config: RunnerConfig = {\n  budgetMs: 8000,\n  cacheMs: 30000,\n  auth: {\n    token: process.env.HEALTHCHECK_TOKEN,\n    allowMonitoring: true\n  },\n  sanitize: process.env.NODE_ENV === 'production' ? 'counts-only' : 'none',\n  \n  checks: [\n    createBuildCheck({\n      criticalEnv: ['CONTENTFUL_SPACE_ID', 'ALGOLIA_ADMIN_API_KEY']\n    }),\n    createAlgoliaCheck({\n      indexName: 'contentful',\n      apiKey: process.env.ALGOLIA_ADMIN_API_KEY,\n      useSearchKey: false,\n      thresholds: {\n        totalRecords: { critical: 50, warning: 100 },\n        categories: {\n          'In the News': {\n            critical: 5,\n            warning: 20,\n            actualField: 'postType',\n            actualValue: 'In the News'\n          }\n        }\n      }\n    }),\n    createPagesCheck({\n      critical: [{ path: '/', name: 'Homepage' }],\n      timeout: 5000,\n      retries: 2\n    })\n  ]\n};\n\nexport default config;\n```\n\n2. **Create your API route:**\n\n```typescript\n// pages/api/healthcheck.ts\nimport { createNextHandler } from '@last-rev/watchtower';\nimport config from '../../healthcheck.config';\n\nexport default createNextHandler(config);\n```\n\n3. **Test your healthcheck:**\n\n```bash\n# Development\ncurl http://localhost:3000/api/healthcheck\n\n# Production (with authentication)\ncurl -H \"Authorization: Bearer YOUR_TOKEN\" https://example.com/api/healthcheck\n```\n\n### Using Templates (Alternative Approach)\n\nFor quick setup, you can use pre-configured templates:\n\n```typescript\n// healthcheck.config.ts\nimport { contentfulSiteTemplate } from '@last-rev/watchtower';\n\nexport default contentfulSiteTemplate({\n  algolia: {\n    indexName: 'contentful',\n    thresholds: {\n      totalRecords: { critical: 50, warning: 100 }\n    }\n  }\n});\n```\n\nThen use it in your API route:\n\n```typescript\n// pages/api/healthcheck.ts\nimport { createNextHandler } from '@last-rev/watchtower';\nimport config from '../../healthcheck.config';\n\nexport default createNextHandler(config);\n```\n\n## Configuration\n\n### Available Checks\n\n#### Algolia Check\nMonitor your Algolia search indices with customizable thresholds.\n\n```typescript\ncreateAlgoliaCheck({\n  indexName: 'contentful',\n  applicationId: process.env.ALGOLIA_APPLICATION_ID,\n  apiKey: process.env.ALGOLIA_ADMIN_API_KEY,\n  useSearchKey: false, // Use admin key for comprehensive monitoring\n  thresholds: {\n    totalRecords: {\n      critical: process.env.NODE_ENV === 'production' ? 100 : 50,\n      warning: process.env.NODE_ENV === 'production' ? 125 : 100\n    },\n    categories: {\n      'In the News': {\n        critical: 5,\n        warning: 20,\n        actualField: 'postType',\n        actualValue: 'In the News'\n      },\n      'Blogs': {\n        critical: 3,\n        warning: 10,\n        actualField: 'postType',\n        actualValue: 'Blog Post'\n      },\n    },\n  },\n  skipHeavyFacets: false, // Enable full facet checking for comprehensive monitoring\n});\n```\n\n#### Pages Check\nVerify critical web pages are accessible and responding correctly.\n\n```typescript\ncreatePagesCheck({\n  critical: [\n    { path: '/', name: 'Homepage' },\n    { path: '/robots.txt', name: 'Robots.txt' },\n    { path: '/sitemap.xml', name: 'Sitemap' },\n  ],\n  important: [\n    { path: '/about', name: 'About Page' },\n    { path: '/blog', name: 'Blog Index' },\n  ],\n  timeout: 5000,\n  retries: 2,\n});\n```\n\n#### HTTP Check\nTest custom HTTP endpoints with full control over requests.\n\n```typescript\ncreateHttpCheck({\n  endpoints: [\n    {\n      path: '/api/graphql',\n      name: 'GraphQL API',\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      body: { query: '{ __typename }' },\n      expectedStatus: 200,\n    },\n  ],\n  timeout: 3000,\n  retries: 1,\n});\n```\n\n#### Build Check\nValidate build integrity and environment variables.\n\n```typescript\ncreateBuildCheck({\n  criticalEnv: [\n    'CONTENTFUL_SPACE_ID',\n    'CONTENTFUL_DELIVERY_TOKEN',\n    'NODE_ENV',\n  ],\n  optionalEnv: [\n    'NEXT_PUBLIC_SITE_URL',\n    'ALGOLIA_APPLICATION_ID',\n  ],\n});\n```\n\n### Templates\n\nWatchtower provides pre-configured templates for common use cases:\n\n#### Contentful Site Template\nOptimized for Contentful-powered websites with Algolia search.\n\n```typescript\nimport { contentfulSiteTemplate } from '@last-rev/watchtower';\n\nexport default contentfulSiteTemplate({\n  algolia: {\n    indexName: 'contentful',\n    thresholds: {\n      totalRecords: { critical: 100, warning: 200 },\n      categories: {\n        'Blog Posts': { \n          critical: 5, \n          warning: 20,\n          actualField: 'contentType',\n          actualValue: 'blogPost'\n        },\n      },\n    },\n  },\n  // Optional: Don't pass contentful config to avoid API checks\n  // contentful: { ... }\n});\n```\n\n**Note:** The `contentfulSiteTemplate` includes Build integrity and Algolia checks. To add Contentful API checks, pass the `contentful` configuration option.\n\n#### Default Template\nA balanced configuration suitable for most web applications. No configuration needed.\n\n```typescript\nimport { defaultTemplate } from '@last-rev/watchtower';\n\n// Returns config with:\n// - Pages check (/, /robots.txt, /sitemap.xml, /favicon.ico)\n// - Build check (NODE_ENV, NEXT_PUBLIC_SITE_URL)\nexport default defaultTemplate();\n```\n\n#### Minimal Template\nLightweight setup with only essential checks. No configuration needed.\n\n```typescript\nimport { minimalTemplate } from '@last-rev/watchtower';\n\n// Returns config with:\n// - Build check (package.json, Node version)\nexport default minimalTemplate();\n```\n\n### Authentication\n\nSecure your healthcheck endpoints with token-based authentication.\n\n```typescript\nconst config = {\n  // ... checks\n  auth: {\n    token: process.env.HEALTHCHECK_TOKEN,\n    allowMonitoring: true, // Allow monitoring services like Datadog, UptimeRobot\n    customValidator: (req) => {\n      // Custom validation logic\n      return req.headers['x-internal-token'] === process.env.INTERNAL_TOKEN;\n    },\n  },\n};\n```\n\n### Sanitization Strategies\n\nControl what information is exposed in healthcheck responses:\n\n#### None (Development)\n```typescript\nsanitize: 'none' // Full details exposed\n```\n\n#### Redact Values (Internal Monitoring)\n```typescript\nsanitize: 'redact-values' // Environment variables and URLs partially masked\n```\n\n#### Counts Only (Production)\n```typescript\nsanitize: 'counts-only' // Only show counts, no sensitive details\n```\n\n## Performance\n\n### Budget and Timeouts\nConfigure performance constraints to ensure healthchecks don't impact your application:\n\n```typescript\nconst config = {\n  budgetMs: 8000,    // Global timeout (8 seconds for comprehensive checks)\n  cacheMs: 30000,    // Cache results for 30 seconds (balance between freshness and performance)\n  aggregationPrecedence: ['Down', 'Partial', 'Unknown', 'Up'],\n};\n```\n\n### Caching\nExpensive operations like Algolia facet queries are automatically cached:\n\n```typescript\nconst config: RunnerConfig = {\n  cacheMs: 30000, // Cache results for 30 seconds\n  checks: [\n    createAlgoliaCheck({\n      indexName: 'contentful',\n      skipHeavyFacets: false // Enable full facet checking (cached)\n    })\n  ]\n};\n```\n\n## Response Format\n\nHealthcheck responses follow a standardized format:\n\n```json\n{\n  \"id\": \"site_healthcheck\",\n  \"name\": \"Site Health\",\n  \"status\": \"Up\",\n  \"message\": \"All systems operational\",\n  \"timestamp\": 1703123456789,\n  \"performance\": {\n    \"totalCheckTime\": 234,\n    \"checksCompleted\": 4,\n    \"checksFailed\": 0\n  },\n  \"services\": [\n    {\n      \"id\": \"algolia\",\n      \"name\": \"Algolia Search\",\n      \"status\": \"Up\",\n      \"message\": \"Algolia Search: All systems operational\",\n      \"timestamp\": 1703123456789,\n      \"services\": [\n        {\n          \"id\": \"record_count\",\n          \"name\": \"Record Count\",\n          \"status\": \"Up\",\n          \"message\": \"1250 total records (234ms)\",\n          \"timestamp\": 1703123456789,\n          \"metadata\": {\n            \"duration\": 234,\n            \"totalRecords\": 1250,\n            \"productionRecords\": 1100,\n            \"previewRecords\": 150\n          }\n        }\n      ]\n    }\n  ]\n}\n```\n\n## HTTP Status Codes\n\nThe API returns appropriate HTTP status codes based on overall health:\n\n- `200` - Up or Partial (service still functional)\n- `503` - Down or Unknown (service unavailable)\n\n## Environment Variables\n\n### Required for Functionality\n- `NEXT_PUBLIC_SITE_URL` - Base URL for page checks (e.g., `https://example.com`)\n- `ALGOLIA_APPLICATION_ID` - Algolia application ID\n- `ALGOLIA_ADMIN_API_KEY` - Algolia admin API key (recommended for comprehensive monitoring)\n- `CONTENTFUL_SPACE_ID` - Contentful space ID\n- `CONTENTFUL_ENV` - Contentful environment (e.g., `master`)\n\n### Security\n- `HEALTHCHECK_TOKEN` - Authentication token for healthcheck endpoint (required in production)\n- `NODE_ENV` - Environment mode (affects sanitization and thresholds)\n\n### Optional\n- `ALGOLIA_SEARCH_API_KEY` - Algolia search-only API key (alternative to admin key)\n- `CONTENTFUL_DELIVERY_TOKEN` - Contentful delivery API token\n- `CONTENTFUL_PREVIEW_TOKEN` - Contentful preview API token\n- `SITE_URL` - Alternative site URL configuration\n- `DOMAIN` - Domain name for URL construction\n- `VERCEL_URL` - Vercel deployment URL (auto-detected)\n- `DATABASE_URL` - Database connection string\n- `REDIS_URL` - Redis connection string\n\n## Advanced Usage\n\n### Custom Check Implementation\n\nCreate your own health checks by implementing the `Check` interface:\n\n```typescript\nimport type { Check, StatusNode } from '@last-rev/watchtower';\nimport { createStatusNode } from '@last-rev/watchtower';\n\nexport function createCustomCheck(): Check {\n  return {\n    id: 'custom',\n    name: 'Custom Service',\n    async run(): Promise<StatusNode> {\n      try {\n        // Your custom health check logic\n        const isHealthy = await checkCustomService();\n\n        return createStatusNode(\n          'custom',\n          'Custom Service',\n          isHealthy ? 'Up' : 'Down',\n          isHealthy ? 'Service operational' : 'Service unavailable'\n        );\n      } catch (error) {\n        return createStatusNode(\n          'custom',\n          'Custom Service',\n          'Unknown',\n          `Check failed: ${(error as Error).message}`\n        );\n      }\n    },\n  };\n}\n```\n\n### Framework Integration\n\n#### Next.js (Pages Router)\n```typescript\n// pages/api/healthcheck.ts\nimport { createNextHandler } from '@last-rev/watchtower';\n\nexport default createNextHandler(config);\n```\n\n#### Next.js (App Router)\n```typescript\n// app/api/healthcheck/route.ts\nimport { createNextHandler } from '@last-rev/watchtower';\n\nexport const GET = createNextHandler(config);\n```\n\n## Migration from Legacy Healthchecks\n\n### From Custom Implementation\n1. Replace your existing healthcheck with Watchtower configuration\n2. Update environment variables to match Watchtower expectations\n3. Test thoroughly in development before deploying\n4. Update monitoring dashboards to handle new response format\n\n### Environment Variable Mapping\n| Legacy            | Watchtower               |\n| ----------------- | ------------------------ |\n| `SITE_URL`        | `NEXT_PUBLIC_SITE_URL`   |\n| `ALGOLIA_APP_ID`  | `ALGOLIA_APPLICATION_ID` |\n| `ALGOLIA_API_KEY` | `ALGOLIA_ADMIN_API_KEY`  |\n\n## Troubleshooting\n\n### Common Issues\n\n**Healthcheck returns 503 but site works fine**\n- Check authentication configuration\n- Verify all required environment variables are set\n- Review individual service status in response details\n\n**Algolia checks failing**\n- Verify Algolia credentials and permissions\n- Check if index exists and is populated\n- Review network connectivity to Algolia API\n\n**Page checks failing**\n- Ensure `NEXT_PUBLIC_SITE_URL` is correctly set\n- Check if pages actually exist at specified paths\n- Verify site is accessible from healthcheck location\n\n**Build failing**\n- Check TypeScript compilation errors\n- Verify all peer dependencies are installed\n- Review build configuration in `tsup.config.ts`\n\n### Debug Mode\nEnable detailed logging in development:\n\n```typescript\nconst config = {\n  // ... configuration\n  sanitize: 'none', // Show full details in development\n};\n```\n\n## Contributing\n\n1. Fork the repository\n2. Create your feature branch (`git checkout -b feature/amazing-feature`)\n3. Commit your changes (`git commit -m 'Add amazing feature'`)\n4. Push to the branch (`git push origin feature/amazing-feature`)\n5. Open a Pull Request\n\n### Adding New Checks\n1. Create new check in `src/checks/`\n2. Add exports to `src/checks/index.ts`\n3. Update types in `src/core/types.ts`\n4. Add documentation and examples\n5. Update tests\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## Publishing\n\nFor maintainers publishing new versions, see [PUBLISHING.md](./PUBLISHING.md) for the complete publishing workflow including:\n- Beta vs stable releases\n- Security checklist\n- Version management with changesets\n- Dry run verification\n\n## Support\n\nFor support and questions:\n- 📧 [Create an issue](https://github.com/last-rev-llc/watchtower/issues)\n- 📚 [Documentation](https://github.com/last-rev-llc/watchtower#readme)\n- 🏢 [LastRev](https://lastrev.com)\n\n---\n\n**Made with ❤️ by [LastRev](https://lastrev.com)**\n","readmeFilename":"README.md"}