@push-rpc/next
Advanced tools
Comparing version 2.0.7 to 2.0.8
@@ -5,2 +5,3 @@ import { RpcContext, Services } from "../rpc.js"; | ||
export type RpcClient = { | ||
readonly clientId: string; | ||
isConnected(): boolean; | ||
@@ -7,0 +8,0 @@ close(): Promise<void>; |
@@ -7,3 +7,3 @@ import { Services } from "../rpc.js"; | ||
constructor(url: string, options: ConsumeServicesOptions); | ||
private readonly clientId; | ||
readonly clientId: string; | ||
private readonly httpClient; | ||
@@ -10,0 +10,0 @@ private readonly remoteSubscriptions; |
@@ -16,5 +16,6 @@ export declare class WebSocketConnection { | ||
close(): Promise<void>; | ||
private waitConnectionPromise; | ||
/** | ||
* Connect to the server, on each disconnect try to disconnect. | ||
* Resolves at first successful connect. Reconnection loop continues even after resolution | ||
* Resolves at next successful connect. Reconnection loop continues even after resolution | ||
* Never rejects | ||
@@ -21,0 +22,0 @@ */ |
@@ -17,3 +17,3 @@ "use strict"; | ||
this.socket = null; | ||
this.disconnectedMark = true; | ||
this.disconnectedMark = false; | ||
this.pingTimeout = null; | ||
@@ -33,3 +33,3 @@ this.clientId = clientId; | ||
* Connect to the server, on each disconnect try to disconnect. | ||
* Resolves at first successful connect. Reconnection loop continues even after resolution | ||
* Resolves at next successful connect. Reconnection loop continues even after resolution | ||
* Never rejects | ||
@@ -43,20 +43,22 @@ */ | ||
// already started connecting | ||
if (this.socket || !this.disconnectedMark) | ||
return Promise.resolve(); | ||
if (this.waitConnectionPromise) | ||
return this.waitConnectionPromise; | ||
// start connection process | ||
this.disconnectedMark = false; | ||
return new Promise(async (resolve) => { | ||
let onFirstConnection = resolve; | ||
let errorDelay = 0; | ||
let resolveConnectionPromise; | ||
let errorDelay = 0; | ||
this.waitConnectionPromise = new Promise(async (resolve) => { | ||
resolveConnectionPromise = resolve; | ||
while (true) { | ||
// connect, and wait for ... | ||
await new Promise((resolve) => { | ||
// 1. ...disconnected | ||
const connectionPromise = this.establishConnection(resolve); | ||
const connectionPromise = this.establishConnection(() => { | ||
// 1. ...disconnected | ||
// recreate promise so new clients will wait for new connection | ||
this.waitConnectionPromise = new Promise((resolve) => (resolveConnectionPromise = resolve)); | ||
resolve(); | ||
}); | ||
connectionPromise.then(() => { | ||
// first reconnect after successful connection is done without delay | ||
errorDelay = 0; | ||
// signal about first connection | ||
onFirstConnection(); | ||
onFirstConnection = () => { }; | ||
resolveConnectionPromise(); | ||
}, (e) => { | ||
@@ -80,2 +82,3 @@ logger_js_1.log.warn("Unable to connect WS", e); | ||
}); | ||
return this.waitConnectionPromise; | ||
} | ||
@@ -82,0 +85,0 @@ isConnected() { |
{ | ||
"name": "@push-rpc/next", | ||
"version": "2.0.7", | ||
"version": "2.0.8", | ||
"main": "dist/index.js", | ||
@@ -5,0 +5,0 @@ "types": "dist/index.d.ts", |
@@ -0,1 +1,3 @@ | ||
Client/server framework | ||
## Glossary | ||
@@ -2,0 +4,0 @@ |
@@ -7,2 +7,4 @@ import {RpcContext, Services} from "../rpc.js" | ||
export type RpcClient = { | ||
readonly clientId: string | ||
isConnected(): boolean | ||
@@ -69,3 +71,3 @@ close(): Promise<void> | ||
getHeaders: async () => ({}), | ||
getSubscriptionsUrl(url: string): string { | ||
@@ -72,0 +74,0 @@ return url.replace(/^https(.*)/, "wss$1").replace(/^http(.*)/, "ws$1") |
@@ -40,3 +40,3 @@ import {CallOptions, InvocationType, RpcContext, Services} from "../rpc.js" | ||
private readonly clientId = nanoid() | ||
public readonly clientId = nanoid() | ||
private readonly httpClient: HttpClient | ||
@@ -43,0 +43,0 @@ private readonly remoteSubscriptions: RemoteSubscriptions |
@@ -36,5 +36,7 @@ import {log} from "../logger.js" | ||
private waitConnectionPromise: Promise<void> | undefined | ||
/** | ||
* Connect to the server, on each disconnect try to disconnect. | ||
* Resolves at first successful connect. Reconnection loop continues even after resolution | ||
* Resolves at next successful connect. Reconnection loop continues even after resolution | ||
* Never rejects | ||
@@ -49,17 +51,26 @@ */ | ||
// already started connecting | ||
if (this.socket || !this.disconnectedMark) return Promise.resolve() | ||
if (this.waitConnectionPromise) return this.waitConnectionPromise | ||
// start connection process | ||
this.disconnectedMark = false | ||
return new Promise<void>(async (resolve) => { | ||
let onFirstConnection = resolve | ||
let errorDelay = 0 | ||
let resolveConnectionPromise: () => void | ||
let errorDelay = 0 | ||
this.waitConnectionPromise = new Promise(async (resolve) => { | ||
resolveConnectionPromise = resolve | ||
while (true) { | ||
// connect, and wait for ... | ||
await new Promise<void>((resolve) => { | ||
// 1. ...disconnected | ||
const connectionPromise = this.establishConnection(resolve) | ||
const connectionPromise = this.establishConnection(() => { | ||
// 1. ...disconnected | ||
// recreate promise so new clients will wait for new connection | ||
this.waitConnectionPromise = new Promise( | ||
(resolve) => (resolveConnectionPromise = resolve) | ||
) | ||
resolve() | ||
}) | ||
connectionPromise.then( | ||
@@ -70,5 +81,3 @@ () => { | ||
// signal about first connection | ||
onFirstConnection() | ||
onFirstConnection = () => {} | ||
resolveConnectionPromise() | ||
}, | ||
@@ -99,2 +108,4 @@ (e) => { | ||
}) | ||
return this.waitConnectionPromise | ||
} | ||
@@ -169,3 +180,3 @@ | ||
private socket: WebSocket | null = null | ||
private disconnectedMark = true | ||
private disconnectedMark = false | ||
private pingTimeout: NodeJS.Timeout | null = null | ||
@@ -172,0 +183,0 @@ |
import {assert} from "chai" | ||
import {createTestClient, startTestServer, testClient, testServer} from "./testUtils.js" | ||
import {adelay} from "../src/utils/promises.js" | ||
import {CallOptions, RpcErrors} from "../src/index.js" | ||
import {CallOptions, RpcConnectionContext, RpcErrors} from "../src/index.js" | ||
import {IncomingMessage} from "http" | ||
import {CLIENT_ID_HEADER} from "../src/rpc.js" | ||
@@ -596,2 +598,61 @@ describe("Subscriptions", () => { | ||
}) | ||
it("subscribe waits for connection", async () => { | ||
const delay = 50 | ||
let connectedClients = 0 | ||
let serverCalled = 0 | ||
const services = await startTestServer( | ||
{ | ||
test: { | ||
async op(params: {key: number}): Promise<number> { | ||
serverCalled++ | ||
return 1 | ||
}, | ||
}, | ||
}, | ||
{ | ||
async createConnectionContext(req: IncomingMessage): Promise<RpcConnectionContext> { | ||
const header = req.headers[CLIENT_ID_HEADER] | ||
connectedClients++ | ||
return { | ||
clientId: (Array.isArray(header) ? header[0] : header) || "anon", | ||
} | ||
}, | ||
} | ||
) | ||
const client = await createTestClient<typeof services>({ | ||
callTimeout: 2 * delay, | ||
}) | ||
let received1 | ||
let received2 | ||
client.test.op.subscribe( | ||
(val) => { | ||
received1 = val | ||
}, | ||
{key: 1} | ||
) | ||
await adelay(40) | ||
client.test.op.subscribe( | ||
(val) => { | ||
received2 = val | ||
}, | ||
{key: 2} | ||
) | ||
await adelay(1.5 * delay) | ||
assert.equal(received1, 1) | ||
assert.equal(received2, 1) | ||
assert.equal(serverCalled, 2) | ||
assert.equal(testServer!._allSubscriptions().length, 2) | ||
}) | ||
}) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
209369
4664
45