@alteriom/mqtt-schema
Version: 
Alteriom MQTT v1 schemas, TypeScript types, and validation helpers for web integration
229 lines (162 loc) • 8.07 kB
Markdown
# @alteriom/mqtt-schema
Alteriom MQTT v1 JSON Schemas, TypeScript types, and production‑ready validation helpers for integrating firmware MQTT payloads into web or backend services.
## Why this exists
Firmware emits structured MQTT payloads that must remain tightly aligned with web, analytics, and gateway logic. This package is the single source of truth for:
- Canonical, versioned JSON Schemas (`schema_version: 1` series)
- Embedded (tree‑shakeable) schema objects — no runtime file I/O
- Precompiled Ajv validators (fast + consistent across CJS / ESM)
- TypeScript interfaces & type guards generated from the same source schema set
- Message classification helper that infers the schema kind heuristically
- Forward‑compatible design: unknown extra properties are ignored unless explicitly constrained
## Features
- Dual build: CommonJS + ESM (Node 16+ / bundlers)
- Zero configuration: just import and validate
- Helpful error paths (JSON Pointer style)
- Lightweight (Ajv peer dependency, schemas embedded)
- Ships original schema JSON files (optional consumption)
## Installation
```bash
npm install @alteriom/mqtt-schema ajv ajv-formats
```
## Quick Start
Validate a JSON payload (object already parsed):
```ts
import { validators } from '@alteriom/mqtt-schema';
const payload = JSON.parse(rawMqttString);
const result = validators.sensorData(payload);
if (!result.valid) {
  console.error('Invalid sensor data', result.errors);
}
```
Classify and validate automatically:
```ts
import { classifyAndValidate } from '@alteriom/mqtt-schema';
const { kind, result } = classifyAndValidate(payload);
if (result.valid) {
  console.log('Message kind:', kind);
} else {
  console.warn('Validation errors', result.errors);
}
```
Use strong TypeScript types after classification:
```ts
import { classifyAndValidate, isSensorDataMessage, SensorDataMessage } from '@alteriom/mqtt-schema';
const { result } = classifyAndValidate(payload);
if (result.valid && isSensorDataMessage(payload)) {
  const data: SensorDataMessage = payload;
  Object.entries(data.sensors).forEach(([name, s]) => {
    console.log(name, s.value, s.unit);
  });
}
```
Access raw schema JSON (if you need to introspect or power form generation):
```ts
import envelopeSchema from '@alteriom/mqtt-schema/schemas/envelope.schema.json';
```
## API Surface
```ts
import { validators, validateMessage, classifyAndValidate } from '@alteriom/mqtt-schema';
// 1. Direct validator by message kind
validators.sensorData(payload); // => { valid: boolean; errors?: string[] }
// 2. Generic function
validateMessage('sensorData', payload);
// 3. Classify unknown payload then validate
const { kind, result } = classifyAndValidate(payload);
// 4. Type guards (when available)
// if (isSensorDataMessage(payload)) { ... }
```
Validation result format:
```ts
interface ValidationResult {
  valid: boolean;
  errors?: string[]; // Each item contains instancePath + message
}
```
### Performance Notes
All Ajv validator functions are compiled once at module load. For typical web usage (tens to hundreds of validations per page/session) this is faster and simpler than on‑demand compilation. If you need custom Ajv options (e.g., different formats), open an issue—an override hook can be added without breaking changes.
### Embedded Schemas
`schema_data.ts` is auto‑generated during build. This avoids dynamic `require()` / `import` of JSON and works cleanly in both Node ESM and bundlers without JSON import assertions. The original JSON files are still published under `schemas/` for tooling or documentation pipelines.
## Provided Schemas (v1)
| File | Purpose |
|------|---------|
| envelope.schema.json | Base required envelope fields |
| sensor_data.schema.json | Telemetry payload with sensors map |
| sensor_heartbeat.schema.json | Lightweight heartbeat (firmware_version may be omitted) |
| sensor_status.schema.json | Sensor status / presence updates |
| gateway_info.schema.json | Gateway identity & capabilities |
| gateway_metrics.schema.json | Gateway performance metrics |
| firmware_status.schema.json | Firmware update lifecycle events |
| control_response.schema.json | Command/control response messages |
## Exports
| Export | Type | Description |
|--------|------|-------------|
| `validators` | object | Precompiled validators per message type |
| `validateMessage(kind,data)` | fn | Run a specific validator by key |
| `classifyAndValidate(data)` | fn | Heuristic classification + validation |
| `SensorDataMessage` etc. | TS interfaces | Strongly typed shapes |
| `isSensorDataMessage` etc. | type guards | Runtime narrowing helpers |
| `schemas/*.json` | JSON | Original schema assets (optional) |
### Validator Keys
`sensorData`, `sensorHeartbeat`, `sensorStatus`, `gatewayInfo`, `gatewayMetrics`, `firmwareStatus`, `controlResponse`
### Classification Heuristics (Simplified)
- `metrics` → `gatewayMetrics`
- `sensors` → `sensorData`
- `progress_pct` or OTA status keywords → `firmwareStatus`
- `status` + `device_type: sensor` → `sensorStatus`
- `status: ok|error` (no other match) → `controlResponse`
- `device_type: gateway` → `gatewayInfo`
- fallback → `sensorHeartbeat`
## Versioning Policy
Schema stability is paramount. We track two related versions:
| Concept | Meaning |
|---------|---------|
| `schema_version` (in payload) | Wire format major. Only increments for breaking changes. |
| npm package version | Follows semver; patch/minor may add optional properties or tooling, major aligns with `schema_version` bump. |
Backward‑compatible additions: new optional properties or enums, documented in CHANGELOG. Breaking: new required fields, structural changes, or removed properties (triggers parallel `v2` schema path & coordinated firmware rollout).
## TypeScript / Bundler Notes
- Works in TS >= 5, Node >= 16, Vite / Webpack / ESBuild.
- Tree shaking: unused validators pruned when using ESM builds.
- No side effects besides Ajv instance creation.
## Roadmap
- Optional custom Ajv injection hook
- JSON Schema → Zod conversion example
- Runtime metrics helper (count validation categories)
## Contributing
Issues & PRs welcome. Ensure firmware repo schemas remain the authoritative source—do not manually edit generated `schema_data.ts`.
## Security
Schemas are static. No network access. Supply-chain risk minimized by keeping dependencies minimal (Ajv + formats only).
## License
MIT
## Registry Mirrors
This package is published to BOTH:
- Public npm registry: `https://registry.npmjs.org` (primary)
- GitHub Packages registry: `https://npm.pkg.github.com` (mirror for visibility in repo Packages tab)
### Installing from GitHub Packages (optional)
Create or update an `.npmrc` with a scoped registry override (auth token with `read:packages` required):
```
@alteriom:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
```
Then install normally:
```
npm install @alteriom/mqtt-schema ajv ajv-formats
```
If you omit the override, npmjs.org is used (recommended for most consumers).
### Why dual publish?
- GitHub Packages listing provides provenance + quick visibility in the repo UI.
- npm remains the canonical public distribution source (faster, anonymous installs allowed).
### Operational Notes
| Aspect | Behavior |
|--------|----------|
| Build artifact | Built once, same tarball published to both registries. |
| Version uniqueness | Same version must not be republished; bump if any change needed. |
| Auth (GitHub) | Always required for install from GitHub Packages, even for public repos. |
| Tarball parity | Do not rebuild between publishes; workflows ensure single build. |
| Fallback strategy | If mirror publish fails (e.g., transient), primary npm publish still stands. |
| Provenance flag | Applied for npm (GitHub ignores currently). |
### Verifying a Release
```bash
npm view @alteriom/mqtt-schema version
npm view @alteriom/mqtt-schema version --registry=https://npm.pkg.github.com
```
Both should return the same version.