{"_id":"@airqo-packages/network-coverage","_rev":"4-3a7d935bf49bdc7e6e8512cc46f07380","name":"@airqo-packages/network-coverage","dist-tags":{"latest":"2.0.0"},"versions":{"1.0.0":{"name":"@airqo-packages/network-coverage","version":"1.0.0","keywords":["airqo","air-quality","network-coverage","monitors","africa"],"author":{"name":"AirQo"},"license":"MIT","_id":"@airqo-packages/network-coverage@1.0.0","maintainers":[{"name":"airqo-collection","email":"airqo.analytics@gmail.com"}],"homepage":"https://github.com/airqo-platform/AirQo-api/tree/main/packages/airqo-network-coverage#readme","bugs":{"url":"https://github.com/airqo-platform/AirQo-api/issues"},"dist":{"shasum":"a633f95745e06f857086280e4a99de27ddffff1a","tarball":"https://registry.npmjs.org/@airqo-packages/network-coverage/-/network-coverage-1.0.0.tgz","fileCount":14,"integrity":"sha512-ueaSHDmbL024y5dYGTr1gn7W3btnVlarvI/4waei+b8W10r3qQXFFk4NXNIcok/BgyRIWH99XjbHHk/MSJoa8w==","signatures":[{"sig":"MEQCIDpQiILy3xjbVGGyICu8MWYFpDu/YgYdyLfEg1vlu/GRAiAxw/N8C0WezD2rLq0043NE4j+qp5cYNmmugMHj03VRTQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":44306},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","require":"./dist/index.js"}},"gitHead":"22b9eb71b3145ba3c8104fd8bc996da64a955bd7","scripts":{"dev":"tsc --watch","lint":"eslint src --ext .ts","test":"jest","build":"tsc --project tsconfig.json","clean":"rimraf dist","lint:fix":"eslint src --ext .ts --fix","test:watch":"jest --watch","test:coverage":"jest --coverage","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"airqo-collection","email":"airqo.analytics@gmail.com"},"repository":{"url":"git+https://github.com/airqo-platform/AirQo-api.git","type":"git","directory":"packages/airqo-network-coverage"},"_npmVersion":"10.2.3","description":"Node.js client for the AirQo Network Coverage API — typed wrappers for all monitor and registry endpoints","directories":{},"_nodeVersion":"20.10.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.54.0","rimraf":"^5.0.5","ts-jest":"^29.1.1","typescript":"^5.4.0","@types/jest":"^29.5.8","@types/node":"^20.11.0","@typescript-eslint/parser":"^6.12.0","@typescript-eslint/eslint-plugin":"^6.12.0"},"_npmOperationalInternal":{"tmp":"tmp/network-coverage_1.0.0_1774339058885_0.5408284118624387","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@airqo-packages/network-coverage","version":"1.0.1","keywords":["airqo","air-quality","network-coverage","monitors","africa"],"author":{"name":"AirQo"},"license":"MIT","_id":"@airqo-packages/network-coverage@1.0.1","maintainers":[{"name":"airqo-collection","email":"airqo.analytics@gmail.com"}],"homepage":"https://github.com/airqo-platform/AirQo-api/tree/staging/packages/airqo-network-coverage#readme","bugs":{"url":"https://github.com/airqo-platform/AirQo-api/issues"},"dist":{"shasum":"a18c5dfefce55df9eec52e697daf9afd069f6f24","tarball":"https://registry.npmjs.org/@airqo-packages/network-coverage/-/network-coverage-1.0.1.tgz","fileCount":14,"integrity":"sha512-djvgWtbr26M/XwC17GkpYFIZzUG0/NDtwNkirSjW05ab0DP6WL5Yjhm9QzSv7WSqHvyCyN9rK70Xb+nsGLV+pg==","signatures":[{"sig":"MEYCIQD0NeMm2s86hl6DmWqKIQfv7IAfxujw/T8RqMz54ASuFQIhAKgpV0xi+Lu09UvzyMrXDPrHnqUoFQU3EaNRWwAyu2H6","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":44309},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","require":"./dist/index.js"}},"gitHead":"91f9251a6c1d6bb06f99b9fd9c467b293db1a2b9","scripts":{"dev":"tsc --watch","lint":"eslint src --ext .ts","test":"jest","build":"tsc --project tsconfig.json","clean":"rimraf dist","lint:fix":"eslint src --ext .ts --fix","test:watch":"jest --watch","test:coverage":"jest --coverage","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"airqo-collection","email":"airqo.analytics@gmail.com"},"repository":{"url":"git+https://github.com/airqo-platform/AirQo-api.git","type":"git","directory":"packages/airqo-network-coverage"},"_npmVersion":"10.2.3","description":"Node.js client for the AirQo Network Coverage API — typed wrappers for all monitor and registry endpoints","directories":{},"_nodeVersion":"20.10.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.54.0","rimraf":"^5.0.5","ts-jest":"^29.1.1","typescript":"^5.4.0","@types/jest":"^29.5.8","@types/node":"^20.11.0","@typescript-eslint/parser":"^6.12.0","@typescript-eslint/eslint-plugin":"^6.12.0"},"_npmOperationalInternal":{"tmp":"tmp/network-coverage_1.0.1_1774473008485_0.26215836752538046","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@airqo-packages/network-coverage","version":"1.1.0","keywords":["airqo","air-quality","network-coverage","monitors","africa"],"author":{"name":"AirQo"},"license":"MIT","_id":"@airqo-packages/network-coverage@1.1.0","maintainers":[{"name":"airqo-collection","email":"airqo.analytics@gmail.com"}],"homepage":"https://github.com/airqo-platform/AirQo-api/tree/staging/packages/airqo-network-coverage#readme","bugs":{"url":"https://github.com/airqo-platform/AirQo-api/issues"},"dist":{"shasum":"627ce575a2befd2d4a6992d8f8247f0040332329","tarball":"https://registry.npmjs.org/@airqo-packages/network-coverage/-/network-coverage-1.1.0.tgz","fileCount":14,"integrity":"sha512-Ygoz6GOoCtG2rsxA/AbfusgYuB+vv+mJ0rcvgL7bbPFIgW3eQQMsgMnHYPvj6vhfv4u1qz63wItJAoE8RvCARQ==","signatures":[{"sig":"MEQCIAnoTzXsKC4qu7ezq4UZAUsP+Cu/Czp24d9qSDhgo24XAiAh5QSwFF6vsqJVEgpP4CGnhHm47stt4eN8itT3t+8kXA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":47028},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","require":"./dist/index.js"}},"gitHead":"a72b919350eb06f234bae157e704ee6e10549389","scripts":{"dev":"tsc --watch","lint":"eslint src --ext .ts","test":"jest","build":"tsc --project tsconfig.json","clean":"rimraf dist","lint:fix":"eslint src --ext .ts --fix","test:watch":"jest --watch","test:coverage":"jest --coverage","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"airqo-collection","email":"airqo.analytics@gmail.com"},"repository":{"url":"git+https://github.com/airqo-platform/AirQo-api.git","type":"git","directory":"packages/airqo-network-coverage"},"_npmVersion":"10.2.3","description":"Node.js client for the AirQo Network Coverage API — typed wrappers for all monitor and registry endpoints","directories":{},"_nodeVersion":"20.10.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.54.0","rimraf":"^5.0.5","ts-jest":"^29.1.1","typescript":"^5.4.0","@types/jest":"^29.5.8","@types/node":"^20.11.0","@typescript-eslint/parser":"^6.12.0","@typescript-eslint/eslint-plugin":"^6.12.0"},"_npmOperationalInternal":{"tmp":"tmp/network-coverage_1.1.0_1774560767442_0.43517993648102005","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"@airqo-packages/network-coverage","version":"2.0.0","description":"Node.js client for the AirQo Network Coverage API — typed wrappers for all monitor and registry endpoints","main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"require":"./dist/index.js","types":"./dist/index.d.ts"}},"engines":{"node":">=18.0.0"},"scripts":{"build":"tsc --project tsconfig.json","dev":"tsc --watch","clean":"rimraf dist","test":"jest","test:watch":"jest --watch","test:coverage":"jest --coverage","lint":"eslint src --ext .ts","lint:fix":"eslint src --ext .ts --fix","prepublishOnly":"npm run clean && npm run build"},"keywords":["airqo","air-quality","network-coverage","monitors","africa"],"author":{"name":"AirQo"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/airqo-platform/AirQo-api.git","directory":"packages/airqo-network-coverage"},"bugs":{"url":"https://github.com/airqo-platform/AirQo-api/issues"},"homepage":"https://github.com/airqo-platform/AirQo-api/tree/staging/packages/airqo-network-coverage#readme","publishConfig":{"access":"public"},"devDependencies":{"@types/jest":"^29.5.8","@types/node":"^20.11.0","@typescript-eslint/eslint-plugin":"^6.12.0","@typescript-eslint/parser":"^6.12.0","eslint":"^8.54.0","jest":"^29.7.0","rimraf":"^5.0.5","ts-jest":"^29.1.1","typescript":"^5.4.0"},"_id":"@airqo-packages/network-coverage@2.0.0","gitHead":"29fc0f08ecefbb1ec1e3449610052b64309df151","_nodeVersion":"20.10.0","_npmVersion":"10.2.3","dist":{"integrity":"sha512-fcFvFX0zBodE1MWgoTCj9PmTn4B8iI0jdPq6shEnfLMtIv2guwx41EvN4ZrbTlLE3DT9GzW22rf2NA2sb7fxlg==","shasum":"9e349b27c61b364a093986b2fae341dea0363fcf","tarball":"https://registry.npmjs.org/@airqo-packages/network-coverage/-/network-coverage-2.0.0.tgz","fileCount":14,"unpackedSize":47122,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDmTjUE3HIL0eNpAuNdw5Udd1utfd7LMh7GOPGHyx10cAIgEXM4bqkhjAZ+EXyTFeSwf3xd82uL5JmeoLM8ZYUWUzw="}]},"_npmUser":{"name":"airqo-collection","email":"airqo.analytics@gmail.com"},"directories":{},"maintainers":[{"name":"airqo-collection","email":"airqo.analytics@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/network-coverage_2.0.0_1774562657038_0.6838809775344681"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-24T07:57:38.736Z","modified":"2026-03-26T22:04:17.336Z","1.0.0":"2026-03-24T07:57:39.044Z","1.0.1":"2026-03-25T21:10:08.632Z","1.1.0":"2026-03-26T21:32:47.586Z","2.0.0":"2026-03-26T22:04:17.182Z"},"bugs":{"url":"https://github.com/airqo-platform/AirQo-api/issues"},"author":{"name":"AirQo"},"license":"MIT","homepage":"https://github.com/airqo-platform/AirQo-api/tree/staging/packages/airqo-network-coverage#readme","keywords":["airqo","air-quality","network-coverage","monitors","africa"],"repository":{"type":"git","url":"git+https://github.com/airqo-platform/AirQo-api.git","directory":"packages/airqo-network-coverage"},"description":"Node.js client for the AirQo Network Coverage API — typed wrappers for all monitor and registry endpoints","maintainers":[{"name":"airqo-collection","email":"airqo.analytics@gmail.com"}],"readme":"# @airqo-packages/network-coverage\n\nNode.js client for the **AirQo Network Coverage API** — typed wrappers for all monitor and registry endpoints that power the _\"Where We Monitor\"_ map.\n\nRequires **Node.js ≥ 18** (uses native `fetch`). No runtime dependencies.\n\n---\n\n## Installation\n\n```bash\nnpm install @airqo-packages/network-coverage\n```\n\n> **Migrating from an earlier version?** Replace `@airqo/network-coverage` with `@airqo-packages/network-coverage` in your `package.json` and all import statements.\n\n---\n\n## Authentication\n\nTo call protected endpoints (registry write operations), you need an AirQo access token.\n\n1. Create an account at [analytics.airqo.net](https://analytics.airqo.net/user/login)\n2. Go to **Account Settings → API tab → Register a client**\n3. Generate an access token from the registered client\n4. Pass the token to the client constructor — it will be appended as `?token=` on every request automatically\n\n```ts\nconst client = new NetworkCoverageClient({\n  token: \"YOUR_ACCESS_TOKEN\",\n});\n```\n\n> **Public endpoints** (`list`, `getMonitor`, `getCountryMonitors`, `exportCsv`) do not require a token. Only `upsertRegistry` and `deleteRegistry` require authentication.\n\n---\n\n## Quick start\n\n```ts\nimport { NetworkCoverageClient } from \"@airqo-packages/network-coverage\";\n\nconst client = new NetworkCoverageClient({\n  baseUrl: \"https://api.airqo.net/api/v2/devices\",\n  defaultTenant: \"airqo\",\n});\n\n// List all monitors grouped by country\nconst data = await client.list();\nconsole.log(data.meta);      // { total, active, inactive, countries }\nconsole.log(data.countries); // CountrySummary[]\nconsole.log(data.monitors);  // MonitorListItem[]\n```\n\n---\n\n## Configuration\n\n```ts\nconst client = new NetworkCoverageClient({\n  /** Base API URL — no trailing slash (default: AirQo production) */\n  baseUrl: \"https://api.airqo.net/api/v2/devices\",\n\n  /** Default tenant for all requests (default: \"airqo\") */\n  defaultTenant: \"airqo\",\n\n  /** Request timeout in ms (default: 30 000) */\n  timeoutMs: 15_000,\n\n  /** AirQo access token — appended as ?token= on every request */\n  token: \"YOUR_ACCESS_TOKEN\",\n\n  /** Additional headers sent with every request */\n  headers: {\n    \"X-Custom-Header\": \"value\",\n  },\n});\n```\n\n---\n\n## API\n\n### `client.list(params?)`\n\nReturns all monitors and per-country aggregates.\n\n```ts\nconst result = await client.list({\n  search: \"kampala\",         // partial name / city / country match\n  activeOnly: true,          // only active monitors\n  types: [\"Reference\", \"LCS\"], // filter by type\n});\n```\n\n**Returns:** `NetworkCoverageListResponse`\n\n```ts\n{\n  success: true,\n  message: \"...\",\n  meta: { total: 120, active: 98, inactive: 22, countries: 11 },\n  countries: [\n    { countryId: \"uganda\", country: \"Uganda\", iso2: \"UG\", total: 45, active: 40, inactive: 5 },\n    // ...\n  ],\n  monitors: [ /* MonitorListItem[] */ ],\n}\n```\n\n---\n\n### `client.getMonitor(monitorId, options?)`\n\nFetches a single monitor by its `_id`.\n\n```ts\nconst result = await client.getMonitor(\"64a1f2b3c4d5e6f7a8b9c0d1\");\n// or with a tenant override (public endpoint — no token needed):\nconst resultWithTenant = await client.getMonitor(\"64a1f2b3c4d5e6f7a8b9c0d1\", { tenant: \"airqo\" });\nconsole.log(result.data); // MonitorListItem\n```\n\n---\n\n### `client.getCountryMonitors(countryId, params?)`\n\nReturns all monitors for a country identified by its URL slug.\n\n```ts\nconst result = await client.getCountryMonitors(\"cote-divoire\", {\n  activeOnly: true,\n});\nconsole.log(result.monitors); // MonitorListItem[]\n```\n\nCountry slugs are lowercase, accent-normalised, and hyphenated:\n\n| Country | Slug |\n|---|---|\n| Uganda | `uganda` |\n| Ghana | `ghana` |\n| Côte d'Ivoire | `cote-divoire` |\n| South Africa | `south-africa` |\n\n---\n\n### `client.exportCsv(params?)`\n\nReturns the raw CSV string (UTF-8 with BOM). Write it to a file or stream it.\n\n```ts\nimport fs from \"fs\";\n\nconst csv = await client.exportCsv({ countryId: \"kenya\", activeOnly: true });\nfs.writeFileSync(\"kenya-monitors.csv\", csv);\n```\n\n---\n\n### `client.upsertRegistry(payload)`\n\nCreates or updates a registry entry. Returns **201** for new records, **200** for updates.\n\n**Shape A — AirQo-site enrichment** (provide `site_id`):\n\n```ts\nawait client.upsertRegistry({\n  site_id: \"64a1f2b3c4d5e6f7a8b9c0d1\",\n  equipment: \"AirQo Binos\",\n  calibrationMethod: \"Colocation\",\n  uptime30d: \"96%\",\n  publicData: \"Yes\",\n});\n```\n\n**Shape B — Standalone external entry** (omit `site_id`):\n\n```ts\nawait client.upsertRegistry({\n  name: \"Makerere KCCA Station\",\n  country: \"Uganda\",\n  city: \"Kampala\",\n  latitude: 0.3476,\n  longitude: 32.5825,\n  type: \"Reference\",\n  network: \"KCCA\",\n  operator: \"Kampala Capital City Authority\",\n  pollutants: [\"PM2.5\", \"PM10\", \"NO2\"],\n});\n```\n\n---\n\n### `client.deleteRegistry(registryId, options?)`\n\nRemoves a registry entry by document `_id`. Requires a valid access token — ensure the client was constructed with one or pass it explicitly via `options.token`.\n\n```ts\n// client must have been constructed with a token:\n// const client = new NetworkCoverageClient({ token: \"YOUR_TOKEN\" });\nawait client.deleteRegistry(\"64a1f2b3c4d5e6f7a8b9c0d1\");\n// or pass token explicitly per-call:\nawait client.deleteRegistry(\"64a1f2b3c4d5e6f7a8b9c0d1\", { token: \"YOUR_TOKEN\" });\n```\n\n---\n\n## Error handling\n\nAll methods throw `NetworkCoverageError` on non-2xx responses or timeouts.\n\n```ts\nimport { NetworkCoverageClient, NetworkCoverageError } from \"@airqo-packages/network-coverage\";\n\ntry {\n  await client.getMonitor(\"nonexistent-id\");\n} catch (err) {\n  if (err instanceof NetworkCoverageError) {\n    console.error(err.message);    // API error message\n    console.error(err.statusCode); // HTTP status code (e.g. 404)\n    console.error(err.body);       // Raw response body\n  }\n}\n```\n\n| Status | Meaning |\n|--------|---------|\n| 400 | Validation error — check your payload |\n| 404 | Monitor or registry entry not found |\n| 409 | Duplicate registry entry for a site |\n| 408 | Request timed out (client-side) |\n| 500 | Internal server error |\n\n---\n\n## TypeScript types\n\nAll types are exported from the package root:\n\n```ts\nimport type {\n  MonitorListItem,\n  CountrySummary,\n  NetworkCoverageMeta,\n  NetworkCoverageListResponse,\n  MonitorDetailResponse,\n  CountryMonitorsResponse,\n  RegistryUpsertPayload,\n  RegistryUpsertResponse,\n  MonitorType,\n  MonitorStatus,\n  MonitorSource,\n  ListParams,\n  ExportCsvParams,\n  NetworkCoverageClientOptions,\n} from \"@airqo-packages/network-coverage\";\n```\n\n---\n\n## License\n\nMIT\n","readmeFilename":"README.md"}