awesomerpc 0.1.0 → 0.2.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/LICENSE.txt +19 -0
- package/README.md +69 -1
- package/lib/index.d.ts +3 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -3
- package/lib/index.js.map +1 -1
- package/lib/integration.test.js +25 -22
- package/lib/integration.test.js.map +1 -1
- package/lib/logger.d.ts +16 -0
- package/lib/logger.d.ts.map +1 -0
- package/lib/logger.js +2 -0
- package/lib/logger.js.map +1 -0
- package/lib/proxy.d.ts +2 -11
- package/lib/proxy.d.ts.map +1 -1
- package/lib/proxy.js +4 -3
- package/lib/proxy.js.map +1 -1
- package/lib/rpc.d.ts +9 -7
- package/lib/rpc.d.ts.map +1 -1
- package/lib/rpc.js +32 -30
- package/lib/rpc.js.map +1 -1
- package/lib/serve/hono.d.ts +3 -2
- package/lib/serve/hono.d.ts.map +1 -1
- package/lib/serve/hono.js +5 -7
- package/lib/serve/hono.js.map +1 -1
- package/lib/transport.d.ts +4 -2
- package/lib/transport.d.ts.map +1 -1
- package/lib/transport.js +3 -2
- package/lib/transport.js.map +1 -1
- package/lib/types.d.ts +71 -46
- package/lib/types.d.ts.map +1 -1
- package/lib/types.js +29 -23
- package/lib/types.js.map +1 -1
- package/lib/util.d.ts +1 -0
- package/lib/util.d.ts.map +1 -1
- package/lib/util.js +6 -0
- package/lib/util.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +3 -3
- package/src/integration.test.ts +27 -24
- package/src/logger.ts +17 -0
- package/src/proxy.ts +6 -14
- package/src/rpc.ts +41 -34
- package/src/serve/hono.ts +20 -14
- package/src/transport.ts +5 -3
- package/src/types.ts +98 -56
- package/src/util.ts +7 -0
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2026 Sam Vervaeck
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
5
|
+
the Software without restriction, including without limitation the rights to
|
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
7
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
|
8
|
+
so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ First, you need to define a _contract_ for your API. A contract is kind of like
|
|
|
10
10
|
a TypeScript interface: it defines what methods (and events) are allowed.
|
|
11
11
|
|
|
12
12
|
```ts
|
|
13
|
+
import t from "reflect-types";
|
|
13
14
|
import { contract } from "awesomerpc";
|
|
14
15
|
|
|
15
16
|
// A client API that connects to the server in the browser via web sockets
|
|
@@ -30,7 +31,7 @@ Next, you need to implement this contract, like so:
|
|
|
30
31
|
```ts
|
|
31
32
|
import { implement } from "awesomerpc";
|
|
32
33
|
|
|
33
|
-
const petStoreClientImpl = implement(
|
|
34
|
+
const petStoreClientImpl = implement(petStoreClientContract, petStoreServerContract)
|
|
34
35
|
.method('refresh', (_ctx) => {
|
|
35
36
|
window.location.reload();
|
|
36
37
|
})
|
|
@@ -57,3 +58,70 @@ console.log(`Available products: ${await rpc.callMethod('getProducts', [])}`);
|
|
|
57
58
|
|
|
58
59
|
For more information, read the full example in the `example/` directory.
|
|
59
60
|
|
|
61
|
+
## API
|
|
62
|
+
|
|
63
|
+
### Transports
|
|
64
|
+
|
|
65
|
+
#### `new WebSocketTransport(url)`
|
|
66
|
+
|
|
67
|
+
Create a new transport that uses WebSocket to send and receive messages.
|
|
68
|
+
|
|
69
|
+
### `Transport.open()`
|
|
70
|
+
|
|
71
|
+
Connect to whatever was specified as the destination address during the
|
|
72
|
+
construction of the transport.
|
|
73
|
+
|
|
74
|
+
This method returns a promise object that may be awaited in order to ensure the
|
|
75
|
+
connection is ready for use.
|
|
76
|
+
|
|
77
|
+
### Top-level Functions
|
|
78
|
+
|
|
79
|
+
#### `connect(impl, state, logger?)`
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import pino from "pino";
|
|
83
|
+
|
|
84
|
+
import { connect } from "awesomerpc";
|
|
85
|
+
|
|
86
|
+
connect(petStoreClientImpl, {});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## FAQ
|
|
90
|
+
|
|
91
|
+
### How do I enable logging?
|
|
92
|
+
|
|
93
|
+
To enable logging, you need to create and object that satisfies the `Logger`
|
|
94
|
+
interface. [Pino][pino] is one such logger that is compatible with this
|
|
95
|
+
interface. You can install it using:
|
|
96
|
+
|
|
97
|
+
```sh
|
|
98
|
+
$ npm install pino
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Next, you need to define the logger object. In this example, the log level is
|
|
102
|
+
read from the environment variable `MYAPP_LOG_LEVEL` and defaults to `info`.
|
|
103
|
+
|
|
104
|
+
**src/logging.ts**
|
|
105
|
+
```ts
|
|
106
|
+
import pino from "pino";
|
|
107
|
+
|
|
108
|
+
export const logger = pino({
|
|
109
|
+
level: process.env.MYAPP_LOG_LEVEL || 'info',
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Pass this object in while creating a connection, like so:
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
import { connect } from "awesomerpc";
|
|
117
|
+
|
|
118
|
+
import { logger } from "./logging";
|
|
119
|
+
|
|
120
|
+
connect(petStoreClientImpl, {}, logger);
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
If no logger is specified, AwesomeRPC will simply not log anything.
|
|
124
|
+
|
|
125
|
+
## License
|
|
126
|
+
|
|
127
|
+
This project is licensed under the MIT license. See `LICENSE.txt` for more information.
|
package/lib/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { type Transport, WebSocketTransport, DummyTransport, RawTransport, createDuplex, } from "./transport.js";
|
|
2
|
-
export { implement, contract } from "./types.js";
|
|
3
|
-
export { RPC, type AnyMethods, type AnyEvents } from "./rpc.js";
|
|
1
|
+
export { type Transport, WebSocketTransport, DummyTransport, RawTransport, createDuplex, TransportError } from "./transport.js";
|
|
2
|
+
export { implement, contract, emptyContract, anyContract } from "./types.js";
|
|
3
|
+
export { RPC, type AnyMethods, type AnyEvents, connect } from "./rpc.js";
|
|
4
4
|
export { createProxy } from "./proxy.js";
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,SAAS,EAAE,kBAAkB,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,SAAS,EAAE,kBAAkB,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAC/H,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAC5E,OAAO,EAAE,GAAG,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AACxE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA"}
|
package/lib/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { WebSocketTransport, DummyTransport, RawTransport, createDuplex, } from "./transport.js";
|
|
2
|
-
export { implement, contract } from "./types.js";
|
|
3
|
-
export { RPC } from "./rpc.js";
|
|
1
|
+
export { WebSocketTransport, DummyTransport, RawTransport, createDuplex, TransportError } from "./transport.js";
|
|
2
|
+
export { implement, contract, emptyContract, anyContract } from "./types.js";
|
|
3
|
+
export { RPC, connect } from "./rpc.js";
|
|
4
4
|
export { createProxy } from "./proxy.js";
|
|
5
5
|
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,kBAAkB,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,kBAAkB,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAC/H,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAC5E,OAAO,EAAE,GAAG,EAAmC,OAAO,EAAE,MAAM,UAAU,CAAA;AACxE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA"}
|
package/lib/integration.test.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { test, expect } from "bun:test";
|
|
2
|
-
import
|
|
2
|
+
import t from "reflect-types";
|
|
3
3
|
import { contract, implement } from "./types.js";
|
|
4
4
|
import { createDuplex } from "./transport.js";
|
|
5
5
|
import { connect } from "./rpc.js";
|
|
6
6
|
import { FailedValidationError } from "./error.js";
|
|
7
|
-
const
|
|
7
|
+
const leftContract = contract({
|
|
8
8
|
methods: {
|
|
9
9
|
getState: t.callable([], t.number()),
|
|
10
10
|
setState: t.callable([t.number()], t.undefined()),
|
|
@@ -14,46 +14,49 @@ const local = contract({
|
|
|
14
14
|
someevent: t.string(),
|
|
15
15
|
}
|
|
16
16
|
});
|
|
17
|
-
const
|
|
17
|
+
const rightContract = contract({
|
|
18
18
|
methods: {
|
|
19
19
|
getState: t.callable([], t.number()),
|
|
20
20
|
setState: t.callable([t.number()], t.undefined()),
|
|
21
21
|
getLength: t.callable([t.string()], t.number()),
|
|
22
22
|
}
|
|
23
23
|
});
|
|
24
|
-
const leftImpl = implement(
|
|
24
|
+
const leftImpl = implement(leftContract, rightContract)
|
|
25
25
|
.state()
|
|
26
|
+
// .methods(stateHandlers())
|
|
26
27
|
.methods({
|
|
27
|
-
getState(
|
|
28
|
-
return
|
|
28
|
+
getState({ state: { foo } }) {
|
|
29
|
+
return foo;
|
|
29
30
|
},
|
|
30
|
-
setState(
|
|
31
|
-
|
|
31
|
+
setState({ state, args: [newFoo] }) {
|
|
32
|
+
state.foo = newFoo;
|
|
32
33
|
},
|
|
34
|
+
})
|
|
35
|
+
.methods({
|
|
33
36
|
// @ts-expect-error Wrong function return type on purpose
|
|
34
37
|
returnInvalid() {
|
|
35
38
|
return "a string";
|
|
36
39
|
}
|
|
37
40
|
})
|
|
38
41
|
.finish();
|
|
39
|
-
const rightImpl = implement(
|
|
42
|
+
const rightImpl = implement(rightContract, leftContract)
|
|
40
43
|
.state()
|
|
41
44
|
.methods({
|
|
42
|
-
getState(
|
|
43
|
-
return
|
|
45
|
+
getState({ state }) {
|
|
46
|
+
return state.foo;
|
|
44
47
|
},
|
|
45
|
-
setState(
|
|
46
|
-
|
|
48
|
+
setState({ state, args: [newState] }) {
|
|
49
|
+
state.foo = newState;
|
|
47
50
|
},
|
|
48
|
-
getLength(
|
|
51
|
+
getLength({ args: [s] }) {
|
|
49
52
|
return s.length;
|
|
50
53
|
}
|
|
51
54
|
})
|
|
52
55
|
.finish();
|
|
53
56
|
test('can call methods on both sides', async () => {
|
|
54
57
|
const [leftTransport, rightTransport] = createDuplex();
|
|
55
|
-
const left = connect(leftImpl,
|
|
56
|
-
const right = connect(rightImpl,
|
|
58
|
+
const left = connect(leftImpl, leftTransport, { foo: 42 });
|
|
59
|
+
const right = connect(rightImpl, rightTransport, { foo: 33 });
|
|
57
60
|
expect(await left.callMethod('getLength', ["foobar"])).toStrictEqual(6);
|
|
58
61
|
expect(await left.callMethod('getState', [])).toStrictEqual(33);
|
|
59
62
|
expect(await right.callMethod('getState', [])).toStrictEqual(42);
|
|
@@ -62,8 +65,8 @@ test('can call methods on both sides', async () => {
|
|
|
62
65
|
});
|
|
63
66
|
test('throws an error on invalid param count', async () => {
|
|
64
67
|
const [leftTransport, rightTransport] = createDuplex();
|
|
65
|
-
const left = connect(leftImpl,
|
|
66
|
-
const right = connect(rightImpl,
|
|
68
|
+
const left = connect(leftImpl, leftTransport, { foo: 42 });
|
|
69
|
+
const right = connect(rightImpl, rightTransport, { foo: 33 });
|
|
67
70
|
expect(left.callMethod('getLength',
|
|
68
71
|
// @ts-expect-error Deliberatly added a param
|
|
69
72
|
["foo", "bar"])).rejects.toBeInstanceOf(Error); // TODO be more specific
|
|
@@ -73,14 +76,14 @@ test('throws an error on invalid param count', async () => {
|
|
|
73
76
|
});
|
|
74
77
|
test('throws an error on invalid return', async () => {
|
|
75
78
|
const [leftTransport, rightTransport] = createDuplex();
|
|
76
|
-
const left = connect(leftImpl,
|
|
77
|
-
const right = connect(rightImpl,
|
|
79
|
+
const left = connect(leftImpl, leftTransport, { foo: 42 });
|
|
80
|
+
const right = connect(rightImpl, rightTransport, { foo: 33 });
|
|
78
81
|
expect(right.callMethod('returnInvalid', [])).rejects.toBeInstanceOf(FailedValidationError);
|
|
79
82
|
});
|
|
80
83
|
test('can send events', done => {
|
|
81
84
|
const [leftTransport, rightTransport] = createDuplex();
|
|
82
|
-
const left = connect(leftImpl,
|
|
83
|
-
const right = connect(rightImpl,
|
|
85
|
+
const left = connect(leftImpl, leftTransport, { foo: 42 });
|
|
86
|
+
const right = connect(rightImpl, rightTransport, { foo: 33 });
|
|
84
87
|
left.getEvent('someevent').subscribe(msg => {
|
|
85
88
|
expect(msg).toStrictEqual('foo');
|
|
86
89
|
done();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration.test.js","sourceRoot":"","sources":["../src/integration.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACvC,OAAO,
|
|
1
|
+
{"version":3,"file":"integration.test.js","sourceRoot":"","sources":["../src/integration.test.ts"],"names":[],"mappings":"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"}
|
package/lib/logger.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { JSONObject } from "./util.js";
|
|
2
|
+
type LogFn = (obj: JSONObject) => void;
|
|
3
|
+
/**
|
|
4
|
+
* Generic logging interface that is compatible with Pino.
|
|
5
|
+
*/
|
|
6
|
+
export interface Logger {
|
|
7
|
+
fatal: LogFn;
|
|
8
|
+
error: LogFn;
|
|
9
|
+
warn: LogFn;
|
|
10
|
+
info: LogFn;
|
|
11
|
+
debug: LogFn;
|
|
12
|
+
trace: LogFn;
|
|
13
|
+
silent: LogFn;
|
|
14
|
+
}
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE5C,KAAK,KAAK,GAAG,CAAC,GAAG,EAAE,UAAU,KAAK,IAAI,CAAC;AAEvC;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,KAAK,CAAC;IACZ,IAAI,EAAE,KAAK,CAAC;IACZ,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,EAAE,KAAK,CAAC;CACf"}
|
package/lib/logger.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":""}
|
package/lib/proxy.d.ts
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
import type { Subject } from "rxjs";
|
|
2
1
|
import type { RPC } from "./rpc.js";
|
|
3
|
-
import type {
|
|
4
|
-
|
|
5
|
-
type Promisify<T extends CallableType> = (...args: T['paramTypes']) => Promise<Awaited<T['returnType']>>;
|
|
6
|
-
type Obj<M extends Record<string, MethodSpec>, E extends Record<string, EventSpec>> = {
|
|
7
|
-
[K in keyof M]: Promisify<M[K]>;
|
|
8
|
-
} & {
|
|
9
|
-
[K in keyof E]: Subject<E[K]['ty']>;
|
|
10
|
-
};
|
|
11
|
-
export declare function createProxy<L extends Impl, R extends Spec>(rpc: RPC<L, R>): Obj<R["methods"], L["spec"]["events"]>;
|
|
12
|
-
export {};
|
|
2
|
+
import type { ClientObj, Contract } from "./types.js";
|
|
3
|
+
export declare function createProxy<L extends Contract, R extends Contract, S extends object>(rpc: RPC<L, R, S>): ClientObj<L, R>;
|
|
13
4
|
//# sourceMappingURL=proxy.d.ts.map
|
package/lib/proxy.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtD,wBAAgB,WAAW,CAAC,CAAC,SAAS,QAAQ,EAAE,CAAC,SAAS,QAAQ,EAAE,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,mBAetG"}
|
package/lib/proxy.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
export function createProxy(rpc) {
|
|
2
|
-
|
|
2
|
+
// FIXME This `any` cast is a hack
|
|
3
|
+
return new Proxy(rpc, {
|
|
3
4
|
get(target, p, receiver) {
|
|
4
5
|
if (typeof (p) === 'string') {
|
|
5
|
-
if (rpc.remote.hasMethod(p)) {
|
|
6
|
+
if (rpc.impl.remote.hasMethod(p)) {
|
|
6
7
|
return (...args) => rpc.callMethod(p, args);
|
|
7
8
|
}
|
|
8
|
-
if (rpc.local.
|
|
9
|
+
if (rpc.impl.local.hasEvent(p)) {
|
|
9
10
|
return rpc.getEvent(p);
|
|
10
11
|
}
|
|
11
12
|
}
|
package/lib/proxy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AAIA,MAAM,UAAU,WAAW,CAA2D,GAAiB;IACrG,kCAAkC;IAClC,OAAO,IAAI,KAAK,CAAkB,GAAU,EAAE;QAC5C,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,QAAQ;YACrB,IAAI,OAAM,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;gBAC3B,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;oBACjC,OAAO,CAAC,GAAG,IAAW,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,EAAE,IAAW,CAAC,CAAC;gBAC5D,CAAC;gBACD,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC/B,OAAO,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;gBACzB,CAAC;YACH,CAAC;YACD,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC;QAC1C,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
|
package/lib/rpc.d.ts
CHANGED
|
@@ -2,7 +2,8 @@ import { BehaviorSubject, Subject } from "rxjs";
|
|
|
2
2
|
import { type Infer } from "reflect-types";
|
|
3
3
|
import { type JSONValue } from "./util.js";
|
|
4
4
|
import type { Transport } from "./transport.js";
|
|
5
|
-
import type {
|
|
5
|
+
import type { Contract, Impl, InferTuple } from "./types.js";
|
|
6
|
+
import type { Logger } from "./logger.js";
|
|
6
7
|
export declare class Resource<T> {
|
|
7
8
|
value: T;
|
|
8
9
|
close: () => void;
|
|
@@ -14,11 +15,11 @@ export type MethodFn = (...args: any[]) => any;
|
|
|
14
15
|
export type AnyEvents = Record<string, Subject<any>>;
|
|
15
16
|
export type AnyMethods = Record<string, MethodFn>;
|
|
16
17
|
export type RPCValue = JSONValue | AsyncIterable<any, any, any> | Resource<Subject<any>> | Resource<BehaviorSubject<any>>;
|
|
17
|
-
export declare class RPC<L extends
|
|
18
|
+
export declare class RPC<L extends Contract, R extends Contract, S extends object> {
|
|
18
19
|
private transport;
|
|
19
|
-
|
|
20
|
-
remote: R;
|
|
20
|
+
impl: Impl<L, R, S>;
|
|
21
21
|
private state;
|
|
22
|
+
private logger?;
|
|
22
23
|
private nextMessageId;
|
|
23
24
|
private nextStreamId;
|
|
24
25
|
private nextSubjectId;
|
|
@@ -28,8 +29,9 @@ export declare class RPC<L extends Impl, R extends Spec> {
|
|
|
28
29
|
private asyncGenerators;
|
|
29
30
|
private events;
|
|
30
31
|
private readerSubscription;
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
private client;
|
|
33
|
+
constructor(transport: Transport, impl: Impl<L, R, S>, state: S, logger?: Logger | undefined);
|
|
34
|
+
getEvent<K extends keyof L['events'] & string>(name: K): Subject<Infer<L['events'][K]['ty']>>;
|
|
33
35
|
getEvent(name: string): undefined;
|
|
34
36
|
notify<K extends keyof R['events']>(eventName: K, value: Infer<R['events'][K]['ty']>): Promise<void>;
|
|
35
37
|
hasMethod(name: string): boolean;
|
|
@@ -39,5 +41,5 @@ export declare class RPC<L extends Impl, R extends Spec> {
|
|
|
39
41
|
private processMessage;
|
|
40
42
|
close(): void;
|
|
41
43
|
}
|
|
42
|
-
export declare function connect<L extends
|
|
44
|
+
export declare function connect<L extends Contract, R extends Contract, S extends object>(impl: Impl<L, R, S>, transport: Transport, state: S, logger?: Logger): RPC<L, R, S>;
|
|
43
45
|
//# sourceMappingURL=rpc.d.ts.map
|
package/lib/rpc.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc.d.ts","sourceRoot":"","sources":["../src/rpc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,OAAO,EAAgB,MAAM,MAAM,CAAC;AAC9D,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,eAAe,CAAC;AAE3C,OAAO,EAA4C,KAAK,SAAS,
|
|
1
|
+
{"version":3,"file":"rpc.d.ts","sourceRoot":"","sources":["../src/rpc.ts"],"names":[],"mappings":"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"}
|
package/lib/rpc.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { BehaviorSubject, Subject, Subscription } from "rxjs";
|
|
2
2
|
import {} from "reflect-types";
|
|
3
|
-
import { isPlainObject, Deferred } from "./util.js";
|
|
3
|
+
import { isPlainObject, Deferred, isPrimitive } from "./util.js";
|
|
4
4
|
import { decodeEvent, decodeObservableComplete, decodeObservableEvent, decodeRequest, decodeRespondError, decodeRespondOk, decodeStreamElement, decodeStreamFinish, MSGID_EVENT, MSGID_OBSERVABLE_CLOSE, MSGID_OBSERVABLE_COMPLETE, MSGID_OBSERVABLE_ERROR, MSGID_OBSERVABLE_EVENT, MSGID_REQUEST, MSGID_RESPOND_ERROR, MSGID_RESPOND_OK, MSGID_STREAM_ELEMENT, MSGID_STREAM_FINISH, TYPE_ARRAY, TYPE_ASYNC_GENERATOR, TYPE_BEHAVIORSUBJECT, TYPE_OBJECT, TYPE_PRIMITIVE, TYPE_SUBJECT, TYPE_UNDEFINED, } from "./protocol.js";
|
|
5
5
|
import { EventNotFoundError, MethodNotFoundError, ProtocolError, RemoteError, RPCError, FailedValidationError } from "./error.js";
|
|
6
|
-
import {
|
|
6
|
+
import { createProxy } from "./proxy.js";
|
|
7
7
|
export class Resource {
|
|
8
8
|
value;
|
|
9
9
|
close;
|
|
@@ -15,17 +15,11 @@ export class Resource {
|
|
|
15
15
|
export function isResource(value) {
|
|
16
16
|
return value instanceof Resource;
|
|
17
17
|
}
|
|
18
|
-
function isPrimitive(value) {
|
|
19
|
-
return value == null
|
|
20
|
-
|| typeof (value) === 'boolean'
|
|
21
|
-
|| typeof (value) === 'number'
|
|
22
|
-
|| typeof (value) === 'string';
|
|
23
|
-
}
|
|
24
18
|
export class RPC {
|
|
25
19
|
transport;
|
|
26
|
-
|
|
27
|
-
remote;
|
|
20
|
+
impl;
|
|
28
21
|
state;
|
|
22
|
+
logger;
|
|
29
23
|
// For sending data
|
|
30
24
|
nextMessageId = 0;
|
|
31
25
|
nextStreamId = 0;
|
|
@@ -38,11 +32,12 @@ export class RPC {
|
|
|
38
32
|
events = Object.create(null);
|
|
39
33
|
// Internal resources
|
|
40
34
|
readerSubscription;
|
|
41
|
-
|
|
35
|
+
client = createProxy(this);
|
|
36
|
+
constructor(transport, impl, state, logger) {
|
|
42
37
|
this.transport = transport;
|
|
43
|
-
this.
|
|
44
|
-
this.remote = remote;
|
|
38
|
+
this.impl = impl;
|
|
45
39
|
this.state = state;
|
|
40
|
+
this.logger = logger;
|
|
46
41
|
// Process incoming messages
|
|
47
42
|
this.readerSubscription = transport.input.subscribe(data => {
|
|
48
43
|
try {
|
|
@@ -59,7 +54,7 @@ export class RPC {
|
|
|
59
54
|
});
|
|
60
55
|
}
|
|
61
56
|
getEvent(name) {
|
|
62
|
-
if (!this.local.
|
|
57
|
+
if (!this.impl.local.hasEvent(name)) {
|
|
63
58
|
return;
|
|
64
59
|
}
|
|
65
60
|
if (this.events[name] === undefined) {
|
|
@@ -71,11 +66,11 @@ export class RPC {
|
|
|
71
66
|
await this.transport.write(JSON.stringify([MSGID_EVENT, eventName, this.encode(value)]));
|
|
72
67
|
}
|
|
73
68
|
hasMethod(name) {
|
|
74
|
-
return this.local.
|
|
69
|
+
return this.impl.local.hasMethod(name);
|
|
75
70
|
}
|
|
76
71
|
async callMethod(name, args) {
|
|
77
72
|
const id = this.nextMessageId++;
|
|
78
|
-
|
|
73
|
+
this.logger?.trace({ rpc: { direction: 'send', msgId: 'REQUEST', id, methodName: name } });
|
|
79
74
|
const deferred = new Deferred();
|
|
80
75
|
this.pending.set(id, { name, deferred });
|
|
81
76
|
const encodedArgs = args.map(this.encode.bind(this));
|
|
@@ -250,28 +245,33 @@ export class RPC {
|
|
|
250
245
|
case MSGID_EVENT:
|
|
251
246
|
{
|
|
252
247
|
const [name, rawValue] = decodeEvent(msg);
|
|
253
|
-
|
|
248
|
+
this.logger?.trace({ rpc: { direction: 'recv', msgId: 'EVENT', eventName: name } });
|
|
254
249
|
const subject = this.getEvent(name);
|
|
255
250
|
if (subject === undefined) {
|
|
256
251
|
throw new EventNotFoundError(name);
|
|
257
252
|
}
|
|
258
253
|
const value = this.decode(rawValue);
|
|
259
|
-
const coerced = this.local.
|
|
254
|
+
const coerced = this.impl.local.validateEventValue(name, value);
|
|
260
255
|
subject.next(coerced);
|
|
261
256
|
break;
|
|
262
257
|
}
|
|
263
258
|
case MSGID_REQUEST:
|
|
264
259
|
{
|
|
265
260
|
const [id, name, encodedArgs] = decodeRequest(msg);
|
|
266
|
-
|
|
261
|
+
this.logger?.trace({ rpc: { direction: 'recv', msgId: 'REQUEST', id, methodName: name } });
|
|
267
262
|
try {
|
|
268
263
|
const args = encodedArgs.map(this.decode.bind(this));
|
|
269
|
-
const method = this.
|
|
264
|
+
const method = this.impl.getHandler(name);
|
|
270
265
|
if (method === undefined) {
|
|
271
266
|
throw new MethodNotFoundError(name);
|
|
272
267
|
}
|
|
273
|
-
const validArgs = this.local.
|
|
274
|
-
Promise.resolve(method(
|
|
268
|
+
const validArgs = this.impl.local.validateArgs(name, args);
|
|
269
|
+
Promise.resolve(method({
|
|
270
|
+
client: this.client,
|
|
271
|
+
state: this.state,
|
|
272
|
+
// We force the compiler to treat `validArgs` as correctly typed
|
|
273
|
+
args: validArgs
|
|
274
|
+
}))
|
|
275
275
|
.then(value => this.transport.write(JSON.stringify([MSGID_RESPOND_OK, id, this.encode(value)])))
|
|
276
276
|
.catch(error => this.transport.write(JSON.stringify([MSGID_RESPOND_ERROR, id, error.message])));
|
|
277
277
|
}
|
|
@@ -287,6 +287,7 @@ export class RPC {
|
|
|
287
287
|
case MSGID_STREAM_ELEMENT:
|
|
288
288
|
{
|
|
289
289
|
const [id, rawValue] = decodeStreamElement(msg);
|
|
290
|
+
this.logger?.trace({ rpc: { direction: 'recv', msgId: 'STREAM_ELEMENT', id } });
|
|
290
291
|
const element = this.decode(rawValue);
|
|
291
292
|
const stream = this.asyncGenerators.get(id);
|
|
292
293
|
if (stream === undefined) {
|
|
@@ -299,6 +300,7 @@ export class RPC {
|
|
|
299
300
|
case MSGID_STREAM_FINISH:
|
|
300
301
|
{
|
|
301
302
|
const [id, rawValue] = decodeStreamFinish(msg);
|
|
303
|
+
this.logger?.trace({ rpc: { direction: 'recv', msgId: 'STREAM_FINISH', id } });
|
|
302
304
|
const generator = this.asyncGenerators.get(id);
|
|
303
305
|
if (generator === undefined) {
|
|
304
306
|
throw new RemoteError(`Async generator with ID ${id} not found.`);
|
|
@@ -311,7 +313,7 @@ export class RPC {
|
|
|
311
313
|
case MSGID_OBSERVABLE_EVENT:
|
|
312
314
|
{
|
|
313
315
|
const [id, rawValue] = decodeObservableEvent(msg);
|
|
314
|
-
|
|
316
|
+
this.logger?.trace({ rpc: { direction: 'recv', msgId: 'OBSERVABLE_EVENT', id } });
|
|
315
317
|
const sub = this.recvSubjects.get(id);
|
|
316
318
|
if (sub === undefined) {
|
|
317
319
|
throw new RemoteError(`Observable with ID ${id} not found.`);
|
|
@@ -322,7 +324,7 @@ export class RPC {
|
|
|
322
324
|
case MSGID_OBSERVABLE_COMPLETE:
|
|
323
325
|
{
|
|
324
326
|
const [id] = decodeObservableComplete(msg);
|
|
325
|
-
|
|
327
|
+
this.logger?.trace({ rpc: { direction: 'recv', msgId: 'OBSERVABLE_COMPLETE', id } });
|
|
326
328
|
const subscriber = this.recvSubjects.get(id);
|
|
327
329
|
if (subscriber === undefined) {
|
|
328
330
|
throw new Error(`Observable with ID ${id} not found.`);
|
|
@@ -334,7 +336,7 @@ export class RPC {
|
|
|
334
336
|
case MSGID_OBSERVABLE_CLOSE:
|
|
335
337
|
{
|
|
336
338
|
const [id] = msg[1];
|
|
337
|
-
|
|
339
|
+
this.logger?.trace({ rpc: { direction: 'recv', msgId: 'OBSERVABLE_CLOSE', id } });
|
|
338
340
|
const sub = this.sendSubscriptions.get(id);
|
|
339
341
|
if (sub === undefined) {
|
|
340
342
|
throw new RemoteError(`Could not find subscription with ID ${id}`);
|
|
@@ -346,7 +348,7 @@ export class RPC {
|
|
|
346
348
|
case MSGID_RESPOND_OK:
|
|
347
349
|
{
|
|
348
350
|
const [id, value] = decodeRespondOk(msg);
|
|
349
|
-
|
|
351
|
+
this.logger?.trace({ rpc: { direction: 'recv', msgId: 'RESPOND_OK', id } });
|
|
350
352
|
const result = this.pending.get(id);
|
|
351
353
|
if (result === undefined) {
|
|
352
354
|
throw new RemoteError(`Pending request with ID ${id} not found.`);
|
|
@@ -356,7 +358,7 @@ export class RPC {
|
|
|
356
358
|
const decoded = this.decode(value);
|
|
357
359
|
let coerced;
|
|
358
360
|
try {
|
|
359
|
-
coerced = this.remote.validateReturns(name, decoded);
|
|
361
|
+
coerced = this.impl.remote.validateReturns(name, decoded);
|
|
360
362
|
}
|
|
361
363
|
catch (error) {
|
|
362
364
|
if (error instanceof FailedValidationError) {
|
|
@@ -371,7 +373,7 @@ export class RPC {
|
|
|
371
373
|
case MSGID_RESPOND_ERROR:
|
|
372
374
|
{
|
|
373
375
|
const [id, message] = decodeRespondError(msg);
|
|
374
|
-
|
|
376
|
+
this.logger?.trace({ rpc: { direction: 'recv', msgId: 'RESPOND_ERROR', id } });
|
|
375
377
|
const result = this.pending.get(id);
|
|
376
378
|
if (result === undefined) {
|
|
377
379
|
throw new RemoteError(`Pending request with ID ${id} not found for error response`);
|
|
@@ -389,7 +391,7 @@ export class RPC {
|
|
|
389
391
|
this.readerSubscription.unsubscribe();
|
|
390
392
|
}
|
|
391
393
|
}
|
|
392
|
-
export function connect(
|
|
393
|
-
return new RPC(transport,
|
|
394
|
+
export function connect(impl, transport, state, logger) {
|
|
395
|
+
return new RPC(transport, impl, state, logger);
|
|
394
396
|
}
|
|
395
397
|
//# sourceMappingURL=rpc.js.map
|