Skip to content

Commit 2c6f27f

Browse files
committed
feat: add schema validation, RPC, testing utilities, typebox & zod adapters
@mqttkit/core 0.2.0: - Standard Schema v1 payload validation with ctx.body type inference - SchemaProvider extension point (addSchemaProvider) for non-Standard libs - app.request() / ctx.reply() RPC over MQTT 5 responseTopic + correlationData - Structured onError lifecycle (route + app level, phase-tagged) - New @mqttkit/core/testing subpath with in-memory TestBroker @mqttkit/aedes 0.2.0: - Forward MQTT 5 publish properties so RPC round-trips work @mqttkit/asyncapi 0.2.0: - Resolve Standard Schema -> JSON Schema via ~jsonSchema convention - README schema integration guide @mqttkit/typebox 0.1.0 (new): - typeboxProvider for raw Type.X(...) schemas, full Static<T> inference @mqttkit/zod 0.1.0 (new): - jsonify() attaches JSON Schema for AsyncAPI doc generation Examples: schema-validation, rpc; asyncapi-docs gains a dev:zod variant. Docs: root + per-package READMEs, root CHANGELOG.md. Publish script: include typebox & zod, add per-package aliases.
1 parent 6f894e1 commit 2c6f27f

54 files changed

Lines changed: 3855 additions & 81 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Changelog
2+
3+
All notable changes to this monorepo are recorded here. Releases for each
4+
package follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html); the
5+
entries below are grouped by version-date so cross-package changes that ship
6+
together stay readable.
7+
8+
The format is loosely based on [Keep a Changelog](https://keepachangelog.com/).
9+
10+
## 2026-06-22
11+
12+
`@mqttkit/core@0.2.0`, `@mqttkit/aedes@0.2.0`, `@mqttkit/asyncapi@0.2.0`,
13+
`@mqttkit/typebox@0.1.0` (initial), `@mqttkit/zod@0.1.0` (initial).
14+
15+
### Added — `@mqttkit/core` 0.2.0
16+
17+
- **Schema validation**. `topic({ schema })` accepts any [Standard
18+
Schema v1](https://standardschema.dev/) validator (zod ≥3.24, valibot ≥1,
19+
arktype, …). Validated output is exposed on `ctx.body` with full type
20+
inference. `validate` option controls direction (`'inbound' | 'outbound' |
21+
'both' | false`).
22+
- **SchemaProvider extension point**. `app.addSchemaProvider(provider)` lets
23+
non-Standard-Schema libraries (e.g. raw TypeBox) plug in without core
24+
depending on them. `MqttkitInferExtensions<T>` provides type-only registration
25+
for `ctx.body` inference.
26+
- **RPC**. `app.request(topic, payload, options?)` sends a request using
27+
MQTT 5 `responseTopic` + `correlationData` and resolves on reply.
28+
`ctx.reply(payload)` answers on the device side.
29+
- **Error lifecycle**. `app.onError(handler)` and per-route `onError` receive
30+
a structured `{ error, topic, phase, route, ctx }` payload. `phase` is one of
31+
`middleware | handler | validation | policy | publish`. Validation failures
32+
emit a `SchemaValidationError` and skip `onMessage`.
33+
- **Testing entry**. New `@mqttkit/core/testing` sub-export ships
34+
`createTestApp()` and an in-memory `TestBroker` with `dispatch()` / `onPublish`
35+
hooks. Lets apps be unit-tested without spawning aedes.
36+
- **Pattern module**. Topic-pattern matching extracted into `pattern.ts` and
37+
exported from the package root.
38+
39+
### Added — `@mqttkit/aedes` 0.2.0
40+
41+
- Forwards MQTT 5 publish properties (`responseTopic`, `correlationData`,
42+
`contentType`, `messageExpiryInterval`, `userProperties`,
43+
`payloadFormatIndicator`) when calling `broker.publish`. Required for
44+
`app.request()` RPC round-trips.
45+
- Bumps `@mqttkit/core` peer to `^0.2.0`.
46+
47+
### Added — `@mqttkit/asyncapi` 0.2.0
48+
49+
- `resolvePayloadSchema()`: route schemas that implement Standard Schema are
50+
now reduced to a JSON Schema for the generated document. Order:
51+
1. raw JSON Schema object — used as-is
52+
2. Standard Schema with `~jsonSchema` attached — uses the attached schema
53+
3. fallback: `{ description: 'Validated by <vendor>' }`
54+
- Lets `@mqttkit/zod`'s `jsonify()` (and TypeBox's native JSON Schema layout)
55+
flow into the AsyncAPI document.
56+
- Bumps `@mqttkit/core` peer to `^0.2.0`.
57+
58+
### Added — `@mqttkit/typebox` 0.1.0 (initial)
59+
60+
- `typeboxProvider`: register with `app.addSchemaProvider(typeboxProvider)` and
61+
pass raw `Type.X(...)` schemas directly to `topic({ schema })`. TypeBox
62+
schemas detected by the `Kind` symbol and validated via
63+
`@sinclair/typebox/value`.
64+
- Type-only module augmentation so `ctx.body` is inferred via `Static<T>`.
65+
- TypeBox schemas are JSON Schema natively, so `@mqttkit/asyncapi` outputs the
66+
full payload automatically.
67+
68+
### Added — `@mqttkit/zod` 0.1.0 (initial)
69+
70+
- `jsonify(schema, options?)`: attaches a JSON Schema representation under
71+
`~jsonSchema` and returns the same zod instance. Closes the gap where
72+
`@mqttkit/asyncapi` falls back to `Validated by zod` because zod 3.x doesn't
73+
expose JSON Schema by default.
74+
- zod ≥3.24 already implements Standard Schema, so runtime validation works
75+
without `jsonify` — this helper is only needed when emitting AsyncAPI docs.
76+
77+
### Examples
78+
79+
- New `examples/schema-validation` with `zod`, `typebox`, `coexist`, and
80+
`manual` variants.
81+
- New `examples/rpc` demonstrating `app.request()` / `ctx.reply()`.
82+
- `examples/asyncapi-docs` now defaults to the TypeBox variant; added a `dev:zod`
83+
script that shows zod + `jsonify` flowing into the AsyncAPI document.
84+
85+
### Internal
86+
87+
- Root `tsconfig.json` excludes `**/dist/**` to avoid a parallel-build race
88+
where one package's tsup pass discovered another package's mid-clean dist.
89+
90+
## 2026-XX-XX — initial release
91+
92+
`@mqttkit/core@0.1.0`, `@mqttkit/aedes@0.1.0`, `@mqttkit/asyncapi@0.1.0`.
93+
94+
Initial publish of the framework: ordered middleware, topic router, typed
95+
context with `decorate()` service injection, broker lifecycle events,
96+
`app.publish()` for service-side push, Aedes TCP + MQTT-over-WebSocket
97+
adapter, and the AsyncAPI 3.0 documentation plugin.

