@fougere/transport-http 0.6.0-alpha.0 → 0.8.0-alpha.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/dist/client.d.ts +4 -31
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +2 -16
- package/dist/client.js.map +1 -1
- package/dist/jsonrpc.d.ts +1 -8
- package/dist/jsonrpc.d.ts.map +1 -1
- package/dist/jsonrpc.js +1 -8
- package/dist/jsonrpc.js.map +1 -1
- package/dist/policy.d.ts +1 -13
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +1 -13
- package/dist/policy.js.map +1 -1
- package/dist/receive.d.ts +5 -19
- package/dist/receive.d.ts.map +1 -1
- package/dist/receive.js +4 -6
- package/dist/receive.js.map +1 -1
- package/dist/serve.d.ts +2 -18
- package/dist/serve.d.ts.map +1 -1
- package/dist/serve.js +2 -15
- package/dist/serve.js.map +1 -1
- package/dist/server.d.ts +3 -14
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +5 -16
- package/dist/server.js.map +1 -1
- package/package.json +4 -4
- package/src/client.ts +4 -31
- package/src/jsonrpc.ts +1 -8
- package/src/policy.ts +1 -13
- package/src/receive.ts +5 -19
- package/src/serve.ts +4 -33
- package/src/server.ts +7 -24
package/dist/client.d.ts
CHANGED
|
@@ -1,24 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sending half — frame the call, POST it, unframe the result.
|
|
3
|
-
*
|
|
4
|
-
* Failure vocabulary is FougereError only: a transport failure becomes a
|
|
5
|
-
* typed error (SERVICE_UNAVAILABLE, GATEWAY_TIMEOUT, BAD_GATEWAY), an
|
|
6
|
-
* application failure comes back as the FougereError the façade threw.
|
|
7
|
-
*/
|
|
1
|
+
/** Sending half — frame the call, POST it, unframe the result. */
|
|
8
2
|
import { type Transport, type FrondCall, type InvocationContext, type SignedCall } from '@fougere/core/contract';
|
|
9
3
|
import type { RpcRequest } from './jsonrpc.js';
|
|
10
4
|
export type { RpcRequest, RpcResponse } from './jsonrpc.js';
|
|
11
5
|
/** Frame a call as a JSON-RPC request. */
|
|
12
6
|
export declare function frameCall(call: FrondCall, invocation: InvocationContext, id: number): RpcRequest;
|
|
13
|
-
/**
|
|
14
|
-
* Unframe a JSON-RPC response — the result, or the revived FougereError thrown.
|
|
15
|
-
*
|
|
16
|
-
* The answer is JUDGED first: it crossed a process boundary, and `as RpcResponse` at the
|
|
17
|
-
* call site is a claim about it, not a check. A 200 carrying neither `result` nor `error`
|
|
18
|
-
* — a proxy's own JSON, a receiver that is not one — used to return `undefined` as if the
|
|
19
|
-
* op had succeeded, and a `null` body raised `Cannot use 'in' operator`, which is not the
|
|
20
|
-
* failure vocabulary this file promises.
|
|
21
|
-
*/
|
|
7
|
+
/** Unframe a JSON-RPC response — the result, or the revived FougereError thrown. */
|
|
22
8
|
export declare function unframeResponse(response: unknown, call: FrondCall): unknown;
|
|
23
9
|
export interface HttpTransportOptions {
|
|
24
10
|
/** Abort a call after this long. A timed-out call may have executed — it is never retried. */
|
|
@@ -26,23 +12,10 @@ export interface HttpTransportOptions {
|
|
|
26
12
|
/** Extra attempts on connection failures only — the request provably never left. */
|
|
27
13
|
retries?: number;
|
|
28
14
|
/**
|
|
29
|
-
* Signs the state this transport sends, turning a claim into something the receiver
|
|
30
|
-
* can check. Supplied rather than built here: signing is `node:crypto`, and this
|
|
31
|
-
* module is published as the browser-safe `/client` subpath — a browser holds no key
|
|
32
|
-
* and never signs. `@fougere/app` wires it from `signEnvelope`.
|
|
33
|
-
*
|
|
34
|
-
* Absent, the state travels as a bare claim and only a receiver that asks for nothing
|
|
35
|
-
* will take it.
|
|
15
|
+
* Signs the state this transport sends, turning a claim into something the receiver can check.
|
|
36
16
|
*/
|
|
37
17
|
sign?: (call: SignedCall) => Promise<string>;
|
|
38
|
-
/**
|
|
39
|
-
* Who performs the request. Defaults to the global `fetch`.
|
|
40
|
-
*
|
|
41
|
-
* A Cloudflare service binding answers this shape (`env.CATALOG.fetch`), and on that
|
|
42
|
-
* platform it is not an optimization: a Worker calling a sibling's public URL is
|
|
43
|
-
* refused by the edge (error 1042), so a binding is the only route between two Workers
|
|
44
|
-
* of one account. Nothing else about the call changes — same envelope, same retries.
|
|
45
|
-
*/
|
|
18
|
+
/** Who performs the request. */
|
|
46
19
|
fetch?: (input: string, init: RequestInit) => Promise<Response>;
|
|
47
20
|
}
|
|
48
21
|
export declare function createHttpTransport(baseUrl: string, options?: HttpTransportOptions): Transport;
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,OAAO,EAA2B,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,KAAK,iBAAiB,EAAE,KAAK,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAC1I,OAAO,KAAK,EAAE,UAAU,EAA8B,MAAM,cAAc,CAAC;AAC3E,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE5D,0CAA0C;AAC1C,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,iBAAiB,EAAE,EAAE,EAAE,MAAM,GAAG,UAAU,CAEhG;AAED,oFAAoF;AACpF,wBAAgB,eAAe,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CA4B3E;AAED,MAAM,WAAW,oBAAoB;IACnC,8FAA8F;IAC9F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oFAAoF;IACpF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7C,gCAAgC;IAChC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CACjE;AAKD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAyB,GAAG,SAAS,CA6ElG"}
|
package/dist/client.js
CHANGED
|
@@ -1,24 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sending half — frame the call, POST it, unframe the result.
|
|
3
|
-
*
|
|
4
|
-
* Failure vocabulary is FougereError only: a transport failure becomes a
|
|
5
|
-
* typed error (SERVICE_UNAVAILABLE, GATEWAY_TIMEOUT, BAD_GATEWAY), an
|
|
6
|
-
* application failure comes back as the FougereError the façade threw.
|
|
7
|
-
*/
|
|
1
|
+
/** Sending half — frame the call, POST it, unframe the result. */
|
|
8
2
|
import { FougereError, ErrorCode } from '@fougere/core/contract';
|
|
9
3
|
/** Frame a call as a JSON-RPC request. */
|
|
10
4
|
export function frameCall(call, invocation, id) {
|
|
11
5
|
return { jsonrpc: '2.0', id, method: `${call.entity}.${call.op}`, params: invocation };
|
|
12
6
|
}
|
|
13
|
-
/**
|
|
14
|
-
* Unframe a JSON-RPC response — the result, or the revived FougereError thrown.
|
|
15
|
-
*
|
|
16
|
-
* The answer is JUDGED first: it crossed a process boundary, and `as RpcResponse` at the
|
|
17
|
-
* call site is a claim about it, not a check. A 200 carrying neither `result` nor `error`
|
|
18
|
-
* — a proxy's own JSON, a receiver that is not one — used to return `undefined` as if the
|
|
19
|
-
* op had succeeded, and a `null` body raised `Cannot use 'in' operator`, which is not the
|
|
20
|
-
* failure vocabulary this file promises.
|
|
21
|
-
*/
|
|
7
|
+
/** Unframe a JSON-RPC response — the result, or the revived FougereError thrown. */
|
|
22
8
|
export function unframeResponse(response, call) {
|
|
23
9
|
if (!response || typeof response !== 'object' || !('result' in response || 'error' in response)) {
|
|
24
10
|
throw new FougereError({
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/jsonrpc.d.ts
CHANGED
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* JSON-RPC 2.0 framing — the standard written form of a call.
|
|
3
|
-
*
|
|
4
|
-
* The spec is the reuse: these few lines follow it, no lib adds anything.
|
|
5
|
-
* `method` is `entity.op`; `params` is the InvocationContext untouched.
|
|
6
|
-
* A FougereError travels whole in `error.data` (spec keeps `error.code`
|
|
7
|
-
* for integers — the semantic code lives in the data).
|
|
8
|
-
*/
|
|
1
|
+
/** JSON-RPC 2.0 framing — the standard written form of a call. */
|
|
9
2
|
export interface RpcRequest {
|
|
10
3
|
jsonrpc: '2.0';
|
|
11
4
|
id: number | string;
|
package/dist/jsonrpc.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsonrpc.d.ts","sourceRoot":"","sources":["../src/jsonrpc.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"jsonrpc.d.ts","sourceRoot":"","sources":["../src/jsonrpc.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAElE,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,MAAM,WAAW,GACnB;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GAC/D;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,aAAa,CAAA;CAAE,CAAC;AAEzE,mEAAmE;AACnE,eAAO,MAAM,SAAS,SAAS,CAAC;AAChC,wCAAwC;AACxC,eAAO,MAAM,WAAW,SAAS,CAAC;AAClC,qDAAqD;AACrD,eAAO,MAAM,eAAe,SAAS,CAAC"}
|
package/dist/jsonrpc.js
CHANGED
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* JSON-RPC 2.0 framing — the standard written form of a call.
|
|
3
|
-
*
|
|
4
|
-
* The spec is the reuse: these few lines follow it, no lib adds anything.
|
|
5
|
-
* `method` is `entity.op`; `params` is the InvocationContext untouched.
|
|
6
|
-
* A FougereError travels whole in `error.data` (spec keeps `error.code`
|
|
7
|
-
* for integers — the semantic code lives in the data).
|
|
8
|
-
*/
|
|
1
|
+
/** JSON-RPC 2.0 framing — the standard written form of a call. */
|
|
9
2
|
/** Application failure — a FougereError, carried in error.data. */
|
|
10
3
|
export const APP_ERROR = -32000;
|
|
11
4
|
/** Body wasn't JSON (spec-reserved). */
|
package/dist/jsonrpc.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsonrpc.js","sourceRoot":"","sources":["../src/jsonrpc.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"jsonrpc.js","sourceRoot":"","sources":["../src/jsonrpc.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAmBlE,mEAAmE;AACnE,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,KAAK,CAAC;AAChC,wCAAwC;AACxC,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,KAAK,CAAC;AAClC,qDAAqD;AACrD,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,KAAK,CAAC"}
|
package/dist/policy.d.ts
CHANGED
|
@@ -1,16 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* What every receiver of the envelope decides the same way, and what it does not.
|
|
3
|
-
*
|
|
4
|
-
* The answers are POLICY. The CAP is core's (`wire/call.ts`) — it was spelled in four packages
|
|
5
|
-
* and omitted by the receiver written by hand, and this file claimed to have consolidated it
|
|
6
|
-
* while three doors kept their own literal. Reading the body is PLUMBING, and it belongs to
|
|
7
|
-
* the transport: a `node:http` stream and a `Request` body are read differently, and putting
|
|
8
|
-
* Node through the Web reader HALVED the door that took it (measured, 2026-08-22: the JSON-RPC
|
|
9
|
-
* door fell from 0.677 to 0.341 of its hand-written baseline, while the REST door, which never
|
|
10
|
-
* traverses `serve()`, sat at 0.653 in that same run).
|
|
11
|
-
*
|
|
12
|
-
* So this file holds the decisions. Each receiver keeps its own reader.
|
|
13
|
-
*/
|
|
1
|
+
/** What every receiver of the envelope decides the same way, and what it does not. */
|
|
14
2
|
import { type RpcResponse } from './jsonrpc.js';
|
|
15
3
|
/** What a receiver accepts before it stops reading — declared by core, re-exported here. */
|
|
16
4
|
export { MAX_BODY_BYTES } from '@fougere/core';
|
package/dist/policy.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAE7D,4FAA4F;AAC5F,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,qFAAqF;AACrF,eAAO,MAAM,SAAS,mBAAmB,CAAC;AAE1C,iDAAiD;AACjD,eAAO,MAAM,UAAU,QAAO,WAI5B,CAAC;AAEH,yCAAyC;AACzC,eAAO,MAAM,QAAQ;IAAY,KAAK;CAAwB,CAAC"}
|
package/dist/policy.js
CHANGED
|
@@ -1,16 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* What every receiver of the envelope decides the same way, and what it does not.
|
|
3
|
-
*
|
|
4
|
-
* The answers are POLICY. The CAP is core's (`wire/call.ts`) — it was spelled in four packages
|
|
5
|
-
* and omitted by the receiver written by hand, and this file claimed to have consolidated it
|
|
6
|
-
* while three doors kept their own literal. Reading the body is PLUMBING, and it belongs to
|
|
7
|
-
* the transport: a `node:http` stream and a `Request` body are read differently, and putting
|
|
8
|
-
* Node through the Web reader HALVED the door that took it (measured, 2026-08-22: the JSON-RPC
|
|
9
|
-
* door fell from 0.677 to 0.341 of its hand-written baseline, while the REST door, which never
|
|
10
|
-
* traverses `serve()`, sat at 0.653 in that same run).
|
|
11
|
-
*
|
|
12
|
-
* So this file holds the decisions. Each receiver keeps its own reader.
|
|
13
|
-
*/
|
|
1
|
+
/** What every receiver of the envelope decides the same way, and what it does not. */
|
|
14
2
|
import { PARSE_ERROR } from './jsonrpc.js';
|
|
15
3
|
/** What a receiver accepts before it stops reading — declared by core, re-exported here. */
|
|
16
4
|
export { MAX_BODY_BYTES } from '@fougere/core';
|
package/dist/policy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,OAAO,EAAE,WAAW,EAAoB,MAAM,cAAc,CAAC;AAE7D,4FAA4F;AAC5F,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,qFAAqF;AACrF,MAAM,CAAC,MAAM,SAAS,GAAG,gBAAgB,CAAC;AAE1C,iDAAiD;AACjD,MAAM,CAAC,MAAM,UAAU,GAAG,GAAgB,EAAE,CAAC,CAAC;IAC5C,OAAO,EAAE,KAAK;IACd,EAAE,EAAE,IAAI;IACR,KAAK,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,aAAa,EAAE;CACrD,CAAC,CAAC;AAEH,yCAAyC;AACzC,MAAM,CAAC,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAAC"}
|
package/dist/receive.d.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The envelope door for a host that speaks `Request`/`Response` — hono, a Worker, Next,
|
|
3
|
-
* SvelteKit. It reads a `Request` body; `serve` reads a `node:http` stream, and the two
|
|
4
|
-
* stay separate on purpose: putting Node through this reader HALVED the Node door
|
|
5
|
-
* (0.677 → 0.341 of its baseline, measured 2026-08-22).
|
|
2
|
+
* The envelope door for a host that speaks `Request`/`Response` — hono, a Worker, Next, SvelteKit.
|
|
6
3
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* It reads a `Request` body and `serve` reads a `node:http` stream; the two stay separate
|
|
5
|
+
* because putting Node through this reader HALVED the Node door — 0.677 to 0.341 of its
|
|
6
|
+
* baseline, measured 2026-08-22.
|
|
9
7
|
*/
|
|
10
8
|
import { type ReceiveOptions } from './server.js';
|
|
11
9
|
import type { Transport } from '@fougere/core/contract';
|
|
@@ -14,19 +12,7 @@ export interface ReceiveHttpOptions extends ReceiveOptions {
|
|
|
14
12
|
maxBodyBytes?: number;
|
|
15
13
|
/** The path this door answers. Default: `/_fougere/call`. */
|
|
16
14
|
path?: string;
|
|
17
|
-
/**
|
|
18
|
-
* Take unsigned calls, deliberately.
|
|
19
|
-
*
|
|
20
|
-
* `serve()` reads the decision off the ADDRESS — loopback by default, and widening it
|
|
21
|
-
* is written down. This door binds nothing: its host mounts it wherever it likes, and a
|
|
22
|
-
* `Request` arrives with no bound address to consult. So the decision has to be stated,
|
|
23
|
-
* and the default is the safe one: with no `verify` and no word here, this REFUSES to
|
|
24
|
-
* be built. A door that starts and then believes whatever `state` it is handed is the
|
|
25
|
-
* hole `identity.ts` exists to close, and it was open here.
|
|
26
|
-
*
|
|
27
|
-
* The one case that legitimately needs it beyond local development is the same as
|
|
28
|
-
* `serve`'s: something in front already established the peer.
|
|
29
|
-
*/
|
|
15
|
+
/** Take unsigned calls, deliberately. */
|
|
30
16
|
allowUnsigned?: boolean;
|
|
31
17
|
}
|
|
32
18
|
export declare function receive(runner: Transport, options?: ReceiveHttpOptions): (request: Request) => Promise<Response>;
|
package/dist/receive.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"receive.d.ts","sourceRoot":"","sources":["../src/receive.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"receive.d.ts","sourceRoot":"","sources":["../src/receive.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAa,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,WAAW,kBAAmB,SAAQ,cAAc;IACxD,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AA4CD,wBAAgB,OAAO,CACrB,MAAM,EAAE,SAAS,EACjB,OAAO,GAAE,kBAAuB,GAC/B,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CA+BzC"}
|
package/dist/receive.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The envelope door for a host that speaks `Request`/`Response` — hono, a Worker, Next,
|
|
3
|
-
* SvelteKit. It reads a `Request` body; `serve` reads a `node:http` stream, and the two
|
|
4
|
-
* stay separate on purpose: putting Node through this reader HALVED the Node door
|
|
5
|
-
* (0.677 → 0.341 of its baseline, measured 2026-08-22).
|
|
2
|
+
* The envelope door for a host that speaks `Request`/`Response` — hono, a Worker, Next, SvelteKit.
|
|
6
3
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* It reads a `Request` body and `serve` reads a `node:http` stream; the two stay separate
|
|
5
|
+
* because putting Node through this reader HALVED the Node door — 0.677 to 0.341 of its
|
|
6
|
+
* baseline, measured 2026-08-22.
|
|
9
7
|
*/
|
|
10
8
|
import { handleRpc } from './server.js';
|
|
11
9
|
import { MAX_BODY_BYTES, CALL_PATH, parseError, tooLarge } from './policy.js';
|
package/dist/receive.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"receive.js","sourceRoot":"","sources":["../src/receive.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"receive.js","sourceRoot":"","sources":["../src/receive.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,SAAS,EAAuB,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAY9E,MAAM,IAAI,GAAG,CAAC,IAAa,EAAE,MAAM,GAAG,GAAG,EAAE,EAAE,CAC3C,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,CAAC,CAAC;AAElG,yFAAyF;AACzF,MAAM,SAAS,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;AAEtC;;;GAGG;AACH,KAAK,UAAU,UAAU,CAAC,OAAgB,EAAE,GAAW;IACrD,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAC/D,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,GAAG,GAAG;QAAE,OAAO,SAAS,CAAC;IAElE,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC;IACzC,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IAEvB,MAAM,MAAM,GAAiB,EAAE,CAAC;IAChC,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,SAAS,CAAC;QACR,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QAC5C,IAAI,IAAI;YAAE,MAAM;QAChB,IAAI,IAAI,KAAK,CAAC,UAAU,CAAC;QACzB,IAAI,IAAI,GAAG,GAAG,EAAE,CAAC;YACf,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;YACtB,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrB,CAAC;IACD,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;AACxD,CAAC;AAED,SAAS,MAAM,CAAC,MAA6B,EAAE,IAAY;IACzD,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,EAAE,GAAG,CAAC,CAAC;IACX,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACnB,EAAE,IAAI,KAAK,CAAC,UAAU,CAAC;IACzB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,OAAO,CACrB,MAAiB,EACjB,OAAO,GAAuB,EAAE;IAEhC,uFAAuF;IACvF,kEAAkE;IAClE,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CACb,iGAAiG;cAC/F,iFAAiF;cACjF,mFAAmF;cACnF,oDAAoD,CACvD,CAAC;IACJ,CAAC;IAED,MAAM,GAAG,GAAG,OAAO,CAAC,YAAY,IAAI,cAAc,CAAC;IACnD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,SAAS,CAAC;IAEvC,OAAO,KAAK,EAAE,OAAO,EAAE,EAAE;QACvB,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;YACxE,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QAC7C,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAC3C,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC,QAAQ,EAAE,EAAE,GAAG,CAAC,CAAC;QAEpD,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;QAC5B,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACxD,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/serve.d.ts
CHANGED
|
@@ -3,27 +3,11 @@ import { type ReceiveOptions } from './server.js';
|
|
|
3
3
|
export interface ServeOptions extends ReceiveOptions {
|
|
4
4
|
/** Port to listen on. 0 (default) picks a free one. */
|
|
5
5
|
port?: number;
|
|
6
|
-
/**
|
|
7
|
-
* The addresses this receiver may bind. Default: loopback only.
|
|
8
|
-
*
|
|
9
|
-
* A receiver reads the caller's identity off the wire and re-establishes
|
|
10
|
-
* nothing, so reaching beyond the machine is a decision its operator takes
|
|
11
|
-
* — stating it here IS taking it. The default is what a frond on a laptop
|
|
12
|
-
* wants; a container wants `hosts: ['0.0.0.0']` and says so.
|
|
13
|
-
*/
|
|
6
|
+
/** The addresses this receiver may bind. */
|
|
14
7
|
hosts?: string[];
|
|
15
8
|
/** Which address to bind. Must be one of `hosts`. Defaults to its first. */
|
|
16
9
|
host?: string;
|
|
17
|
-
/**
|
|
18
|
-
* Serve unsigned calls beyond loopback, deliberately.
|
|
19
|
-
*
|
|
20
|
-
* The one case that legitimately needs it: something in front already established the
|
|
21
|
-
* peer — a service mesh whose sidecar terminated mTLS, an ingress doing client certs.
|
|
22
|
-
* Asking for a second signature there would redo what was just done a centimetre away.
|
|
23
|
-
*
|
|
24
|
-
* It is spelled separately from `requireIdentity` on purpose: that one arrives `false`
|
|
25
|
-
* by default from `identityFromEnv`, so it cannot also mean "I thought about this".
|
|
26
|
-
*/
|
|
10
|
+
/** Serve unsigned calls beyond loopback, deliberately. */
|
|
27
11
|
allowUnsigned?: boolean;
|
|
28
12
|
/** Maximum JSON-RPC body size. Default: 1 MiB. */
|
|
29
13
|
maxBodyBytes?: number;
|
package/dist/serve.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C,OAAO,EAAa,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAG7D,MAAM,WAAW,YAAa,SAAQ,cAAc;IAClD,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,qDAAqD;AACrD,eAAO,MAAM,cAAc,UAAoC,CAAC;AAEhE,wBAAgB,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,eAAe,CAAC,CAqE7F"}
|
package/dist/serve.js
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Standalone receiver — enough to host a Frond in its own process.
|
|
3
|
-
*
|
|
4
|
-
* Plain node:http, one route: POST /_fougere/call. Mounting the receiving
|
|
5
|
-
* half inside an existing server (@fougere/http, Nitro) is layer-2 work.
|
|
6
|
-
*/
|
|
1
|
+
/** Standalone receiver — enough to host a Frond in its own process. */
|
|
7
2
|
import { createServer } from 'node:http';
|
|
8
3
|
import { handleRpc } from './server.js';
|
|
9
4
|
import { MAX_BODY_BYTES, CALL_PATH, parseError, tooLarge } from './policy.js';
|
|
@@ -22,15 +17,7 @@ export function serve(runner, options = {}) {
|
|
|
22
17
|
if (!allowed.includes(host)) {
|
|
23
18
|
return Promise.reject(new Error(`A Fougere receiver binds one of [${allowed.join(', ')}], got '${host}' — add it to \`hosts\` to allow it`));
|
|
24
19
|
}
|
|
25
|
-
/**
|
|
26
|
-
* Loopback or signed — there is no third way to serve.
|
|
27
|
-
*
|
|
28
|
-
* The address already carries the decision: binding beyond loopback is a deliberate
|
|
29
|
-
* act (`hosts` says so), and a receiver reachable from outside that establishes nothing
|
|
30
|
-
* takes the `state` it is handed. Refusing at BOOT and not per call is the point — a
|
|
31
|
-
* receiver that starts and then rejects everything is discovered in production, one
|
|
32
|
-
* that will not start is discovered at deployment.
|
|
33
|
-
*/
|
|
20
|
+
/** Loopback or signed — there is no third way to serve. */
|
|
34
21
|
if (!LOOPBACK_HOSTS.includes(host) && !options.verify && !options.allowUnsigned) {
|
|
35
22
|
return Promise.reject(new Error(`A Fougere receiver on '${host}' is reachable from outside this machine and would believe whatever `
|
|
36
23
|
+ 'state it is handed.\n'
|
package/dist/serve.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serve.js","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"serve.js","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
CHANGED
|
@@ -1,26 +1,15 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Receiving half — unframe the call, run it, frame what comes out.
|
|
3
|
-
*
|
|
4
|
-
* Judges nothing: validation and middlewares live with the handler, inside
|
|
5
|
-
* the runner. The error a façade throws is framed whole, never flattened.
|
|
6
|
-
*/
|
|
1
|
+
/** Receiving half — unframe the call, run it, frame what comes out. */
|
|
7
2
|
import { type SignedCall, type Transport } from '@fougere/core/contract';
|
|
8
3
|
import { type RpcResponse } from './jsonrpc.js';
|
|
9
4
|
/** What a receiver does with the caller's envelope. */
|
|
10
5
|
export interface ReceiveOptions {
|
|
11
|
-
/**
|
|
12
|
-
* Establishes who signed, or throws. Supplied rather than built here for the reason
|
|
13
|
-
* the sender's `sign` is: verifying is `node:crypto` and this package carries none.
|
|
14
|
-
* `@fougere/app` wires it from `verifyEnvelope` and the root public key.
|
|
15
|
-
*/
|
|
6
|
+
/** Establishes who signed, or throws. */
|
|
16
7
|
verify?: (identity: string, presented: SignedCall) => Promise<{
|
|
17
8
|
caller: string;
|
|
18
9
|
state: Record<string, unknown>;
|
|
19
10
|
}>;
|
|
20
11
|
/**
|
|
21
|
-
* Refuse a call carrying no verifiable identity — the whole of "secure by default"
|
|
22
|
-
* at the wire. Without it a receiver takes the state it is handed and its only
|
|
23
|
-
* protection is the address it bound.
|
|
12
|
+
* Refuse a call carrying no verifiable identity — the whole of "secure by default" at the wire.
|
|
24
13
|
*/
|
|
25
14
|
requireIdentity?: boolean;
|
|
26
15
|
}
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,OAAO,EAAkE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACzI,OAAO,EAA+C,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAE7F,uDAAuD;AACvD,MAAM,WAAW,cAAc;IAC7B,yCAAyC;IACzC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,KAAK,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;IAClH;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,wBAAsB,SAAS,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,WAAW,CAAC,CAoEnH"}
|
package/dist/server.js
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Receiving half — unframe the call, run it, frame what comes out.
|
|
3
|
-
*
|
|
4
|
-
* Judges nothing: validation and middlewares live with the handler, inside
|
|
5
|
-
* the runner. The error a façade throws is framed whole, never flattened.
|
|
6
|
-
*/
|
|
1
|
+
/** Receiving half — unframe the call, run it, frame what comes out. */
|
|
7
2
|
import { FougereError, ErrorCode, toPublicError } from '@fougere/core/contract';
|
|
8
3
|
import { APP_ERROR, INVALID_REQUEST } from './jsonrpc.js';
|
|
9
4
|
export async function handleRpc(runner, raw, options = {}) {
|
|
@@ -24,13 +19,7 @@ export async function handleRpc(runner, raw, options = {}) {
|
|
|
24
19
|
const op = req.method.slice(dot + 1);
|
|
25
20
|
// Fresh objects — middlewares deposit into state, nothing may be shared.
|
|
26
21
|
const sent = (req.params ?? {});
|
|
27
|
-
/**
|
|
28
|
-
* State is ESTABLISHED here, or it is only claimed.
|
|
29
|
-
*
|
|
30
|
-
* `sent.state` is what the caller typed; a verified envelope is what it can prove, and
|
|
31
|
-
* when a verifier is wired the envelope REPLACES the claim rather than enriching it —
|
|
32
|
-
* carrying both would leave every reader downstream choosing between them.
|
|
33
|
-
*/
|
|
22
|
+
/** State is ESTABLISHED here, or it is only claimed. */
|
|
34
23
|
let state = sent.state ?? {};
|
|
35
24
|
let caller;
|
|
36
25
|
if (options.verify && sent.identity) {
|
|
@@ -41,7 +30,7 @@ export async function handleRpc(runner, raw, options = {}) {
|
|
|
41
30
|
op,
|
|
42
31
|
params: sent.params ?? {},
|
|
43
32
|
query: sent.query ?? {},
|
|
44
|
-
|
|
33
|
+
input: sent.input,
|
|
45
34
|
}));
|
|
46
35
|
}
|
|
47
36
|
catch (err) {
|
|
@@ -51,12 +40,12 @@ export async function handleRpc(runner, raw, options = {}) {
|
|
|
51
40
|
else if (options.requireIdentity) {
|
|
52
41
|
return refused(id, 'this receiver takes signed calls only', entity, op);
|
|
53
42
|
}
|
|
54
|
-
// Built field by field, never spread from `sent`: everything here is either
|
|
43
|
+
// Built field by field, never spread from `sent`: everything here is either validated
|
|
55
44
|
// above or carried deliberately, and `caller` is ours to write alone.
|
|
56
45
|
const invocation = {
|
|
57
46
|
params: sent.params ?? {},
|
|
58
47
|
query: sent.query ?? {},
|
|
59
|
-
|
|
48
|
+
input: sent.input,
|
|
60
49
|
state,
|
|
61
50
|
trace: sent.trace,
|
|
62
51
|
...(caller ? { caller } : {}),
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fougere/transport-http",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0-alpha.0",
|
|
4
4
|
"description": "The call contract on the wire, in JSON-RPC 2.0 (server + client).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fougere",
|
|
@@ -38,12 +38,12 @@
|
|
|
38
38
|
"src"
|
|
39
39
|
],
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@fougere/core": "0.
|
|
41
|
+
"@fougere/core": "0.8.0-alpha.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"jiti": "^2.6.1",
|
|
45
|
-
"@fougere/
|
|
46
|
-
"@fougere/
|
|
45
|
+
"@fougere/container": "0.8.0-alpha.0",
|
|
46
|
+
"@fougere/schema": "0.8.0-alpha.0"
|
|
47
47
|
},
|
|
48
48
|
"publishConfig": {
|
|
49
49
|
"access": "public"
|
package/src/client.ts
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sending half — frame the call, POST it, unframe the result.
|
|
3
|
-
*
|
|
4
|
-
* Failure vocabulary is FougereError only: a transport failure becomes a
|
|
5
|
-
* typed error (SERVICE_UNAVAILABLE, GATEWAY_TIMEOUT, BAD_GATEWAY), an
|
|
6
|
-
* application failure comes back as the FougereError the façade threw.
|
|
7
|
-
*/
|
|
1
|
+
/** Sending half — frame the call, POST it, unframe the result. */
|
|
8
2
|
import { FougereError, ErrorCode, type Transport, type FrondCall, type InvocationContext, type SignedCall } from '@fougere/core/contract';
|
|
9
3
|
import type { RpcRequest, RpcResponse, RpcErrorShape } from './jsonrpc.js';
|
|
10
4
|
export type { RpcRequest, RpcResponse } from './jsonrpc.js';
|
|
@@ -14,15 +8,7 @@ export function frameCall(call: FrondCall, invocation: InvocationContext, id: nu
|
|
|
14
8
|
return { jsonrpc: '2.0', id, method: `${call.entity}.${call.op}`, params: invocation };
|
|
15
9
|
}
|
|
16
10
|
|
|
17
|
-
/**
|
|
18
|
-
* Unframe a JSON-RPC response — the result, or the revived FougereError thrown.
|
|
19
|
-
*
|
|
20
|
-
* The answer is JUDGED first: it crossed a process boundary, and `as RpcResponse` at the
|
|
21
|
-
* call site is a claim about it, not a check. A 200 carrying neither `result` nor `error`
|
|
22
|
-
* — a proxy's own JSON, a receiver that is not one — used to return `undefined` as if the
|
|
23
|
-
* op had succeeded, and a `null` body raised `Cannot use 'in' operator`, which is not the
|
|
24
|
-
* failure vocabulary this file promises.
|
|
25
|
-
*/
|
|
11
|
+
/** Unframe a JSON-RPC response — the result, or the revived FougereError thrown. */
|
|
26
12
|
export function unframeResponse(response: unknown, call: FrondCall): unknown {
|
|
27
13
|
if (!response || typeof response !== 'object' || !('result' in response || 'error' in response)) {
|
|
28
14
|
throw new FougereError({
|
|
@@ -59,23 +45,10 @@ export interface HttpTransportOptions {
|
|
|
59
45
|
/** Extra attempts on connection failures only — the request provably never left. */
|
|
60
46
|
retries?: number;
|
|
61
47
|
/**
|
|
62
|
-
* Signs the state this transport sends, turning a claim into something the receiver
|
|
63
|
-
* can check. Supplied rather than built here: signing is `node:crypto`, and this
|
|
64
|
-
* module is published as the browser-safe `/client` subpath — a browser holds no key
|
|
65
|
-
* and never signs. `@fougere/app` wires it from `signEnvelope`.
|
|
66
|
-
*
|
|
67
|
-
* Absent, the state travels as a bare claim and only a receiver that asks for nothing
|
|
68
|
-
* will take it.
|
|
48
|
+
* Signs the state this transport sends, turning a claim into something the receiver can check.
|
|
69
49
|
*/
|
|
70
50
|
sign?: (call: SignedCall) => Promise<string>;
|
|
71
|
-
/**
|
|
72
|
-
* Who performs the request. Defaults to the global `fetch`.
|
|
73
|
-
*
|
|
74
|
-
* A Cloudflare service binding answers this shape (`env.CATALOG.fetch`), and on that
|
|
75
|
-
* platform it is not an optimization: a Worker calling a sibling's public URL is
|
|
76
|
-
* refused by the edge (error 1042), so a binding is the only route between two Workers
|
|
77
|
-
* of one account. Nothing else about the call changes — same envelope, same retries.
|
|
78
|
-
*/
|
|
51
|
+
/** Who performs the request. */
|
|
79
52
|
fetch?: (input: string, init: RequestInit) => Promise<Response>;
|
|
80
53
|
}
|
|
81
54
|
|
package/src/jsonrpc.ts
CHANGED
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* JSON-RPC 2.0 framing — the standard written form of a call.
|
|
3
|
-
*
|
|
4
|
-
* The spec is the reuse: these few lines follow it, no lib adds anything.
|
|
5
|
-
* `method` is `entity.op`; `params` is the InvocationContext untouched.
|
|
6
|
-
* A FougereError travels whole in `error.data` (spec keeps `error.code`
|
|
7
|
-
* for integers — the semantic code lives in the data).
|
|
8
|
-
*/
|
|
1
|
+
/** JSON-RPC 2.0 framing — the standard written form of a call. */
|
|
9
2
|
|
|
10
3
|
export interface RpcRequest {
|
|
11
4
|
jsonrpc: '2.0';
|
package/src/policy.ts
CHANGED
|
@@ -1,16 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* What every receiver of the envelope decides the same way, and what it does not.
|
|
3
|
-
*
|
|
4
|
-
* The answers are POLICY. The CAP is core's (`wire/call.ts`) — it was spelled in four packages
|
|
5
|
-
* and omitted by the receiver written by hand, and this file claimed to have consolidated it
|
|
6
|
-
* while three doors kept their own literal. Reading the body is PLUMBING, and it belongs to
|
|
7
|
-
* the transport: a `node:http` stream and a `Request` body are read differently, and putting
|
|
8
|
-
* Node through the Web reader HALVED the door that took it (measured, 2026-08-22: the JSON-RPC
|
|
9
|
-
* door fell from 0.677 to 0.341 of its hand-written baseline, while the REST door, which never
|
|
10
|
-
* traverses `serve()`, sat at 0.653 in that same run).
|
|
11
|
-
*
|
|
12
|
-
* So this file holds the decisions. Each receiver keeps its own reader.
|
|
13
|
-
*/
|
|
1
|
+
/** What every receiver of the envelope decides the same way, and what it does not. */
|
|
14
2
|
import { PARSE_ERROR, type RpcResponse } from './jsonrpc.js';
|
|
15
3
|
|
|
16
4
|
/** What a receiver accepts before it stops reading — declared by core, re-exported here. */
|
package/src/receive.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The envelope door for a host that speaks `Request`/`Response` — hono, a Worker, Next,
|
|
3
|
-
* SvelteKit. It reads a `Request` body; `serve` reads a `node:http` stream, and the two
|
|
4
|
-
* stay separate on purpose: putting Node through this reader HALVED the Node door
|
|
5
|
-
* (0.677 → 0.341 of its baseline, measured 2026-08-22).
|
|
2
|
+
* The envelope door for a host that speaks `Request`/`Response` — hono, a Worker, Next, SvelteKit.
|
|
6
3
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* It reads a `Request` body and `serve` reads a `node:http` stream; the two stay separate
|
|
5
|
+
* because putting Node through this reader HALVED the Node door — 0.677 to 0.341 of its
|
|
6
|
+
* baseline, measured 2026-08-22.
|
|
9
7
|
*/
|
|
10
8
|
import { handleRpc, type ReceiveOptions } from './server.js';
|
|
11
9
|
import { MAX_BODY_BYTES, CALL_PATH, parseError, tooLarge } from './policy.js';
|
|
@@ -16,19 +14,7 @@ export interface ReceiveHttpOptions extends ReceiveOptions {
|
|
|
16
14
|
maxBodyBytes?: number;
|
|
17
15
|
/** The path this door answers. Default: `/_fougere/call`. */
|
|
18
16
|
path?: string;
|
|
19
|
-
/**
|
|
20
|
-
* Take unsigned calls, deliberately.
|
|
21
|
-
*
|
|
22
|
-
* `serve()` reads the decision off the ADDRESS — loopback by default, and widening it
|
|
23
|
-
* is written down. This door binds nothing: its host mounts it wherever it likes, and a
|
|
24
|
-
* `Request` arrives with no bound address to consult. So the decision has to be stated,
|
|
25
|
-
* and the default is the safe one: with no `verify` and no word here, this REFUSES to
|
|
26
|
-
* be built. A door that starts and then believes whatever `state` it is handed is the
|
|
27
|
-
* hole `identity.ts` exists to close, and it was open here.
|
|
28
|
-
*
|
|
29
|
-
* The one case that legitimately needs it beyond local development is the same as
|
|
30
|
-
* `serve`'s: something in front already established the peer.
|
|
31
|
-
*/
|
|
17
|
+
/** Take unsigned calls, deliberately. */
|
|
32
18
|
allowUnsigned?: boolean;
|
|
33
19
|
}
|
|
34
20
|
|
package/src/serve.ts
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Standalone receiver — enough to host a Frond in its own process.
|
|
3
|
-
*
|
|
4
|
-
* Plain node:http, one route: POST /_fougere/call. Mounting the receiving
|
|
5
|
-
* half inside an existing server (@fougere/http, Nitro) is layer-2 work.
|
|
6
|
-
*/
|
|
1
|
+
/** Standalone receiver — enough to host a Frond in its own process. */
|
|
7
2
|
import { createServer } from 'node:http';
|
|
8
3
|
import type { Transport } from '@fougere/core';
|
|
9
4
|
import type { RpcResponse } from './jsonrpc.js';
|
|
@@ -13,27 +8,11 @@ import { MAX_BODY_BYTES, CALL_PATH, parseError, tooLarge } from './policy.js';
|
|
|
13
8
|
export interface ServeOptions extends ReceiveOptions {
|
|
14
9
|
/** Port to listen on. 0 (default) picks a free one. */
|
|
15
10
|
port?: number;
|
|
16
|
-
/**
|
|
17
|
-
* The addresses this receiver may bind. Default: loopback only.
|
|
18
|
-
*
|
|
19
|
-
* A receiver reads the caller's identity off the wire and re-establishes
|
|
20
|
-
* nothing, so reaching beyond the machine is a decision its operator takes
|
|
21
|
-
* — stating it here IS taking it. The default is what a frond on a laptop
|
|
22
|
-
* wants; a container wants `hosts: ['0.0.0.0']` and says so.
|
|
23
|
-
*/
|
|
11
|
+
/** The addresses this receiver may bind. */
|
|
24
12
|
hosts?: string[];
|
|
25
13
|
/** Which address to bind. Must be one of `hosts`. Defaults to its first. */
|
|
26
14
|
host?: string;
|
|
27
|
-
/**
|
|
28
|
-
* Serve unsigned calls beyond loopback, deliberately.
|
|
29
|
-
*
|
|
30
|
-
* The one case that legitimately needs it: something in front already established the
|
|
31
|
-
* peer — a service mesh whose sidecar terminated mTLS, an ingress doing client certs.
|
|
32
|
-
* Asking for a second signature there would redo what was just done a centimetre away.
|
|
33
|
-
*
|
|
34
|
-
* It is spelled separately from `requireIdentity` on purpose: that one arrives `false`
|
|
35
|
-
* by default from `identityFromEnv`, so it cannot also mean "I thought about this".
|
|
36
|
-
*/
|
|
15
|
+
/** Serve unsigned calls beyond loopback, deliberately. */
|
|
37
16
|
allowUnsigned?: boolean;
|
|
38
17
|
/** Maximum JSON-RPC body size. Default: 1 MiB. */
|
|
39
18
|
maxBodyBytes?: number;
|
|
@@ -64,15 +43,7 @@ export function serve(runner: Transport, options: ServeOptions = {}): Promise<Ru
|
|
|
64
43
|
new Error(`A Fougere receiver binds one of [${allowed.join(', ')}], got '${host}' — add it to \`hosts\` to allow it`),
|
|
65
44
|
);
|
|
66
45
|
}
|
|
67
|
-
/**
|
|
68
|
-
* Loopback or signed — there is no third way to serve.
|
|
69
|
-
*
|
|
70
|
-
* The address already carries the decision: binding beyond loopback is a deliberate
|
|
71
|
-
* act (`hosts` says so), and a receiver reachable from outside that establishes nothing
|
|
72
|
-
* takes the `state` it is handed. Refusing at BOOT and not per call is the point — a
|
|
73
|
-
* receiver that starts and then rejects everything is discovered in production, one
|
|
74
|
-
* that will not start is discovered at deployment.
|
|
75
|
-
*/
|
|
46
|
+
/** Loopback or signed — there is no third way to serve. */
|
|
76
47
|
if (!LOOPBACK_HOSTS.includes(host) && !options.verify && !options.allowUnsigned) {
|
|
77
48
|
return Promise.reject(
|
|
78
49
|
new Error(
|
package/src/server.ts
CHANGED
|
@@ -1,24 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Receiving half — unframe the call, run it, frame what comes out.
|
|
3
|
-
*
|
|
4
|
-
* Judges nothing: validation and middlewares live with the handler, inside
|
|
5
|
-
* the runner. The error a façade throws is framed whole, never flattened.
|
|
6
|
-
*/
|
|
1
|
+
/** Receiving half — unframe the call, run it, frame what comes out. */
|
|
7
2
|
import { FougereError, ErrorCode, toPublicError, type InvocationContext, type SignedCall, type Transport } from '@fougere/core/contract';
|
|
8
3
|
import { APP_ERROR, INVALID_REQUEST, type RpcRequest, type RpcResponse } from './jsonrpc.js';
|
|
9
4
|
|
|
10
5
|
/** What a receiver does with the caller's envelope. */
|
|
11
6
|
export interface ReceiveOptions {
|
|
12
|
-
/**
|
|
13
|
-
* Establishes who signed, or throws. Supplied rather than built here for the reason
|
|
14
|
-
* the sender's `sign` is: verifying is `node:crypto` and this package carries none.
|
|
15
|
-
* `@fougere/app` wires it from `verifyEnvelope` and the root public key.
|
|
16
|
-
*/
|
|
7
|
+
/** Establishes who signed, or throws. */
|
|
17
8
|
verify?: (identity: string, presented: SignedCall) => Promise<{ caller: string; state: Record<string, unknown> }>;
|
|
18
9
|
/**
|
|
19
|
-
* Refuse a call carrying no verifiable identity — the whole of "secure by default"
|
|
20
|
-
* at the wire. Without it a receiver takes the state it is handed and its only
|
|
21
|
-
* protection is the address it bound.
|
|
10
|
+
* Refuse a call carrying no verifiable identity — the whole of "secure by default" at the wire.
|
|
22
11
|
*/
|
|
23
12
|
requireIdentity?: boolean;
|
|
24
13
|
}
|
|
@@ -45,13 +34,7 @@ export async function handleRpc(runner: Transport, raw: unknown, options: Receiv
|
|
|
45
34
|
// Fresh objects — middlewares deposit into state, nothing may be shared.
|
|
46
35
|
const sent = (req.params ?? {}) as Partial<InvocationContext>;
|
|
47
36
|
|
|
48
|
-
/**
|
|
49
|
-
* State is ESTABLISHED here, or it is only claimed.
|
|
50
|
-
*
|
|
51
|
-
* `sent.state` is what the caller typed; a verified envelope is what it can prove, and
|
|
52
|
-
* when a verifier is wired the envelope REPLACES the claim rather than enriching it —
|
|
53
|
-
* carrying both would leave every reader downstream choosing between them.
|
|
54
|
-
*/
|
|
37
|
+
/** State is ESTABLISHED here, or it is only claimed. */
|
|
55
38
|
let state: Record<string, unknown> = sent.state ?? {};
|
|
56
39
|
let caller: string | undefined;
|
|
57
40
|
if (options.verify && sent.identity) {
|
|
@@ -62,7 +45,7 @@ export async function handleRpc(runner: Transport, raw: unknown, options: Receiv
|
|
|
62
45
|
op,
|
|
63
46
|
params: sent.params ?? {},
|
|
64
47
|
query: sent.query ?? {},
|
|
65
|
-
|
|
48
|
+
input: sent.input,
|
|
66
49
|
}));
|
|
67
50
|
} catch (err) {
|
|
68
51
|
return refused(id, (err as Error)?.message ?? 'unverifiable identity', entity, op);
|
|
@@ -71,12 +54,12 @@ export async function handleRpc(runner: Transport, raw: unknown, options: Receiv
|
|
|
71
54
|
return refused(id, 'this receiver takes signed calls only', entity, op);
|
|
72
55
|
}
|
|
73
56
|
|
|
74
|
-
// Built field by field, never spread from `sent`: everything here is either
|
|
57
|
+
// Built field by field, never spread from `sent`: everything here is either validated
|
|
75
58
|
// above or carried deliberately, and `caller` is ours to write alone.
|
|
76
59
|
const invocation: InvocationContext = {
|
|
77
60
|
params: sent.params ?? {},
|
|
78
61
|
query: sent.query ?? {},
|
|
79
|
-
|
|
62
|
+
input: sent.input,
|
|
80
63
|
state,
|
|
81
64
|
trace: sent.trace,
|
|
82
65
|
...(caller ? { caller } : {}),
|