@push-rpc/next 2.0.11 → 2.0.13
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/README.md +214 -20
- package/dist/client/RpcClientImpl.js +2 -2
- package/dist/client/RpcClientImpl.js.map +1 -1
- package/dist/client/WebSocketConnection.d.ts +1 -1
- package/dist/client/WebSocketConnection.js +2 -2
- package/dist/client/WebSocketConnection.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/utils/middleware.js +5 -2
- package/dist/utils/middleware.js.map +1 -1
- package/example/client.ts +1 -1
- package/example/server.ts +2 -2
- package/package.json +1 -1
- package/src/client/RpcClientImpl.ts +11 -3
- package/src/client/WebSocketConnection.ts +7 -3
- package/src/client/index.ts +2 -2
- package/src/utils/middleware.ts +7 -2
- package/tests/middleware.ts +104 -7
- package/tests/testUtils.ts +2 -2
package/README.md
CHANGED
|
@@ -1,4 +1,211 @@
|
|
|
1
|
-
|
|
1
|
+
A TypeScript framework for organizing bidirectional typesafe client-server communication. Supports
|
|
2
|
+
server-initiated data push (subscriptions). Uses HTTP, JSON and, optionally, WebSockets.
|
|
3
|
+
Main focus is on simplicity and good developer experience.
|
|
4
|
+
|
|
5
|
+
Best used with monorepos using TypeScript. Can also be used with JavaScript and non-JS clients.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Developer friendly - remote call is a plain TS call for easy tracing between client and server and good
|
|
10
|
+
IDE integration.
|
|
11
|
+
- Based on HTTP, easy to integrate with existing infrastructure. Call visibility in browser DevTools.
|
|
12
|
+
- Gradually upgradeable - WS is only used when you need subscriptions.
|
|
13
|
+
- Server runs on Node.JS, client runs in the Node.JS/Browser/ReactNative.
|
|
14
|
+
|
|
15
|
+
Extra:
|
|
16
|
+
|
|
17
|
+
- Client & Server middlewares.
|
|
18
|
+
- Consume compressed HTTP requests.
|
|
19
|
+
- Send & receive plain-text data.
|
|
20
|
+
- Throttling for subscriptions.
|
|
21
|
+
- Broken WS connection detection & auto-reconnecting.
|
|
22
|
+
|
|
23
|
+
## History note!
|
|
24
|
+
|
|
25
|
+
Initially this project supported WS-only communication akin to OCPP-J protocol for EV charging stations.
|
|
26
|
+
Since that, library for OCPP-J was extracted to a separate project and this project was reworked to focus on generic
|
|
27
|
+
client/server communication.
|
|
28
|
+
|
|
29
|
+
## Getting Started
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
npm install @push-rpc/next
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
For implementing subscriptions at backend, you'll also need to install ws package:
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
npm install ws
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Contract between server and client is defined in shared module.
|
|
42
|
+
|
|
43
|
+
api.ts:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
// Note that API definition is plain TypeScript and is independent of the library
|
|
47
|
+
|
|
48
|
+
export type Services = {
|
|
49
|
+
todo: TodoService
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type TodoService = {
|
|
53
|
+
addTodo(req: {text: string}, ctx?: any): Promise<void>
|
|
54
|
+
getTodos(ctx?: any): Promise<Todo[]>
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export type Todo = {
|
|
58
|
+
id: string
|
|
59
|
+
text: string
|
|
60
|
+
status: "open" | "closed"
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Contact then used in client.ts:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
import {Services} from "./api"
|
|
69
|
+
import {consumeServices} from "@push-rpc/next"
|
|
70
|
+
|
|
71
|
+
async function startClient() {
|
|
72
|
+
const {remote} = await consumeServices<Services>("http://127.0.0.1:8080/rpc")
|
|
73
|
+
|
|
74
|
+
console.log("Client created")
|
|
75
|
+
|
|
76
|
+
await remote.todo.getTodos.subscribe((todos) => {
|
|
77
|
+
console.log("Got todo items", todos)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
await remote.todo.addTodo({text: "Buy groceries"})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
startClient()
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
And implemented in server.ts:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
import {Todo, TodoService} from "./api"
|
|
91
|
+
import {publishServices} from "@push-rpc/next"
|
|
92
|
+
|
|
93
|
+
async function startServer() {
|
|
94
|
+
const storage: Todo[] = []
|
|
95
|
+
|
|
96
|
+
class TodoServiceImpl implements TodoService {
|
|
97
|
+
async addTodo({text}: {text: string}) {
|
|
98
|
+
storage.push({
|
|
99
|
+
id: "" + Math.random(),
|
|
100
|
+
text,
|
|
101
|
+
status: "open",
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
console.log("New todo item added")
|
|
105
|
+
services.todo.getTodos.trigger()
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async getTodos() {
|
|
109
|
+
return storage
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const {services} = await publishServices(
|
|
114
|
+
{
|
|
115
|
+
todo: new TodoServiceImpl(),
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
port: 8080,
|
|
119
|
+
path: "/rpc",
|
|
120
|
+
}
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
console.log("RPC Server started at http://localhost:8080/rpc")
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
startServer()
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Run server.ts and then client.ts.
|
|
130
|
+
|
|
131
|
+
Server will send empty todo list on client connecting and then will send updated list on adding new item.
|
|
132
|
+
|
|
133
|
+
## Protocol Details
|
|
134
|
+
|
|
135
|
+
There are the types of messages that can be sent from client to server:
|
|
136
|
+
|
|
137
|
+
1. **Call** - a request to synchronously execute a remote function. Implemented as HTTP POST request. URL contains the
|
|
138
|
+
remote function name. Body contains JSON-encoded list of arguments. Response is JSON-encoded result of the
|
|
139
|
+
function.
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
POST /rpc/todo/addTodo HTTP/1.1
|
|
143
|
+
Content-Type: application/json
|
|
144
|
+
X-Rpc-Client-Id: GoQ_xVYcthSEqMxDGV212
|
|
145
|
+
|
|
146
|
+
[{"text": "Buy groceries"}]
|
|
147
|
+
|
|
148
|
+
...
|
|
149
|
+
HTTP/1.1 200 OK
|
|
150
|
+
Content-Type: application/json
|
|
151
|
+
{"id": "123"}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
`X-Rpc-Client-Id` header is used to identify caller clients. In can be used for session tracking. Client ID is
|
|
155
|
+
available at server side in the context.
|
|
156
|
+
|
|
157
|
+
2. **Subscribe** - a request to subscribe to a remote function updates. Implemented as HTTP PUT request. URL contains
|
|
158
|
+
the remote function name. Body contains JSON-encoded list of arguments. Response is JSON-encoded result of the
|
|
159
|
+
function.
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
PUT /rpc/todo/getTodos HTTP/1.1
|
|
163
|
+
Content-Type: application/json
|
|
164
|
+
|
|
165
|
+
[]
|
|
166
|
+
|
|
167
|
+
...
|
|
168
|
+
HTTP/1.1 200 OK
|
|
169
|
+
Content-Type: application/json
|
|
170
|
+
[{"id": 1, text: "Buy groceries", status: "open"}]
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Before subscribing, client would establish a WebSocket connection to the server. Server would then use established
|
|
174
|
+
connection to send subscription updates. Client ID is used to link WebSocket connection and HTTP requests.
|
|
175
|
+
|
|
176
|
+
3. **Unsubscribe** - a request to unsubscribe from a remote function updates. Implemented as HTTP PATCH request. URL
|
|
177
|
+
contains the remote function name. Body contains JSON-encoded list of arguments. Response is always empty.
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
PATCH /rpc/todo/getTodos HTTP/1.1
|
|
181
|
+
Content-Type: application/json
|
|
182
|
+
|
|
183
|
+
[]
|
|
184
|
+
|
|
185
|
+
...
|
|
186
|
+
HTTP/1.1 204 No Content
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Server sends updates to subscribed clients using WebSocket connection. Clients establish WebSocket connection using
|
|
190
|
+
the base URL:
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
GET /rpc HTTP/1.1
|
|
194
|
+
Connection: Upgrade
|
|
195
|
+
Upgrade: websocket
|
|
196
|
+
Sec-Websocket-Protocol: GoQ_xVYcthSEqMxDGV212
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
`Sec-Websocket-Protocol` header is used to transfer client ID.
|
|
200
|
+
|
|
201
|
+
After successful subscription, server sends updates to the client. Each update is a JSON-encoded message containing
|
|
202
|
+
topic name, remote function result and subscription parameters if any:
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
["todo/getTodos", [{"id": 1, text: "Buy groceries", status: "open"}], ...]
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Both client & server will try to connect broken connections by sending WS ping/pongs.
|
|
2
209
|
|
|
3
210
|
## Glossary
|
|
4
211
|
|
|
@@ -19,30 +226,17 @@ subscribe' invocation and copied to each 'trigger' invocation. Context, created
|
|
|
19
226
|
contain only JSON data, to allow copying. Context can be modified in middlewares; these modification doesn't have to be
|
|
20
227
|
JSON-only.
|
|
21
228
|
|
|
22
|
-
**Middlewares**. Middlewares are used to intercept client
|
|
23
|
-
intercepted. Middlewares can be attached on both client and server
|
|
24
|
-
|
|
229
|
+
**Middlewares**. Middlewares are used to intercept client requests, server requests implementations and client
|
|
230
|
+
notifications. Both calls and subscriptions can be intercepted. Middlewares can be attached on both client and server
|
|
231
|
+
side. Middleware can modify context and parameters and data.
|
|
232
|
+
|
|
233
|
+
Request middleware is called with parameters (ctx, next, ...parameters)
|
|
234
|
+
Client notifications middleware is called with parameters (ctx, next, data, parameters).
|
|
25
235
|
|
|
26
236
|
**Throttling**. Used to limit number of notifications from the remote functions. With throttling enabled, not all
|
|
27
237
|
triggers will result in new notifications. Throttling can be used with reducers to aggregate values supplied in
|
|
28
238
|
triggers.
|
|
29
239
|
|
|
30
|
-
## Issues / TBDs
|
|
31
|
-
|
|
32
|
-
- [important] Importing index.js from the root of the package will import node's http package. Not good for clients.
|
|
33
|
-
- Browser sockets don't have 'ping' event. Need to find a different way to detect connection loss.
|
|
34
|
-
- Перевірити, що throttling працює відразу для всіх підписників
|
|
35
|
-
|
|
36
|
-
## Features
|
|
37
|
-
|
|
38
|
-
- Developer friendly - everything is plain TypeScript calls, easy call tracing between client and server, good
|
|
39
|
-
integration with IDE & Browser DevTools
|
|
40
|
-
- Based on HTTP, easy to integrate with existing infrastructure
|
|
41
|
-
- Gradually upgradeable - WS is only used when you need subscriptions
|
|
42
|
-
- Supports compressed HTTP requests.
|
|
43
|
-
- Server runs on Node.JS, client runs in the Node.JS/Browser/ReactNative. For RN some extra setup is required (
|
|
44
|
-
document). Bun/Deno should also work, but not officially supported.
|
|
45
|
-
|
|
46
240
|
# Limitations
|
|
47
241
|
|
|
48
242
|
- Cookies are not been sent during HTTP & WS requests.
|
|
@@ -71,8 +71,8 @@ class RpcClientImpl {
|
|
|
71
71
|
itemName,
|
|
72
72
|
invocationType: rpc_js_1.InvocationType.Update,
|
|
73
73
|
};
|
|
74
|
-
const next = async (
|
|
75
|
-
return (0, middleware_js_1.withMiddlewares)(ctx, this.options.
|
|
74
|
+
const next = async (nextData = data, nextParameters = parameters) => this.remoteSubscriptions.consume(itemName, nextParameters, nextData);
|
|
75
|
+
return (0, middleware_js_1.withMiddlewares)(ctx, this.options.notificationsMiddleware, next, data, parameters);
|
|
76
76
|
}, () => {
|
|
77
77
|
this.resubscribe();
|
|
78
78
|
options.onConnected();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RpcClientImpl.js","sourceRoot":"","sources":["../../src/client/RpcClientImpl.ts"],"names":[],"mappings":";;;AAAA,sCAA2E;AAC3E,mDAA0C;AAC1C,qEAA4D;AAC5D,qEAA4D;AAC5D,mCAA6B;AAC7B,2CAAmE;AAEnE,
|
|
1
|
+
{"version":3,"file":"RpcClientImpl.js","sourceRoot":"","sources":["../../src/client/RpcClientImpl.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,7 +10,7 @@ export declare class WebSocketConnection {
|
|
|
10
10
|
reconnectDelay: number;
|
|
11
11
|
errorDelayMaxDuration: number;
|
|
12
12
|
pingInterval: number | null;
|
|
13
|
-
}, consume: (itemName: string, parameters: unknown[], data: unknown) =>
|
|
13
|
+
}, consume: (itemName: string, parameters: unknown[], data: unknown) => Promise<unknown>, onConnected: () => void, onDisconnected: () => void);
|
|
14
14
|
private resolveClose;
|
|
15
15
|
close(): Promise<void>;
|
|
16
16
|
private waitConnectionPromise;
|
|
@@ -146,10 +146,10 @@ class WebSocketConnection {
|
|
|
146
146
|
try {
|
|
147
147
|
const msg = rawMessage.toString();
|
|
148
148
|
const [itemName, data, ...parameters] = (0, json_js_1.safeParseJson)(msg);
|
|
149
|
-
this.consume(itemName, parameters, data);
|
|
149
|
+
await this.consume(itemName, parameters, data);
|
|
150
150
|
}
|
|
151
151
|
catch (e) {
|
|
152
|
-
logger_js_1.log.
|
|
152
|
+
logger_js_1.log.error("Can't handle notification", e);
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
// test-only
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebSocketConnection.js","sourceRoot":"","sources":["../../src/client/WebSocketConnection.ts"],"names":[],"mappings":";;;AAAA,4CAAgC;AAChC,8CAA8C;AAC9C,sDAA2C;AAE3C,MAAa,mBAAmB;IAC9B,YACmB,GAAW,EACX,QAAgB,EAChB,OAKhB,EACgB,
|
|
1
|
+
{"version":3,"file":"WebSocketConnection.js","sourceRoot":"","sources":["../../src/client/WebSocketConnection.ts"],"names":[],"mappings":"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"}
|
package/dist/client/index.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export type ConsumeServicesOptions = {
|
|
|
16
16
|
pingInterval: number | null;
|
|
17
17
|
subscriptions: boolean;
|
|
18
18
|
middleware: Middleware<RpcContext>[];
|
|
19
|
-
|
|
19
|
+
notificationsMiddleware: Middleware<RpcContext>[];
|
|
20
20
|
connectOnCreate: boolean;
|
|
21
21
|
onConnected: () => void;
|
|
22
22
|
onDisconnected: () => void;
|
package/dist/client/index.js
CHANGED
|
@@ -27,7 +27,7 @@ const defaultOptions = {
|
|
|
27
27
|
pingInterval: null, // if set, should be in-sync with server, ie 30 * 1000
|
|
28
28
|
subscriptions: true,
|
|
29
29
|
middleware: [],
|
|
30
|
-
|
|
30
|
+
notificationsMiddleware: [],
|
|
31
31
|
connectOnCreate: false,
|
|
32
32
|
onConnected: () => { },
|
|
33
33
|
onDisconnected: () => { },
|
package/dist/client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":";;;AAEA,yDAAgD;AA8BzC,KAAK,UAAU,eAAe,CACnC,GAAW,EACX,kBAAmD,EAAE;IAKrD,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAA;IAC5C,CAAC;IAED,MAAM,OAAO,GAAG;QACd,GAAG,cAAc;QACjB,GAAG,eAAe;KACnB,CAAA;IAED,MAAM,MAAM,GAAG,IAAI,gCAAa,CAAI,GAAG,EAAE,OAAO,CAAC,CAAA;IAEjD,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;QAC5B,MAAM,MAAM,CAAC,OAAO,EAAE,CAAA;IACxB,CAAC;IAED,OAAO;QACL,MAAM;QACN,MAAM,EAAE,MAAM,CAAC,YAAY,EAAE;KAC9B,CAAA;AACH,CAAC;AA1BD,0CA0BC;AAED,MAAM,cAAc,GAA2B;IAC7C,WAAW,EAAE,CAAC,GAAG,IAAI;IACrB,cAAc,EAAE,CAAC;IACjB,qBAAqB,EAAE,EAAE,GAAG,IAAI;IAChC,YAAY,EAAE,IAAI,EAAE,sDAAsD;IAC1E,aAAa,EAAE,IAAI;IACnB,UAAU,EAAE,EAAE;IACd,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":";;;AAEA,yDAAgD;AA8BzC,KAAK,UAAU,eAAe,CACnC,GAAW,EACX,kBAAmD,EAAE;IAKrD,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAA;IAC5C,CAAC;IAED,MAAM,OAAO,GAAG;QACd,GAAG,cAAc;QACjB,GAAG,eAAe;KACnB,CAAA;IAED,MAAM,MAAM,GAAG,IAAI,gCAAa,CAAI,GAAG,EAAE,OAAO,CAAC,CAAA;IAEjD,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;QAC5B,MAAM,MAAM,CAAC,OAAO,EAAE,CAAA;IACxB,CAAC;IAED,OAAO;QACL,MAAM;QACN,MAAM,EAAE,MAAM,CAAC,YAAY,EAAE;KAC9B,CAAA;AACH,CAAC;AA1BD,0CA0BC;AAED,MAAM,cAAc,GAA2B;IAC7C,WAAW,EAAE,CAAC,GAAG,IAAI;IACrB,cAAc,EAAE,CAAC;IACjB,qBAAqB,EAAE,EAAE,GAAG,IAAI;IAChC,YAAY,EAAE,IAAI,EAAE,sDAAsD;IAC1E,aAAa,EAAE,IAAI;IACnB,UAAU,EAAE,EAAE;IACd,uBAAuB,EAAE,EAAE;IAC3B,eAAe,EAAE,KAAK;IACtB,WAAW,EAAE,GAAG,EAAE,GAAE,CAAC;IACrB,cAAc,EAAE,GAAG,EAAE,GAAE,CAAC;IACxB,UAAU,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;IAE5B,mBAAmB,CAAC,GAAW;QAC7B,OAAO,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,CAAA;IACxE,CAAC;CACF,CAAA"}
|
package/dist/utils/middleware.js
CHANGED
|
@@ -14,12 +14,15 @@ function withMiddlewares(ctx, middlewares, final, ...params) {
|
|
|
14
14
|
}
|
|
15
15
|
index = i;
|
|
16
16
|
try {
|
|
17
|
+
let result;
|
|
17
18
|
if (i === middlewares.length) {
|
|
18
|
-
|
|
19
|
+
result = next(...p);
|
|
19
20
|
}
|
|
20
21
|
else {
|
|
21
|
-
|
|
22
|
+
const dispatchNextMiddleware = dispatch.bind(null, i + 1);
|
|
23
|
+
result = middlewares[i](ctx, dispatchNextMiddleware, ...p);
|
|
22
24
|
}
|
|
25
|
+
return Promise.resolve(result);
|
|
23
26
|
}
|
|
24
27
|
catch (err) {
|
|
25
28
|
return Promise.reject(err);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/utils/middleware.ts"],"names":[],"mappings":";;;AAMA,SAAgB,eAAe,CAC7B,GAAY,EACZ,WAAkC,EAClC,KAAiD,EACjD,GAAG,MAAW;IAEd,OAAO,CAAC,UAAU,IAAI,EAAE,GAAG,MAAM;QAC/B,IAAI,KAAK,GAAG,CAAC,CAAC,CAAA;QACd,OAAO,QAAQ,CAAC,CAAC,EAAE,GAAG,MAAM,CAAC,CAAA;QAE7B,SAAS,QAAQ,CAAC,CAAS,EAAE,GAAG,CAAY;YAC1C,IAAI,CAAC,IAAI,KAAK;gBAAE,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC,CAAA;YAEhF,iCAAiC;YACjC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;gBACd,CAAC,GAAG,MAAM,CAAA;YACZ,CAAC;YAED,KAAK,GAAG,CAAC,CAAA;YAET,IAAI,CAAC;gBACH,IAAI,CAAC,KAAK,WAAW,CAAC,MAAM,EAAE,CAAC;oBAC7B,
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/utils/middleware.ts"],"names":[],"mappings":";;;AAMA,SAAgB,eAAe,CAC7B,GAAY,EACZ,WAAkC,EAClC,KAAiD,EACjD,GAAG,MAAW;IAEd,OAAO,CAAC,UAAU,IAAI,EAAE,GAAG,MAAM;QAC/B,IAAI,KAAK,GAAG,CAAC,CAAC,CAAA;QACd,OAAO,QAAQ,CAAC,CAAC,EAAE,GAAG,MAAM,CAAC,CAAA;QAE7B,SAAS,QAAQ,CAAC,CAAS,EAAE,GAAG,CAAY;YAC1C,IAAI,CAAC,IAAI,KAAK;gBAAE,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC,CAAA;YAEhF,iCAAiC;YACjC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;gBACd,CAAC,GAAG,MAAM,CAAA;YACZ,CAAC;YAED,KAAK,GAAG,CAAC,CAAA;YAET,IAAI,CAAC;gBACH,IAAI,MAAM,CAAA;gBAEV,IAAI,CAAC,KAAK,WAAW,CAAC,MAAM,EAAE,CAAC;oBAC7B,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;gBACrB,CAAC;qBAAM,CAAC;oBACN,MAAM,sBAAsB,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAA;oBACzD,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,sBAAsB,EAAE,GAAG,CAAC,CAAC,CAAA;gBAC5D,CAAC;gBAED,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;YAChC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YAC5B,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC,KAAK,EAAE,GAAG,MAAM,CAAC,CAAA;AACtB,CAAC;AApCD,0CAoCC"}
|
package/example/client.ts
CHANGED
package/example/server.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {Todo, TodoService} from "./api"
|
|
2
2
|
import {publishServices} from "../src/server/index"
|
|
3
3
|
|
|
4
|
-
const storage: Todo[] = []
|
|
5
|
-
|
|
6
4
|
async function startServer() {
|
|
5
|
+
const storage: Todo[] = []
|
|
6
|
+
|
|
7
7
|
class TodoServiceImpl implements TodoService {
|
|
8
8
|
async addTodo({text}: {text: string}) {
|
|
9
9
|
storage.push({
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@ import {WebSocketConnection} from "./WebSocketConnection.js"
|
|
|
5
5
|
import {nanoid} from "nanoid"
|
|
6
6
|
import {createRemote, ServicesWithSubscriptions} from "./remote.js"
|
|
7
7
|
import {ConsumeServicesOptions, RpcClient} from "./index.js"
|
|
8
|
-
import {withMiddlewares} from "../utils/middleware.js"
|
|
8
|
+
import {Middleware, withMiddlewares} from "../utils/middleware.js"
|
|
9
9
|
|
|
10
10
|
export class RpcClientImpl<S extends Services<S>> implements RpcClient {
|
|
11
11
|
constructor(
|
|
@@ -31,8 +31,16 @@ export class RpcClientImpl<S extends Services<S>> implements RpcClient {
|
|
|
31
31
|
invocationType: InvocationType.Update,
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
const next = async (
|
|
35
|
-
|
|
34
|
+
const next = async (nextData = data, nextParameters = parameters) =>
|
|
35
|
+
this.remoteSubscriptions.consume(itemName, nextParameters, nextData)
|
|
36
|
+
|
|
37
|
+
return withMiddlewares(
|
|
38
|
+
ctx,
|
|
39
|
+
this.options.notificationsMiddleware,
|
|
40
|
+
next as any,
|
|
41
|
+
data,
|
|
42
|
+
parameters
|
|
43
|
+
)
|
|
36
44
|
},
|
|
37
45
|
() => {
|
|
38
46
|
this.resubscribe()
|
|
@@ -12,7 +12,11 @@ export class WebSocketConnection {
|
|
|
12
12
|
errorDelayMaxDuration: number
|
|
13
13
|
pingInterval: number | null
|
|
14
14
|
},
|
|
15
|
-
private readonly consume: (
|
|
15
|
+
private readonly consume: (
|
|
16
|
+
itemName: string,
|
|
17
|
+
parameters: unknown[],
|
|
18
|
+
data: unknown
|
|
19
|
+
) => Promise<unknown>,
|
|
16
20
|
private readonly onConnected: () => void,
|
|
17
21
|
private readonly onDisconnected: () => void
|
|
18
22
|
) {
|
|
@@ -194,9 +198,9 @@ export class WebSocketConnection {
|
|
|
194
198
|
|
|
195
199
|
const [itemName, data, ...parameters] = safeParseJson(msg)
|
|
196
200
|
|
|
197
|
-
this.consume(itemName, parameters, data)
|
|
201
|
+
await this.consume(itemName, parameters, data)
|
|
198
202
|
} catch (e) {
|
|
199
|
-
log.
|
|
203
|
+
log.error("Can't handle notification", e)
|
|
200
204
|
}
|
|
201
205
|
}
|
|
202
206
|
|
package/src/client/index.ts
CHANGED
|
@@ -22,7 +22,7 @@ export type ConsumeServicesOptions = {
|
|
|
22
22
|
pingInterval: number | null
|
|
23
23
|
subscriptions: boolean
|
|
24
24
|
middleware: Middleware<RpcContext>[]
|
|
25
|
-
|
|
25
|
+
notificationsMiddleware: Middleware<RpcContext>[]
|
|
26
26
|
connectOnCreate: boolean
|
|
27
27
|
onConnected: () => void
|
|
28
28
|
onDisconnected: () => void
|
|
@@ -65,7 +65,7 @@ const defaultOptions: ConsumeServicesOptions = {
|
|
|
65
65
|
pingInterval: null, // if set, should be in-sync with server, ie 30 * 1000
|
|
66
66
|
subscriptions: true,
|
|
67
67
|
middleware: [],
|
|
68
|
-
|
|
68
|
+
notificationsMiddleware: [],
|
|
69
69
|
connectOnCreate: false,
|
|
70
70
|
onConnected: () => {},
|
|
71
71
|
onDisconnected: () => {},
|
package/src/utils/middleware.ts
CHANGED
|
@@ -25,11 +25,16 @@ export function withMiddlewares<Context>(
|
|
|
25
25
|
index = i
|
|
26
26
|
|
|
27
27
|
try {
|
|
28
|
+
let result
|
|
29
|
+
|
|
28
30
|
if (i === middlewares.length) {
|
|
29
|
-
|
|
31
|
+
result = next(...p)
|
|
30
32
|
} else {
|
|
31
|
-
|
|
33
|
+
const dispatchNextMiddleware = dispatch.bind(null, i + 1)
|
|
34
|
+
result = middlewares[i](ctx, dispatchNextMiddleware, ...p)
|
|
32
35
|
}
|
|
36
|
+
|
|
37
|
+
return Promise.resolve(result)
|
|
33
38
|
} catch (err) {
|
|
34
39
|
return Promise.reject(err)
|
|
35
40
|
}
|
package/tests/middleware.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {assert} from "chai"
|
|
2
|
-
import {Middleware, withMiddlewares} from "../src/index.js"
|
|
2
|
+
import {Middleware, setLogger, withMiddlewares} from "../src/index.js"
|
|
3
3
|
import {createTestClient, startTestServer} from "./testUtils.js"
|
|
4
4
|
import {adelay} from "../src/utils/promises.js"
|
|
5
5
|
|
|
@@ -110,19 +110,20 @@ describe("middleware", () => {
|
|
|
110
110
|
}
|
|
111
111
|
})
|
|
112
112
|
|
|
113
|
-
it("
|
|
113
|
+
it("notifications middlewares", async () => {
|
|
114
114
|
let count = 1
|
|
115
115
|
|
|
116
116
|
const services = await startTestServer({
|
|
117
|
-
async remote() {
|
|
117
|
+
async remote(filter: {param: number}) {
|
|
118
118
|
return count++
|
|
119
119
|
},
|
|
120
120
|
})
|
|
121
121
|
|
|
122
122
|
const client = await createTestClient<typeof services>({
|
|
123
|
-
|
|
124
|
-
(ctx, next, r) => {
|
|
123
|
+
notificationsMiddleware: [
|
|
124
|
+
(ctx, next, r, params) => {
|
|
125
125
|
assert.ok(ctx.itemName)
|
|
126
|
+
assert.deepEqual(params, [{param: 1}])
|
|
126
127
|
|
|
127
128
|
return next((r as number) + 1)
|
|
128
129
|
},
|
|
@@ -130,14 +131,110 @@ describe("middleware", () => {
|
|
|
130
131
|
})
|
|
131
132
|
|
|
132
133
|
let response
|
|
134
|
+
await client.remote.subscribe(
|
|
135
|
+
(r) => {
|
|
136
|
+
response = r
|
|
137
|
+
},
|
|
138
|
+
{param: 1}
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
services.remote.trigger({param: 1})
|
|
142
|
+
|
|
143
|
+
await adelay(20)
|
|
144
|
+
|
|
145
|
+
assert.equal(response, 3)
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it("error in notificationsMiddleware logged", async () => {
|
|
149
|
+
let log = ""
|
|
150
|
+
|
|
151
|
+
setLogger({
|
|
152
|
+
error(s: unknown, ...params) {
|
|
153
|
+
log += s + "\n" + params
|
|
154
|
+
},
|
|
155
|
+
warn: console.warn,
|
|
156
|
+
debug: console.debug,
|
|
157
|
+
info: console.info,
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
let count = 1
|
|
161
|
+
let result = 0
|
|
162
|
+
|
|
163
|
+
const services = await startTestServer({
|
|
164
|
+
async remote() {
|
|
165
|
+
return count++
|
|
166
|
+
},
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
const client = await createTestClient<typeof services>({
|
|
170
|
+
notificationsMiddleware: [
|
|
171
|
+
() => {
|
|
172
|
+
throw new Error("Test error")
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
})
|
|
176
|
+
|
|
133
177
|
await client.remote.subscribe((r) => {
|
|
134
|
-
|
|
178
|
+
result = r
|
|
135
179
|
})
|
|
136
180
|
|
|
137
181
|
services.remote.trigger()
|
|
138
182
|
|
|
139
183
|
await adelay(20)
|
|
140
184
|
|
|
141
|
-
assert.equal(
|
|
185
|
+
assert.equal(result, 1)
|
|
186
|
+
|
|
187
|
+
assert.include(log, "Test error")
|
|
188
|
+
|
|
189
|
+
setLogger(console)
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
it("error in client request middleware propagated", async () => {
|
|
193
|
+
let count = 1
|
|
194
|
+
|
|
195
|
+
const services = await startTestServer({
|
|
196
|
+
async remote() {
|
|
197
|
+
return count++
|
|
198
|
+
},
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
const client = await createTestClient<typeof services>({
|
|
202
|
+
middleware: [
|
|
203
|
+
() => {
|
|
204
|
+
throw new Error("Error")
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
try {
|
|
210
|
+
await client.remote()
|
|
211
|
+
assert.fail("Expected to fail")
|
|
212
|
+
} catch (e) {}
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
it("error in server request middleware propagated", async () => {
|
|
216
|
+
let count = 1
|
|
217
|
+
|
|
218
|
+
const services = await startTestServer(
|
|
219
|
+
{
|
|
220
|
+
async remote() {
|
|
221
|
+
return count++
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
middleware: [
|
|
226
|
+
() => {
|
|
227
|
+
throw new Error("Error")
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
}
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
const client = await createTestClient<typeof services>({})
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
await client.remote()
|
|
237
|
+
assert.fail("Expected to fail")
|
|
238
|
+
} catch (e) {}
|
|
142
239
|
})
|
|
143
240
|
})
|
package/tests/testUtils.ts
CHANGED
|
@@ -39,8 +39,8 @@ export async function createTestClient<S extends Services<S>>(
|
|
|
39
39
|
if (!options) options = {}
|
|
40
40
|
if (!options.middleware) options.middleware = []
|
|
41
41
|
options.middleware = [logMiddleware, ...options.middleware]
|
|
42
|
-
if (!options.
|
|
43
|
-
options.
|
|
42
|
+
if (!options.notificationsMiddleware) options.notificationsMiddleware = []
|
|
43
|
+
options.notificationsMiddleware = [logUpdatesMiddleware, ...options.notificationsMiddleware]
|
|
44
44
|
|
|
45
45
|
const r = await consumeServices<S>(`http://127.0.0.1:${TEST_PORT}/rpc`, options)
|
|
46
46
|
testClient = r.client
|