@push-rpc/next 2.0.10 → 2.0.12
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 +216 -22
- package/example/client.ts +1 -1
- package/example/server.ts +2 -2
- package/package-lock.json +1291 -0
- package/package.json +1 -1
- package/src/client/HttpClient.ts +1 -20
- package/src/client/RpcClientImpl.ts +18 -6
- package/src/client/WebSocketConnection.ts +8 -27
- package/src/client/index.ts +2 -0
- package/src/index.ts +1 -0
- package/src/rpc.ts +1 -0
- package/src/server/RpcServerImpl.ts +12 -8
- package/src/server/index.ts +2 -0
- package/src/utils/middleware.ts +7 -2
- package/tests/connection.ts +2 -87
- package/tests/middleware.ts +129 -1
- package/tests/testUtils.ts +25 -0
- package/dist/client/HttpClient.d.ts +0 -13
- package/dist/client/HttpClient.js +0 -98
- package/dist/client/HttpClient.js.map +0 -1
- package/dist/client/RemoteSubscriptions.d.ts +0 -18
- package/dist/client/RemoteSubscriptions.js +0 -120
- package/dist/client/RemoteSubscriptions.js.map +0 -1
- package/dist/client/RpcClientImpl.d.ts +0 -23
- package/dist/client/RpcClientImpl.js +0 -117
- package/dist/client/RpcClientImpl.js.map +0 -1
- package/dist/client/WebSocketConnection.d.ts +0 -38
- package/dist/client/WebSocketConnection.js +0 -180
- package/dist/client/WebSocketConnection.js.map +0 -1
- package/dist/client/index.d.ts +0 -28
- package/dist/client/index.js +0 -38
- package/dist/client/index.js.map +0 -1
- package/dist/client/remote.d.ts +0 -14
- package/dist/client/remote.js +0 -66
- package/dist/client/remote.js.map +0 -1
- package/dist/index.d.ts +0 -12
- package/dist/index.js.map +0 -1
- package/dist/logger.d.ts +0 -8
- package/dist/logger.js +0 -9
- package/dist/logger.js.map +0 -1
- package/dist/rpc.d.ts +0 -36
- package/dist/rpc.js +0 -32
- package/dist/rpc.js.map +0 -1
- package/dist/server/ConnectionsServer.d.ts +0 -13
- package/dist/server/ConnectionsServer.js +0 -72
- package/dist/server/ConnectionsServer.js.map +0 -1
- package/dist/server/LocalSubscriptions.d.ts +0 -12
- package/dist/server/LocalSubscriptions.js +0 -113
- package/dist/server/LocalSubscriptions.js.map +0 -1
- package/dist/server/RpcServerImpl.d.ts +0 -24
- package/dist/server/RpcServerImpl.js +0 -195
- package/dist/server/RpcServerImpl.js.map +0 -1
- package/dist/server/http.d.ts +0 -9
- package/dist/server/http.js +0 -89
- package/dist/server/http.js.map +0 -1
- package/dist/server/index.d.ts +0 -30
- package/dist/server/index.js +0 -33
- package/dist/server/index.js.map +0 -1
- package/dist/server/local.d.ts +0 -15
- package/dist/server/local.js +0 -46
- package/dist/server/local.js.map +0 -1
- package/dist/utils/cookies.d.ts +0 -7
- package/dist/utils/cookies.js +0 -31
- package/dist/utils/cookies.js.map +0 -1
- package/dist/utils/env.d.ts +0 -6
- package/dist/utils/env.js +0 -22
- package/dist/utils/env.js.map +0 -1
- package/dist/utils/json.d.ts +0 -2
- package/dist/utils/json.js +0 -34
- package/dist/utils/json.js.map +0 -1
- package/dist/utils/middleware.d.ts +0 -2
- package/dist/utils/middleware.js +0 -31
- package/dist/utils/middleware.js.map +0 -1
- package/dist/utils/promises.d.ts +0 -5
- package/dist/utils/promises.js +0 -29
- package/dist/utils/promises.js.map +0 -1
- package/dist/utils/server.d.ts +0 -5
- package/dist/utils/server.js +0 -48
- package/dist/utils/server.js.map +0 -1
- package/dist/utils/throttle.d.ts +0 -4
- package/dist/utils/throttle.js +0 -40
- package/dist/utils/throttle.js.map +0 -1
- package/dist/utils/types.d.ts +0 -1
- package/dist/utils/types.js +0 -3
- package/dist/utils/types.js.map +0 -1
package/package.json
CHANGED
package/src/client/HttpClient.ts
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import {CLIENT_ID_HEADER, RpcErrors} from "../rpc.js"
|
|
2
2
|
import {safeParseJson, safeStringify} from "../utils/json.js"
|
|
3
|
-
import {ClientCookies} from "../utils/cookies.js"
|
|
4
|
-
import {environment, Environment} from "../utils/env.js"
|
|
5
3
|
|
|
6
4
|
export class HttpClient {
|
|
7
5
|
constructor(
|
|
8
6
|
private url: string,
|
|
9
7
|
private clientId: string,
|
|
10
|
-
private getHeaders: () => Promise<Record<string, string
|
|
11
|
-
private cookies: ClientCookies
|
|
8
|
+
private getHeaders: () => Promise<Record<string, string>>
|
|
12
9
|
) {}
|
|
13
10
|
|
|
14
11
|
async call(itemName: string, params: unknown[], callTimeout: number): Promise<unknown> {
|
|
@@ -39,14 +36,6 @@ export class HttpClient {
|
|
|
39
36
|
try {
|
|
40
37
|
const {signal, finished} = timeoutSignal(callTimeout)
|
|
41
38
|
|
|
42
|
-
if (environment != Environment.Browser) {
|
|
43
|
-
const cookie = this.cookies.getCookieString()
|
|
44
|
-
|
|
45
|
-
if (cookie) {
|
|
46
|
-
headers["Cookie"] = cookie
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
39
|
const response = await fetch(itemUrl, {
|
|
51
40
|
method,
|
|
52
41
|
headers: {
|
|
@@ -60,14 +49,6 @@ export class HttpClient {
|
|
|
60
49
|
|
|
61
50
|
finished()
|
|
62
51
|
|
|
63
|
-
if (environment != Environment.Browser) {
|
|
64
|
-
const cookie = response.headers.get("set-cookie")
|
|
65
|
-
|
|
66
|
-
if (cookie) {
|
|
67
|
-
this.cookies.updateCookies(cookie.split(","))
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
52
|
if (response.status == 204) {
|
|
72
53
|
return
|
|
73
54
|
}
|
|
@@ -5,21 +5,19 @@ 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"
|
|
9
|
-
import {ClientCookies} from "../utils/cookies.js"
|
|
8
|
+
import {Middleware, withMiddlewares} from "../utils/middleware.js"
|
|
10
9
|
|
|
11
10
|
export class RpcClientImpl<S extends Services<S>> implements RpcClient {
|
|
12
11
|
constructor(
|
|
13
12
|
url: string,
|
|
14
13
|
private readonly options: ConsumeServicesOptions
|
|
15
14
|
) {
|
|
16
|
-
this.httpClient = new HttpClient(url, this.clientId, options.getHeaders
|
|
15
|
+
this.httpClient = new HttpClient(url, this.clientId, options.getHeaders)
|
|
17
16
|
this.remoteSubscriptions = new RemoteSubscriptions()
|
|
18
17
|
|
|
19
18
|
this.connection = new WebSocketConnection(
|
|
20
19
|
options.getSubscriptionsUrl(url),
|
|
21
20
|
this.clientId,
|
|
22
|
-
this.cookies,
|
|
23
21
|
{
|
|
24
22
|
subscriptions: options.subscriptions,
|
|
25
23
|
errorDelayMaxDuration: options.errorDelayMaxDuration,
|
|
@@ -27,7 +25,22 @@ export class RpcClientImpl<S extends Services<S>> implements RpcClient {
|
|
|
27
25
|
pingInterval: options.pingInterval,
|
|
28
26
|
},
|
|
29
27
|
(itemName, parameters, data) => {
|
|
30
|
-
|
|
28
|
+
const ctx: RpcContext = {
|
|
29
|
+
clientId: this.clientId,
|
|
30
|
+
itemName,
|
|
31
|
+
invocationType: InvocationType.Update,
|
|
32
|
+
}
|
|
33
|
+
|
|
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
|
+
)
|
|
31
44
|
},
|
|
32
45
|
() => {
|
|
33
46
|
this.resubscribe()
|
|
@@ -43,7 +56,6 @@ export class RpcClientImpl<S extends Services<S>> implements RpcClient {
|
|
|
43
56
|
private readonly httpClient: HttpClient
|
|
44
57
|
private readonly remoteSubscriptions: RemoteSubscriptions
|
|
45
58
|
private readonly connection: WebSocketConnection
|
|
46
|
-
private readonly cookies: ClientCookies = new ClientCookies()
|
|
47
59
|
|
|
48
60
|
isConnected() {
|
|
49
61
|
return this.connection.isConnected()
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import {log} from "../logger.js"
|
|
2
2
|
import {safeParseJson} from "../utils/json.js"
|
|
3
3
|
import {adelay} from "../utils/promises.js"
|
|
4
|
-
import {environment, Environment} from "../utils/env.js"
|
|
5
|
-
import {ClientCookies} from "../utils/cookies.js"
|
|
6
|
-
import type {IncomingMessage} from "http"
|
|
7
4
|
|
|
8
5
|
export class WebSocketConnection {
|
|
9
6
|
constructor(
|
|
10
7
|
private readonly url: string,
|
|
11
8
|
private readonly clientId: string,
|
|
12
|
-
private readonly cookies: ClientCookies,
|
|
13
9
|
private readonly options: {
|
|
14
10
|
subscriptions: boolean
|
|
15
11
|
reconnectDelay: number
|
|
16
12
|
errorDelayMaxDuration: number
|
|
17
13
|
pingInterval: number | null
|
|
18
14
|
},
|
|
19
|
-
private readonly consume: (
|
|
15
|
+
private readonly consume: (
|
|
16
|
+
itemName: string,
|
|
17
|
+
parameters: unknown[],
|
|
18
|
+
data: unknown
|
|
19
|
+
) => Promise<unknown>,
|
|
20
20
|
private readonly onConnected: () => void,
|
|
21
21
|
private readonly onDisconnected: () => void
|
|
22
22
|
) {
|
|
@@ -122,26 +122,7 @@ export class WebSocketConnection {
|
|
|
122
122
|
private async establishConnection(onDisconnected: () => void): Promise<void> {
|
|
123
123
|
return new Promise(async (resolve, reject) => {
|
|
124
124
|
try {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if ([Environment.ReactNative, Environment.Node].includes(environment)) {
|
|
128
|
-
// use RN WS or node-ws headers extensions to set cookie
|
|
129
|
-
let options = undefined
|
|
130
|
-
|
|
131
|
-
const cookie = this.cookies.getCookieString()
|
|
132
|
-
if (cookie) {
|
|
133
|
-
options = {
|
|
134
|
-
headers: {
|
|
135
|
-
Cookie: cookie,
|
|
136
|
-
},
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
socket = new (WebSocket as any)(this.url, this.clientId, options)
|
|
141
|
-
} else {
|
|
142
|
-
// rely on browser cookie handling
|
|
143
|
-
socket = new WebSocket(this.url, this.clientId)
|
|
144
|
-
}
|
|
125
|
+
const socket = new WebSocket(this.url, this.clientId)
|
|
145
126
|
|
|
146
127
|
let connected = false
|
|
147
128
|
|
|
@@ -217,9 +198,9 @@ export class WebSocketConnection {
|
|
|
217
198
|
|
|
218
199
|
const [itemName, data, ...parameters] = safeParseJson(msg)
|
|
219
200
|
|
|
220
|
-
this.consume(itemName, parameters, data)
|
|
201
|
+
await this.consume(itemName, parameters, data)
|
|
221
202
|
} catch (e) {
|
|
222
|
-
log.
|
|
203
|
+
log.error("Can't handle notification", e)
|
|
223
204
|
}
|
|
224
205
|
}
|
|
225
206
|
|
package/src/client/index.ts
CHANGED
|
@@ -22,6 +22,7 @@ export type ConsumeServicesOptions = {
|
|
|
22
22
|
pingInterval: number | null
|
|
23
23
|
subscriptions: boolean
|
|
24
24
|
middleware: Middleware<RpcContext>[]
|
|
25
|
+
notificationsMiddleware: Middleware<RpcContext>[]
|
|
25
26
|
connectOnCreate: boolean
|
|
26
27
|
onConnected: () => void
|
|
27
28
|
onDisconnected: () => void
|
|
@@ -64,6 +65,7 @@ const defaultOptions: ConsumeServicesOptions = {
|
|
|
64
65
|
pingInterval: null, // if set, should be in-sync with server, ie 30 * 1000
|
|
65
66
|
subscriptions: true,
|
|
66
67
|
middleware: [],
|
|
68
|
+
notificationsMiddleware: [],
|
|
67
69
|
connectOnCreate: false,
|
|
68
70
|
onConnected: () => {},
|
|
69
71
|
onDisconnected: () => {},
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ export type {Middleware} from "./utils/middleware.js"
|
|
|
5
5
|
export {withMiddlewares} from "./utils/middleware.js"
|
|
6
6
|
|
|
7
7
|
export type {RpcServer, PublishServicesOptions} from "./server/index.js"
|
|
8
|
+
export type {HttpServerHooks} from "./server/http.js"
|
|
8
9
|
export {publishServices} from "./server/index.js"
|
|
9
10
|
|
|
10
11
|
export type {ServicesWithTriggers} from "./server/local.js"
|
package/src/rpc.ts
CHANGED
|
@@ -38,19 +38,23 @@ export class RpcServerImpl<S extends Services<S>, C extends RpcContext> implemen
|
|
|
38
38
|
})
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
this.httpServer.addListener("request", (req, res) =>
|
|
41
|
+
this.httpServer.addListener("request", (req, res) => {
|
|
42
|
+
const hooks = {
|
|
43
|
+
call: this.call,
|
|
44
|
+
subscribe: this.subscribe,
|
|
45
|
+
unsubscribe: this.unsubscribe,
|
|
46
|
+
}
|
|
47
|
+
|
|
42
48
|
serveHttpRequest(
|
|
43
49
|
req,
|
|
44
50
|
res,
|
|
45
51
|
options.path,
|
|
46
|
-
|
|
47
|
-
call: this.call,
|
|
48
|
-
subscribe: this.subscribe,
|
|
49
|
-
unsubscribe: this.unsubscribe,
|
|
50
|
-
},
|
|
52
|
+
options.createServerHooks ? options.createServerHooks(hooks, req) : hooks,
|
|
51
53
|
options.createConnectionContext
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
+
).catch((e) => {
|
|
55
|
+
log.warn("Unhandled error serving HTTP request", e)
|
|
56
|
+
})
|
|
57
|
+
})
|
|
54
58
|
}
|
|
55
59
|
|
|
56
60
|
private async createConnectionsServer() {
|
package/src/server/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {ServicesWithTriggers} from "./local.js"
|
|
|
3
3
|
import {Middleware} from "../utils/middleware.js"
|
|
4
4
|
import {RpcServerImpl} from "./RpcServerImpl.js"
|
|
5
5
|
import http, {IncomingMessage} from "http"
|
|
6
|
+
import {HttpServerHooks} from "./http"
|
|
6
7
|
|
|
7
8
|
export async function publishServices<S extends Services<S>, C extends RpcContext>(
|
|
8
9
|
services: S,
|
|
@@ -41,6 +42,7 @@ export type PublishServicesOptions<C extends RpcContext> = {
|
|
|
41
42
|
pingInterval: number
|
|
42
43
|
subscriptions: boolean
|
|
43
44
|
createConnectionContext(req: IncomingMessage): Promise<RpcConnectionContext>
|
|
45
|
+
createServerHooks?(hooks: HttpServerHooks, req: IncomingMessage): HttpServerHooks
|
|
44
46
|
} & (
|
|
45
47
|
| {
|
|
46
48
|
server: http.Server
|
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/connection.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import {assert} from "chai"
|
|
2
|
-
import {createTestClient, startTestServer,
|
|
3
|
-
import WebSocket
|
|
2
|
+
import {createTestClient, startTestServer, testClient, testServer} from "./testUtils.js"
|
|
3
|
+
import WebSocket from "ws"
|
|
4
4
|
import {adelay} from "../src/utils/promises.js"
|
|
5
|
-
import http, {IncomingMessage} from "http"
|
|
6
|
-
import {parseCookies} from "../src/utils/cookies.js"
|
|
7
5
|
|
|
8
6
|
describe("connection", () => {
|
|
9
7
|
it("server close connection on ping timeout", async () => {
|
|
@@ -134,87 +132,4 @@ describe("connection", () => {
|
|
|
134
132
|
|
|
135
133
|
assert.equal(disconnected, true)
|
|
136
134
|
})
|
|
137
|
-
|
|
138
|
-
describe("cookies", () => {
|
|
139
|
-
it("handle cookies in subseq requests", async () => {
|
|
140
|
-
let call = 0
|
|
141
|
-
|
|
142
|
-
let sentClientCookies: Record<string, string> = {}
|
|
143
|
-
|
|
144
|
-
const httpServer = http.createServer((req, res) => {
|
|
145
|
-
const headers: Record<string, string> = {"Content-Type": "text/plain"}
|
|
146
|
-
|
|
147
|
-
if (!call++) {
|
|
148
|
-
headers["Set-Cookie"] = `name=value; path=/; secure; samesite=none; httponly`
|
|
149
|
-
} else {
|
|
150
|
-
sentClientCookies = parseCookies(req.headers.cookie || "")
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
res.writeHead(200, headers)
|
|
154
|
-
|
|
155
|
-
res.end("ok")
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
let resolveStarted = () => {}
|
|
159
|
-
const started = new Promise<void>((r) => (resolveStarted = r))
|
|
160
|
-
httpServer.listen(TEST_PORT, () => resolveStarted())
|
|
161
|
-
await started
|
|
162
|
-
|
|
163
|
-
const client = await createTestClient<{call(): Promise<string>}>()
|
|
164
|
-
|
|
165
|
-
await client.call()
|
|
166
|
-
await client.call()
|
|
167
|
-
|
|
168
|
-
assert.equal(Object.keys(sentClientCookies).length, 1)
|
|
169
|
-
assert.equal(sentClientCookies["name"], "value")
|
|
170
|
-
|
|
171
|
-
let resolveStopped = () => {}
|
|
172
|
-
const stopped = new Promise<void>((r) => (resolveStopped = r))
|
|
173
|
-
httpServer.closeAllConnections()
|
|
174
|
-
httpServer.close(() => resolveStopped())
|
|
175
|
-
await stopped
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
it("set cookie in http, use in ws", async () => {
|
|
179
|
-
const httpServer = http.createServer((req, res) => {
|
|
180
|
-
const headers: Record<string, string> = {
|
|
181
|
-
"Content-Type": "text/plain",
|
|
182
|
-
"Set-Cookie": "name=value; path=/; secure; samesite=none; httponly",
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
res.writeHead(200, headers)
|
|
186
|
-
|
|
187
|
-
res.end("ok")
|
|
188
|
-
})
|
|
189
|
-
|
|
190
|
-
const wss = new WebSocketServer({server: httpServer})
|
|
191
|
-
|
|
192
|
-
let sentClientCookies: Record<string, string> = {}
|
|
193
|
-
wss.on("connection", (ws: unknown, req: IncomingMessage) => {
|
|
194
|
-
sentClientCookies = parseCookies(req.headers.cookie || "")
|
|
195
|
-
})
|
|
196
|
-
|
|
197
|
-
let resolveStarted = () => {}
|
|
198
|
-
const started = new Promise<void>((r) => (resolveStarted = r))
|
|
199
|
-
httpServer.listen(TEST_PORT, () => resolveStarted())
|
|
200
|
-
await started
|
|
201
|
-
|
|
202
|
-
const client = await createTestClient<{call(): Promise<string>}>()
|
|
203
|
-
await client.call()
|
|
204
|
-
|
|
205
|
-
await client.call.subscribe(() => {})
|
|
206
|
-
|
|
207
|
-
assert.equal(Object.keys(sentClientCookies).length, 1)
|
|
208
|
-
assert.equal(sentClientCookies["name"], "value")
|
|
209
|
-
|
|
210
|
-
await testClient!.close()
|
|
211
|
-
|
|
212
|
-
let resolveStopped = () => {}
|
|
213
|
-
const stopped = new Promise<void>((r) => (resolveStopped = r))
|
|
214
|
-
httpServer.closeIdleConnections()
|
|
215
|
-
httpServer.closeAllConnections()
|
|
216
|
-
httpServer.close(() => resolveStopped())
|
|
217
|
-
await stopped
|
|
218
|
-
})
|
|
219
|
-
})
|
|
220
135
|
})
|
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
|
|
|
@@ -109,4 +109,132 @@ describe("middleware", () => {
|
|
|
109
109
|
assert.equal(e.message, "msg")
|
|
110
110
|
}
|
|
111
111
|
})
|
|
112
|
+
|
|
113
|
+
it("notifications middlewares", async () => {
|
|
114
|
+
let count = 1
|
|
115
|
+
|
|
116
|
+
const services = await startTestServer({
|
|
117
|
+
async remote(filter: {param: number}) {
|
|
118
|
+
return count++
|
|
119
|
+
},
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
const client = await createTestClient<typeof services>({
|
|
123
|
+
notificationsMiddleware: [
|
|
124
|
+
(ctx, next, r, params) => {
|
|
125
|
+
assert.ok(ctx.itemName)
|
|
126
|
+
assert.deepEqual(params, [{param: 1}])
|
|
127
|
+
|
|
128
|
+
return next((r as number) + 1)
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
})
|
|
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
|
+
|
|
177
|
+
await client.remote.subscribe((r) => {
|
|
178
|
+
result = r
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
services.remote.trigger()
|
|
182
|
+
|
|
183
|
+
await adelay(20)
|
|
184
|
+
|
|
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) {}
|
|
239
|
+
})
|
|
112
240
|
})
|
package/tests/testUtils.ts
CHANGED
|
@@ -36,6 +36,12 @@ export let testClient: RpcClient | null = null
|
|
|
36
36
|
export async function createTestClient<S extends Services<S>>(
|
|
37
37
|
options?: Partial<ConsumeServicesOptions>
|
|
38
38
|
): Promise<ServicesWithSubscriptions<S>> {
|
|
39
|
+
if (!options) options = {}
|
|
40
|
+
if (!options.middleware) options.middleware = []
|
|
41
|
+
options.middleware = [logMiddleware, ...options.middleware]
|
|
42
|
+
if (!options.notificationsMiddleware) options.notificationsMiddleware = []
|
|
43
|
+
options.notificationsMiddleware = [logUpdatesMiddleware, ...options.notificationsMiddleware]
|
|
44
|
+
|
|
39
45
|
const r = await consumeServices<S>(`http://127.0.0.1:${TEST_PORT}/rpc`, options)
|
|
40
46
|
testClient = r.client
|
|
41
47
|
return r.remote
|
|
@@ -53,3 +59,22 @@ afterEach(async function () {
|
|
|
53
59
|
testServer = null
|
|
54
60
|
}
|
|
55
61
|
})
|
|
62
|
+
|
|
63
|
+
async function logMiddleware(ctx: RpcContext, next: any, ...params: any) {
|
|
64
|
+
try {
|
|
65
|
+
console.log(`OUT:${ctx.invocationType} '${ctx.itemName}'`, ...params)
|
|
66
|
+
|
|
67
|
+
const r = await next()
|
|
68
|
+
|
|
69
|
+
console.log(`IN:${ctx.invocationType} '${ctx.itemName}'`, r)
|
|
70
|
+
return r
|
|
71
|
+
} catch (e) {
|
|
72
|
+
console.log(`ERR:${ctx.invocationType} '${ctx.itemName}'`, e)
|
|
73
|
+
throw e
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function logUpdatesMiddleware(ctx: RpcContext, next: any, res: any) {
|
|
78
|
+
console.log(`IN:${ctx.invocationType} '${ctx.itemName}'`, res)
|
|
79
|
+
return next()
|
|
80
|
+
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { ClientCookies } from "../utils/cookies.js";
|
|
2
|
-
export declare class HttpClient {
|
|
3
|
-
private url;
|
|
4
|
-
private clientId;
|
|
5
|
-
private getHeaders;
|
|
6
|
-
private cookies;
|
|
7
|
-
constructor(url: string, clientId: string, getHeaders: () => Promise<Record<string, string>>, cookies: ClientCookies);
|
|
8
|
-
call(itemName: string, params: unknown[], callTimeout: number): Promise<unknown>;
|
|
9
|
-
subscribe(itemName: string, params: unknown[], callTimeout: number): Promise<unknown>;
|
|
10
|
-
unsubscribe(itemName: string, params: unknown[], callTimeout: number): Promise<void>;
|
|
11
|
-
private getItemUrl;
|
|
12
|
-
private httpRequest;
|
|
13
|
-
}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.HttpClient = void 0;
|
|
4
|
-
const rpc_js_1 = require("../rpc.js");
|
|
5
|
-
const json_js_1 = require("../utils/json.js");
|
|
6
|
-
const env_js_1 = require("../utils/env.js");
|
|
7
|
-
class HttpClient {
|
|
8
|
-
constructor(url, clientId, getHeaders, cookies) {
|
|
9
|
-
this.url = url;
|
|
10
|
-
this.clientId = clientId;
|
|
11
|
-
this.getHeaders = getHeaders;
|
|
12
|
-
this.cookies = cookies;
|
|
13
|
-
}
|
|
14
|
-
async call(itemName, params, callTimeout) {
|
|
15
|
-
return this.httpRequest("POST", itemName, params, callTimeout, await this.getHeaders());
|
|
16
|
-
}
|
|
17
|
-
async subscribe(itemName, params, callTimeout) {
|
|
18
|
-
return this.httpRequest("PUT", itemName, params, callTimeout, await this.getHeaders());
|
|
19
|
-
}
|
|
20
|
-
async unsubscribe(itemName, params, callTimeout) {
|
|
21
|
-
await this.httpRequest("PATCH", itemName, params, callTimeout, await this.getHeaders());
|
|
22
|
-
}
|
|
23
|
-
getItemUrl(itemName) {
|
|
24
|
-
return `${this.url}/${itemName}`;
|
|
25
|
-
}
|
|
26
|
-
async httpRequest(method, itemName, params, callTimeout, headers) {
|
|
27
|
-
const itemUrl = this.getItemUrl(itemName);
|
|
28
|
-
try {
|
|
29
|
-
const { signal, finished } = timeoutSignal(callTimeout);
|
|
30
|
-
if (env_js_1.environment != env_js_1.Environment.Browser) {
|
|
31
|
-
const cookie = this.cookies.getCookieString();
|
|
32
|
-
if (cookie) {
|
|
33
|
-
headers["Cookie"] = cookie;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
const response = await fetch(itemUrl, {
|
|
37
|
-
method,
|
|
38
|
-
headers: {
|
|
39
|
-
"Content-Type": "application/json",
|
|
40
|
-
[rpc_js_1.CLIENT_ID_HEADER]: this.clientId,
|
|
41
|
-
...headers,
|
|
42
|
-
},
|
|
43
|
-
body: (0, json_js_1.safeStringify)(params),
|
|
44
|
-
signal,
|
|
45
|
-
});
|
|
46
|
-
finished();
|
|
47
|
-
if (env_js_1.environment != env_js_1.Environment.Browser) {
|
|
48
|
-
const cookie = response.headers.get("set-cookie");
|
|
49
|
-
if (cookie) {
|
|
50
|
-
this.cookies.updateCookies(cookie.split(","));
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
if (response.status == 204) {
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
const contentType = response.headers.get("content-type");
|
|
57
|
-
const text = await response.text();
|
|
58
|
-
const res = contentType && contentType.includes("application/json") ? (0, json_js_1.safeParseJson)(text) : text;
|
|
59
|
-
if (response.status < 200 || response.status >= 300) {
|
|
60
|
-
const error = new Error(response.statusText);
|
|
61
|
-
Object.assign(error, { code: response.status });
|
|
62
|
-
if (typeof res == "object") {
|
|
63
|
-
Object.assign(error, res);
|
|
64
|
-
}
|
|
65
|
-
throw error;
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
return res;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
catch (e) {
|
|
72
|
-
if (e.message == "Error" || !e.message) {
|
|
73
|
-
e.message = `Error ${e.code} while ${itemUrl}`;
|
|
74
|
-
}
|
|
75
|
-
if (e.message == "fetch failed" && e.cause) {
|
|
76
|
-
e = e.cause;
|
|
77
|
-
}
|
|
78
|
-
if (e.message?.toLowerCase()?.includes("timeout")) {
|
|
79
|
-
// NodeJS undici http client timeout
|
|
80
|
-
const error = new Error("Timeout");
|
|
81
|
-
Object.assign(error, { code: rpc_js_1.RpcErrors.Timeout });
|
|
82
|
-
throw error;
|
|
83
|
-
}
|
|
84
|
-
throw e;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
exports.HttpClient = HttpClient;
|
|
89
|
-
// AbortSignal.timeout polyfill for RN
|
|
90
|
-
function timeoutSignal(time) {
|
|
91
|
-
const controller = new AbortController();
|
|
92
|
-
const timeout = setTimeout(() => controller.abort(new Error("TimeoutError")), time);
|
|
93
|
-
return {
|
|
94
|
-
signal: controller.signal,
|
|
95
|
-
finished: () => clearTimeout(timeout),
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
//# sourceMappingURL=HttpClient.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"HttpClient.js","sourceRoot":"","sources":["../../src/client/HttpClient.ts"],"names":[],"mappings":"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"}
|