@inline-chat/realtime-sdk 0.0.2 → 0.0.4
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/dist/index.d.ts +1 -1
- package/dist/realtime/protocol-client.d.ts +11 -5
- package/dist/realtime/protocol-client.js +86 -34
- package/dist/sdk/inline-sdk-client.d.ts +8 -5
- package/dist/sdk/inline-sdk-client.js +14 -1
- package/dist/sdk/types.d.ts +16 -0
- package/dist/sdk/types.js +6 -0
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { InlineSdkClient } from "./sdk/inline-sdk-client.js";
|
|
2
|
-
export type { InlineSdkClientOptions, InlineSdkSendMessageMedia, InlineSdkSendMessageParams, InlineSdkState, InlineSdkStateStore, InlineSdkUploadFileParams, InlineSdkUploadFileResult, InlineSdkUploadFileType, InlineInboundEvent, RpcInputForMethod, RpcResultForMethod, } from "./sdk/types.js";
|
|
2
|
+
export type { InlineSdkClientOptions, InlineSdkGetMessagesParams, InlineSdkSendMessageMedia, InlineSdkSendMessageParams, InlineSdkState, InlineSdkStateStore, InlineSdkUploadFileParams, InlineSdkUploadFileResult, InlineSdkUploadFileType, InlineInboundEvent, RpcInputForMethod, RpcResultForMethod, } from "./sdk/types.js";
|
|
3
3
|
export type { InlineSdkLogger } from "./sdk/logger.js";
|
|
4
4
|
export { JsonFileStateStore } from "./state/json-file-state-store.js";
|
|
5
5
|
export { serializeStateV1, deserializeStateV1 } from "./state/serde.js";
|
|
@@ -9,6 +9,7 @@ export type ProtocolClientOptions = {
|
|
|
9
9
|
transport: Transport;
|
|
10
10
|
getConnectionInit: () => ConnectionInit | null;
|
|
11
11
|
logger?: InlineSdkLogger;
|
|
12
|
+
defaultRpcTimeoutMs?: number | null;
|
|
12
13
|
};
|
|
13
14
|
export declare class ProtocolClient {
|
|
14
15
|
readonly events: AsyncChannel<ClientEvent>;
|
|
@@ -17,7 +18,8 @@ export declare class ProtocolClient {
|
|
|
17
18
|
state: ClientState;
|
|
18
19
|
private readonly log;
|
|
19
20
|
private readonly getConnectionInit;
|
|
20
|
-
private
|
|
21
|
+
private readonly defaultRpcTimeoutMs;
|
|
22
|
+
private pendingRpcRequests;
|
|
21
23
|
private seq;
|
|
22
24
|
private lastTimestamp;
|
|
23
25
|
private sequence;
|
|
@@ -35,7 +37,7 @@ export declare class ProtocolClient {
|
|
|
35
37
|
}): Promise<void>;
|
|
36
38
|
sendRpc(method: Method, input?: RpcCall["input"]): Promise<bigint>;
|
|
37
39
|
callRpc(method: Method, input?: RpcCall["input"], options?: {
|
|
38
|
-
timeoutMs?: number;
|
|
40
|
+
timeoutMs?: number | null;
|
|
39
41
|
}): Promise<RpcResult["result"]>;
|
|
40
42
|
private startListeners;
|
|
41
43
|
private handleTransportMessage;
|
|
@@ -53,10 +55,14 @@ export declare class ProtocolClient {
|
|
|
53
55
|
private generateId;
|
|
54
56
|
private currentTimestamp;
|
|
55
57
|
private completeRpcResult;
|
|
58
|
+
private ensureOpenForRpc;
|
|
56
59
|
private completeRpcError;
|
|
57
|
-
private
|
|
58
|
-
private
|
|
59
|
-
private
|
|
60
|
+
private failPendingRpcRequest;
|
|
61
|
+
private getAndRemovePendingRpcRequest;
|
|
62
|
+
private cancelAllPendingRpcRequests;
|
|
63
|
+
private resolveRpcTimeoutMs;
|
|
64
|
+
private resendPendingRpcRequests;
|
|
65
|
+
private trySendPendingRpcRequest;
|
|
60
66
|
}
|
|
61
67
|
export declare class ProtocolClientError extends Error {
|
|
62
68
|
constructor(code: "not-authorized" | "not-connected" | "rpc-error" | "stopped" | "timeout", details?: {
|
|
@@ -2,6 +2,7 @@ import { ClientMessage, ServerProtocolMessage } from "@inline-chat/protocol/core
|
|
|
2
2
|
import { AsyncChannel } from "../utils/async-channel.js";
|
|
3
3
|
import { PingPongService } from "./ping-pong.js";
|
|
4
4
|
const emptyRpcInput = { oneofKind: undefined };
|
|
5
|
+
const defaultRpcTimeoutMs = 30_000;
|
|
5
6
|
export class ProtocolClient {
|
|
6
7
|
events = new AsyncChannel();
|
|
7
8
|
transport;
|
|
@@ -9,7 +10,8 @@ export class ProtocolClient {
|
|
|
9
10
|
state = "connecting";
|
|
10
11
|
log;
|
|
11
12
|
getConnectionInit;
|
|
12
|
-
|
|
13
|
+
defaultRpcTimeoutMs;
|
|
14
|
+
pendingRpcRequests = new Map();
|
|
13
15
|
seq = 0;
|
|
14
16
|
lastTimestamp = 0;
|
|
15
17
|
sequence = 0;
|
|
@@ -22,6 +24,7 @@ export class ProtocolClient {
|
|
|
22
24
|
this.transport = options.transport;
|
|
23
25
|
this.log = options.logger ?? {};
|
|
24
26
|
this.getConnectionInit = options.getConnectionInit;
|
|
27
|
+
this.defaultRpcTimeoutMs = normalizeRpcTimeoutMs(options.defaultRpcTimeoutMs, defaultRpcTimeoutMs);
|
|
25
28
|
this.pingPong = new PingPongService({ logger: this.log });
|
|
26
29
|
this.pingPong.configure(this);
|
|
27
30
|
this.startListeners();
|
|
@@ -48,6 +51,7 @@ export class ProtocolClient {
|
|
|
48
51
|
await this.transport.reconnect({ skipDelay: options?.skipDelay });
|
|
49
52
|
}
|
|
50
53
|
async sendRpc(method, input = emptyRpcInput) {
|
|
54
|
+
this.ensureOpenForRpc();
|
|
51
55
|
const message = this.wrapMessage({
|
|
52
56
|
oneofKind: "rpcCall",
|
|
53
57
|
rpcCall: { method, input },
|
|
@@ -61,17 +65,20 @@ export class ProtocolClient {
|
|
|
61
65
|
rpcCall: { method, input },
|
|
62
66
|
});
|
|
63
67
|
return await new Promise((resolve, reject) => {
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
68
|
+
const pending = {
|
|
69
|
+
message,
|
|
70
|
+
resolve,
|
|
71
|
+
reject,
|
|
72
|
+
timeoutMs: this.resolveRpcTimeoutMs(options?.timeoutMs),
|
|
73
|
+
sending: false,
|
|
74
|
+
};
|
|
75
|
+
this.pendingRpcRequests.set(message.id, pending);
|
|
76
|
+
if (pending.timeoutMs !== null) {
|
|
77
|
+
pending.timeout = setTimeout(() => {
|
|
78
|
+
this.failPendingRpcRequest(message.id, new ProtocolClientError("timeout"));
|
|
79
|
+
}, pending.timeoutMs);
|
|
74
80
|
}
|
|
81
|
+
this.trySendPendingRpcRequest(message.id);
|
|
75
82
|
});
|
|
76
83
|
}
|
|
77
84
|
async startListeners() {
|
|
@@ -168,6 +175,7 @@ export class ProtocolClient {
|
|
|
168
175
|
}
|
|
169
176
|
this.connectionAttemptNo = 0;
|
|
170
177
|
this.pingPong.start();
|
|
178
|
+
this.resendPendingRpcRequests();
|
|
171
179
|
}
|
|
172
180
|
async connecting() {
|
|
173
181
|
this.state = "connecting";
|
|
@@ -176,7 +184,7 @@ export class ProtocolClient {
|
|
|
176
184
|
async reset() {
|
|
177
185
|
this.pingPong.stop();
|
|
178
186
|
this.stopAuthenticationTimeout();
|
|
179
|
-
this.
|
|
187
|
+
this.cancelAllPendingRpcRequests(new ProtocolClientError("stopped"));
|
|
180
188
|
this.state = "connecting";
|
|
181
189
|
}
|
|
182
190
|
startAuthenticationTimeout() {
|
|
@@ -195,8 +203,8 @@ export class ProtocolClient {
|
|
|
195
203
|
}
|
|
196
204
|
handleClientFailure() {
|
|
197
205
|
this.pingPong.stop();
|
|
198
|
-
this.cancelAllRpcContinuations(new ProtocolClientError("not-connected"));
|
|
199
206
|
this.stopAuthenticationTimeout();
|
|
207
|
+
this.state = "connecting";
|
|
200
208
|
if (this.reconnectionTimer) {
|
|
201
209
|
clearTimeout(this.reconnectionTimer);
|
|
202
210
|
}
|
|
@@ -239,36 +247,80 @@ export class ProtocolClient {
|
|
|
239
247
|
return Math.floor(Date.now() / 1000) - this.epochSeconds;
|
|
240
248
|
}
|
|
241
249
|
completeRpcResult(msgId, rpcResult) {
|
|
242
|
-
const
|
|
243
|
-
|
|
250
|
+
const pending = this.getAndRemovePendingRpcRequest(msgId);
|
|
251
|
+
pending?.resolve(rpcResult);
|
|
252
|
+
}
|
|
253
|
+
ensureOpenForRpc() {
|
|
254
|
+
if (this.state !== "open") {
|
|
255
|
+
throw new ProtocolClientError("not-connected");
|
|
256
|
+
}
|
|
244
257
|
}
|
|
245
258
|
completeRpcError(msgId, rpcError) {
|
|
246
259
|
const error = new ProtocolClientError("rpc-error", { code: rpcError.code, message: rpcError.message });
|
|
247
|
-
const
|
|
248
|
-
|
|
260
|
+
const pending = this.getAndRemovePendingRpcRequest(msgId);
|
|
261
|
+
pending?.reject(error);
|
|
249
262
|
}
|
|
250
|
-
|
|
251
|
-
const
|
|
252
|
-
|
|
263
|
+
failPendingRpcRequest(msgId, error) {
|
|
264
|
+
const pending = this.getAndRemovePendingRpcRequest(msgId);
|
|
265
|
+
pending?.reject(error);
|
|
253
266
|
}
|
|
254
|
-
|
|
255
|
-
const
|
|
256
|
-
if (!
|
|
267
|
+
getAndRemovePendingRpcRequest(msgId) {
|
|
268
|
+
const pending = this.pendingRpcRequests.get(msgId);
|
|
269
|
+
if (!pending)
|
|
257
270
|
return null;
|
|
258
|
-
if (
|
|
259
|
-
clearTimeout(
|
|
260
|
-
this.
|
|
261
|
-
return
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
for (const
|
|
265
|
-
|
|
266
|
-
if (
|
|
267
|
-
clearTimeout(
|
|
271
|
+
if (pending.timeout)
|
|
272
|
+
clearTimeout(pending.timeout);
|
|
273
|
+
this.pendingRpcRequests.delete(msgId);
|
|
274
|
+
return pending;
|
|
275
|
+
}
|
|
276
|
+
cancelAllPendingRpcRequests(error) {
|
|
277
|
+
for (const pending of this.pendingRpcRequests.values()) {
|
|
278
|
+
pending.reject(error);
|
|
279
|
+
if (pending.timeout)
|
|
280
|
+
clearTimeout(pending.timeout);
|
|
281
|
+
}
|
|
282
|
+
this.pendingRpcRequests.clear();
|
|
283
|
+
}
|
|
284
|
+
resolveRpcTimeoutMs(timeoutMs) {
|
|
285
|
+
return normalizeRpcTimeoutMs(timeoutMs, this.defaultRpcTimeoutMs);
|
|
286
|
+
}
|
|
287
|
+
resendPendingRpcRequests() {
|
|
288
|
+
for (const msgId of this.pendingRpcRequests.keys()) {
|
|
289
|
+
this.trySendPendingRpcRequest(msgId);
|
|
268
290
|
}
|
|
269
|
-
|
|
291
|
+
}
|
|
292
|
+
trySendPendingRpcRequest(msgId) {
|
|
293
|
+
const pending = this.pendingRpcRequests.get(msgId);
|
|
294
|
+
if (!pending)
|
|
295
|
+
return;
|
|
296
|
+
if (this.state !== "open")
|
|
297
|
+
return;
|
|
298
|
+
if (pending.sending)
|
|
299
|
+
return;
|
|
300
|
+
pending.sending = true;
|
|
301
|
+
void this.transport
|
|
302
|
+
.send(pending.message)
|
|
303
|
+
.catch((error) => {
|
|
304
|
+
this.log.warn?.("Failed to send RPC request; waiting for reconnect", error);
|
|
305
|
+
this.handleClientFailure();
|
|
306
|
+
})
|
|
307
|
+
.finally(() => {
|
|
308
|
+
pending.sending = false;
|
|
309
|
+
});
|
|
270
310
|
}
|
|
271
311
|
}
|
|
312
|
+
const normalizeRpcTimeoutMs = (timeoutMs, fallback) => {
|
|
313
|
+
const resolved = timeoutMs === undefined ? fallback : timeoutMs;
|
|
314
|
+
if (resolved == null)
|
|
315
|
+
return null;
|
|
316
|
+
if (resolved === Number.POSITIVE_INFINITY)
|
|
317
|
+
return null;
|
|
318
|
+
if (!Number.isFinite(resolved))
|
|
319
|
+
return null;
|
|
320
|
+
if (resolved <= 0)
|
|
321
|
+
return null;
|
|
322
|
+
return Math.floor(resolved);
|
|
323
|
+
};
|
|
272
324
|
export class ProtocolClientError extends Error {
|
|
273
325
|
constructor(code, details) {
|
|
274
326
|
super(details?.message ?? code);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Method, type Peer, type RpcCall, type RpcResult } from "@inline-chat/protocol/core";
|
|
1
|
+
import { Method, type Message, type Peer, type RpcCall, type RpcResult } from "@inline-chat/protocol/core";
|
|
2
2
|
import { type InlineIdLike } from "../ids.js";
|
|
3
|
-
import type { InlineSdkClientOptions, InlineInboundEvent, InlineSdkSendMessageParams, InlineSdkState, InlineSdkUploadFileParams, InlineSdkUploadFileResult, MappedMethod, RpcInputForMethod, RpcResultForMethod } from "./types.js";
|
|
3
|
+
import type { InlineSdkClientOptions, InlineInboundEvent, InlineSdkGetMessagesParams, InlineSdkSendMessageParams, InlineSdkState, InlineSdkUploadFileParams, InlineSdkUploadFileResult, MappedMethod, RpcInputForMethod, RpcResultForMethod } from "./types.js";
|
|
4
4
|
export declare class InlineSdkClient {
|
|
5
5
|
private readonly options;
|
|
6
6
|
private readonly log;
|
|
@@ -33,6 +33,9 @@ export declare class InlineSdkClient {
|
|
|
33
33
|
peer?: Peer;
|
|
34
34
|
title: string;
|
|
35
35
|
}>;
|
|
36
|
+
getMessages(params: InlineSdkGetMessagesParams): Promise<{
|
|
37
|
+
messages: Message[];
|
|
38
|
+
}>;
|
|
36
39
|
sendMessage(params: InlineSdkSendMessageParams): Promise<{
|
|
37
40
|
messageId: bigint | null;
|
|
38
41
|
}>;
|
|
@@ -42,13 +45,13 @@ export declare class InlineSdkClient {
|
|
|
42
45
|
typing: boolean;
|
|
43
46
|
}): Promise<void>;
|
|
44
47
|
invokeRaw(method: Method, input?: RpcCall["input"], options?: {
|
|
45
|
-
timeoutMs?: number;
|
|
48
|
+
timeoutMs?: number | null;
|
|
46
49
|
}): Promise<RpcResult["result"]>;
|
|
47
50
|
invokeUncheckedRaw(method: Method, input?: RpcCall["input"], options?: {
|
|
48
|
-
timeoutMs?: number;
|
|
51
|
+
timeoutMs?: number | null;
|
|
49
52
|
}): Promise<RpcResult["result"]>;
|
|
50
53
|
invoke<M extends MappedMethod>(method: M, input: RpcInputForMethod<M>, options?: {
|
|
51
|
-
timeoutMs?: number;
|
|
54
|
+
timeoutMs?: number | null;
|
|
52
55
|
}): Promise<RpcResultForMethod<M>>;
|
|
53
56
|
private assertMethodInputMatch;
|
|
54
57
|
private assertMethodResultMatch;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { GetChatInput, GetMeInput, GetUpdatesInput, GetUpdatesResult_ResultType, GetUpdatesStateInput, InputPeer, MessageSendMode, Method, UpdateBucket, UpdateComposeAction_ComposeAction, } from "@inline-chat/protocol/core";
|
|
1
|
+
import { GetChatInput, GetMessagesInput, GetMeInput, GetUpdatesInput, GetUpdatesResult_ResultType, GetUpdatesStateInput, InputPeer, MessageSendMode, Method, UpdateBucket, UpdateComposeAction_ComposeAction, } from "@inline-chat/protocol/core";
|
|
2
2
|
import { asInlineId } from "../ids.js";
|
|
3
3
|
import { AsyncChannel } from "../utils/async-channel.js";
|
|
4
4
|
import { ProtocolClient } from "../realtime/protocol-client.js";
|
|
@@ -53,6 +53,7 @@ export class InlineSdkClient {
|
|
|
53
53
|
clientVersion: getSdkVersion(),
|
|
54
54
|
}),
|
|
55
55
|
logger: options.logger,
|
|
56
|
+
defaultRpcTimeoutMs: options.rpcTimeoutMs,
|
|
56
57
|
});
|
|
57
58
|
void this.startListeners();
|
|
58
59
|
}
|
|
@@ -146,6 +147,18 @@ export class InlineSdkClient {
|
|
|
146
147
|
throw new Error("getChat: missing chat");
|
|
147
148
|
return { chatId: chat.id, peer: chat.peerId, title: chat.title };
|
|
148
149
|
}
|
|
150
|
+
async getMessages(params) {
|
|
151
|
+
const peerId = this.inputPeerFromTarget(params, "getMessages");
|
|
152
|
+
const messageIds = params.messageIds.map((messageId, index) => asInlineId(messageId, `messageIds[${index}]`));
|
|
153
|
+
const result = await this.invoke(Method.GET_MESSAGES, {
|
|
154
|
+
oneofKind: "getMessages",
|
|
155
|
+
getMessages: GetMessagesInput.create({
|
|
156
|
+
peerId,
|
|
157
|
+
messageIds,
|
|
158
|
+
}),
|
|
159
|
+
});
|
|
160
|
+
return { messages: result.getMessages.messages };
|
|
161
|
+
}
|
|
149
162
|
async sendMessage(params) {
|
|
150
163
|
if (params.entities != null && params.parseMarkdown != null) {
|
|
151
164
|
throw new Error("sendMessage: provide either `entities` or `parseMarkdown`, not both");
|
package/dist/sdk/types.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type { Transport } from "../realtime/transport.js";
|
|
|
6
6
|
export type InlineSdkClientOptions = {
|
|
7
7
|
baseUrl?: string;
|
|
8
8
|
token: string;
|
|
9
|
+
rpcTimeoutMs?: number | null;
|
|
9
10
|
logger?: InlineSdkLogger;
|
|
10
11
|
state?: InlineSdkStateStore;
|
|
11
12
|
transport?: Transport;
|
|
@@ -40,6 +41,15 @@ export type InlineSdkSendMessageParams = {
|
|
|
40
41
|
sendMode?: "silent";
|
|
41
42
|
entities?: MessageEntities;
|
|
42
43
|
};
|
|
44
|
+
export type InlineSdkGetMessagesParams = {
|
|
45
|
+
chatId: InlineIdLike;
|
|
46
|
+
userId?: never;
|
|
47
|
+
messageIds: InlineIdLike[];
|
|
48
|
+
} | {
|
|
49
|
+
userId: InlineIdLike;
|
|
50
|
+
chatId?: never;
|
|
51
|
+
messageIds: InlineIdLike[];
|
|
52
|
+
};
|
|
43
53
|
export type InlineSdkBinaryInput = Blob | Uint8Array | ArrayBuffer | SharedArrayBuffer;
|
|
44
54
|
export type InlineSdkUploadFileType = "photo" | "video" | "document";
|
|
45
55
|
export type InlineSdkUploadFileParams = {
|
|
@@ -151,6 +161,9 @@ export declare const rpcInputKindByMethod: {
|
|
|
151
161
|
readonly 33: "listBots";
|
|
152
162
|
readonly 34: "revealBotToken";
|
|
153
163
|
readonly 35: "moveThread";
|
|
164
|
+
readonly 36: "rotateBotToken";
|
|
165
|
+
readonly 37: "updateBotProfile";
|
|
166
|
+
readonly 38: "getMessages";
|
|
154
167
|
};
|
|
155
168
|
export declare const rpcResultKindByMethod: {
|
|
156
169
|
readonly 0: undefined;
|
|
@@ -189,6 +202,9 @@ export declare const rpcResultKindByMethod: {
|
|
|
189
202
|
readonly 33: "listBots";
|
|
190
203
|
readonly 34: "revealBotToken";
|
|
191
204
|
readonly 35: "moveThread";
|
|
205
|
+
readonly 36: "rotateBotToken";
|
|
206
|
+
readonly 37: "updateBotProfile";
|
|
207
|
+
readonly 38: "getMessages";
|
|
192
208
|
};
|
|
193
209
|
type RpcInputKindByMethod = typeof rpcInputKindByMethod;
|
|
194
210
|
type RpcResultKindByMethod = typeof rpcResultKindByMethod;
|
package/dist/sdk/types.js
CHANGED
|
@@ -35,6 +35,9 @@ export const rpcInputKindByMethod = {
|
|
|
35
35
|
33: "listBots",
|
|
36
36
|
34: "revealBotToken",
|
|
37
37
|
35: "moveThread",
|
|
38
|
+
36: "rotateBotToken",
|
|
39
|
+
37: "updateBotProfile",
|
|
40
|
+
38: "getMessages",
|
|
38
41
|
};
|
|
39
42
|
export const rpcResultKindByMethod = {
|
|
40
43
|
0: undefined, // UNSPECIFIED
|
|
@@ -73,4 +76,7 @@ export const rpcResultKindByMethod = {
|
|
|
73
76
|
33: "listBots",
|
|
74
77
|
34: "revealBotToken",
|
|
75
78
|
35: "moveThread",
|
|
79
|
+
36: "rotateBotToken",
|
|
80
|
+
37: "updateBotProfile",
|
|
81
|
+
38: "getMessages",
|
|
76
82
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inline-chat/realtime-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"test": "vitest run --coverage"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@inline-chat/protocol": "^0.0.
|
|
23
|
+
"@inline-chat/protocol": "^0.0.2",
|
|
24
24
|
"ws": "^8.18.3"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|