@telorun/http-server 0.18.0 → 0.18.1
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 +21 -0
- package/README.md +2 -2
- package/dist/http-api-controller.js +4 -1
- package/dist/http-server-controller.js +11 -2
- package/package.json +2 -2
- package/src/http-api-controller.ts +4 -1
- package/src/http-server-controller.ts +11 -2
- package/tests/request-cancel.test.ts +12 -1
- package/tests/request-span.test.ts +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @telorun/http-server
|
|
2
2
|
|
|
3
|
+
## 0.18.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 8a9b494: Inbound dispatch now goes through `ctx.rootContext()` — the route handler, the
|
|
8
|
+
`notFoundHandler`, and a `contentTypeParsers` parser all receive a context
|
|
9
|
+
minted for the request rather than whatever was ambient when the route was
|
|
10
|
+
registered.
|
|
11
|
+
|
|
12
|
+
No behaviour changes for an app today: a route handler already ran on a
|
|
13
|
+
per-request cancellation context, and the other two are dispatched from socket
|
|
14
|
+
callbacks where the ambient is empty anyway. What changes is that the guarantee
|
|
15
|
+
is now _stated_ rather than incidental. Execution zones (`kernel/specs/execution-zones.md`
|
|
16
|
+
§7) make it a conformance obligation on every inbound registrant, and the
|
|
17
|
+
analyzer's hard error on `trigger.inbound` edges — a zone requirement reaching
|
|
18
|
+
an HTTP route is `ZONE_REQUIREMENT_UNSATISFIED` — rests on it holding. Before,
|
|
19
|
+
it held because every shipped inbound kind happens to be a `Telo.Service`,
|
|
20
|
+
which is a property of those kinds rather than of the edge.
|
|
21
|
+
|
|
22
|
+
- @telorun/http-dispatch@0.4.2
|
|
23
|
+
|
|
3
24
|
## 0.18.0
|
|
4
25
|
|
|
5
26
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -27,8 +27,8 @@ Language- and framework-agnostic HTTP server for Telo. Declarative routes, schem
|
|
|
27
27
|
kind: Telo.Application
|
|
28
28
|
metadata: { name: hello-http, version: 1.0.0 }
|
|
29
29
|
imports:
|
|
30
|
-
Http:
|
|
31
|
-
JS:
|
|
30
|
+
Http: oci://ghcr.io/telorun/http-server@0.19.1
|
|
31
|
+
JS: oci://ghcr.io/telorun/javascript@0.7.0
|
|
32
32
|
targets: [ !ref Server ]
|
|
33
33
|
---
|
|
34
34
|
kind: Http.Server
|
|
@@ -139,7 +139,10 @@ export class HttpServerApi {
|
|
|
139
139
|
// Open a request span rooting this request's own trace: the handler (and
|
|
140
140
|
// its nested invokes) nest under it, and it's labelled with the route so
|
|
141
141
|
// the trace shows the actual method+path, attributed to this Http.Api.
|
|
142
|
-
|
|
142
|
+
// rootContext: an inbound registrant dispatches with a context that
|
|
143
|
+
// inherits nothing ambient — no zones, no trace parent, no caller token
|
|
144
|
+
// (the conformance obligation in kernel/specs/execution-zones.md §7).
|
|
145
|
+
const span = await this.ctx.openSpan(this.ctx.rootContext({ cancellation }), {
|
|
143
146
|
ref: { kind: "Http.Api", name: this.apiName },
|
|
144
147
|
label: `${route.request.method} ${route.request.path}`,
|
|
145
148
|
attributes: { method: route.request.method, path: route.request.path },
|
|
@@ -77,7 +77,12 @@ class HttpServer {
|
|
|
77
77
|
else if (parser) {
|
|
78
78
|
this.app.addContentTypeParser(contentType, { parseAs: "string" }, async (_req, body, done) => {
|
|
79
79
|
try {
|
|
80
|
-
|
|
80
|
+
// The bound entry point forwards every argument, so the root
|
|
81
|
+
// context rides in as the InvokeContext — §7's obligation for an
|
|
82
|
+
// inbound registrant. (This path still calls the instance
|
|
83
|
+
// directly rather than going through `invokeResolved`, so it is
|
|
84
|
+
// untraced; that predates zones and is tracked separately.)
|
|
85
|
+
done(null, await parser.invoke({ body }, this.ctx.rootContext()));
|
|
81
86
|
}
|
|
82
87
|
catch (err) {
|
|
83
88
|
done(err, undefined);
|
|
@@ -230,7 +235,11 @@ class HttpServer {
|
|
|
230
235
|
};
|
|
231
236
|
let result;
|
|
232
237
|
try {
|
|
233
|
-
|
|
238
|
+
// rootContext: an inbound registrant dispatches with a context that
|
|
239
|
+
// inherits nothing ambient (kernel/specs/execution-zones.md §7).
|
|
240
|
+
result = await this.ctx.invoke(handler.kind, handler.name, invokeInput, {
|
|
241
|
+
ctx: this.ctx.rootContext(),
|
|
242
|
+
});
|
|
234
243
|
}
|
|
235
244
|
catch (err) {
|
|
236
245
|
if (!isInvokeError(err))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telorun/http-server",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.1",
|
|
4
4
|
"description": "Telo HTTP Server module - HTTP server and API resource kinds for Telo manifests.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"telo",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"@types/node": "^20.0.0",
|
|
56
56
|
"typescript": "^5.0.0",
|
|
57
57
|
"vitest": "^2.1.8",
|
|
58
|
-
"@telorun/sdk": "0.
|
|
58
|
+
"@telorun/sdk": "0.67.0"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
61
|
"@telorun/sdk": "*"
|
|
@@ -174,7 +174,10 @@ export class HttpServerApi implements ResourceInstance {
|
|
|
174
174
|
// Open a request span rooting this request's own trace: the handler (and
|
|
175
175
|
// its nested invokes) nest under it, and it's labelled with the route so
|
|
176
176
|
// the trace shows the actual method+path, attributed to this Http.Api.
|
|
177
|
-
|
|
177
|
+
// rootContext: an inbound registrant dispatches with a context that
|
|
178
|
+
// inherits nothing ambient — no zones, no trace parent, no caller token
|
|
179
|
+
// (the conformance obligation in kernel/specs/execution-zones.md §7).
|
|
180
|
+
const span = await this.ctx.openSpan(this.ctx.rootContext({ cancellation }), {
|
|
178
181
|
ref: { kind: "Http.Api", name: this.apiName },
|
|
179
182
|
label: `${route.request.method} ${route.request.path}`,
|
|
180
183
|
attributes: { method: route.request.method, path: route.request.path },
|
|
@@ -159,7 +159,12 @@ class HttpServer implements ResourceInstance {
|
|
|
159
159
|
{ parseAs: "string" },
|
|
160
160
|
async (_req, body, done) => {
|
|
161
161
|
try {
|
|
162
|
-
|
|
162
|
+
// The bound entry point forwards every argument, so the root
|
|
163
|
+
// context rides in as the InvokeContext — §7's obligation for an
|
|
164
|
+
// inbound registrant. (This path still calls the instance
|
|
165
|
+
// directly rather than going through `invokeResolved`, so it is
|
|
166
|
+
// untraced; that predates zones and is tracked separately.)
|
|
167
|
+
done(null, await parser.invoke({ body }, this.ctx.rootContext()));
|
|
163
168
|
} catch (err) {
|
|
164
169
|
done(err as Error, undefined);
|
|
165
170
|
}
|
|
@@ -322,7 +327,11 @@ class HttpServer implements ResourceInstance {
|
|
|
322
327
|
|
|
323
328
|
let result: any;
|
|
324
329
|
try {
|
|
325
|
-
|
|
330
|
+
// rootContext: an inbound registrant dispatches with a context that
|
|
331
|
+
// inherits nothing ambient (kernel/specs/execution-zones.md §7).
|
|
332
|
+
result = await this.ctx.invoke(handler.kind, handler.name, invokeInput, {
|
|
333
|
+
ctx: this.ctx.rootContext(),
|
|
334
|
+
});
|
|
326
335
|
} catch (err) {
|
|
327
336
|
if (!isInvokeError(err)) throw err;
|
|
328
337
|
return dispatchCatches(
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ERR_INVOKE_CANCELLED,
|
|
3
|
+
InvokeError,
|
|
4
|
+
UNCANCELLABLE_CONTEXT,
|
|
5
|
+
createCancellationSource,
|
|
6
|
+
} from "@telorun/sdk";
|
|
2
7
|
import Fastify from "fastify";
|
|
3
8
|
import net from "node:net";
|
|
4
9
|
import type { AddressInfo } from "node:net";
|
|
@@ -45,6 +50,12 @@ describe("http-server request cancellation", () => {
|
|
|
45
50
|
ensureKindRef: () => ({ kind: "Test.Handler", name: "SlowWork" }),
|
|
46
51
|
moduleContext: { expandWith: (value: unknown) => value },
|
|
47
52
|
createCancellationSource: () => createCancellationSource(),
|
|
53
|
+
// As the kernel implements it: an inbound registrant dispatches through a
|
|
54
|
+
// context it minted rather than the ambient (execution-zones spec §7), and
|
|
55
|
+
// the request's cancellation token rides in on it — which is what makes
|
|
56
|
+
// the disconnect below reach the handler.
|
|
57
|
+
rootContext: (opts?: { cancellation?: { context: unknown } }) =>
|
|
58
|
+
opts?.cancellation?.context ?? UNCANCELLABLE_CONTEXT,
|
|
48
59
|
invokeResolved: (_kind: string, _name: string, h: typeof handler, input: unknown, c: unknown) =>
|
|
49
60
|
h.invoke(input, c as { cancellation?: any }),
|
|
50
61
|
emitEvent: () => {},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createCancellationSource } from "@telorun/sdk";
|
|
1
|
+
import { UNCANCELLABLE_CONTEXT, createCancellationSource } from "@telorun/sdk";
|
|
2
2
|
import Fastify from "fastify";
|
|
3
3
|
import type { AddressInfo } from "node:net";
|
|
4
4
|
import { describe, expect, it } from "vitest";
|
|
@@ -31,6 +31,10 @@ describe("http-server request span", () => {
|
|
|
31
31
|
ensureKindRef: () => ({ kind: "JS.Script", name: "Echo" }),
|
|
32
32
|
moduleContext: { expandWith: (value: unknown) => value },
|
|
33
33
|
createCancellationSource: () => createCancellationSource(),
|
|
34
|
+
// The controller dispatches through a context it minted rather than the
|
|
35
|
+
// ambient — execution-zones spec §7 — so the mock must offer it.
|
|
36
|
+
rootContext: (opts?: { cancellation?: { context: unknown } }) =>
|
|
37
|
+
opts?.cancellation?.context ?? UNCANCELLABLE_CONTEXT,
|
|
34
38
|
invokeResolved: (_kind: string, _name: string, h: typeof handler, input: unknown) =>
|
|
35
39
|
h.invoke(input),
|
|
36
40
|
emitEvent: () => {},
|