@zeroxsolutions/server 0.6.8 → 0.7.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 +20 -0
- package/README.md +15 -1
- package/dist/error-handler.js +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/trace-request.d.ts +27 -0
- package/dist/trace-request.d.ts.map +1 -0
- package/dist/trace-request.js +31 -0
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
## 0.7.0 (2026-11-10)
|
|
2
|
+
|
|
3
|
+
### 🚀 Features
|
|
4
|
+
|
|
5
|
+
- ⚠️ **server:** name the request's span by its route template ([e6b922d](https://github.com/zeroxsolutions/zeroxsolutions/commit/e6b922d))
|
|
6
|
+
- **server:** write the fields a resolved error logs ([6a53f5d](https://github.com/zeroxsolutions/zeroxsolutions/commit/6a53f5d))
|
|
7
|
+
|
|
8
|
+
### 🩹 Fixes
|
|
9
|
+
|
|
10
|
+
- **server:** name the span's route where a middleware answered ([f3cc2ed](https://github.com/zeroxsolutions/zeroxsolutions/commit/f3cc2ed))
|
|
11
|
+
|
|
12
|
+
### ⚠️ Breaking Changes
|
|
13
|
+
|
|
14
|
+
- **server:** name the request's span by its route template ([e6b922d](https://github.com/zeroxsolutions/zeroxsolutions/commit/e6b922d))
|
|
15
|
+
hono peer floor is now ^4.8.0 (hono/route)
|
|
16
|
+
|
|
17
|
+
### ❤️ Thank You
|
|
18
|
+
|
|
19
|
+
- luongvantuit
|
|
20
|
+
|
|
1
21
|
## 0.6.8 (2026-10-23)
|
|
2
22
|
|
|
3
23
|
This was a version bump only for @zeroxsolutions/server to align it with other projects, there were no code changes.
|
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@ this one holds the handler that calls it. That split is what leaves the concern
|
|
|
8
8
|
| ------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
|
|
9
9
|
| `createEnvironment` | the worker's own `bindingsSchema` - no package owns a worker's bindings |
|
|
10
10
|
| `createErrorHandler`, `createValidationHook`, `jsonApiContentNegotiation` | `@zeroxsolutions/response`, `@zeroxsolutions/jsonapi` |
|
|
11
|
+
| `traceRequest` | the runtime's own span, passed as `activeSpan` |
|
|
11
12
|
|
|
12
13
|
## What the split bought
|
|
13
14
|
|
|
@@ -96,7 +97,8 @@ Whichever step answers, the handler writes one `http.error.answered` line under
|
|
|
96
97
|
document goes out, at the level the answered status class decides: a 5xx at error, anything else at
|
|
97
98
|
warn. The line carries the request id, the method, the path, the answered status, the step that claimed
|
|
98
99
|
the failure (`query`, `request`, `resolve`, `auth`, `validation`, `jsonapi`, `status`) and the error's type
|
|
99
|
-
and message
|
|
100
|
+
and message, then the `logged` fields of the error map entry that answered, if it names any; where a
|
|
101
|
+
field name appears in both, the handler's own wins. Without it a mapped 500 or a guard's 403 reaches the client with nothing in the log for an
|
|
100
102
|
operator to join it to.
|
|
101
103
|
|
|
102
104
|
The `requestId` option's value becomes the `id` of each error object that carries none, so a client
|
|
@@ -138,6 +140,18 @@ does not serve stays a 404.
|
|
|
138
140
|
app.notFound(jsonApiNotFound({ namespace: 'api', routes: app.routes }));
|
|
139
141
|
```
|
|
140
142
|
|
|
143
|
+
## The request's span
|
|
144
|
+
|
|
145
|
+
`traceRequest({ activeSpan })` names the request's server span after the answer exists:
|
|
146
|
+
`<METHOD> <route template>`, with `http.route` set to the template. A 5xx answer sets `error.type` to its
|
|
147
|
+
status and the span's status to error; a 4xx answer leaves the status unset. Where no route matched, the
|
|
148
|
+
name is the method alone. `activeSpan` returns the runtime's span, or nothing, and then nothing is
|
|
149
|
+
written. It needs hono 4.8.0 or later for `hono/route`.
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
app.use('*', traceRequest({ activeSpan: () => tracing.getActiveSpan() }));
|
|
153
|
+
```
|
|
154
|
+
|
|
141
155
|
## Why a binder rather than a ready middleware
|
|
142
156
|
|
|
143
157
|
The schema and the log category are the package's, not the library's: the schema is what the transport
|
package/dist/error-handler.js
CHANGED
|
@@ -53,7 +53,7 @@ function claimError(err, c, options) {
|
|
|
53
53
|
}
|
|
54
54
|
const resolved = options.resolve?.(err, c);
|
|
55
55
|
if (resolved) {
|
|
56
|
-
return { claimedBy: 'resolve', resolved };
|
|
56
|
+
return { claimedBy: 'resolve', resolved, logged: resolved.logged };
|
|
57
57
|
}
|
|
58
58
|
if (err instanceof Unauthenticated) {
|
|
59
59
|
const error = { status: '401', code: `${options.namespace}.unauthenticated`, title: 'Unauthenticated' };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Every Hono handler the house ships - the bindings gate,
|
|
2
|
+
* Every Hono handler the house ships - the bindings gate, the three the wire spelling decides, and the one
|
|
3
|
+
* that names a request's span.
|
|
3
4
|
*/
|
|
4
5
|
export { createEnvironment, type EnvironmentOptions } from './environment.js';
|
|
5
6
|
export type { SurfaceVocabulary } from './surface-vocabulary.js';
|
|
@@ -9,4 +10,5 @@ export { jsonApiNotFound, type NotFoundOptions } from './not-found.js';
|
|
|
9
10
|
export { PermissionRefused } from './permission-refused.js';
|
|
10
11
|
export { Unauthenticated } from './unauthenticated.js';
|
|
11
12
|
export { createValidationHook } from './validation-hook.js';
|
|
13
|
+
export { traceRequest, type RequestSpan, type TraceRequestOptions } from './trace-request.js';
|
|
12
14
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,iBAAiB,EAAE,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC9E,YAAY,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAC5F,OAAO,EAAE,kBAAkB,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAClF,OAAO,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Every Hono handler the house ships - the bindings gate,
|
|
2
|
+
* Every Hono handler the house ships - the bindings gate, the three the wire spelling decides, and the one
|
|
3
|
+
* that names a request's span.
|
|
3
4
|
*/
|
|
4
5
|
export { createEnvironment } from './environment.js';
|
|
5
6
|
export { jsonApiContentNegotiation, NEGOTIATION_RESPONSES } from './content-negotiation.js';
|
|
@@ -8,3 +9,4 @@ export { jsonApiNotFound } from './not-found.js';
|
|
|
8
9
|
export { PermissionRefused } from './permission-refused.js';
|
|
9
10
|
export { Unauthenticated } from './unauthenticated.js';
|
|
10
11
|
export { createValidationHook } from './validation-hook.js';
|
|
12
|
+
export { traceRequest } from './trace-request.js';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { MiddlewareHandler } from 'hono';
|
|
2
|
+
/** The part of a runtime's span {@link traceRequest} writes; Workers' span satisfies it. */
|
|
3
|
+
export interface RequestSpan {
|
|
4
|
+
updateName(name: string): unknown;
|
|
5
|
+
setAttribute(key: string, value: string): unknown;
|
|
6
|
+
setStatus(status: {
|
|
7
|
+
code: 'error';
|
|
8
|
+
}): unknown;
|
|
9
|
+
}
|
|
10
|
+
/** What {@link traceRequest} is told about the runtime it traces for. */
|
|
11
|
+
export interface TraceRequestOptions {
|
|
12
|
+
/** The request's server span, read after the answer; `undefined` or `null` where the runtime has none. */
|
|
13
|
+
readonly activeSpan: () => RequestSpan | undefined | null;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Middleware that names the request's span `<METHOD> <route template>` once the answer exists, sets
|
|
17
|
+
* `http.route` to the template, and marks a 5xx answer as an error with its status as `error.type`. A 4xx
|
|
18
|
+
* answer leaves the span's status unset. The name is the method alone, and
|
|
19
|
+
* `http.route` is not set, only where no route matched. It does nothing where `activeSpan` returns no span.
|
|
20
|
+
*
|
|
21
|
+
* Mount it before the routes, with `app.use('*', ...)`, so a refusal rendered by `onError` is the answer it reads.
|
|
22
|
+
* The template is the path in full, mount prefix included, of the route that answered, or of the route that
|
|
23
|
+
* would have answered after the middleware that refused or threw. A middleware (method `ALL`) is never the
|
|
24
|
+
* template, so a route registered with `app.all` is read as one and its span gets the method alone. Needs hono 4.8.0 or later for `hono/route`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function traceRequest(options: TraceRequestOptions): MiddlewareHandler;
|
|
27
|
+
//# sourceMappingURL=trace-request.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace-request.d.ts","sourceRoot":"","sources":["../src/trace-request.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAI9C,4FAA4F;AAC5F,MAAM,WAAW,WAAW;IAC1B,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAClC,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAClD,SAAS,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC;CAC/C;AAED,yEAAyE;AACzE,MAAM,WAAW,mBAAmB;IAClC,0GAA0G;IAC1G,QAAQ,CAAC,UAAU,EAAE,MAAM,WAAW,GAAG,SAAS,GAAG,IAAI,CAAC;CAC3D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,iBAAiB,CAe5E"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { createMiddleware } from 'hono/factory';
|
|
2
|
+
import { matchedRoutes } from 'hono/route';
|
|
3
|
+
/**
|
|
4
|
+
* Middleware that names the request's span `<METHOD> <route template>` once the answer exists, sets
|
|
5
|
+
* `http.route` to the template, and marks a 5xx answer as an error with its status as `error.type`. A 4xx
|
|
6
|
+
* answer leaves the span's status unset. The name is the method alone, and
|
|
7
|
+
* `http.route` is not set, only where no route matched. It does nothing where `activeSpan` returns no span.
|
|
8
|
+
*
|
|
9
|
+
* Mount it before the routes, with `app.use('*', ...)`, so a refusal rendered by `onError` is the answer it reads.
|
|
10
|
+
* The template is the path in full, mount prefix included, of the route that answered, or of the route that
|
|
11
|
+
* would have answered after the middleware that refused or threw. A middleware (method `ALL`) is never the
|
|
12
|
+
* template, so a route registered with `app.all` is read as one and its span gets the method alone. Needs hono 4.8.0 or later for `hono/route`.
|
|
13
|
+
*/
|
|
14
|
+
export function traceRequest(options) {
|
|
15
|
+
return createMiddleware(async (c, next) => {
|
|
16
|
+
await next();
|
|
17
|
+
const span = options.activeSpan();
|
|
18
|
+
if (!span)
|
|
19
|
+
return;
|
|
20
|
+
const route = matchedRoutes(c)
|
|
21
|
+
.slice(c.req.routeIndex)
|
|
22
|
+
.find((matched) => matched.method !== 'ALL')?.path;
|
|
23
|
+
span.updateName(route ? `${c.req.method} ${route}` : c.req.method);
|
|
24
|
+
if (route)
|
|
25
|
+
span.setAttribute('http.route', route);
|
|
26
|
+
if (c.res.status >= 500) {
|
|
27
|
+
span.setAttribute('error.type', String(c.res.status));
|
|
28
|
+
span.setStatus({ code: 'error' });
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeroxsolutions/server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -21,14 +21,14 @@
|
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"tslib": "^2.3.0",
|
|
24
|
-
"@zeroxsolutions/jsonapi": "0.14.
|
|
24
|
+
"@zeroxsolutions/jsonapi": "0.14.1",
|
|
25
25
|
"@zeroxsolutions/query": "0.7.5",
|
|
26
|
-
"@zeroxsolutions/response": "0.14.
|
|
26
|
+
"@zeroxsolutions/response": "0.14.8"
|
|
27
27
|
},
|
|
28
28
|
"peerDependencies": {
|
|
29
29
|
"@hono/zod-openapi": "^1.0.0",
|
|
30
30
|
"@logtape/logtape": "^2.0.0",
|
|
31
|
-
"hono": "^4.
|
|
31
|
+
"hono": "^4.8.0",
|
|
32
32
|
"zod": "^4.3.6"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|