Fix strict TypeScript checks for server
This commit is contained in:
@@ -35,7 +35,7 @@ function normalizeOptionalString(value: unknown): string | undefined {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isValidPort(value: unknown): value is number {
|
function isValidPort(value: unknown): value is number {
|
||||||
return Number.isInteger(value) && value >= 1 && value <= 65535;
|
return typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= 65535;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isValidWsUrl(value: string): boolean {
|
function isValidWsUrl(value: string): boolean {
|
||||||
@@ -67,18 +67,18 @@ export function validateYonexusServerConfig(raw: unknown): YonexusServerConfig {
|
|||||||
issues.push("followerIdentifiers must not contain duplicates");
|
issues.push("followerIdentifiers must not contain duplicates");
|
||||||
}
|
}
|
||||||
|
|
||||||
const notifyBotToken = source.notifyBotToken;
|
const rawNotifyBotToken = source.notifyBotToken;
|
||||||
if (!isNonEmptyString(notifyBotToken)) {
|
if (!isNonEmptyString(rawNotifyBotToken)) {
|
||||||
issues.push("notifyBotToken is required");
|
issues.push("notifyBotToken is required");
|
||||||
}
|
}
|
||||||
|
|
||||||
const adminUserId = source.adminUserId;
|
const rawAdminUserId = source.adminUserId;
|
||||||
if (!isNonEmptyString(adminUserId)) {
|
if (!isNonEmptyString(rawAdminUserId)) {
|
||||||
issues.push("adminUserId is required");
|
issues.push("adminUserId is required");
|
||||||
}
|
}
|
||||||
|
|
||||||
const listenPort = source.listenPort;
|
const rawListenPort = source.listenPort;
|
||||||
if (!isValidPort(listenPort)) {
|
if (!isValidPort(rawListenPort)) {
|
||||||
issues.push("listenPort must be an integer between 1 and 65535");
|
issues.push("listenPort must be an integer between 1 and 65535");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,6 +93,10 @@ export function validateYonexusServerConfig(raw: unknown): YonexusServerConfig {
|
|||||||
throw new YonexusServerConfigError(issues);
|
throw new YonexusServerConfigError(issues);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const notifyBotToken = rawNotifyBotToken as string;
|
||||||
|
const adminUserId = rawAdminUserId as string;
|
||||||
|
const listenPort = rawListenPort as number;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
followerIdentifiers,
|
followerIdentifiers,
|
||||||
notifyBotToken: notifyBotToken.trim(),
|
notifyBotToken: notifyBotToken.trim(),
|
||||||
|
|||||||
@@ -79,6 +79,9 @@ export interface ClientRecord {
|
|||||||
/** Last successful authentication timestamp (UTC unix seconds) */
|
/** Last successful authentication timestamp (UTC unix seconds) */
|
||||||
lastAuthenticatedAt?: number;
|
lastAuthenticatedAt?: number;
|
||||||
|
|
||||||
|
/** Last successful pairing timestamp (UTC unix seconds) */
|
||||||
|
pairedAt?: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recent nonces used in authentication attempts.
|
* Recent nonces used in authentication attempts.
|
||||||
* This is a rolling window that may be cleared on restart.
|
* This is a rolling window that may be cleared on restart.
|
||||||
@@ -151,6 +154,7 @@ export interface SerializedClientRecord {
|
|||||||
status: ClientLivenessStatus;
|
status: ClientLivenessStatus;
|
||||||
lastHeartbeatAt?: number;
|
lastHeartbeatAt?: number;
|
||||||
lastAuthenticatedAt?: number;
|
lastAuthenticatedAt?: number;
|
||||||
|
pairedAt?: number;
|
||||||
createdAt: number;
|
createdAt: number;
|
||||||
updatedAt: number;
|
updatedAt: number;
|
||||||
// Note: recentNonces and recentHandshakeAttempts are intentionally
|
// Note: recentNonces and recentHandshakeAttempts are intentionally
|
||||||
@@ -203,6 +207,7 @@ export function serializeClientRecord(record: ClientRecord): SerializedClientRec
|
|||||||
status: record.status,
|
status: record.status,
|
||||||
lastHeartbeatAt: record.lastHeartbeatAt,
|
lastHeartbeatAt: record.lastHeartbeatAt,
|
||||||
lastAuthenticatedAt: record.lastAuthenticatedAt,
|
lastAuthenticatedAt: record.lastAuthenticatedAt,
|
||||||
|
pairedAt: record.pairedAt,
|
||||||
createdAt: record.createdAt,
|
createdAt: record.createdAt,
|
||||||
updatedAt: record.updatedAt
|
updatedAt: record.updatedAt
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ export interface YonexusServerRuntimeOptions {
|
|||||||
config: YonexusServerConfig;
|
config: YonexusServerConfig;
|
||||||
store: YonexusServerStore;
|
store: YonexusServerStore;
|
||||||
transport: ServerTransport;
|
transport: ServerTransport;
|
||||||
|
notificationService?: DiscordNotificationService;
|
||||||
now?: () => number;
|
now?: () => number;
|
||||||
sweepIntervalMs?: number;
|
sweepIntervalMs?: number;
|
||||||
}
|
}
|
||||||
@@ -80,10 +81,12 @@ export class YonexusServerRuntime {
|
|||||||
};
|
};
|
||||||
this.sweepIntervalMs = options.sweepIntervalMs ?? 30_000;
|
this.sweepIntervalMs = options.sweepIntervalMs ?? 30_000;
|
||||||
this.pairingService = createPairingService({ now: this.now });
|
this.pairingService = createPairingService({ now: this.now });
|
||||||
this.notificationService = createDiscordNotificationService({
|
this.notificationService =
|
||||||
botToken: options.config.notifyBotToken,
|
options.notificationService ??
|
||||||
adminUserId: options.config.adminUserId
|
createDiscordNotificationService({
|
||||||
});
|
botToken: options.config.notifyBotToken,
|
||||||
|
adminUserId: options.config.adminUserId
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get state(): ServerLifecycleState {
|
get state(): ServerLifecycleState {
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ interface CreateDmChannelResponse {
|
|||||||
interface SendDiscordDirectMessageOptions {
|
interface SendDiscordDirectMessageOptions {
|
||||||
config: DiscordNotificationConfig;
|
config: DiscordNotificationConfig;
|
||||||
message: string;
|
message: string;
|
||||||
fetcher?: DiscordFetch;
|
fetcher: DiscordFetch;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DISCORD_API_BASE_URL = "https://discord.com/api/v10";
|
const DISCORD_API_BASE_URL = "https://discord.com/api/v10";
|
||||||
|
|||||||
21
plugin/types/ws.d.ts
vendored
Normal file
21
plugin/types/ws.d.ts
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
declare module "ws" {
|
||||||
|
export type RawData = Buffer | ArrayBuffer | Buffer[] | string;
|
||||||
|
|
||||||
|
export class WebSocket {
|
||||||
|
static readonly OPEN: number;
|
||||||
|
readonly readyState: number;
|
||||||
|
send(data: string): void;
|
||||||
|
close(code?: number, reason?: string): void;
|
||||||
|
on(event: "message", listener: (data: RawData) => void): this;
|
||||||
|
on(event: "close", listener: (code: number, reason: Buffer) => void): this;
|
||||||
|
on(event: "error", listener: (error: Error) => void): this;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WebSocketServer {
|
||||||
|
constructor(options: { host?: string; port: number });
|
||||||
|
on(event: "error", listener: (error: Error) => void): this;
|
||||||
|
on(event: "listening", listener: () => void): this;
|
||||||
|
on(event: "connection", listener: (ws: WebSocket, req: import("http").IncomingMessage) => void): this;
|
||||||
|
close(callback?: () => void): void;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import { join } from "node:path";
|
|||||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
import { createYonexusServerRuntime } from "../plugin/core/runtime.js";
|
import { createYonexusServerRuntime } from "../plugin/core/runtime.js";
|
||||||
|
import { createMockNotificationService } from "../plugin/notifications/discord.js";
|
||||||
import {
|
import {
|
||||||
createYonexusServerStore,
|
createYonexusServerStore,
|
||||||
loadServerStore,
|
loadServerStore,
|
||||||
@@ -90,6 +91,7 @@ describe("YNX-1105e: Server state recovery", () => {
|
|||||||
},
|
},
|
||||||
store,
|
store,
|
||||||
transport: firstTransport.transport,
|
transport: firstTransport.transport,
|
||||||
|
notificationService: createMockNotificationService(),
|
||||||
now: () => now
|
now: () => now
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -145,6 +147,7 @@ describe("YNX-1105e: Server state recovery", () => {
|
|||||||
},
|
},
|
||||||
store,
|
store,
|
||||||
transport: secondTransport.transport,
|
transport: secondTransport.transport,
|
||||||
|
notificationService: createMockNotificationService(),
|
||||||
now: () => now
|
now: () => now
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"module": "NodeNext",
|
"module": "NodeNext",
|
||||||
"moduleResolution": "NodeNext",
|
"moduleResolution": "NodeNext",
|
||||||
"outDir": "dist",
|
"outDir": "dist",
|
||||||
"rootDir": ".",
|
"rootDir": "..",
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
@@ -15,7 +15,9 @@
|
|||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"plugin/**/*.ts",
|
"plugin/**/*.ts",
|
||||||
"servers/**/*.ts"
|
"plugin/**/*.d.ts",
|
||||||
|
"servers/**/*.ts",
|
||||||
|
"../Yonexus.Protocol/src/**/*.ts"
|
||||||
],
|
],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"dist",
|
"dist",
|
||||||
|
|||||||
Reference in New Issue
Block a user