tons of bugfixes and additional logging

This commit is contained in:
2026-04-23 09:08:45 -05:00
parent 8837f56540
commit 82a462ee11
5 changed files with 70 additions and 28 deletions
+1
View File
@@ -1,6 +1,7 @@
export declare class TetherClient { export declare class TetherClient {
private websocketHandler; private websocketHandler;
private subscribedQueries; private subscribedQueries;
private pendingMutations;
connect: (url: string) => void; connect: (url: string) => void;
disconnect: () => void; disconnect: () => void;
subscribe: (queryName: string, params: any, callback: (data: any) => void) => void; subscribe: (queryName: string, params: any, callback: (data: any) => void) => void;
+18 -11
View File
@@ -2,6 +2,7 @@ import { WebSocketHandler } from './utils/websocket.js';
export class TetherClient { export class TetherClient {
websocketHandler = new WebSocketHandler(); websocketHandler = new WebSocketHandler();
subscribedQueries = new Map(); subscribedQueries = new Map();
pendingMutations = new Map();
connect = (url) => { connect = (url) => {
this.websocketHandler.startConnection(url); this.websocketHandler.startConnection(url);
this.websocketHandler.onQuery = (location, data) => { this.websocketHandler.onQuery = (location, data) => {
@@ -10,6 +11,15 @@ export class TetherClient {
callback?.(data); callback?.(data);
} }
}; };
this.websocketHandler.onMutation = (incoming_id, data) => {
const pending = this.pendingMutations.get(incoming_id);
if (!pending) {
return;
}
clearTimeout(pending.timeoutId);
this.pendingMutations.delete(incoming_id);
pending.resolve(data);
};
this.websocketHandler.onOpen = () => { this.websocketHandler.onOpen = () => {
this.subscribedQueries.forEach(({ params }, queryName) => { this.subscribedQueries.forEach(({ params }, queryName) => {
this.websocketHandler.send(JSON.stringify({ this.websocketHandler.send(JSON.stringify({
@@ -40,22 +50,19 @@ export class TetherClient {
}; };
sendMutation = (mutationName, params) => { sendMutation = (mutationName, params) => {
const mutation_id = crypto.randomUUID(); const mutation_id = crypto.randomUUID();
const promise = new Promise((resolve, reject) => {
const timeoutId = setTimeout(() => {
this.pendingMutations.delete(mutation_id);
reject(new Error('Mutation timeout'));
}, 10000);
this.pendingMutations.set(mutation_id, { resolve, reject, timeoutId });
});
this.websocketHandler.send(JSON.stringify({ this.websocketHandler.send(JSON.stringify({
type: 'mutation', type: 'mutation',
location: mutationName, location: mutationName,
params: params, params: params,
mutation_id: mutation_id mutation_id: mutation_id
})); }));
return new Promise((resolve, reject) => { return promise;
const timeoutId = setTimeout(() => {
reject(new Error('Mutation timeout'));
}, 10000);
this.websocketHandler.onMutation = (incoming_id, data) => {
if (incoming_id === mutation_id) {
clearTimeout(timeoutId);
resolve(data);
}
};
});
}; };
} }
+10 -3
View File
@@ -23,7 +23,14 @@ export class WebSocketHandler {
this.reconnectAttempts = 0; this.reconnectAttempts = 0;
}; };
ws.onmessage = (event) => { ws.onmessage = (event) => {
const data = JSON.parse(String(event.data)); let data;
try {
data = JSON.parse(String(event.data));
}
catch (e) {
console.error('Tether: invalid JSON message', event.data, e);
return;
}
if (data.type === 'query') { if (data.type === 'query') {
this.onQuery(data.location, data.data); this.onQuery(data.location, data.data);
} }
@@ -34,8 +41,8 @@ export class WebSocketHandler {
console.error(data.error); console.error(data.error);
} }
}; };
ws.onclose = () => { ws.onclose = (event) => {
console.log('Disconnected from Tether'); console.log('Disconnected from Tether', 'code:', event.code, 'reason:', event.reason || '(none)', 'wasClean:', event.wasClean);
this.attemptReconnect(); this.attemptReconnect();
}; };
}; };
+24 -11
View File
@@ -1,7 +1,14 @@
import { WebSocketHandler } from './utils/websocket.js'; import { WebSocketHandler } from './utils/websocket.js';
type PendingMutation = {
resolve: (value: unknown) => void;
reject: (reason: Error) => void;
timeoutId: ReturnType<typeof setTimeout>;
};
export class TetherClient { export class TetherClient {
private websocketHandler: WebSocketHandler = new WebSocketHandler(); private websocketHandler: WebSocketHandler = new WebSocketHandler();
private subscribedQueries = new Map<string, { callback: (data: any) => void, params: any }>(); private subscribedQueries = new Map<string, { callback: (data: any) => void, params: any }>();
private pendingMutations = new Map<string, PendingMutation>();
connect = (url: string) => { connect = (url: string) => {
this.websocketHandler.startConnection(url); this.websocketHandler.startConnection(url);
@@ -11,6 +18,15 @@ export class TetherClient {
callback?.(data); callback?.(data);
} }
}; };
this.websocketHandler.onMutation = (incoming_id, data) => {
const pending = this.pendingMutations.get(incoming_id);
if (!pending) {
return;
}
clearTimeout(pending.timeoutId);
this.pendingMutations.delete(incoming_id);
pending.resolve(data);
};
this.websocketHandler.onOpen = () => { this.websocketHandler.onOpen = () => {
this.subscribedQueries.forEach(({ params }, queryName) => { this.subscribedQueries.forEach(({ params }, queryName) => {
this.websocketHandler.send(JSON.stringify({ this.websocketHandler.send(JSON.stringify({
@@ -45,22 +61,19 @@ export class TetherClient {
sendMutation = (mutationName: string, params: any) => { sendMutation = (mutationName: string, params: any) => {
const mutation_id = crypto.randomUUID(); const mutation_id = crypto.randomUUID();
const promise = new Promise((resolve, reject) => {
const timeoutId = setTimeout(() => {
this.pendingMutations.delete(mutation_id);
reject(new Error('Mutation timeout'));
}, 10000);
this.pendingMutations.set(mutation_id, { resolve, reject, timeoutId });
});
this.websocketHandler.send(JSON.stringify({ this.websocketHandler.send(JSON.stringify({
type: 'mutation', type: 'mutation',
location: mutationName, location: mutationName,
params: params, params: params,
mutation_id: mutation_id mutation_id: mutation_id
})); }));
return new Promise((resolve, reject) => { return promise;
const timeoutId = setTimeout(() => {
reject(new Error('Mutation timeout'));
}, 10000);
this.websocketHandler.onMutation = (incoming_id: string, data: any) => {
if (incoming_id === mutation_id) {
clearTimeout(timeoutId);
resolve(data);
}
};
});
}; };
} }
+17 -3
View File
@@ -25,13 +25,19 @@ export class WebSocketHandler {
}; };
ws.onmessage = (event: MessageEvent) => { ws.onmessage = (event: MessageEvent) => {
const data = JSON.parse(String(event.data)) as { let data: {
type: string; type: string;
location?: string; location?: string;
data?: unknown; data?: unknown;
error?: string; error?: string;
mutation_id?: string; mutation_id?: string;
}; };
try {
data = JSON.parse(String(event.data));
} catch (e) {
console.error('Tether: invalid JSON message', event.data, e);
return;
}
if (data.type === 'query') { if (data.type === 'query') {
this.onQuery(data.location, data.data); this.onQuery(data.location, data.data);
} else if (data.type === 'mutation') { } else if (data.type === 'mutation') {
@@ -41,8 +47,16 @@ export class WebSocketHandler {
} }
}; };
ws.onclose = () => { ws.onclose = (event: CloseEvent) => {
console.log('Disconnected from Tether'); console.log(
'Disconnected from Tether',
'code:',
event.code,
'reason:',
event.reason || '(none)',
'wasClean:',
event.wasClean
);
this.attemptReconnect(); this.attemptReconnect();
}; };
}; };