@alteriom/mqtt-schema 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/README.md +73 -49
- package/package.json +30 -4
- package/types/ota-manifest.d.ts +63 -0
package/CHANGELOG.md
ADDED
package/README.md
CHANGED
@@ -1,7 +1,71 @@
|
|
1
1
|
# @alteriom/mqtt-schema
|
2
2
|
|
3
|
+

|
4
|
+

|
5
|
+

|
6
|
+

|
7
|
+

|
8
|
+

|
9
|
+

|
10
|
+

|
11
|
+
[](https://bundlephobia.com/package/@alteriom/mqtt-schema)
|
12
|
+
|
3
13
|
Alteriom MQTT v1 JSON Schemas, TypeScript types, and production‑ready validation helpers for integrating firmware MQTT payloads into web or backend services.
|
4
14
|
|
15
|
+
> NOTE: OTA manifest CI validation workflow file could not be added via automated API due to path constraints; it can be added manually post-merge if still absent.
|
16
|
+
|
17
|
+
See also: `docs/SCHEMA_MAP.md` for complete schema listing.
|
18
|
+
|
19
|
+
## OTA Firmware Manifest Schema (NEW in 0.3.0)
|
20
|
+
|
21
|
+
The package now includes the OTA firmware manifest schema used by build + deployment tooling.
|
22
|
+
|
23
|
+
Import the schema JSON directly:
|
24
|
+
```ts
|
25
|
+
import otaManifestSchema from '@alteriom/mqtt-schema/schemas/ota/ota-manifest.schema.json';
|
26
|
+
```
|
27
|
+
|
28
|
+
Types:
|
29
|
+
```ts
|
30
|
+
import { OtaManifest, isRichManifest } from '@alteriom/mqtt-schema/types/ota-manifest';
|
31
|
+
```
|
32
|
+
|
33
|
+
Shapes supported (oneOf):
|
34
|
+
- Rich manifest:
|
35
|
+
```json
|
36
|
+
{
|
37
|
+
"environment": "universal-sensor",
|
38
|
+
"branch": "main",
|
39
|
+
"manifests": { "dev": { /* richEntry */ }, "prod": { /* richEntry */ } }
|
40
|
+
}
|
41
|
+
```
|
42
|
+
- Minimal environment map:
|
43
|
+
```json
|
44
|
+
{
|
45
|
+
"universal-sensor": { "dev": { /* minimalChannel */ } }
|
46
|
+
}
|
47
|
+
```
|
48
|
+
|
49
|
+
Chunk hashing variants (mutually exclusive):
|
50
|
+
1. Structured objects (offset + size + sha256)
|
51
|
+
2. Array of lowercase sha256 strings
|
52
|
+
|
53
|
+
Validation example:
|
54
|
+
```ts
|
55
|
+
import Ajv from 'ajv';
|
56
|
+
import schema from '@alteriom/mqtt-schema/schemas/ota/ota-manifest.schema.json';
|
57
|
+
import { OtaManifest } from '@alteriom/mqtt-schema/types/ota-manifest';
|
58
|
+
|
59
|
+
const ajv = new Ajv({ allErrors: true });
|
60
|
+
const validate = ajv.compile<OtaManifest>(schema as any);
|
61
|
+
const manifest: OtaManifest = JSON.parse(raw);
|
62
|
+
if (!validate(manifest)) {
|
63
|
+
console.error(validate.errors);
|
64
|
+
}
|
65
|
+
```
|
66
|
+
|
67
|
+
---
|
68
|
+
|
5
69
|
## Why this exists
|
6
70
|
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:
|
7
71
|
|
@@ -101,9 +165,11 @@ interface ValidationResult {
|
|
101
165
|
```
|
102
166
|
|
103
167
|
### Performance Notes
|
168
|
+
|
104
169
|
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.
|
105
170
|
|
106
171
|
### Embedded Schemas
|
172
|
+
|
107
173
|
`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.
|
108
174
|
|
109
175
|
## Provided Schemas (v1)
|
@@ -129,8 +195,11 @@ All Ajv validator functions are compiled once at module load. For typical web us
|
|
129
195
|
| `SensorDataMessage` etc. | TS interfaces | Strongly typed shapes |
|
130
196
|
| `isSensorDataMessage` etc. | type guards | Runtime narrowing helpers |
|
131
197
|
| `schemas/*.json` | JSON | Original schema assets (optional) |
|
198
|
+
| `schemas/ota/ota-manifest.schema.json` | JSON | OTA firmware manifest schema (rich + minimal) |
|
199
|
+
| `types/ota-manifest` | TS types | OtaManifest union + helpers |
|
132
200
|
|
133
201
|
### Validator Keys
|
202
|
+
|
134
203
|
`sensorData`, `sensorHeartbeat`, `sensorStatus`, `gatewayInfo`, `gatewayMetrics`, `firmwareStatus`, `controlResponse`
|
135
204
|
|
136
205
|
### Classification Heuristics (Simplified)
|
@@ -154,6 +223,8 @@ Schema stability is paramount. We track two related versions:
|
|
154
223
|
|
155
224
|
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).
|
156
225
|
|
226
|
+
Backward-compatible schema additions to OTA manifest WILL use minor bumps.
|
227
|
+
|
157
228
|
## TypeScript / Bundler Notes
|
158
229
|
|
159
230
|
- Works in TS >= 5, Node >= 16, Vite / Webpack / ESBuild.
|
@@ -165,6 +236,8 @@ Backward‑compatible additions: new optional properties or enums, documented in
|
|
165
236
|
- Optional custom Ajv injection hook
|
166
237
|
- JSON Schema → Zod conversion example
|
167
238
|
- Runtime metrics helper (count validation categories)
|
239
|
+
- Signed OTA manifest extension
|
240
|
+
- Delta / compressed OTA metadata fields
|
168
241
|
|
169
242
|
## Contributing
|
170
243
|
|
@@ -177,52 +250,3 @@ Schemas are static. No network access. Supply-chain risk minimized by keeping de
|
|
177
250
|
## License
|
178
251
|
|
179
252
|
MIT
|
180
|
-
|
181
|
-
## Registry Mirrors
|
182
|
-
|
183
|
-
This package is published to BOTH:
|
184
|
-
|
185
|
-
- Public npm registry: `https://registry.npmjs.org` (primary)
|
186
|
-
- GitHub Packages registry: `https://npm.pkg.github.com` (mirror for visibility in repo Packages tab)
|
187
|
-
|
188
|
-
### Installing from GitHub Packages (optional)
|
189
|
-
|
190
|
-
Create or update an `.npmrc` with a scoped registry override (auth token with `read:packages` required):
|
191
|
-
|
192
|
-
```
|
193
|
-
@alteriom:registry=https://npm.pkg.github.com
|
194
|
-
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
|
195
|
-
```
|
196
|
-
|
197
|
-
Then install normally:
|
198
|
-
|
199
|
-
```
|
200
|
-
npm install @alteriom/mqtt-schema ajv ajv-formats
|
201
|
-
```
|
202
|
-
|
203
|
-
If you omit the override, npmjs.org is used (recommended for most consumers).
|
204
|
-
|
205
|
-
### Why dual publish?
|
206
|
-
|
207
|
-
- GitHub Packages listing provides provenance + quick visibility in the repo UI.
|
208
|
-
- npm remains the canonical public distribution source (faster, anonymous installs allowed).
|
209
|
-
|
210
|
-
### Operational Notes
|
211
|
-
|
212
|
-
| Aspect | Behavior |
|
213
|
-
|--------|----------|
|
214
|
-
| Build artifact | Built once, same tarball published to both registries. |
|
215
|
-
| Version uniqueness | Same version must not be republished; bump if any change needed. |
|
216
|
-
| Auth (GitHub) | Always required for install from GitHub Packages, even for public repos. |
|
217
|
-
| Tarball parity | Do not rebuild between publishes; workflows ensure single build. |
|
218
|
-
| Fallback strategy | If mirror publish fails (e.g., transient), primary npm publish still stands. |
|
219
|
-
| Provenance flag | Applied for npm (GitHub ignores currently). |
|
220
|
-
|
221
|
-
### Verifying a Release
|
222
|
-
|
223
|
-
```bash
|
224
|
-
npm view @alteriom/mqtt-schema version
|
225
|
-
npm view @alteriom/mqtt-schema version --registry=https://npm.pkg.github.com
|
226
|
-
```
|
227
|
-
|
228
|
-
Both should return the same version.
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@alteriom/mqtt-schema",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.3.0",
|
4
4
|
"description": "Alteriom MQTT v1 schemas, TypeScript types, and validation helpers for web integration",
|
5
5
|
"license": "MIT",
|
6
6
|
"author": "Alteriom Development Team",
|
@@ -39,6 +39,13 @@
|
|
39
39
|
"./schemas/*": {
|
40
40
|
"default": "./schemas/*"
|
41
41
|
},
|
42
|
+
"./schemas/ota/ota-manifest.schema.json": {
|
43
|
+
"default": "./schemas/ota/ota-manifest.schema.json"
|
44
|
+
},
|
45
|
+
"./types/ota-manifest": {
|
46
|
+
"types": "./types/ota-manifest.d.ts",
|
47
|
+
"default": "./types/ota-manifest.d.ts"
|
48
|
+
},
|
42
49
|
"./package.json": "./package.json"
|
43
50
|
},
|
44
51
|
"typesVersions": {
|
@@ -51,18 +58,26 @@
|
|
51
58
|
],
|
52
59
|
"schemas/*": [
|
53
60
|
"schemas/*"
|
61
|
+
],
|
62
|
+
"types/ota-manifest": [
|
63
|
+
"types/ota-manifest.d.ts"
|
54
64
|
]
|
55
65
|
}
|
56
66
|
},
|
57
67
|
"files": [
|
58
68
|
"dist/",
|
59
69
|
"schemas/",
|
70
|
+
"types/",
|
60
71
|
"README.md",
|
61
|
-
"LICENSE"
|
72
|
+
"LICENSE",
|
73
|
+
"CHANGELOG.md"
|
62
74
|
],
|
63
75
|
"publishConfig": {
|
64
76
|
"access": "public"
|
65
77
|
},
|
78
|
+
"engines": {
|
79
|
+
"node": ">=16.0.0"
|
80
|
+
},
|
66
81
|
"scripts": {
|
67
82
|
"prebuild": "npm run clean && node scripts/copy-schemas.cjs",
|
68
83
|
"build:cjs": "tsc -p tsconfig.json",
|
@@ -75,7 +90,13 @@
|
|
75
90
|
"verify:schemas": "node scripts/check-schema-sync.cjs",
|
76
91
|
"verify:release": "node scripts/check-release-integrity.cjs",
|
77
92
|
"verify": "npm run verify:schemas && npm run verify:release && npm test",
|
78
|
-
"release:prepare": "node scripts/release-prepare.cjs"
|
93
|
+
"release:prepare": "node scripts/release-prepare.cjs",
|
94
|
+
"wiki:generate": "node scripts/generate-wiki.cjs",
|
95
|
+
"metadata:report": "alteriom-metadata report --org-tag alteriom || echo 'metadata report failed'",
|
96
|
+
"metadata:validate": "alteriom-metadata validate --org-tag alteriom || echo 'metadata validate failed'",
|
97
|
+
"metadata:apply": "alteriom-metadata apply --org-tag alteriom",
|
98
|
+
"metadata:apply:dry": "alteriom-metadata dry-run --org-tag alteriom",
|
99
|
+
"validate:ota": "node scripts/validate-ota-manifest.js"
|
79
100
|
},
|
80
101
|
"keywords": [
|
81
102
|
"alteriom",
|
@@ -83,15 +104,20 @@
|
|
83
104
|
"iot",
|
84
105
|
"schema",
|
85
106
|
"validation",
|
86
|
-
"typescript"
|
107
|
+
"typescript",
|
108
|
+
"ota"
|
87
109
|
],
|
88
110
|
"peerDependencies": {
|
89
111
|
"ajv": ">=8.0.0"
|
90
112
|
},
|
91
113
|
"devDependencies": {
|
114
|
+
"@alteriom/repository-metadata-manager": "^1.2.4",
|
92
115
|
"ajv": "^8.17.0",
|
93
116
|
"ajv-formats": "^2.1.1",
|
94
117
|
"rimraf": "^5.0.5",
|
95
118
|
"typescript": "^5.4.0"
|
119
|
+
},
|
120
|
+
"dependencies": {
|
121
|
+
"dotenv": "^17.2.2"
|
96
122
|
}
|
97
123
|
}
|
@@ -0,0 +1,63 @@
|
|
1
|
+
// Type definitions for Alteriom OTA Manifest (rich + minimal variants)
|
2
|
+
// Generated manually alongside schema at schemas/ota/ota-manifest.schema.json
|
3
|
+
|
4
|
+
export interface OtaChunkObject {
|
5
|
+
index: number; // 0-based sequential index
|
6
|
+
offset: number; // byte offset within firmware binary
|
7
|
+
size: number; // chunk size in bytes
|
8
|
+
sha256: string; // lowercase hex sha256 of the chunk
|
9
|
+
}
|
10
|
+
|
11
|
+
// Rich manifest build entry (dev or prod)
|
12
|
+
export interface OtaRichEntryBase {
|
13
|
+
build_type: 'dev' | 'prod';
|
14
|
+
file: string; // firmware-dev.bin or firmware-prod.bin
|
15
|
+
size: number; // total firmware size (bytes)
|
16
|
+
sha256: string; // full firmware binary sha256
|
17
|
+
firmware_version: string; // semantic or build version string
|
18
|
+
built: string; // ISO8601 timestamp
|
19
|
+
ota_url: string; // absolute or relative fetch URL
|
20
|
+
chunk_size?: number; // size each chunk except possibly last
|
21
|
+
// Either structured objects OR array of sha256 strings
|
22
|
+
chunks?: OtaChunkObject[] | string[];
|
23
|
+
}
|
24
|
+
|
25
|
+
export type OtaRichEntry = OtaRichEntryBase;
|
26
|
+
|
27
|
+
export interface OtaRichManifest {
|
28
|
+
environment: string; // e.g. universal-sensor
|
29
|
+
branch: string; // source control branch build originated from
|
30
|
+
manifests: {
|
31
|
+
dev?: OtaRichEntry;
|
32
|
+
prod?: OtaRichEntry;
|
33
|
+
};
|
34
|
+
}
|
35
|
+
|
36
|
+
// Minimal variant channel entry
|
37
|
+
export interface OtaMinimalChannel {
|
38
|
+
file: string; // firmware-dev.bin / firmware-prod.bin
|
39
|
+
size: number; // bytes
|
40
|
+
sha256: string; // full firmware sha256
|
41
|
+
version: string; // version string
|
42
|
+
timestamp: string; // ISO8601 build time
|
43
|
+
chunk_size?: number;
|
44
|
+
chunks?: string[]; // hash-only list (no structured offsets)
|
45
|
+
}
|
46
|
+
|
47
|
+
export interface OtaMinimalEnv {
|
48
|
+
dev?: OtaMinimalChannel;
|
49
|
+
prod?: OtaMinimalChannel;
|
50
|
+
}
|
51
|
+
|
52
|
+
// Root minimal map: environment -> channels
|
53
|
+
export type OtaMinimalManifest = Record<string, OtaMinimalEnv>;
|
54
|
+
|
55
|
+
export type OtaManifest = OtaRichManifest | OtaMinimalManifest;
|
56
|
+
|
57
|
+
export function isRichManifest(m: OtaManifest): m is OtaRichManifest {
|
58
|
+
return (m as OtaRichManifest).manifests !== undefined && typeof (m as any).environment === 'string';
|
59
|
+
}
|
60
|
+
|
61
|
+
export function isMinimalManifest(m: OtaManifest): m is OtaMinimalManifest {
|
62
|
+
return !isRichManifest(m);
|
63
|
+
}
|