krawlet-js 2.1.2 → 2.2.1
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.cjs +315 -2
- package/dist/index.d.cts +84 -1
- package/dist/index.d.ts +84 -1
- package/dist/index.js +312 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -31,7 +31,9 @@ __export(index_exports, {
|
|
|
31
31
|
ReportsResource: () => ReportsResource,
|
|
32
32
|
ShopsResource: () => ShopsResource,
|
|
33
33
|
StorageResource: () => StorageResource,
|
|
34
|
-
TransfersResource: () => TransfersResource
|
|
34
|
+
TransfersResource: () => TransfersResource,
|
|
35
|
+
WebsocketProtocolError: () => WebsocketProtocolError,
|
|
36
|
+
WebsocketsResource: () => WebsocketsResource
|
|
35
37
|
});
|
|
36
38
|
module.exports = __toCommonJS(index_exports);
|
|
37
39
|
|
|
@@ -88,6 +90,12 @@ var HttpClient = class {
|
|
|
88
90
|
getRateLimit() {
|
|
89
91
|
return this.lastRateLimit;
|
|
90
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Get configured API base URL
|
|
95
|
+
*/
|
|
96
|
+
getBaseUrl() {
|
|
97
|
+
return this.config.baseUrl;
|
|
98
|
+
}
|
|
91
99
|
/**
|
|
92
100
|
* Make an HTTP request
|
|
93
101
|
*/
|
|
@@ -828,6 +836,308 @@ var TransfersResource = class {
|
|
|
828
836
|
}
|
|
829
837
|
};
|
|
830
838
|
|
|
839
|
+
// src/resources/websockets.ts
|
|
840
|
+
var WebsocketProtocolError = class extends Error {
|
|
841
|
+
constructor(message, code, requestId) {
|
|
842
|
+
super(message);
|
|
843
|
+
this.name = "WebsocketProtocolError";
|
|
844
|
+
this.code = code;
|
|
845
|
+
this.requestId = requestId;
|
|
846
|
+
}
|
|
847
|
+
};
|
|
848
|
+
var WebsocketsResource = class {
|
|
849
|
+
constructor(client, options = {}) {
|
|
850
|
+
this.client = client;
|
|
851
|
+
this.ws = null;
|
|
852
|
+
this.connectPromise = null;
|
|
853
|
+
this.pending = /* @__PURE__ */ new Map();
|
|
854
|
+
this.transferUpdateListeners = /* @__PURE__ */ new Set();
|
|
855
|
+
this.nextRequestId = 1;
|
|
856
|
+
this.connectTimeoutMs = options.connectTimeoutMs ?? 1e4;
|
|
857
|
+
this.requestTimeoutMs = options.requestTimeoutMs ?? 15e3;
|
|
858
|
+
this.webSocketFactory = options.webSocketFactory ?? this.getDefaultWebSocketFactory();
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* Open websocket connection and wait for initial hello
|
|
862
|
+
*/
|
|
863
|
+
async start() {
|
|
864
|
+
if (this.connectPromise) {
|
|
865
|
+
return this.connectPromise;
|
|
866
|
+
}
|
|
867
|
+
const url = this.buildWebSocketUrl();
|
|
868
|
+
this.connectPromise = new Promise((resolve, reject) => {
|
|
869
|
+
const ws = this.webSocketFactory(url);
|
|
870
|
+
this.ws = ws;
|
|
871
|
+
const timeout = setTimeout(() => {
|
|
872
|
+
this.connectPromise = null;
|
|
873
|
+
this.ws = null;
|
|
874
|
+
try {
|
|
875
|
+
ws.close();
|
|
876
|
+
} catch {
|
|
877
|
+
}
|
|
878
|
+
reject(new Error(`WebSocket connection timeout after ${this.connectTimeoutMs}ms`));
|
|
879
|
+
}, this.connectTimeoutMs);
|
|
880
|
+
ws.onopen = () => {
|
|
881
|
+
};
|
|
882
|
+
ws.onmessage = (event) => {
|
|
883
|
+
this.handleMessageEvent(event.data, resolve, reject, timeout);
|
|
884
|
+
};
|
|
885
|
+
ws.onerror = () => {
|
|
886
|
+
clearTimeout(timeout);
|
|
887
|
+
this.connectPromise = null;
|
|
888
|
+
this.ws = null;
|
|
889
|
+
reject(new Error("WebSocket connection failed"));
|
|
890
|
+
};
|
|
891
|
+
ws.onclose = (event) => {
|
|
892
|
+
clearTimeout(timeout);
|
|
893
|
+
this.connectPromise = null;
|
|
894
|
+
this.ws = null;
|
|
895
|
+
this.rejectAllPending(
|
|
896
|
+
new Error(`WebSocket closed (${event.code}${event.reason ? `: ${event.reason}` : ""})`)
|
|
897
|
+
);
|
|
898
|
+
};
|
|
899
|
+
});
|
|
900
|
+
return this.connectPromise;
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* Authenticate this connection as a client key
|
|
904
|
+
*/
|
|
905
|
+
async authenticate(token, id = this.createRequestId()) {
|
|
906
|
+
const response = await this.sendAndWait("auth", {
|
|
907
|
+
type: "auth",
|
|
908
|
+
id,
|
|
909
|
+
token
|
|
910
|
+
});
|
|
911
|
+
if (response.type !== "auth_ok") {
|
|
912
|
+
throw new Error(`Unexpected auth response type: ${response.type}`);
|
|
913
|
+
}
|
|
914
|
+
return response.payload;
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
917
|
+
* Ping the websocket server
|
|
918
|
+
*/
|
|
919
|
+
async ping(id = this.createRequestId()) {
|
|
920
|
+
const response = await this.sendAndWait("ping", {
|
|
921
|
+
type: "ping",
|
|
922
|
+
id
|
|
923
|
+
});
|
|
924
|
+
if (response.type !== "pong") {
|
|
925
|
+
throw new Error(`Unexpected ping response type: ${response.type}`);
|
|
926
|
+
}
|
|
927
|
+
return response.payload;
|
|
928
|
+
}
|
|
929
|
+
/**
|
|
930
|
+
* Create a transfer over websocket
|
|
931
|
+
*/
|
|
932
|
+
async createTransfer(payload, id = this.createRequestId()) {
|
|
933
|
+
const response = await this.sendAndWait("create_transfer", {
|
|
934
|
+
type: "create_transfer",
|
|
935
|
+
id,
|
|
936
|
+
payload
|
|
937
|
+
});
|
|
938
|
+
if (response.type !== "create_transfer_ok") {
|
|
939
|
+
throw new Error(`Unexpected create_transfer response type: ${response.type}`);
|
|
940
|
+
}
|
|
941
|
+
return response.payload;
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* Fetch transfer by id
|
|
945
|
+
*/
|
|
946
|
+
async getTransfer(transferId, id = this.createRequestId()) {
|
|
947
|
+
const response = await this.sendAndWait("get_transfer", {
|
|
948
|
+
type: "get_transfer",
|
|
949
|
+
id,
|
|
950
|
+
payload: { transferId }
|
|
951
|
+
});
|
|
952
|
+
if (response.type !== "get_transfer_ok") {
|
|
953
|
+
throw new Error(`Unexpected get_transfer response type: ${response.type}`);
|
|
954
|
+
}
|
|
955
|
+
return response.payload;
|
|
956
|
+
}
|
|
957
|
+
/**
|
|
958
|
+
* Cancel transfer by id
|
|
959
|
+
*/
|
|
960
|
+
async cancelTransfer(transferId, id = this.createRequestId()) {
|
|
961
|
+
const response = await this.sendAndWait("cancel_transfer", {
|
|
962
|
+
type: "cancel_transfer",
|
|
963
|
+
id,
|
|
964
|
+
payload: { transferId }
|
|
965
|
+
});
|
|
966
|
+
if (response.type !== "cancel_transfer_ok") {
|
|
967
|
+
throw new Error(`Unexpected cancel_transfer response type: ${response.type}`);
|
|
968
|
+
}
|
|
969
|
+
return response.payload;
|
|
970
|
+
}
|
|
971
|
+
/**
|
|
972
|
+
* List transfers for this client
|
|
973
|
+
*/
|
|
974
|
+
async listTransfers(id = this.createRequestId()) {
|
|
975
|
+
const response = await this.sendAndWait("list_transfers", {
|
|
976
|
+
type: "list_transfers",
|
|
977
|
+
id,
|
|
978
|
+
payload: {}
|
|
979
|
+
});
|
|
980
|
+
if (response.type !== "list_transfers_ok") {
|
|
981
|
+
throw new Error(`Unexpected list_transfers response type: ${response.type}`);
|
|
982
|
+
}
|
|
983
|
+
return response.payload.transfers;
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* List targets available to this client
|
|
987
|
+
*/
|
|
988
|
+
async listTargets(id = this.createRequestId()) {
|
|
989
|
+
const response = await this.sendAndWait("list_targets", {
|
|
990
|
+
type: "list_targets",
|
|
991
|
+
id,
|
|
992
|
+
payload: {}
|
|
993
|
+
});
|
|
994
|
+
if (response.type !== "list_targets_ok") {
|
|
995
|
+
throw new Error(`Unexpected list_targets response type: ${response.type}`);
|
|
996
|
+
}
|
|
997
|
+
return response.payload.targets;
|
|
998
|
+
}
|
|
999
|
+
/**
|
|
1000
|
+
* Subscribe to unsolicited transfer updates.
|
|
1001
|
+
* Returns an unsubscribe function.
|
|
1002
|
+
*/
|
|
1003
|
+
onTransferUpdate(listener) {
|
|
1004
|
+
this.transferUpdateListeners.add(listener);
|
|
1005
|
+
return () => {
|
|
1006
|
+
this.transferUpdateListeners.delete(listener);
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
/**
|
|
1010
|
+
* Close the websocket connection
|
|
1011
|
+
*/
|
|
1012
|
+
close(code, reason) {
|
|
1013
|
+
if (!this.ws) {
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
try {
|
|
1017
|
+
this.ws.close(code, reason);
|
|
1018
|
+
} finally {
|
|
1019
|
+
this.ws = null;
|
|
1020
|
+
this.connectPromise = null;
|
|
1021
|
+
this.rejectAllPending(new Error("WebSocket connection closed"));
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
/**
|
|
1025
|
+
* True if the websocket is currently open
|
|
1026
|
+
*/
|
|
1027
|
+
isConnected() {
|
|
1028
|
+
return Boolean(this.ws && this.ws.readyState === 1);
|
|
1029
|
+
}
|
|
1030
|
+
async sendAndWait(messageType, message) {
|
|
1031
|
+
if (!this.ws || this.ws.readyState !== 1) {
|
|
1032
|
+
await this.start();
|
|
1033
|
+
}
|
|
1034
|
+
if (!this.ws || this.ws.readyState !== 1) {
|
|
1035
|
+
throw new Error("WebSocket is not connected");
|
|
1036
|
+
}
|
|
1037
|
+
const requestId = message.id;
|
|
1038
|
+
const responsePromise = new Promise((resolve, reject) => {
|
|
1039
|
+
const timeout = setTimeout(() => {
|
|
1040
|
+
this.pending.delete(requestId);
|
|
1041
|
+
reject(new Error(`WebSocket ${messageType} request timed out after ${this.requestTimeoutMs}ms`));
|
|
1042
|
+
}, this.requestTimeoutMs);
|
|
1043
|
+
this.pending.set(requestId, {
|
|
1044
|
+
resolve: (value) => resolve(value),
|
|
1045
|
+
reject,
|
|
1046
|
+
timeout
|
|
1047
|
+
});
|
|
1048
|
+
});
|
|
1049
|
+
this.ws.send(JSON.stringify(message));
|
|
1050
|
+
return responsePromise;
|
|
1051
|
+
}
|
|
1052
|
+
handleMessageEvent(rawData, connectResolve, connectReject, connectTimeout) {
|
|
1053
|
+
const parsed = this.safeParse(rawData);
|
|
1054
|
+
if (!parsed) {
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
if (parsed.type === "hello") {
|
|
1058
|
+
clearTimeout(connectTimeout);
|
|
1059
|
+
connectResolve(parsed);
|
|
1060
|
+
return;
|
|
1061
|
+
}
|
|
1062
|
+
if (parsed.type === "transfer_update") {
|
|
1063
|
+
this.emitTransferUpdate(parsed.payload);
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
1066
|
+
if (typeof parsed.id === "undefined") {
|
|
1067
|
+
if (parsed.type === "error") {
|
|
1068
|
+
const payload = parsed.payload;
|
|
1069
|
+
connectReject(new WebsocketProtocolError(payload.message, payload.code));
|
|
1070
|
+
}
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
const pending = this.pending.get(parsed.id);
|
|
1074
|
+
if (!pending) {
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
clearTimeout(pending.timeout);
|
|
1078
|
+
this.pending.delete(parsed.id);
|
|
1079
|
+
if (parsed.type === "error") {
|
|
1080
|
+
const payload = parsed.payload;
|
|
1081
|
+
pending.reject(new WebsocketProtocolError(payload.message, payload.code, parsed.id));
|
|
1082
|
+
return;
|
|
1083
|
+
}
|
|
1084
|
+
pending.resolve(parsed);
|
|
1085
|
+
}
|
|
1086
|
+
emitTransferUpdate(transfer) {
|
|
1087
|
+
for (const listener of this.transferUpdateListeners) {
|
|
1088
|
+
listener(transfer);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
safeParse(rawData) {
|
|
1092
|
+
try {
|
|
1093
|
+
const parsed = JSON.parse(rawData);
|
|
1094
|
+
if (!parsed || typeof parsed !== "object" || typeof parsed.type !== "string") {
|
|
1095
|
+
return null;
|
|
1096
|
+
}
|
|
1097
|
+
return parsed;
|
|
1098
|
+
} catch {
|
|
1099
|
+
return null;
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
createRequestId() {
|
|
1103
|
+
const id = this.nextRequestId;
|
|
1104
|
+
this.nextRequestId += 1;
|
|
1105
|
+
return id;
|
|
1106
|
+
}
|
|
1107
|
+
rejectAllPending(error) {
|
|
1108
|
+
for (const entry of this.pending.values()) {
|
|
1109
|
+
clearTimeout(entry.timeout);
|
|
1110
|
+
entry.reject(error);
|
|
1111
|
+
}
|
|
1112
|
+
this.pending.clear();
|
|
1113
|
+
}
|
|
1114
|
+
buildWebSocketUrl() {
|
|
1115
|
+
const base = new URL(this.client.getBaseUrl());
|
|
1116
|
+
base.protocol = base.protocol === "https:" ? "wss:" : "ws:";
|
|
1117
|
+
const trimmedPath = base.pathname.replace(/\/+$/, "");
|
|
1118
|
+
if (trimmedPath.endsWith("/api/v1")) {
|
|
1119
|
+
base.pathname = `${trimmedPath}/ws`;
|
|
1120
|
+
} else if (trimmedPath.endsWith("/api")) {
|
|
1121
|
+
base.pathname = `${trimmedPath}/v1/ws`;
|
|
1122
|
+
} else if (trimmedPath.endsWith("/v1")) {
|
|
1123
|
+
base.pathname = `${trimmedPath}/ws`;
|
|
1124
|
+
} else {
|
|
1125
|
+
base.pathname = "/v1/ws";
|
|
1126
|
+
}
|
|
1127
|
+
base.search = "";
|
|
1128
|
+
base.hash = "";
|
|
1129
|
+
return base.toString();
|
|
1130
|
+
}
|
|
1131
|
+
getDefaultWebSocketFactory() {
|
|
1132
|
+
return (url) => {
|
|
1133
|
+
if (typeof globalThis.WebSocket === "undefined") {
|
|
1134
|
+
throw new Error("WebSocket is not available. Provide a webSocketFactory in WebsocketsResource options.");
|
|
1135
|
+
}
|
|
1136
|
+
return new globalThis.WebSocket(url);
|
|
1137
|
+
};
|
|
1138
|
+
}
|
|
1139
|
+
};
|
|
1140
|
+
|
|
831
1141
|
// src/client.ts
|
|
832
1142
|
var KrawletClient = class {
|
|
833
1143
|
/**
|
|
@@ -854,6 +1164,7 @@ var KrawletClient = class {
|
|
|
854
1164
|
this.reports = new ReportsResource(this.httpClient);
|
|
855
1165
|
this.apiKey = new ApiKeyResource(this.httpClient);
|
|
856
1166
|
this.transfers = new TransfersResource(this.httpClient);
|
|
1167
|
+
this.websockets = new WebsocketsResource(this.httpClient);
|
|
857
1168
|
}
|
|
858
1169
|
/**
|
|
859
1170
|
* Get the last known rate limit information
|
|
@@ -894,5 +1205,7 @@ var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
|
|
|
894
1205
|
ReportsResource,
|
|
895
1206
|
ShopsResource,
|
|
896
1207
|
StorageResource,
|
|
897
|
-
TransfersResource
|
|
1208
|
+
TransfersResource,
|
|
1209
|
+
WebsocketProtocolError,
|
|
1210
|
+
WebsocketsResource
|
|
898
1211
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -458,6 +458,31 @@ interface PublicStorageTransferResponse {
|
|
|
458
458
|
transfer: Transfer;
|
|
459
459
|
sourceEntity: PublicStorageSourceEntity;
|
|
460
460
|
}
|
|
461
|
+
type WebsocketMessageId = string | number;
|
|
462
|
+
interface WebsocketEnvelope<TPayload = unknown> {
|
|
463
|
+
type: string;
|
|
464
|
+
id?: WebsocketMessageId;
|
|
465
|
+
payload: TPayload;
|
|
466
|
+
}
|
|
467
|
+
interface WebsocketErrorPayload {
|
|
468
|
+
code: string;
|
|
469
|
+
message: string;
|
|
470
|
+
}
|
|
471
|
+
interface WebsocketHelloPayload {
|
|
472
|
+
supportedClientMessages?: string[];
|
|
473
|
+
[key: string]: unknown;
|
|
474
|
+
}
|
|
475
|
+
interface WebsocketAuthOk {
|
|
476
|
+
tier: ApiKeyTier;
|
|
477
|
+
name: string;
|
|
478
|
+
role: 'client';
|
|
479
|
+
}
|
|
480
|
+
interface WebsocketTransferListPayload {
|
|
481
|
+
transfers: Transfer[];
|
|
482
|
+
}
|
|
483
|
+
interface WebsocketTargetListPayload {
|
|
484
|
+
targets: TransferTarget[];
|
|
485
|
+
}
|
|
461
486
|
interface ReportRecords<TRecord = unknown> {
|
|
462
487
|
count: number;
|
|
463
488
|
records: TRecord[];
|
|
@@ -522,6 +547,7 @@ declare class HttpClient {
|
|
|
522
547
|
private lastRateLimit?;
|
|
523
548
|
constructor(config: HttpClientConfig);
|
|
524
549
|
getRateLimit(): RateLimit | undefined;
|
|
550
|
+
getBaseUrl(): string;
|
|
525
551
|
request<T>(path: string, options?: RequestOptions): Promise<ApiResponse<T>>;
|
|
526
552
|
private buildUrl;
|
|
527
553
|
private buildHeaders;
|
|
@@ -632,6 +658,62 @@ declare class TransfersResource {
|
|
|
632
658
|
requestPublicStorage(data: PublicStorageTransferRequest): Promise<PublicStorageTransferResponse>;
|
|
633
659
|
}
|
|
634
660
|
|
|
661
|
+
interface WebSocketLike {
|
|
662
|
+
readyState: number;
|
|
663
|
+
send(data: string): void;
|
|
664
|
+
close(code?: number, reason?: string): void;
|
|
665
|
+
onopen: (() => void) | null;
|
|
666
|
+
onmessage: ((event: {
|
|
667
|
+
data: string;
|
|
668
|
+
}) => void) | null;
|
|
669
|
+
onerror: (() => void) | null;
|
|
670
|
+
onclose: ((event: {
|
|
671
|
+
code: number;
|
|
672
|
+
reason: string;
|
|
673
|
+
}) => void) | null;
|
|
674
|
+
}
|
|
675
|
+
interface WebsocketClientOptions {
|
|
676
|
+
connectTimeoutMs?: number;
|
|
677
|
+
requestTimeoutMs?: number;
|
|
678
|
+
webSocketFactory?: (url: string) => WebSocketLike;
|
|
679
|
+
}
|
|
680
|
+
declare class WebsocketProtocolError extends Error {
|
|
681
|
+
readonly code: string;
|
|
682
|
+
readonly requestId?: WebsocketMessageId;
|
|
683
|
+
constructor(message: string, code: string, requestId?: WebsocketMessageId);
|
|
684
|
+
}
|
|
685
|
+
declare class WebsocketsResource {
|
|
686
|
+
private client;
|
|
687
|
+
private ws;
|
|
688
|
+
private connectPromise;
|
|
689
|
+
private pending;
|
|
690
|
+
private transferUpdateListeners;
|
|
691
|
+
private nextRequestId;
|
|
692
|
+
private readonly connectTimeoutMs;
|
|
693
|
+
private readonly requestTimeoutMs;
|
|
694
|
+
private readonly webSocketFactory;
|
|
695
|
+
constructor(client: HttpClient, options?: WebsocketClientOptions);
|
|
696
|
+
start(): Promise<WebsocketEnvelope>;
|
|
697
|
+
authenticate(token: string, id?: WebsocketMessageId): Promise<WebsocketAuthOk>;
|
|
698
|
+
ping(id?: WebsocketMessageId): Promise<number>;
|
|
699
|
+
createTransfer(payload: TransferCreateRequest, id?: WebsocketMessageId): Promise<Transfer>;
|
|
700
|
+
getTransfer(transferId: string, id?: WebsocketMessageId): Promise<Transfer>;
|
|
701
|
+
cancelTransfer(transferId: string, id?: WebsocketMessageId): Promise<Transfer>;
|
|
702
|
+
listTransfers(id?: WebsocketMessageId): Promise<Transfer[]>;
|
|
703
|
+
listTargets(id?: WebsocketMessageId): Promise<TransferTarget[]>;
|
|
704
|
+
onTransferUpdate(listener: (transfer: Transfer) => void): () => void;
|
|
705
|
+
close(code?: number, reason?: string): void;
|
|
706
|
+
isConnected(): boolean;
|
|
707
|
+
private sendAndWait;
|
|
708
|
+
private handleMessageEvent;
|
|
709
|
+
private emitTransferUpdate;
|
|
710
|
+
private safeParse;
|
|
711
|
+
private createRequestId;
|
|
712
|
+
private rejectAllPending;
|
|
713
|
+
private buildWebSocketUrl;
|
|
714
|
+
private getDefaultWebSocketFactory;
|
|
715
|
+
}
|
|
716
|
+
|
|
635
717
|
interface KrawletClientConfig {
|
|
636
718
|
baseUrl?: string;
|
|
637
719
|
apiKey?: string;
|
|
@@ -652,6 +734,7 @@ declare class KrawletClient {
|
|
|
652
734
|
readonly reports: ReportsResource;
|
|
653
735
|
readonly apiKey: ApiKeyResource;
|
|
654
736
|
readonly transfers: TransfersResource;
|
|
737
|
+
readonly websockets: WebsocketsResource;
|
|
655
738
|
constructor(config?: KrawletClientConfig);
|
|
656
739
|
getRateLimit(): RateLimit | undefined;
|
|
657
740
|
}
|
|
@@ -668,4 +751,4 @@ declare class KrawletError extends Error {
|
|
|
668
751
|
isRateLimitError(): boolean;
|
|
669
752
|
}
|
|
670
753
|
|
|
671
|
-
export { AddressesResource, type ApiKeyInfo, ApiKeyResource, type ApiKeyTier, type ApiKeyUsage, type ApiResponse, type ApiResponseMeta, type BaseQueryParams, type ChangeLogOptions, type ChangeLogResult, type ChatboxServiceInfo, type DetailedHealthResponse, type DiscordServiceInfo, type EStorageEntityType, type EnderStorageApiPayload, type EnderStorageChest, type EnderStorageCollection, type EnderStorageColor, type EnderStorageItem, type EnderStorageMeta, type EnderStoragePayload, ErrorCode, type ErrorResponse, type HealthChecks, HealthResource, type HealthResponse, type HealthServices, type HealthServicesDetailed, type Item, type ItemChangeLog, type ItemChangeLogResponse, type ItemChangeRecord, type ItemChangeType, type ItemChangesParams, type ItemSummary, type ItemUpdateSummary, ItemsResource, type KnownAddress, type KnownAddressType, KrawletClient, type KrawletClientConfig, KrawletError, type KromerServiceInfo, type Player, type PlayerNotifications, PlayersResource, type Price, type PriceChangeLog, type PriceChangeLogResponse, type PriceChangesParams, type PublicStorageSourceEntity, type PublicStorageTransferRequest, type PublicStorageTransferResponse, type QuickCodeGenerateResponse, type QuickCodeRedeemResponse, type RateLimit, type ReportRecords, type ReporterStats, ReportsResource, type RequestLog, type RequestLogsResponse, type ServiceInfo, type ServiceName, type ServiceStatus, type Shop, type ShopChangeField, type ShopChangeLog, type ShopChangeLogResponse, type ShopChangeRecord, type ShopChangesParams, type ShopSourceType, type ShopSyncData, ShopsResource, type StorageData, StorageResource, type StorageSlotContents, type StorageSlotItem, type SuccessfulPostRecord, type Transfer, type TransferCreateRequest, type TransferStatus, type TransferTarget, type TransferTargetLink, TransfersResource, type ValidationFailureRecord };
|
|
754
|
+
export { AddressesResource, type ApiKeyInfo, ApiKeyResource, type ApiKeyTier, type ApiKeyUsage, type ApiResponse, type ApiResponseMeta, type BaseQueryParams, type ChangeLogOptions, type ChangeLogResult, type ChatboxServiceInfo, type DetailedHealthResponse, type DiscordServiceInfo, type EStorageEntityType, type EnderStorageApiPayload, type EnderStorageChest, type EnderStorageCollection, type EnderStorageColor, type EnderStorageItem, type EnderStorageMeta, type EnderStoragePayload, ErrorCode, type ErrorResponse, type HealthChecks, HealthResource, type HealthResponse, type HealthServices, type HealthServicesDetailed, type Item, type ItemChangeLog, type ItemChangeLogResponse, type ItemChangeRecord, type ItemChangeType, type ItemChangesParams, type ItemSummary, type ItemUpdateSummary, ItemsResource, type KnownAddress, type KnownAddressType, KrawletClient, type KrawletClientConfig, KrawletError, type KromerServiceInfo, type Player, type PlayerNotifications, PlayersResource, type Price, type PriceChangeLog, type PriceChangeLogResponse, type PriceChangesParams, type PublicStorageSourceEntity, type PublicStorageTransferRequest, type PublicStorageTransferResponse, type QuickCodeGenerateResponse, type QuickCodeRedeemResponse, type RateLimit, type ReportRecords, type ReporterStats, ReportsResource, type RequestLog, type RequestLogsResponse, type ServiceInfo, type ServiceName, type ServiceStatus, type Shop, type ShopChangeField, type ShopChangeLog, type ShopChangeLogResponse, type ShopChangeRecord, type ShopChangesParams, type ShopSourceType, type ShopSyncData, ShopsResource, type StorageData, StorageResource, type StorageSlotContents, type StorageSlotItem, type SuccessfulPostRecord, type Transfer, type TransferCreateRequest, type TransferStatus, type TransferTarget, type TransferTargetLink, TransfersResource, type ValidationFailureRecord, type WebsocketAuthOk, type WebsocketClientOptions, type WebsocketEnvelope, type WebsocketErrorPayload, type WebsocketHelloPayload, type WebsocketMessageId, WebsocketProtocolError, type WebsocketTargetListPayload, type WebsocketTransferListPayload, WebsocketsResource };
|
package/dist/index.d.ts
CHANGED
|
@@ -458,6 +458,31 @@ interface PublicStorageTransferResponse {
|
|
|
458
458
|
transfer: Transfer;
|
|
459
459
|
sourceEntity: PublicStorageSourceEntity;
|
|
460
460
|
}
|
|
461
|
+
type WebsocketMessageId = string | number;
|
|
462
|
+
interface WebsocketEnvelope<TPayload = unknown> {
|
|
463
|
+
type: string;
|
|
464
|
+
id?: WebsocketMessageId;
|
|
465
|
+
payload: TPayload;
|
|
466
|
+
}
|
|
467
|
+
interface WebsocketErrorPayload {
|
|
468
|
+
code: string;
|
|
469
|
+
message: string;
|
|
470
|
+
}
|
|
471
|
+
interface WebsocketHelloPayload {
|
|
472
|
+
supportedClientMessages?: string[];
|
|
473
|
+
[key: string]: unknown;
|
|
474
|
+
}
|
|
475
|
+
interface WebsocketAuthOk {
|
|
476
|
+
tier: ApiKeyTier;
|
|
477
|
+
name: string;
|
|
478
|
+
role: 'client';
|
|
479
|
+
}
|
|
480
|
+
interface WebsocketTransferListPayload {
|
|
481
|
+
transfers: Transfer[];
|
|
482
|
+
}
|
|
483
|
+
interface WebsocketTargetListPayload {
|
|
484
|
+
targets: TransferTarget[];
|
|
485
|
+
}
|
|
461
486
|
interface ReportRecords<TRecord = unknown> {
|
|
462
487
|
count: number;
|
|
463
488
|
records: TRecord[];
|
|
@@ -522,6 +547,7 @@ declare class HttpClient {
|
|
|
522
547
|
private lastRateLimit?;
|
|
523
548
|
constructor(config: HttpClientConfig);
|
|
524
549
|
getRateLimit(): RateLimit | undefined;
|
|
550
|
+
getBaseUrl(): string;
|
|
525
551
|
request<T>(path: string, options?: RequestOptions): Promise<ApiResponse<T>>;
|
|
526
552
|
private buildUrl;
|
|
527
553
|
private buildHeaders;
|
|
@@ -632,6 +658,62 @@ declare class TransfersResource {
|
|
|
632
658
|
requestPublicStorage(data: PublicStorageTransferRequest): Promise<PublicStorageTransferResponse>;
|
|
633
659
|
}
|
|
634
660
|
|
|
661
|
+
interface WebSocketLike {
|
|
662
|
+
readyState: number;
|
|
663
|
+
send(data: string): void;
|
|
664
|
+
close(code?: number, reason?: string): void;
|
|
665
|
+
onopen: (() => void) | null;
|
|
666
|
+
onmessage: ((event: {
|
|
667
|
+
data: string;
|
|
668
|
+
}) => void) | null;
|
|
669
|
+
onerror: (() => void) | null;
|
|
670
|
+
onclose: ((event: {
|
|
671
|
+
code: number;
|
|
672
|
+
reason: string;
|
|
673
|
+
}) => void) | null;
|
|
674
|
+
}
|
|
675
|
+
interface WebsocketClientOptions {
|
|
676
|
+
connectTimeoutMs?: number;
|
|
677
|
+
requestTimeoutMs?: number;
|
|
678
|
+
webSocketFactory?: (url: string) => WebSocketLike;
|
|
679
|
+
}
|
|
680
|
+
declare class WebsocketProtocolError extends Error {
|
|
681
|
+
readonly code: string;
|
|
682
|
+
readonly requestId?: WebsocketMessageId;
|
|
683
|
+
constructor(message: string, code: string, requestId?: WebsocketMessageId);
|
|
684
|
+
}
|
|
685
|
+
declare class WebsocketsResource {
|
|
686
|
+
private client;
|
|
687
|
+
private ws;
|
|
688
|
+
private connectPromise;
|
|
689
|
+
private pending;
|
|
690
|
+
private transferUpdateListeners;
|
|
691
|
+
private nextRequestId;
|
|
692
|
+
private readonly connectTimeoutMs;
|
|
693
|
+
private readonly requestTimeoutMs;
|
|
694
|
+
private readonly webSocketFactory;
|
|
695
|
+
constructor(client: HttpClient, options?: WebsocketClientOptions);
|
|
696
|
+
start(): Promise<WebsocketEnvelope>;
|
|
697
|
+
authenticate(token: string, id?: WebsocketMessageId): Promise<WebsocketAuthOk>;
|
|
698
|
+
ping(id?: WebsocketMessageId): Promise<number>;
|
|
699
|
+
createTransfer(payload: TransferCreateRequest, id?: WebsocketMessageId): Promise<Transfer>;
|
|
700
|
+
getTransfer(transferId: string, id?: WebsocketMessageId): Promise<Transfer>;
|
|
701
|
+
cancelTransfer(transferId: string, id?: WebsocketMessageId): Promise<Transfer>;
|
|
702
|
+
listTransfers(id?: WebsocketMessageId): Promise<Transfer[]>;
|
|
703
|
+
listTargets(id?: WebsocketMessageId): Promise<TransferTarget[]>;
|
|
704
|
+
onTransferUpdate(listener: (transfer: Transfer) => void): () => void;
|
|
705
|
+
close(code?: number, reason?: string): void;
|
|
706
|
+
isConnected(): boolean;
|
|
707
|
+
private sendAndWait;
|
|
708
|
+
private handleMessageEvent;
|
|
709
|
+
private emitTransferUpdate;
|
|
710
|
+
private safeParse;
|
|
711
|
+
private createRequestId;
|
|
712
|
+
private rejectAllPending;
|
|
713
|
+
private buildWebSocketUrl;
|
|
714
|
+
private getDefaultWebSocketFactory;
|
|
715
|
+
}
|
|
716
|
+
|
|
635
717
|
interface KrawletClientConfig {
|
|
636
718
|
baseUrl?: string;
|
|
637
719
|
apiKey?: string;
|
|
@@ -652,6 +734,7 @@ declare class KrawletClient {
|
|
|
652
734
|
readonly reports: ReportsResource;
|
|
653
735
|
readonly apiKey: ApiKeyResource;
|
|
654
736
|
readonly transfers: TransfersResource;
|
|
737
|
+
readonly websockets: WebsocketsResource;
|
|
655
738
|
constructor(config?: KrawletClientConfig);
|
|
656
739
|
getRateLimit(): RateLimit | undefined;
|
|
657
740
|
}
|
|
@@ -668,4 +751,4 @@ declare class KrawletError extends Error {
|
|
|
668
751
|
isRateLimitError(): boolean;
|
|
669
752
|
}
|
|
670
753
|
|
|
671
|
-
export { AddressesResource, type ApiKeyInfo, ApiKeyResource, type ApiKeyTier, type ApiKeyUsage, type ApiResponse, type ApiResponseMeta, type BaseQueryParams, type ChangeLogOptions, type ChangeLogResult, type ChatboxServiceInfo, type DetailedHealthResponse, type DiscordServiceInfo, type EStorageEntityType, type EnderStorageApiPayload, type EnderStorageChest, type EnderStorageCollection, type EnderStorageColor, type EnderStorageItem, type EnderStorageMeta, type EnderStoragePayload, ErrorCode, type ErrorResponse, type HealthChecks, HealthResource, type HealthResponse, type HealthServices, type HealthServicesDetailed, type Item, type ItemChangeLog, type ItemChangeLogResponse, type ItemChangeRecord, type ItemChangeType, type ItemChangesParams, type ItemSummary, type ItemUpdateSummary, ItemsResource, type KnownAddress, type KnownAddressType, KrawletClient, type KrawletClientConfig, KrawletError, type KromerServiceInfo, type Player, type PlayerNotifications, PlayersResource, type Price, type PriceChangeLog, type PriceChangeLogResponse, type PriceChangesParams, type PublicStorageSourceEntity, type PublicStorageTransferRequest, type PublicStorageTransferResponse, type QuickCodeGenerateResponse, type QuickCodeRedeemResponse, type RateLimit, type ReportRecords, type ReporterStats, ReportsResource, type RequestLog, type RequestLogsResponse, type ServiceInfo, type ServiceName, type ServiceStatus, type Shop, type ShopChangeField, type ShopChangeLog, type ShopChangeLogResponse, type ShopChangeRecord, type ShopChangesParams, type ShopSourceType, type ShopSyncData, ShopsResource, type StorageData, StorageResource, type StorageSlotContents, type StorageSlotItem, type SuccessfulPostRecord, type Transfer, type TransferCreateRequest, type TransferStatus, type TransferTarget, type TransferTargetLink, TransfersResource, type ValidationFailureRecord };
|
|
754
|
+
export { AddressesResource, type ApiKeyInfo, ApiKeyResource, type ApiKeyTier, type ApiKeyUsage, type ApiResponse, type ApiResponseMeta, type BaseQueryParams, type ChangeLogOptions, type ChangeLogResult, type ChatboxServiceInfo, type DetailedHealthResponse, type DiscordServiceInfo, type EStorageEntityType, type EnderStorageApiPayload, type EnderStorageChest, type EnderStorageCollection, type EnderStorageColor, type EnderStorageItem, type EnderStorageMeta, type EnderStoragePayload, ErrorCode, type ErrorResponse, type HealthChecks, HealthResource, type HealthResponse, type HealthServices, type HealthServicesDetailed, type Item, type ItemChangeLog, type ItemChangeLogResponse, type ItemChangeRecord, type ItemChangeType, type ItemChangesParams, type ItemSummary, type ItemUpdateSummary, ItemsResource, type KnownAddress, type KnownAddressType, KrawletClient, type KrawletClientConfig, KrawletError, type KromerServiceInfo, type Player, type PlayerNotifications, PlayersResource, type Price, type PriceChangeLog, type PriceChangeLogResponse, type PriceChangesParams, type PublicStorageSourceEntity, type PublicStorageTransferRequest, type PublicStorageTransferResponse, type QuickCodeGenerateResponse, type QuickCodeRedeemResponse, type RateLimit, type ReportRecords, type ReporterStats, ReportsResource, type RequestLog, type RequestLogsResponse, type ServiceInfo, type ServiceName, type ServiceStatus, type Shop, type ShopChangeField, type ShopChangeLog, type ShopChangeLogResponse, type ShopChangeRecord, type ShopChangesParams, type ShopSourceType, type ShopSyncData, ShopsResource, type StorageData, StorageResource, type StorageSlotContents, type StorageSlotItem, type SuccessfulPostRecord, type Transfer, type TransferCreateRequest, type TransferStatus, type TransferTarget, type TransferTargetLink, TransfersResource, type ValidationFailureRecord, type WebsocketAuthOk, type WebsocketClientOptions, type WebsocketEnvelope, type WebsocketErrorPayload, type WebsocketHelloPayload, type WebsocketMessageId, WebsocketProtocolError, type WebsocketTargetListPayload, type WebsocketTransferListPayload, WebsocketsResource };
|
package/dist/index.js
CHANGED
|
@@ -51,6 +51,12 @@ var HttpClient = class {
|
|
|
51
51
|
getRateLimit() {
|
|
52
52
|
return this.lastRateLimit;
|
|
53
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Get configured API base URL
|
|
56
|
+
*/
|
|
57
|
+
getBaseUrl() {
|
|
58
|
+
return this.config.baseUrl;
|
|
59
|
+
}
|
|
54
60
|
/**
|
|
55
61
|
* Make an HTTP request
|
|
56
62
|
*/
|
|
@@ -791,6 +797,308 @@ var TransfersResource = class {
|
|
|
791
797
|
}
|
|
792
798
|
};
|
|
793
799
|
|
|
800
|
+
// src/resources/websockets.ts
|
|
801
|
+
var WebsocketProtocolError = class extends Error {
|
|
802
|
+
constructor(message, code, requestId) {
|
|
803
|
+
super(message);
|
|
804
|
+
this.name = "WebsocketProtocolError";
|
|
805
|
+
this.code = code;
|
|
806
|
+
this.requestId = requestId;
|
|
807
|
+
}
|
|
808
|
+
};
|
|
809
|
+
var WebsocketsResource = class {
|
|
810
|
+
constructor(client, options = {}) {
|
|
811
|
+
this.client = client;
|
|
812
|
+
this.ws = null;
|
|
813
|
+
this.connectPromise = null;
|
|
814
|
+
this.pending = /* @__PURE__ */ new Map();
|
|
815
|
+
this.transferUpdateListeners = /* @__PURE__ */ new Set();
|
|
816
|
+
this.nextRequestId = 1;
|
|
817
|
+
this.connectTimeoutMs = options.connectTimeoutMs ?? 1e4;
|
|
818
|
+
this.requestTimeoutMs = options.requestTimeoutMs ?? 15e3;
|
|
819
|
+
this.webSocketFactory = options.webSocketFactory ?? this.getDefaultWebSocketFactory();
|
|
820
|
+
}
|
|
821
|
+
/**
|
|
822
|
+
* Open websocket connection and wait for initial hello
|
|
823
|
+
*/
|
|
824
|
+
async start() {
|
|
825
|
+
if (this.connectPromise) {
|
|
826
|
+
return this.connectPromise;
|
|
827
|
+
}
|
|
828
|
+
const url = this.buildWebSocketUrl();
|
|
829
|
+
this.connectPromise = new Promise((resolve, reject) => {
|
|
830
|
+
const ws = this.webSocketFactory(url);
|
|
831
|
+
this.ws = ws;
|
|
832
|
+
const timeout = setTimeout(() => {
|
|
833
|
+
this.connectPromise = null;
|
|
834
|
+
this.ws = null;
|
|
835
|
+
try {
|
|
836
|
+
ws.close();
|
|
837
|
+
} catch {
|
|
838
|
+
}
|
|
839
|
+
reject(new Error(`WebSocket connection timeout after ${this.connectTimeoutMs}ms`));
|
|
840
|
+
}, this.connectTimeoutMs);
|
|
841
|
+
ws.onopen = () => {
|
|
842
|
+
};
|
|
843
|
+
ws.onmessage = (event) => {
|
|
844
|
+
this.handleMessageEvent(event.data, resolve, reject, timeout);
|
|
845
|
+
};
|
|
846
|
+
ws.onerror = () => {
|
|
847
|
+
clearTimeout(timeout);
|
|
848
|
+
this.connectPromise = null;
|
|
849
|
+
this.ws = null;
|
|
850
|
+
reject(new Error("WebSocket connection failed"));
|
|
851
|
+
};
|
|
852
|
+
ws.onclose = (event) => {
|
|
853
|
+
clearTimeout(timeout);
|
|
854
|
+
this.connectPromise = null;
|
|
855
|
+
this.ws = null;
|
|
856
|
+
this.rejectAllPending(
|
|
857
|
+
new Error(`WebSocket closed (${event.code}${event.reason ? `: ${event.reason}` : ""})`)
|
|
858
|
+
);
|
|
859
|
+
};
|
|
860
|
+
});
|
|
861
|
+
return this.connectPromise;
|
|
862
|
+
}
|
|
863
|
+
/**
|
|
864
|
+
* Authenticate this connection as a client key
|
|
865
|
+
*/
|
|
866
|
+
async authenticate(token, id = this.createRequestId()) {
|
|
867
|
+
const response = await this.sendAndWait("auth", {
|
|
868
|
+
type: "auth",
|
|
869
|
+
id,
|
|
870
|
+
token
|
|
871
|
+
});
|
|
872
|
+
if (response.type !== "auth_ok") {
|
|
873
|
+
throw new Error(`Unexpected auth response type: ${response.type}`);
|
|
874
|
+
}
|
|
875
|
+
return response.payload;
|
|
876
|
+
}
|
|
877
|
+
/**
|
|
878
|
+
* Ping the websocket server
|
|
879
|
+
*/
|
|
880
|
+
async ping(id = this.createRequestId()) {
|
|
881
|
+
const response = await this.sendAndWait("ping", {
|
|
882
|
+
type: "ping",
|
|
883
|
+
id
|
|
884
|
+
});
|
|
885
|
+
if (response.type !== "pong") {
|
|
886
|
+
throw new Error(`Unexpected ping response type: ${response.type}`);
|
|
887
|
+
}
|
|
888
|
+
return response.payload;
|
|
889
|
+
}
|
|
890
|
+
/**
|
|
891
|
+
* Create a transfer over websocket
|
|
892
|
+
*/
|
|
893
|
+
async createTransfer(payload, id = this.createRequestId()) {
|
|
894
|
+
const response = await this.sendAndWait("create_transfer", {
|
|
895
|
+
type: "create_transfer",
|
|
896
|
+
id,
|
|
897
|
+
payload
|
|
898
|
+
});
|
|
899
|
+
if (response.type !== "create_transfer_ok") {
|
|
900
|
+
throw new Error(`Unexpected create_transfer response type: ${response.type}`);
|
|
901
|
+
}
|
|
902
|
+
return response.payload;
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* Fetch transfer by id
|
|
906
|
+
*/
|
|
907
|
+
async getTransfer(transferId, id = this.createRequestId()) {
|
|
908
|
+
const response = await this.sendAndWait("get_transfer", {
|
|
909
|
+
type: "get_transfer",
|
|
910
|
+
id,
|
|
911
|
+
payload: { transferId }
|
|
912
|
+
});
|
|
913
|
+
if (response.type !== "get_transfer_ok") {
|
|
914
|
+
throw new Error(`Unexpected get_transfer response type: ${response.type}`);
|
|
915
|
+
}
|
|
916
|
+
return response.payload;
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* Cancel transfer by id
|
|
920
|
+
*/
|
|
921
|
+
async cancelTransfer(transferId, id = this.createRequestId()) {
|
|
922
|
+
const response = await this.sendAndWait("cancel_transfer", {
|
|
923
|
+
type: "cancel_transfer",
|
|
924
|
+
id,
|
|
925
|
+
payload: { transferId }
|
|
926
|
+
});
|
|
927
|
+
if (response.type !== "cancel_transfer_ok") {
|
|
928
|
+
throw new Error(`Unexpected cancel_transfer response type: ${response.type}`);
|
|
929
|
+
}
|
|
930
|
+
return response.payload;
|
|
931
|
+
}
|
|
932
|
+
/**
|
|
933
|
+
* List transfers for this client
|
|
934
|
+
*/
|
|
935
|
+
async listTransfers(id = this.createRequestId()) {
|
|
936
|
+
const response = await this.sendAndWait("list_transfers", {
|
|
937
|
+
type: "list_transfers",
|
|
938
|
+
id,
|
|
939
|
+
payload: {}
|
|
940
|
+
});
|
|
941
|
+
if (response.type !== "list_transfers_ok") {
|
|
942
|
+
throw new Error(`Unexpected list_transfers response type: ${response.type}`);
|
|
943
|
+
}
|
|
944
|
+
return response.payload.transfers;
|
|
945
|
+
}
|
|
946
|
+
/**
|
|
947
|
+
* List targets available to this client
|
|
948
|
+
*/
|
|
949
|
+
async listTargets(id = this.createRequestId()) {
|
|
950
|
+
const response = await this.sendAndWait("list_targets", {
|
|
951
|
+
type: "list_targets",
|
|
952
|
+
id,
|
|
953
|
+
payload: {}
|
|
954
|
+
});
|
|
955
|
+
if (response.type !== "list_targets_ok") {
|
|
956
|
+
throw new Error(`Unexpected list_targets response type: ${response.type}`);
|
|
957
|
+
}
|
|
958
|
+
return response.payload.targets;
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* Subscribe to unsolicited transfer updates.
|
|
962
|
+
* Returns an unsubscribe function.
|
|
963
|
+
*/
|
|
964
|
+
onTransferUpdate(listener) {
|
|
965
|
+
this.transferUpdateListeners.add(listener);
|
|
966
|
+
return () => {
|
|
967
|
+
this.transferUpdateListeners.delete(listener);
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
/**
|
|
971
|
+
* Close the websocket connection
|
|
972
|
+
*/
|
|
973
|
+
close(code, reason) {
|
|
974
|
+
if (!this.ws) {
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
try {
|
|
978
|
+
this.ws.close(code, reason);
|
|
979
|
+
} finally {
|
|
980
|
+
this.ws = null;
|
|
981
|
+
this.connectPromise = null;
|
|
982
|
+
this.rejectAllPending(new Error("WebSocket connection closed"));
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* True if the websocket is currently open
|
|
987
|
+
*/
|
|
988
|
+
isConnected() {
|
|
989
|
+
return Boolean(this.ws && this.ws.readyState === 1);
|
|
990
|
+
}
|
|
991
|
+
async sendAndWait(messageType, message) {
|
|
992
|
+
if (!this.ws || this.ws.readyState !== 1) {
|
|
993
|
+
await this.start();
|
|
994
|
+
}
|
|
995
|
+
if (!this.ws || this.ws.readyState !== 1) {
|
|
996
|
+
throw new Error("WebSocket is not connected");
|
|
997
|
+
}
|
|
998
|
+
const requestId = message.id;
|
|
999
|
+
const responsePromise = new Promise((resolve, reject) => {
|
|
1000
|
+
const timeout = setTimeout(() => {
|
|
1001
|
+
this.pending.delete(requestId);
|
|
1002
|
+
reject(new Error(`WebSocket ${messageType} request timed out after ${this.requestTimeoutMs}ms`));
|
|
1003
|
+
}, this.requestTimeoutMs);
|
|
1004
|
+
this.pending.set(requestId, {
|
|
1005
|
+
resolve: (value) => resolve(value),
|
|
1006
|
+
reject,
|
|
1007
|
+
timeout
|
|
1008
|
+
});
|
|
1009
|
+
});
|
|
1010
|
+
this.ws.send(JSON.stringify(message));
|
|
1011
|
+
return responsePromise;
|
|
1012
|
+
}
|
|
1013
|
+
handleMessageEvent(rawData, connectResolve, connectReject, connectTimeout) {
|
|
1014
|
+
const parsed = this.safeParse(rawData);
|
|
1015
|
+
if (!parsed) {
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
if (parsed.type === "hello") {
|
|
1019
|
+
clearTimeout(connectTimeout);
|
|
1020
|
+
connectResolve(parsed);
|
|
1021
|
+
return;
|
|
1022
|
+
}
|
|
1023
|
+
if (parsed.type === "transfer_update") {
|
|
1024
|
+
this.emitTransferUpdate(parsed.payload);
|
|
1025
|
+
return;
|
|
1026
|
+
}
|
|
1027
|
+
if (typeof parsed.id === "undefined") {
|
|
1028
|
+
if (parsed.type === "error") {
|
|
1029
|
+
const payload = parsed.payload;
|
|
1030
|
+
connectReject(new WebsocketProtocolError(payload.message, payload.code));
|
|
1031
|
+
}
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
const pending = this.pending.get(parsed.id);
|
|
1035
|
+
if (!pending) {
|
|
1036
|
+
return;
|
|
1037
|
+
}
|
|
1038
|
+
clearTimeout(pending.timeout);
|
|
1039
|
+
this.pending.delete(parsed.id);
|
|
1040
|
+
if (parsed.type === "error") {
|
|
1041
|
+
const payload = parsed.payload;
|
|
1042
|
+
pending.reject(new WebsocketProtocolError(payload.message, payload.code, parsed.id));
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
pending.resolve(parsed);
|
|
1046
|
+
}
|
|
1047
|
+
emitTransferUpdate(transfer) {
|
|
1048
|
+
for (const listener of this.transferUpdateListeners) {
|
|
1049
|
+
listener(transfer);
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
safeParse(rawData) {
|
|
1053
|
+
try {
|
|
1054
|
+
const parsed = JSON.parse(rawData);
|
|
1055
|
+
if (!parsed || typeof parsed !== "object" || typeof parsed.type !== "string") {
|
|
1056
|
+
return null;
|
|
1057
|
+
}
|
|
1058
|
+
return parsed;
|
|
1059
|
+
} catch {
|
|
1060
|
+
return null;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
createRequestId() {
|
|
1064
|
+
const id = this.nextRequestId;
|
|
1065
|
+
this.nextRequestId += 1;
|
|
1066
|
+
return id;
|
|
1067
|
+
}
|
|
1068
|
+
rejectAllPending(error) {
|
|
1069
|
+
for (const entry of this.pending.values()) {
|
|
1070
|
+
clearTimeout(entry.timeout);
|
|
1071
|
+
entry.reject(error);
|
|
1072
|
+
}
|
|
1073
|
+
this.pending.clear();
|
|
1074
|
+
}
|
|
1075
|
+
buildWebSocketUrl() {
|
|
1076
|
+
const base = new URL(this.client.getBaseUrl());
|
|
1077
|
+
base.protocol = base.protocol === "https:" ? "wss:" : "ws:";
|
|
1078
|
+
const trimmedPath = base.pathname.replace(/\/+$/, "");
|
|
1079
|
+
if (trimmedPath.endsWith("/api/v1")) {
|
|
1080
|
+
base.pathname = `${trimmedPath}/ws`;
|
|
1081
|
+
} else if (trimmedPath.endsWith("/api")) {
|
|
1082
|
+
base.pathname = `${trimmedPath}/v1/ws`;
|
|
1083
|
+
} else if (trimmedPath.endsWith("/v1")) {
|
|
1084
|
+
base.pathname = `${trimmedPath}/ws`;
|
|
1085
|
+
} else {
|
|
1086
|
+
base.pathname = "/v1/ws";
|
|
1087
|
+
}
|
|
1088
|
+
base.search = "";
|
|
1089
|
+
base.hash = "";
|
|
1090
|
+
return base.toString();
|
|
1091
|
+
}
|
|
1092
|
+
getDefaultWebSocketFactory() {
|
|
1093
|
+
return (url) => {
|
|
1094
|
+
if (typeof globalThis.WebSocket === "undefined") {
|
|
1095
|
+
throw new Error("WebSocket is not available. Provide a webSocketFactory in WebsocketsResource options.");
|
|
1096
|
+
}
|
|
1097
|
+
return new globalThis.WebSocket(url);
|
|
1098
|
+
};
|
|
1099
|
+
}
|
|
1100
|
+
};
|
|
1101
|
+
|
|
794
1102
|
// src/client.ts
|
|
795
1103
|
var KrawletClient = class {
|
|
796
1104
|
/**
|
|
@@ -817,6 +1125,7 @@ var KrawletClient = class {
|
|
|
817
1125
|
this.reports = new ReportsResource(this.httpClient);
|
|
818
1126
|
this.apiKey = new ApiKeyResource(this.httpClient);
|
|
819
1127
|
this.transfers = new TransfersResource(this.httpClient);
|
|
1128
|
+
this.websockets = new WebsocketsResource(this.httpClient);
|
|
820
1129
|
}
|
|
821
1130
|
/**
|
|
822
1131
|
* Get the last known rate limit information
|
|
@@ -856,5 +1165,7 @@ export {
|
|
|
856
1165
|
ReportsResource,
|
|
857
1166
|
ShopsResource,
|
|
858
1167
|
StorageResource,
|
|
859
|
-
TransfersResource
|
|
1168
|
+
TransfersResource,
|
|
1169
|
+
WebsocketProtocolError,
|
|
1170
|
+
WebsocketsResource
|
|
860
1171
|
};
|