README.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,14 @@ mqttkit does not reimplement the MQTT protocol. A broker such as Aedes owns CONN
1111
- Ordered `use()` middleware for auth, logging, audit, validation, and interception.
1212
- `router().topic()` declares MQTT topic routes, publish policy, and subscribe policy.
1313
- `ctx.params` extracts topic params such as `devices/:uid/events`.
14+
- `ctx.body` carries the validated payload — any [Standard Schema](https://standardschema.dev/) validator (zod, valibot, arktype, …) works, with full type inference. Use `@mqttkit/typebox` / `@mqttkit/zod` for raw TypeBox or for attaching JSON Schema.
1415
- `ctx.services` injects Kafka, Redis, database, audit, or domain services.
16+
- `app.request()` does MQTT 5 RPC over `responseTopic` + `correlationData`; `ctx.reply()` answers on the device side.
17+
- `app.onError()` (and per-route `onError`) capture validation, handler, policy, middleware and publish failures with a structured `phase`.
1518
- `app.on()` observes broker lifecycle events such as client, publish, subscribe, ack, and errors.
1619
- `app.publish()` lets services, workers, and consumers push messages to MQTT clients through the broker.
17-
- `@mqttkit/aedes` provides TCP MQTT and MQTT-over-WebSocket support through Aedes.
20+
- `@mqttkit/core/testing` ships an in-memory `TestBroker` so apps can be unit-tested without spawning aedes.
21+
- `@mqttkit/aedes` provides TCP MQTT and MQTT-over-WebSocket support through Aedes (with MQTT 5 properties forwarded for RPC).
1822
- `@mqttkit/asyncapi` generates AsyncAPI 3.0 documentation from routes and serves a browsable docs page.
1923
- Built for Bun and TypeScript.
2024

@@ -280,7 +284,9 @@ Use Aedes persistence adapters for offline queues, retain, QoS sessions, and dur
280284
- `examples/events`: broker lifecycle events.
281285
- `examples/service-push`: external service calls `app.publish()`, then Aedes delivers to subscribed MQTT clients.
282286
- `examples/kafka-bridge`: MQTT messages go to Kafka, and Kafka consumer messages are forwarded to MQTT clients.
283-
- `examples/asyncapi-docs`: AsyncAPI documentation served from `@mqttkit/asyncapi` (standalone HTTP).
287+
- `examples/schema-validation`: payload schemas with zod, TypeBox, and coexistence between the two.
288+
- `examples/rpc`: MQTT 5 RPC round-trip with `app.request()` and `ctx.reply()`.
289+
- `examples/asyncapi-docs`: AsyncAPI documentation served from `@mqttkit/asyncapi` (standalone HTTP). `dev:zod` variant shows zod + `jsonify` in the doc.
284290
- `examples/asyncapi-elysia`: share a single Elysia port for both AsyncAPI docs and MQTT-over-WebSocket (aedes ws attached to the same `http.Server`).
285291

286292
Run an example:
@@ -308,6 +314,8 @@ bun run build
308314

309315
## Packages
310316

311-
- `@mqttkit/core`: core application, router, middleware, context, event types, and broker adapter interface.
312-
- `@mqttkit/aedes`: Aedes adapter for TCP MQTT and MQTT-over-WebSocket.
317+
- `@mqttkit/core`: core application, router, middleware, context, schema validation, RPC, event types, broker adapter interface, and `@mqttkit/core/testing` in-memory broker.
318+
- `@mqttkit/aedes`: Aedes adapter for TCP MQTT and MQTT-over-WebSocket (forwards MQTT 5 properties for RPC).
313319
- `@mqttkit/asyncapi`: AsyncAPI 3.0 generator and HTTP plugin for browsable docs.
320+
- `@mqttkit/typebox`: TypeBox schema provider — register once, then pass raw `Type.X(...)` schemas directly.
321+
- `@mqttkit/zod`: zod helper that attaches a JSON Schema representation so AsyncAPI documents the full payload.

README.zh-CN.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,14 @@ mqttkit 不重新实现 MQTT 协议。CONNECT、SUBSCRIBE、PUBLISH、QoS、reta
1111
- 有序 `use()` middleware,可用于鉴权、日志、审计、校验和拦截。
1212
- `router().topic()` 声明 MQTT topic route、发布策略和订阅策略。
1313
- `ctx.params` 自动提取 `devices/:uid/events` 这类 topic 参数。
14+
- `ctx.body` 承载校验后的 payload,任意 [Standard Schema](https://standardschema.dev/) 校验器(zod、valibot、arktype 等)都能直接用,并自动推断类型;原始 TypeBox 与给 zod 挂 JSON Schema 用 `@mqttkit/typebox` / `@mqttkit/zod`
1415
- `ctx.services` 注入 Kafka、Redis、数据库、审计服务或业务服务。
16+
- `app.request()` 通过 MQTT 5 `responseTopic` + `correlationData` 做 RPC 往返;设备一侧用 `ctx.reply()` 回复。
17+
- `app.onError()`(以及路由级 `onError`)捕获 validation / handler / policy / middleware / publish 失败,并带结构化 `phase`
1518
- `app.on()` 监听 client、publish、subscribe、ack、错误等 broker lifecycle events。
1619
- `app.publish()` 让外部服务、worker、consumer 通过 broker 主动推送消息给 MQTT client。
17-
- `@mqttkit/aedes` 基于 Aedes 提供 TCP MQTT 与 MQTT-over-WebSocket。
20+
- `@mqttkit/core/testing` 内置内存版 `TestBroker`,无需起 aedes 即可对 app 做单测。
21+
- `@mqttkit/aedes` 基于 Aedes 提供 TCP MQTT 与 MQTT-over-WebSocket(透传 MQTT 5 properties 给 RPC 用)。
1822
- `@mqttkit/asyncapi` 从路由生成 AsyncAPI 3.0 文档,并通过 HTTP 提供可浏览的文档页面。
1923
- 面向 Bun 与 TypeScript。
2024

@@ -280,7 +284,9 @@ new MqttApp()
280284
- `examples/events`:监听 broker lifecycle events。
281285
- `examples/service-push`:外部服务调用 `app.publish()`,消息经 Aedes 投递到订阅中的 MQTT client。
282286
- `examples/kafka-bridge`:MQTT 消息写入 Kafka,并把 Kafka consumer 消息透传给 MQTT client。
283-
- `examples/asyncapi-docs`:通过 `@mqttkit/asyncapi` 提供 AsyncAPI 文档与浏览页面(独立 HTTP 端口)。
287+
- `examples/schema-validation`:zod、TypeBox 以及两者共存的 payload 校验示例。
288+
- `examples/rpc`:用 `app.request()``ctx.reply()` 完成 MQTT 5 RPC 往返。
289+
- `examples/asyncapi-docs`:通过 `@mqttkit/asyncapi` 提供 AsyncAPI 文档与浏览页面(独立 HTTP 端口),`dev:zod` 版本演示 zod + `jsonify`
284290
- `examples/asyncapi-elysia`:Elysia 端口同时承载 AsyncAPI 文档与 MQTT-over-WebSocket(aedes ws 复用同一个 `http.Server`)。
285291

286292
运行示例:
@@ -308,6 +314,8 @@ bun run build
308314

309315
##
310316

311-
- `@mqttkit/core`:core application、router、middleware、context、event typesbroker adapter 接口。
312-
- `@mqttkit/aedes`:Aedes adapter,提供 TCP MQTT 与 MQTT-over-WebSocket 接入
317+
- `@mqttkit/core`:core application、router、middleware、context、schema 校验、RPC、event typesbroker adapter 接口,以及 `@mqttkit/core/testing` 内存 broker
318+
- `@mqttkit/aedes`:Aedes adapter,提供 TCP MQTT 与 MQTT-over-WebSocket(透传 MQTT 5 properties 给 RPC 用)
313319
- `@mqttkit/asyncapi`:基于路由生成 AsyncAPI 3.0 文档与 HTTP 浏览页面。
320+
- `@mqttkit/typebox`:TypeBox schema 适配——注册一次,直接传原始 `Type.X(...)`
321+
- `@mqttkit/zod`:给 zod schema 挂上 JSON Schema 表达,让 AsyncAPI 输出完整 payload。

bun.lock

Lines changed: 68 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/asyncapi-docs/package.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,17 @@
44
"type": "module",
55
"scripts": {
66
"dev": "bun run src/index.ts",
7+
"dev:zod": "bun run src/zod.ts",
78
"typecheck": "tsc --noEmit"
89
},
910
"dependencies": {
1011
"@mqttkit/aedes": "workspace:*",
1112
"@mqttkit/asyncapi": "workspace:*",
12-
"@mqttkit/core": "workspace:*"
13+
"@mqttkit/core": "workspace:*",
14+
"@mqttkit/typebox": "workspace:*",
15+
"@mqttkit/zod": "workspace:*",
16+
"@sinclair/typebox": "^0.34.49",
17+
"zod": "^3.24.1",
18+
"zod-to-json-schema": "^3.24.6"
1319
}
1420
}

0 commit comments

Comments
 (0)