@fougere/transport-http 0.12.4-alpha.0 → 0.12.5-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.map +1 -1
- package/dist/client.js +28 -9
- package/dist/client.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +25 -0
- package/dist/server.js.map +1 -1
- package/package.json +5 -5
- package/src/client.ts +27 -9
- package/src/server.ts +27 -0
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,OAAO,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,OAAO,EAA2C,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,KAAK,iBAAiB,EAAE,KAAK,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAE1J,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAE1D,YAAY,EAAE,WAAW,EAAE,MAAM,0BAA0B,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,CA6B3E;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,CAoFlG"}
|
package/dist/client.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Sending half — frame the call, POST it, unframe the result. */
|
|
2
|
-
import { FougereError, ErrorCode } from '@fougere/core/contract';
|
|
2
|
+
import { FougereError, ErrorCode, MAX_BODY_BYTES } from '@fougere/core/contract';
|
|
3
3
|
/** Frame a call as a JSON-RPC request. */
|
|
4
4
|
export function frameCall(call, invocation, id) {
|
|
5
5
|
return { jsonrpc: '2.0', id, method: `${call.entity}.${call.op}`, params: invocation };
|
|
@@ -54,19 +54,23 @@ export function createHttpTransport(baseUrl, options = {}) {
|
|
|
54
54
|
// receiver choosing between a proof and a claim about the same thing.
|
|
55
55
|
// `caller` is dropped on every hop: it names who signed THIS call, so carrying the
|
|
56
56
|
// one this process was handed would make `shop → catalog → billing` read `shop`.
|
|
57
|
-
// It travels outside the envelope anyway, so a stale one would be unsigned too.
|
|
58
|
-
|
|
57
|
+
// It travels outside the envelope anyway, so a stale one would be unsigned too. `crossed`
|
|
58
|
+
// is the next receiver's to write, for the same reason.
|
|
59
|
+
const { caller: _established, crossed: _received, ...forwarded } = invocation;
|
|
59
60
|
const sent = options.sign
|
|
60
61
|
? { ...forwarded, state: {}, identity: await options.sign({ ...call, ...invocation }) }
|
|
61
62
|
: forwarded;
|
|
62
63
|
const request = frameCall(call, sent, nextId++);
|
|
64
|
+
const body = JSON.stringify(request);
|
|
65
|
+
if (new TextEncoder().encode(body).byteLength > MAX_BODY_BYTES)
|
|
66
|
+
throw tooLarge(call, baseUrl);
|
|
63
67
|
for (let attempt = 0;; attempt++) {
|
|
64
68
|
let res;
|
|
65
69
|
try {
|
|
66
70
|
res = await send(url, {
|
|
67
71
|
method: 'POST',
|
|
68
72
|
headers: { 'content-type': 'application/json' },
|
|
69
|
-
body
|
|
73
|
+
body,
|
|
70
74
|
signal: AbortSignal.timeout(timeoutMs),
|
|
71
75
|
});
|
|
72
76
|
}
|
|
@@ -74,7 +78,8 @@ export function createHttpTransport(baseUrl, options = {}) {
|
|
|
74
78
|
if (isTimeout(err)) {
|
|
75
79
|
throw new FougereError({
|
|
76
80
|
code: ErrorCode.GATEWAY_TIMEOUT,
|
|
77
|
-
message: `${call.entity}.${call.op} timed out after ${timeoutMs}ms
|
|
81
|
+
message: `${call.entity}.${call.op} timed out after ${timeoutMs}ms`,
|
|
82
|
+
cause: new Error(`${baseUrl} did not answer in ${timeoutMs}ms`),
|
|
78
83
|
entity: call.entity,
|
|
79
84
|
operation: call.op,
|
|
80
85
|
});
|
|
@@ -83,16 +88,19 @@ export function createHttpTransport(baseUrl, options = {}) {
|
|
|
83
88
|
continue;
|
|
84
89
|
throw new FougereError({
|
|
85
90
|
code: ErrorCode.SERVICE_UNAVAILABLE,
|
|
86
|
-
message: `${
|
|
91
|
+
message: `${call.entity}.${call.op}: the process serving it is unreachable`,
|
|
87
92
|
entity: call.entity,
|
|
88
93
|
operation: call.op,
|
|
89
|
-
cause: err,
|
|
94
|
+
cause: new Error(`${baseUrl} unreachable: ${err?.message ?? err}`, { cause: err }),
|
|
90
95
|
});
|
|
91
96
|
}
|
|
97
|
+
if (res.status === 413)
|
|
98
|
+
throw tooLarge(call, baseUrl);
|
|
92
99
|
if (!res.ok) {
|
|
93
100
|
throw new FougereError({
|
|
94
101
|
code: ErrorCode.BAD_GATEWAY,
|
|
95
|
-
message: `${
|
|
102
|
+
message: `${call.entity}.${call.op}: the receiver answered HTTP ${res.status} — not a Fougere receiver?`,
|
|
103
|
+
cause: new Error(`${baseUrl} answered HTTP ${res.status}`),
|
|
96
104
|
entity: call.entity,
|
|
97
105
|
operation: call.op,
|
|
98
106
|
});
|
|
@@ -104,7 +112,8 @@ export function createHttpTransport(baseUrl, options = {}) {
|
|
|
104
112
|
catch {
|
|
105
113
|
throw new FougereError({
|
|
106
114
|
code: ErrorCode.BAD_GATEWAY,
|
|
107
|
-
message: `${
|
|
115
|
+
message: `${call.entity}.${call.op}: the receiver answered non-JSON`,
|
|
116
|
+
cause: new Error(`${baseUrl} answered non-JSON`),
|
|
108
117
|
entity: call.entity,
|
|
109
118
|
operation: call.op,
|
|
110
119
|
});
|
|
@@ -113,6 +122,16 @@ export function createHttpTransport(baseUrl, options = {}) {
|
|
|
113
122
|
}
|
|
114
123
|
};
|
|
115
124
|
}
|
|
125
|
+
/** A frame over the receiver's limit — said before sending, or when a receiver answers 413. */
|
|
126
|
+
function tooLarge(call, baseUrl) {
|
|
127
|
+
return new FougereError({
|
|
128
|
+
code: ErrorCode.PAYLOAD_TOO_LARGE,
|
|
129
|
+
message: `${call.entity}.${call.op} carries more than ${MAX_BODY_BYTES} bytes, the limit a receiver takes.`,
|
|
130
|
+
cause: new Error(`refused for ${baseUrl}`),
|
|
131
|
+
entity: call.entity,
|
|
132
|
+
operation: call.op,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
116
135
|
function isTimeout(err) {
|
|
117
136
|
return err instanceof DOMException && (err.name === 'TimeoutError' || err.name === 'AbortError');
|
|
118
137
|
}
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,OAAO,EAAE,YAAY,EAAE,SAAS,EAA2E,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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;AAGzI,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE5D,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,
|
|
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;AAGzI,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE5D,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,CA4EnH"}
|
package/dist/server.js
CHANGED
|
@@ -19,6 +19,11 @@ export async function handleRpc(runner, raw, options = {}) {
|
|
|
19
19
|
const op = req.method.slice(dot + 1);
|
|
20
20
|
// Fresh objects — middlewares deposit into state, nothing may be shared.
|
|
21
21
|
const sent = (req.params ?? {});
|
|
22
|
+
const strangers = Object.entries(sent)
|
|
23
|
+
.filter(([key, value]) => value !== undefined && !SENT[key])
|
|
24
|
+
.map(([key]) => key);
|
|
25
|
+
if (strangers.length > 0)
|
|
26
|
+
return malformed(id, strangers, entity, op);
|
|
22
27
|
/** State is ESTABLISHED here, or it is only claimed. */
|
|
23
28
|
let state = sent.state ?? {};
|
|
24
29
|
let caller;
|
|
@@ -51,6 +56,7 @@ export async function handleRpc(runner, raw, options = {}) {
|
|
|
51
56
|
trace: sent.trace,
|
|
52
57
|
...(sent.runAt === undefined ? {} : { runAt: sent.runAt }),
|
|
53
58
|
...(caller ? { caller } : {}),
|
|
59
|
+
crossed: true,
|
|
54
60
|
};
|
|
55
61
|
try {
|
|
56
62
|
return { jsonrpc: '2.0', id, result: await runner({ entity, op }, invocation) };
|
|
@@ -69,6 +75,25 @@ export async function handleRpc(runner, raw, options = {}) {
|
|
|
69
75
|
return { jsonrpc: '2.0', id, error: { code: APP_ERROR, message: data.message, data } };
|
|
70
76
|
}
|
|
71
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* The members a caller may send. `caller` and `crossed` are written by the receiver and never claimed, and
|
|
80
|
+
* a key outside the list is refused by name: `body` used to be dropped, and the call then ran
|
|
81
|
+
* with no input and no validation.
|
|
82
|
+
*/
|
|
83
|
+
const SENT = {
|
|
84
|
+
params: true, query: true, input: true, state: true, trace: true, identity: true, runAt: true, caller: false,
|
|
85
|
+
crossed: false,
|
|
86
|
+
};
|
|
87
|
+
function malformed(id, strangers, entity, op) {
|
|
88
|
+
const admitted = Object.keys(SENT).filter((key) => SENT[key]);
|
|
89
|
+
const data = toPublicError(new FougereError({
|
|
90
|
+
code: ErrorCode.BAD_REQUEST,
|
|
91
|
+
message: `Unknown invocation member ${strangers.map((key) => `'${key}'`).join(', ')} — one of ${admitted.join(', ')}.`,
|
|
92
|
+
entity,
|
|
93
|
+
operation: op,
|
|
94
|
+
}));
|
|
95
|
+
return { jsonrpc: '2.0', id, error: { code: APP_ERROR, message: data.message, data } };
|
|
96
|
+
}
|
|
72
97
|
/** An admission refusal, framed like any other failure so a caller reads one vocabulary. */
|
|
73
98
|
function refused(id, why, entity, op) {
|
|
74
99
|
const data = toPublicError(new FougereError({ code: ErrorCode.UNAUTHORIZED, message: `Refused: ${why}`, entity, operation: op }));
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAA2D,MAAM,wBAAwB,CAAC;AACzI,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAcxE,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAAiB,EAAE,GAAY,EAAE,OAAO,GAAmB,EAAE;IAC3F,MAAM,GAAG,GAAG,GAAiC,CAAC;IAC9C,MAAM,EAAE,GAAG,OAAO,GAAG,EAAE,EAAE,KAAK,QAAQ,IAAI,OAAO,GAAG,EAAE,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAEtF,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,KAAK,KAAK,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;QACnF,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,8BAA8B,EAAE,EAAE,CAAC;IAC3G,CAAC;IAED,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,KAAK,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9C,OAAO;YACL,OAAO,EAAE,KAAK;YACd,EAAE;YACF,KAAK,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,mBAAmB,GAAG,CAAC,MAAM,0BAA0B,EAAE;SACnG,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACxC,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;IAErC,yEAAyE;IACzE,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAA+B,CAAC;
|
|
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.12.
|
|
3
|
+
"version": "0.12.5-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,13 +38,13 @@
|
|
|
38
38
|
"src"
|
|
39
39
|
],
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@fougere/core": "0.12.
|
|
41
|
+
"@fougere/core": "0.12.5-alpha.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"jiti": "^2.6.1",
|
|
45
|
-
"@fougere/
|
|
46
|
-
"@fougere/
|
|
47
|
-
"@fougere/compiler": "0.12.
|
|
45
|
+
"@fougere/schema": "0.12.5-alpha.0",
|
|
46
|
+
"@fougere/container": "0.12.5-alpha.0",
|
|
47
|
+
"@fougere/compiler": "0.12.5-alpha.0"
|
|
48
48
|
},
|
|
49
49
|
"publishConfig": {
|
|
50
50
|
"access": "public"
|
package/src/client.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Sending half — frame the call, POST it, unframe the result. */
|
|
2
|
-
import { FougereError, ErrorCode, type Transport, type FrondCall, type InvocationContext, type SignedCall } from '@fougere/core/contract';
|
|
2
|
+
import { FougereError, ErrorCode, MAX_BODY_BYTES, type Transport, type FrondCall, type InvocationContext, type SignedCall } from '@fougere/core/contract';
|
|
3
3
|
import type { RpcErrorShape } from './jsonrpc/RpcErrorShape.js';
|
|
4
4
|
import type { RpcRequest } from './jsonrpc/RpcRequest.js';
|
|
5
5
|
import type { RpcResponse } from './jsonrpc/RpcResponse.js';
|
|
@@ -76,12 +76,15 @@ export function createHttpTransport(baseUrl: string, options: HttpTransportOptio
|
|
|
76
76
|
// receiver choosing between a proof and a claim about the same thing.
|
|
77
77
|
// `caller` is dropped on every hop: it names who signed THIS call, so carrying the
|
|
78
78
|
// one this process was handed would make `shop → catalog → billing` read `shop`.
|
|
79
|
-
// It travels outside the envelope anyway, so a stale one would be unsigned too.
|
|
80
|
-
|
|
79
|
+
// It travels outside the envelope anyway, so a stale one would be unsigned too. `crossed`
|
|
80
|
+
// is the next receiver's to write, for the same reason.
|
|
81
|
+
const { caller: _established, crossed: _received, ...forwarded } = invocation;
|
|
81
82
|
const sent = options.sign
|
|
82
83
|
? { ...forwarded, state: {}, identity: await options.sign({ ...call, ...invocation }) }
|
|
83
84
|
: forwarded;
|
|
84
85
|
const request = frameCall(call, sent, nextId++);
|
|
86
|
+
const body = JSON.stringify(request);
|
|
87
|
+
if (new TextEncoder().encode(body).byteLength > MAX_BODY_BYTES) throw tooLarge(call, baseUrl);
|
|
85
88
|
|
|
86
89
|
for (let attempt = 0; ; attempt++) {
|
|
87
90
|
let res: Response;
|
|
@@ -89,14 +92,15 @@ export function createHttpTransport(baseUrl: string, options: HttpTransportOptio
|
|
|
89
92
|
res = await send(url, {
|
|
90
93
|
method: 'POST',
|
|
91
94
|
headers: { 'content-type': 'application/json' },
|
|
92
|
-
body
|
|
95
|
+
body,
|
|
93
96
|
signal: AbortSignal.timeout(timeoutMs),
|
|
94
97
|
});
|
|
95
98
|
} catch (err) {
|
|
96
99
|
if (isTimeout(err)) {
|
|
97
100
|
throw new FougereError({
|
|
98
101
|
code: ErrorCode.GATEWAY_TIMEOUT,
|
|
99
|
-
message: `${call.entity}.${call.op} timed out after ${timeoutMs}ms
|
|
102
|
+
message: `${call.entity}.${call.op} timed out after ${timeoutMs}ms`,
|
|
103
|
+
cause: new Error(`${baseUrl} did not answer in ${timeoutMs}ms`),
|
|
100
104
|
entity: call.entity,
|
|
101
105
|
operation: call.op,
|
|
102
106
|
});
|
|
@@ -104,17 +108,19 @@ export function createHttpTransport(baseUrl: string, options: HttpTransportOptio
|
|
|
104
108
|
if (attempt < retries && isConnectionFailure(err)) continue;
|
|
105
109
|
throw new FougereError({
|
|
106
110
|
code: ErrorCode.SERVICE_UNAVAILABLE,
|
|
107
|
-
message: `${
|
|
111
|
+
message: `${call.entity}.${call.op}: the process serving it is unreachable`,
|
|
108
112
|
entity: call.entity,
|
|
109
113
|
operation: call.op,
|
|
110
|
-
cause: err,
|
|
114
|
+
cause: new Error(`${baseUrl} unreachable: ${(err as Error)?.message ?? err}`, { cause: err }),
|
|
111
115
|
});
|
|
112
116
|
}
|
|
113
117
|
|
|
118
|
+
if (res.status === 413) throw tooLarge(call, baseUrl);
|
|
114
119
|
if (!res.ok) {
|
|
115
120
|
throw new FougereError({
|
|
116
121
|
code: ErrorCode.BAD_GATEWAY,
|
|
117
|
-
message: `${
|
|
122
|
+
message: `${call.entity}.${call.op}: the receiver answered HTTP ${res.status} — not a Fougere receiver?`,
|
|
123
|
+
cause: new Error(`${baseUrl} answered HTTP ${res.status}`),
|
|
118
124
|
entity: call.entity,
|
|
119
125
|
operation: call.op,
|
|
120
126
|
});
|
|
@@ -126,7 +132,8 @@ export function createHttpTransport(baseUrl: string, options: HttpTransportOptio
|
|
|
126
132
|
} catch {
|
|
127
133
|
throw new FougereError({
|
|
128
134
|
code: ErrorCode.BAD_GATEWAY,
|
|
129
|
-
message: `${
|
|
135
|
+
message: `${call.entity}.${call.op}: the receiver answered non-JSON`,
|
|
136
|
+
cause: new Error(`${baseUrl} answered non-JSON`),
|
|
130
137
|
entity: call.entity,
|
|
131
138
|
operation: call.op,
|
|
132
139
|
});
|
|
@@ -137,6 +144,17 @@ export function createHttpTransport(baseUrl: string, options: HttpTransportOptio
|
|
|
137
144
|
};
|
|
138
145
|
}
|
|
139
146
|
|
|
147
|
+
/** A frame over the receiver's limit — said before sending, or when a receiver answers 413. */
|
|
148
|
+
function tooLarge(call: FrondCall, baseUrl: string): FougereError {
|
|
149
|
+
return new FougereError({
|
|
150
|
+
code: ErrorCode.PAYLOAD_TOO_LARGE,
|
|
151
|
+
message: `${call.entity}.${call.op} carries more than ${MAX_BODY_BYTES} bytes, the limit a receiver takes.`,
|
|
152
|
+
cause: new Error(`refused for ${baseUrl}`),
|
|
153
|
+
entity: call.entity,
|
|
154
|
+
operation: call.op,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
140
158
|
function isTimeout(err: unknown): boolean {
|
|
141
159
|
return err instanceof DOMException && (err.name === 'TimeoutError' || err.name === 'AbortError');
|
|
142
160
|
}
|
package/src/server.ts
CHANGED
|
@@ -35,6 +35,10 @@ export async function handleRpc(runner: Transport, raw: unknown, options: Receiv
|
|
|
35
35
|
|
|
36
36
|
// Fresh objects — middlewares deposit into state, nothing may be shared.
|
|
37
37
|
const sent = (req.params ?? {}) as Partial<InvocationContext>;
|
|
38
|
+
const strangers = Object.entries(sent)
|
|
39
|
+
.filter(([key, value]) => value !== undefined && !SENT[key as keyof InvocationContext])
|
|
40
|
+
.map(([key]) => key);
|
|
41
|
+
if (strangers.length > 0) return malformed(id, strangers, entity, op);
|
|
38
42
|
|
|
39
43
|
/** State is ESTABLISHED here, or it is only claimed. */
|
|
40
44
|
let state: Record<string, unknown> = sent.state ?? {};
|
|
@@ -67,6 +71,7 @@ export async function handleRpc(runner: Transport, raw: unknown, options: Receiv
|
|
|
67
71
|
trace: sent.trace,
|
|
68
72
|
...(sent.runAt === undefined ? {} : { runAt: sent.runAt }),
|
|
69
73
|
...(caller ? { caller } : {}),
|
|
74
|
+
crossed: true,
|
|
70
75
|
};
|
|
71
76
|
|
|
72
77
|
try {
|
|
@@ -87,6 +92,28 @@ export async function handleRpc(runner: Transport, raw: unknown, options: Receiv
|
|
|
87
92
|
}
|
|
88
93
|
}
|
|
89
94
|
|
|
95
|
+
/**
|
|
96
|
+
* The members a caller may send. `caller` and `crossed` are written by the receiver and never claimed, and
|
|
97
|
+
* a key outside the list is refused by name: `body` used to be dropped, and the call then ran
|
|
98
|
+
* with no input and no validation.
|
|
99
|
+
*/
|
|
100
|
+
const SENT = {
|
|
101
|
+
params: true, query: true, input: true, state: true, trace: true, identity: true, runAt: true, caller: false,
|
|
102
|
+
crossed: false,
|
|
103
|
+
} satisfies Record<keyof InvocationContext, boolean>;
|
|
104
|
+
|
|
105
|
+
function malformed(id: string | number, strangers: string[], entity: string, op: string): RpcResponse {
|
|
106
|
+
const admitted = Object.keys(SENT).filter((key) => SENT[key as keyof InvocationContext]);
|
|
107
|
+
const data = toPublicError(new FougereError({
|
|
108
|
+
code: ErrorCode.BAD_REQUEST,
|
|
109
|
+
message: `Unknown invocation member ${strangers.map((key) => `'${key}'`).join(', ')} — one of ${admitted.join(', ')}.`,
|
|
110
|
+
entity,
|
|
111
|
+
operation: op,
|
|
112
|
+
}));
|
|
113
|
+
|
|
114
|
+
return { jsonrpc: '2.0', id, error: { code: APP_ERROR, message: data.message, data } };
|
|
115
|
+
}
|
|
116
|
+
|
|
90
117
|
/** An admission refusal, framed like any other failure so a caller reads one vocabulary. */
|
|
91
118
|
function refused(id: string | number, why: string, entity: string, op: string): RpcResponse {
|
|
92
119
|
const data = toPublicError(
|