@travetto/web-rpc 7.1.3 → 8.0.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/package.json +4 -4
- package/src/controller.ts +4 -4
- package/support/client/rpc-node.ts +3 -5
- package/support/client/rpc.ts +30 -17
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/web-rpc",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.0.0-alpha.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "RPC support for a Web Application",
|
|
6
6
|
"keywords": [
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
"directory": "module/web-rpc"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@travetto/config": "^
|
|
31
|
-
"@travetto/schema": "^
|
|
32
|
-
"@travetto/web": "^
|
|
30
|
+
"@travetto/config": "^8.0.0-alpha.0",
|
|
31
|
+
"@travetto/schema": "^8.0.0-alpha.0",
|
|
32
|
+
"@travetto/web": "^8.0.0-alpha.0"
|
|
33
33
|
},
|
|
34
34
|
"travetto": {
|
|
35
35
|
"displayName": "Web RPC Support"
|
package/src/controller.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Inject } from '@travetto/di';
|
|
2
|
-
import { type Any,
|
|
2
|
+
import { type Any, RuntimeError, JSONUtil } from '@travetto/runtime';
|
|
3
3
|
import { IsPrivate } from '@travetto/schema';
|
|
4
4
|
import {
|
|
5
5
|
HeaderParam, Controller, ExcludeInterceptors, ControllerRegistryIndex,
|
|
@@ -36,7 +36,7 @@ export class WebRpcController {
|
|
|
36
36
|
const endpoint = ControllerRegistryIndex.getEndpointConfigById(target);
|
|
37
37
|
|
|
38
38
|
if (!endpoint || !endpoint.filter) {
|
|
39
|
-
throw new
|
|
39
|
+
throw new RuntimeError('Unknown endpoint', { category: 'notfound' });
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
const { request } = this.ctx;
|
|
@@ -45,7 +45,7 @@ export class WebRpcController {
|
|
|
45
45
|
|
|
46
46
|
// Allow request to read inputs from header
|
|
47
47
|
if (paramInput) {
|
|
48
|
-
params = JSONUtil.
|
|
48
|
+
params = JSONUtil.fromBase64(paramInput);
|
|
49
49
|
} else if (Array.isArray(body)) { // Params passed via body
|
|
50
50
|
params = body;
|
|
51
51
|
|
|
@@ -54,7 +54,7 @@ export class WebRpcController {
|
|
|
54
54
|
request.body = params[bodyParamIdx];
|
|
55
55
|
}
|
|
56
56
|
} else if (body) {
|
|
57
|
-
throw new
|
|
57
|
+
throw new RuntimeError('Invalid parameters, must be an array', { category: 'data' });
|
|
58
58
|
} else {
|
|
59
59
|
params = [];
|
|
60
60
|
}
|
|
@@ -2,11 +2,9 @@ import { consumeError } from './rpc.ts';
|
|
|
2
2
|
|
|
3
3
|
export async function toNodeError(payload: unknown): Promise<Error> {
|
|
4
4
|
try {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
if (result) {
|
|
9
|
-
return result;
|
|
5
|
+
const { JSONUtil } = await import('@travetto/runtime');
|
|
6
|
+
if (JSONUtil.isJSONError(payload)) {
|
|
7
|
+
return JSONUtil.jsonErrorToError(payload);
|
|
10
8
|
}
|
|
11
9
|
} catch { }
|
|
12
10
|
return consumeError(payload);
|
package/support/client/rpc.ts
CHANGED
|
@@ -17,6 +17,22 @@ const extendHeaders = (base: RequestInit['headers'], toAdd: Record<string, strin
|
|
|
17
17
|
return headers;
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
+
const jsonToString = (input: unknown): string =>
|
|
21
|
+
JSON.stringify(input, (key, value): unknown => typeof value === 'bigint' ? `${value.toString()}n` : value);
|
|
22
|
+
|
|
23
|
+
const stringToJson = <T = unknown>(input: string): T =>
|
|
24
|
+
JSON.parse(input, (key, value): unknown => {
|
|
25
|
+
if (typeof value === 'string') {
|
|
26
|
+
if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[.]\d{3}Z$/.test(value)) {
|
|
27
|
+
return new Date(value);
|
|
28
|
+
} else if (/^-?d+n$/.test(value)) {
|
|
29
|
+
return BigInt(value.slice(0, -1));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return value;
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
|
|
20
36
|
export type PreRequestHandler = (item: RequestInit) => Promise<RequestInit | undefined | void>;
|
|
21
37
|
export type PostResponseHandler = (item: Response) => Promise<Response | undefined | void>;
|
|
22
38
|
|
|
@@ -57,14 +73,14 @@ function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
|
57
73
|
&& Object.prototype.toString.call(value) === '[object Object]'; // separate build-in like Math
|
|
58
74
|
}
|
|
59
75
|
|
|
60
|
-
function registerTimeout<T
|
|
76
|
+
function registerTimeout<T>(
|
|
61
77
|
controller: AbortController,
|
|
62
78
|
timeout: number,
|
|
63
79
|
start: (fn: (...args: unknown[]) => unknown, delay: number) => T,
|
|
64
80
|
stop: (value: T) => void
|
|
65
81
|
): void {
|
|
66
82
|
const timer = start(() => controller.abort(), timeout);
|
|
67
|
-
if (
|
|
83
|
+
if (timer && typeof timer === 'object' && 'unref' in timer && typeof timer.unref === 'function') {
|
|
68
84
|
timer.unref();
|
|
69
85
|
}
|
|
70
86
|
controller.signal.onabort = (): void => { timer && stop(timer); };
|
|
@@ -85,14 +101,14 @@ export function getBody(inputs: unknown[], isBodyRequest: boolean): { body: Form
|
|
|
85
101
|
return {
|
|
86
102
|
body: undefined,
|
|
87
103
|
headers: {
|
|
88
|
-
'X-TRV-RPC-INPUTS': btoa(encodeURIComponent(
|
|
104
|
+
'X-TRV-RPC-INPUTS': btoa(encodeURIComponent(jsonToString(inputs)))
|
|
89
105
|
}
|
|
90
106
|
};
|
|
91
107
|
}
|
|
92
108
|
// If we do not have a blob, simple output
|
|
93
109
|
if (!inputs.some(isBlobLike)) {
|
|
94
110
|
return {
|
|
95
|
-
body:
|
|
111
|
+
body: jsonToString(inputs),
|
|
96
112
|
headers: {
|
|
97
113
|
'Content-Type': 'application/json'
|
|
98
114
|
}
|
|
@@ -115,27 +131,24 @@ export function getBody(inputs: unknown[], isBodyRequest: boolean): { body: Form
|
|
|
115
131
|
return {
|
|
116
132
|
body: form,
|
|
117
133
|
headers: {
|
|
118
|
-
'X-TRV-RPC-INPUTS': btoa(encodeURIComponent(
|
|
134
|
+
'X-TRV-RPC-INPUTS': btoa(encodeURIComponent(jsonToString(plainInputs)))
|
|
119
135
|
}
|
|
120
136
|
};
|
|
121
137
|
}
|
|
122
138
|
|
|
123
|
-
export function consumeJSON<T>(
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
} else if (text === null || text === undefined || text === '') {
|
|
139
|
+
export function consumeJSON<T>(input: string | unknown): T {
|
|
140
|
+
let text: string;
|
|
141
|
+
if (input === null || input === undefined || input === '') {
|
|
127
142
|
return undefined!;
|
|
143
|
+
} else if (typeof input !== 'string') {
|
|
144
|
+
text = jsonToString(input);
|
|
145
|
+
} else {
|
|
146
|
+
text = input;
|
|
128
147
|
}
|
|
129
148
|
try {
|
|
130
|
-
return
|
|
131
|
-
if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[.]\d{3}Z/.test(value)) {
|
|
132
|
-
return new Date(value);
|
|
133
|
-
} else {
|
|
134
|
-
return value;
|
|
135
|
-
}
|
|
136
|
-
});
|
|
149
|
+
return stringToJson<T>(text);
|
|
137
150
|
} catch (error) {
|
|
138
|
-
throw new Error(`Unable to parse response: ${text}
|
|
151
|
+
throw new Error(`Unable to parse response: ${text}`, { cause: error });
|
|
139
152
|
}
|
|
140
153
|
}
|
|
141
154
|
|