This commit is contained in:
elpresidank 2026-05-12 08:06:58 -05:00
parent e8c7a4f6e0
commit ffd97375a8
160 changed files with 6704 additions and 1895 deletions

View file

@ -6,10 +6,13 @@
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc",
"build": "bunx --bun tsc",
"dev": "tsc --watch",
"clean": "rm -rf dist",
"test": "vitest run"
"test": "bunx --bun vitest run"
},
"dependencies": {
"effect": "4.0.0-beta.65"
},
"peerDependencies": {
"ws": "^8.0.0"
@ -20,10 +23,11 @@
}
},
"devDependencies": {
"@effect/vitest": "4.0.0-beta.65",
"@types/node": "^22.0.0",
"@types/ws": "^8.5.0",
"typescript": "^5.8.0",
"vitest": "^3.1.0",
"vitest": "^4.1.6",
"happy-dom": "^20.0.0"
},
"license": "Apache-2.0"

View file

@ -1,4 +1,4 @@
import { Triple, Term } from "./Triple.js";
import type { Term, Triple } from "./Triple.js";
export type Request = object;
export type Response = object;

View file

@ -1,4 +1,4 @@
import { RequestMessage } from "../models/messages.js";
import type { RequestMessage } from "../models/messages.js";
import { WS_OPEN, WS_CONNECTING, type IsomorphicWebSocket } from "./websocket-adapter.js";
// Constant defining the delay before attempting to reconnect a WebSocket
@ -8,8 +8,14 @@ export const SOCKET_RECONNECTION_TIMEOUT = 2000;
// Forward declare Socket type to avoid circular dependency
// Using a minimal interface that matches what BaseApi provides
interface Socket {
ws?: IsomorphicWebSocket;
inflight: { [key: string]: ServiceCallMulti };
ws: IsomorphicWebSocket | null | undefined;
inflight: {
[key: string]: {
onReceived: (resp: object) => void;
retryNow: () => void;
error: (err: object | string) => void;
};
};
reopen: () => void;
getNextId?: () => string;
user?: string;
@ -42,7 +48,7 @@ export class ServiceCallMulti {
success: (resp: unknown) => void;
error: (err: object | string) => void;
receiver: (resp: unknown) => boolean;
timeoutId?: ReturnType<typeof setTimeout>;
timeoutId: ReturnType<typeof setTimeout> | undefined = undefined;
timeout: number;
retries: number;
socket: Socket;
@ -121,7 +127,7 @@ export class ServiceCallMulti {
}
// Check if WebSocket connection is available and ready
if (this.socket.ws && this.socket.ws.readyState === WS_OPEN) {
if (this.socket.ws !== null && this.socket.ws !== undefined && this.socket.ws.readyState === WS_OPEN) {
try {
this.socket.ws.send(JSON.stringify(this.msg));
this.timeoutId = setTimeout(this.onTimeout.bind(this), this.timeout);
@ -148,7 +154,8 @@ export class ServiceCallMulti {
// No WebSocket connection available or not ready
// Check if socket is connecting
if (
this.socket.ws &&
this.socket.ws !== null &&
this.socket.ws !== undefined &&
this.socket.ws.readyState === WS_CONNECTING
) {
// Wait a bit longer for connection to establish

View file

@ -1,4 +1,4 @@
import { RequestMessage } from "../models/messages.js";
import type { RequestMessage } from "../models/messages.js";
import { WS_OPEN, type IsomorphicWebSocket } from "./websocket-adapter.js";
// Constant defining the delay before attempting to reconnect a WebSocket
@ -8,8 +8,14 @@ export const SOCKET_RECONNECTION_TIMEOUT = 2000;
// Forward declare Socket type to avoid circular dependency
// Using a minimal interface that matches what BaseApi provides
interface Socket {
ws?: IsomorphicWebSocket;
inflight: { [key: string]: ServiceCall };
ws: IsomorphicWebSocket | null | undefined;
inflight: {
[key: string]: {
onReceived: (resp: object) => void;
retryNow: () => void;
error: (err: object | string) => void;
};
};
reopen: () => void;
getNextId?: () => string;
user?: string;
@ -52,7 +58,7 @@ export class ServiceCall {
msg: RequestMessage; // The request message
success: (resp: unknown) => void; // Success callback
error: (err: object | string) => void; // Error callback
timeoutId?: ReturnType<typeof setTimeout>; // Reference to the active timeout timer
timeoutId: ReturnType<typeof setTimeout> | undefined = undefined; // Reference to the active timeout timer
timeout: number; // Timeout duration in milliseconds
retries: number; // Remaining retry attempts
socket: Socket; // WebSocket connection reference
@ -77,7 +83,10 @@ export class ServiceCall {
*/
onReceived(resp: object) {
// Guard: ignore duplicate responses after completion
if (this.complete) return;
if (this.complete) {
console.log(this.mid, "should not happen, request is already complete");
return;
}
// Mark as complete to prevent duplicate processing
this.complete = true;
@ -93,18 +102,18 @@ export class ServiceCall {
let errorToHandle: unknown = null;
// Check for direct error in response
if (resp && typeof resp === "object" && "error" in resp) {
if (resp !== null && typeof resp === "object" && "error" in resp) {
errorToHandle = (resp as Record<string, unknown>).error;
}
// Check for nested error under response property
else if (resp && typeof resp === "object" && "response" in resp) {
else if (resp !== null && typeof resp === "object" && "response" in resp) {
const response = (resp as Record<string, unknown>).response;
if (response && typeof response === "object" && "error" in response) {
if (response !== null && typeof response === "object" && "error" in response) {
errorToHandle = (response as Record<string, unknown>).error;
}
}
if (errorToHandle) {
if (errorToHandle !== null && errorToHandle !== undefined) {
// Response contains an error - call error callback
const errorObj = errorToHandle as Record<string, unknown>;
const errorMessage =
@ -151,7 +160,13 @@ export class ServiceCall {
*/
onTimeout() {
// Guard: ignore timeout after completion
if (this.complete) return;
if (this.complete) {
console.log(
this.mid,
"timeout should not happen, request is already complete",
);
return;
}
console.log("Request", this.mid, "timed out");
@ -180,7 +195,13 @@ export class ServiceCall {
*/
attempt() {
// Guard: don't retry completed requests
if (this.complete) return;
if (this.complete) {
console.log(
this.mid,
"attempt should not be called, request is already complete",
);
return;
}
// Decrement retry counter
this.retries--;
@ -197,7 +218,7 @@ export class ServiceCall {
}
// Check if WebSocket connection is available and ready
if (this.socket.ws && this.socket.ws.readyState === WS_OPEN) {
if (this.socket.ws !== null && this.socket.ws !== undefined && this.socket.ws.readyState === WS_OPEN) {
try {
// Attempt to send the message as JSON
this.socket.ws.send(JSON.stringify(this.msg));

File diff suppressed because it is too large Load diff

View file

@ -117,7 +117,9 @@ export function getDefaultSocketUrl(): string {
*/
export function getRandomValues(array: Uint32Array): Uint32Array {
if (typeof globalThis.crypto?.getRandomValues === "function") {
return globalThis.crypto.getRandomValues(array);
const random = globalThis.crypto.getRandomValues(new Uint32Array(array.length));
array.set(random);
return array;
}
// Node.js fallback for versions < 19 where globalThis.crypto may not exist
try {

View file

@ -4,6 +4,7 @@
"outDir": "dist",
"rootDir": "src",
"lib": ["ES2022", "DOM"],
"types": ["node"],
"composite": true
},
"include": ["src"],