@rotorsoft/act-http 1.3.0 → 1.5.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/README.md +81 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/@types/hono/index.d.ts +109 -0
- package/dist/@types/hono/index.d.ts.map +1 -0
- package/dist/@types/openapi/index.d.ts +191 -0
- package/dist/@types/openapi/index.d.ts.map +1 -0
- package/dist/api/index.js +1 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-PZ5AY32C.js.map +1 -0
- package/dist/hono/index.cjs +192 -0
- package/dist/hono/index.cjs.map +1 -0
- package/dist/hono/index.js +117 -0
- package/dist/hono/index.js.map +1 -0
- package/dist/openapi/index.cjs +14682 -0
- package/dist/openapi/index.cjs.map +1 -0
- package/dist/openapi/index.js +14659 -0
- package/dist/openapi/index.js.map +1 -0
- package/dist/receiver/express/index.js +1 -0
- package/dist/receiver/express/index.js.map +1 -1
- package/dist/receiver/fastify/index.js +1 -0
- package/dist/receiver/fastify/index.js.map +1 -1
- package/dist/receiver/hono/index.js +1 -0
- package/dist/receiver/index.js +1 -0
- package/dist/receiver/index.js.map +1 -1
- package/dist/receiver/trpc/index.js +1 -0
- package/dist/receiver/trpc/index.js.map +1 -1
- package/dist/sse/index.js +2 -0
- package/dist/sse/index.js.map +1 -1
- package/dist/trpc/index.js +1 -0
- package/dist/trpc/index.js.map +1 -1
- package/dist/webhook/index.js +2 -0
- package/dist/webhook/index.js.map +1 -1
- package/package.json +14 -3
package/README.md
CHANGED
|
@@ -171,6 +171,70 @@ const authed = t.procedure.use(authenticated(myExtractor));
|
|
|
171
171
|
// procedures alongside the generated ones.
|
|
172
172
|
```
|
|
173
173
|
|
|
174
|
+
### `hono` — auto-generated REST surface
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
import { hono } from "@rotorsoft/act-http/hono";
|
|
178
|
+
import { serve } from "@hono/node-server";
|
|
179
|
+
|
|
180
|
+
const api = hono(app, {
|
|
181
|
+
// ActorExtractor from `@rotorsoft/act-http/api` — host's auth seam.
|
|
182
|
+
actor: (c) => resolveActorFromJwt(c),
|
|
183
|
+
// Resolve the target stream per call; singleton aggregates return a constant.
|
|
184
|
+
stream: (action, input, c) => `tenant-${c.req.header("x-tenant")}`,
|
|
185
|
+
// Optional optimistic concurrency — return `undefined` to skip the check.
|
|
186
|
+
expectedVersion: (action, input, c) => {
|
|
187
|
+
const v = c.req.header("if-match");
|
|
188
|
+
return v ? Number.parseInt(v, 10) : undefined;
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
serve({ fetch: api.fetch, port: 4000 });
|
|
193
|
+
// POST /api/actions/OpenTicket body: { title }
|
|
194
|
+
// → 200 [Snapshot] | 4xx ApiError envelope
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
The generator walks `app.registry.actions` once and registers one `POST /actions/<actionName>` per action under the configured `basePath` (default `/api`). Bodies are validated with `@hono/zod-validator` against the action's registered Zod schema; failures short-circuit with `400`. The internal `authenticated(extractor)` middleware runs `options.actor` once per request and stashes the resolved `Actor` under `c.get("actor")`, available to any downstream middleware (logging, tracing, hand-written routes mounted on the same Hono instance). `Idempotency-Key` is read from the request header by default; `keyFrom` overrides for hosts that prefer a custom convention.
|
|
198
|
+
|
|
199
|
+
Errors map through the shared `toApiError(...)` table at `@rotorsoft/act-http/api`: `ConcurrencyError → 412 / CONCURRENCY`, `InvariantError → 409 / INVARIANT`, `ValidationError → 422 / VALIDATION`, `StreamClosedError → 410 / STREAM_CLOSED`, `NonRetryableError → 400 / NON_RETRYABLE`. The envelope (`{ error, detail?, code? }`) is the same shape every other act-http transport ships so a client speaking REST plus tRPC never sees two formats for the same framework error.
|
|
200
|
+
|
|
201
|
+
Edge-runtime ready — Hono runs unchanged on Node, Bun, Cloudflare Workers, Vercel Edge, and AWS Lambda. Operators wiring `idempotency` on edge runtimes should verify the `IdempotencyStore` is edge-compatible (in-memory works per worker; cross-worker requires a distributed store).
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
// Compose the auth middleware into your own Hono chain instead:
|
|
205
|
+
import { Hono } from "hono";
|
|
206
|
+
import { authenticated, type ActMiddlewareVariables } from "@rotorsoft/act-http/hono";
|
|
207
|
+
|
|
208
|
+
const api = new Hono<{ Variables: ActMiddlewareVariables }>();
|
|
209
|
+
api.use("*", authenticated((c) => resolveActorFromJwt(c)));
|
|
210
|
+
// Hand-written routes alongside the generated ones now see c.get("actor") typed.
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### `openapi` — OpenAPI 3.1 document emitter
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
import { openapi } from "@rotorsoft/act-http/openapi";
|
|
217
|
+
|
|
218
|
+
const doc = openapi(app, {
|
|
219
|
+
info: { title: "Wolfdesk API", version: "1.0.0" },
|
|
220
|
+
servers: [{ url: "https://api.example.com" }],
|
|
221
|
+
// Document the optional cross-cutting headers that the live REST API accepts.
|
|
222
|
+
idempotency: true,
|
|
223
|
+
expectedVersion: true,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// Serve alongside the Hono adapter:
|
|
227
|
+
api.get("/openapi.json", (c) => c.json(doc));
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Pure data emit — no runtime dep on Hono or tRPC. The doc walks `app.registry.actions` once at function call and emits one `POST <basePath>/actions/<actionName>` operation per action, deriving the request-body schema from each action's Zod definition via Zod 4's native `z.toJSONSchema` (OpenAPI 3.1 uses JSON Schema 2020-12 as its schema dialect — no conversion layer needed).
|
|
231
|
+
|
|
232
|
+
**The doc describes the Hono REST surface, not tRPC.** tRPC's URL convention (`POST /trpc/<procedure>`, JSON-RPC-style body framing, batching) doesn't model cleanly as OpenAPI operations; tRPC consumers share types directly via `typeof router` and don't need a doc. The path shape this emitter produces matches `@rotorsoft/act-http/hono` by construction: same default `basePath` (`/api`), same request/response shapes, same error envelope. If the operator overrides `basePath` on the Hono adapter, pass the same value here so the doc keeps describing the live routes. tRPC and Hono can run side-by-side at different mount points on the same Act instance.
|
|
233
|
+
|
|
234
|
+
The same shared `ApiError` envelope underwrites cross-transport consistency: it's referenced once from `components.responses` and every error code (`400`, `409`, `410`, `412`, `422`, `500`) points at it. The doc and the live REST API agree on framework-error shapes by construction.
|
|
235
|
+
|
|
236
|
+
Output is deterministic given the same registry — entries land in `Object.entries(app.registry.actions)` iteration order. CI can snapshot the result to catch unintended API-surface changes; one merge that quietly changes a Zod schema or adds a new action surfaces as a doc diff in the same PR.
|
|
237
|
+
|
|
174
238
|
### `sse` — live state broadcast
|
|
175
239
|
|
|
176
240
|
```ts
|
|
@@ -244,6 +308,23 @@ Auto-generated tRPC router for the act-http-api epic (#835).
|
|
|
244
308
|
- **`authenticated(extractor) → middleware`** — standalone export of the auth middleware the generator uses internally. Hosts composing their own procedure chain (logging + tracing + custom auth flavors) use `t.procedure.use(authenticated(extractor))` to inject `ctx.actor` without going through the generator. The middleware is structurally-typed so any host `t` instance accepts it.
|
|
245
309
|
- **`TrpcOptions<Ctx>`** — `{ actor, stream, expectedVersion?, idempotency? }`. `actor` is the `ActorExtractor` from `@rotorsoft/act-http/api`. `stream` returns the target stream per call. `expectedVersion` is optional — `(action, input, ctx) => number | undefined` — when set, the procedure threads the resolved value through `Target.expectedVersion` so `app.do` enforces optimistic concurrency; hosts typically read it from an `If-Match` header or the client's last-known snapshot, and returning `undefined` skips the check for that call. `idempotency` is optional — `{ store: IdempotencyStore; keyFrom: (ctx) => string | undefined }` — when set, the procedure honors `Idempotency-Key` via `withIdempotency`. Duplicate claims throw `CONFLICT` (the contract intentionally doesn't cache the original handler's result).
|
|
246
310
|
|
|
311
|
+
### `/hono` subpath
|
|
312
|
+
|
|
313
|
+
Auto-generated REST surface for the act-http-api epic (#835).
|
|
314
|
+
|
|
315
|
+
- **`hono(app, options) → Hono`** — generator function. Walks `app.registry.actions` once and registers a `POST /actions/<actionName>` per action under `basePath` (default `/api`). Body is validated with `@hono/zod-validator` against the action's Zod schema; failures short-circuit with `400`. The internal `authenticated(options.actor)` middleware stashes the resolved `Actor` under `c.get("actor")`. Routes return `200 Snapshot[]` on success, `4xx ApiError` envelope (with the conventional HTTP status) on framework errors, `500 / INTERNAL` on unknown throws.
|
|
316
|
+
- **`authenticated(extractor) → MiddlewareHandler`** — standalone export of the auth middleware the generator uses internally. Hosts composing their own Hono chain wire `api.use("*", authenticated(extractor))` and downstream routes read `c.get("actor"): Actor`. Errors thrown by the extractor surface as `401 / UNAUTHORIZED`.
|
|
317
|
+
- **`HonoOptions`** — `{ actor, stream, expectedVersion?, idempotency?, basePath? }`. `actor` is the `ActorExtractor`. `stream` returns the target stream per call. `expectedVersion` threads `Target.expectedVersion` for optimistic concurrency — returning `undefined` skips the check. `idempotency` is optional — `{ store: IdempotencyStore; keyFrom?: (c) => string | undefined }` — when set, the route honors `Idempotency-Key` via `withIdempotency`; default `keyFrom` reads the `Idempotency-Key` header. Duplicate claims return `409 / CONFLICT`. `basePath` defaults to `/api`.
|
|
318
|
+
- **`ActMiddlewareVariables`** — `{ actor: Actor }`. Use as the `Variables` generic on a host Hono instance to get `c.get("actor")` typed downstream of `authenticated`.
|
|
319
|
+
|
|
320
|
+
### `/openapi` subpath
|
|
321
|
+
|
|
322
|
+
OpenAPI 3.1 document emitter for the act-http-api epic (#835).
|
|
323
|
+
|
|
324
|
+
- **`openapi(app, options) → OpenAPIDocument`** — generator function. Walks `app.registry.actions` once and returns a valid OpenAPI 3.1 document object. Each action becomes a `POST <basePath>/actions/<actionName>` operation; the request-body schema comes from Zod 4's `z.toJSONSchema` against the action's registered schema. Error responses reference the shared `#/components/responses/ApiError`. Output is pure data — serve as `/openapi.json`, write to disk during CI, pipe to client codegen.
|
|
325
|
+
- **`OpenAPIOptions`** — `{ info, servers?, basePath?, idempotency?, expectedVersion? }`. `info.title` and `info.version` are required non-empty strings. `servers[].url` may contain `{variable}` template syntax; bare URLs are validated through `URL`'s parser. `basePath` defaults to `/api` (mirrors the Hono sibling). `idempotency` and `expectedVersion` are booleans (default `false`); when `true`, every mutation operation documents the corresponding optional header (`Idempotency-Key` / `If-Match`).
|
|
326
|
+
- **`OpenAPIDocument`** + a structural subset of the OpenAPI 3.1 types (`OpenAPIInfo`, `OpenAPIServer`, `OpenAPIPathItem`, `OpenAPIOperation`, `OpenAPIParameter`, `OpenAPIRequestBody`, `OpenAPIResponse`, `OpenAPIComponents`). Intentionally lighter than the full `openapi-types` package so post-processing doesn't fight the type system; cast to a fuller type when downstream consumers need it.
|
|
327
|
+
|
|
247
328
|
### `/sse` subpath
|
|
248
329
|
|
|
249
330
|
- **`BroadcastChannel<S>`** — server-side broadcast manager with per-stream subscriber sets and an LRU state cache.
|