chore: initial commit

This commit is contained in:
2026-10-22 17:07:08 +08:00
commit 6ccb102d38
20 changed files with 5949 additions and 0 deletions
+65
View File
@@ -0,0 +1,65 @@
import type {ForwardServerOptions, RelayLimits} from '../types.ts';
export const DEFAULT_LIMITS: Readonly<RelayLimits> = Object.freeze({
auth_timeout_ms: 10_000,
close_timeout_ms: 5_000,
rpc_timeout_ms: 5_000,
unused_timeout_ms: 60_000,
idle_timeout_ms: 30_000,
max_token_age_ms: 300_000,
join_window_ms: 60_000,
clock_skew_ms: 5_000,
max_lease_ms: 300_000,
max_token_bytes: 8_192,
max_payload_bytes: 8 * 1024 * 1024,
max_chat_length: 256,
max_connections: 2_000,
max_sessions: 500,
max_grants_per_session: 32,
max_pending_per_web: 16,
max_pending_total: 4_096,
requests_per_second: 30,
events_per_second: 200,
max_dedupe_entries: 256,
dedupe_ttl_ms: 30_000,
max_buffered_packets: 128,
});
export function resolve_options(options: ForwardServerOptions) {
if (!options || !Number.isSafeInteger(options.forward_id) || options.forward_id < 0) {
throw new Error('forward_id must be a nonnegative safe integer');
}
if (typeof options.key !== 'string' || options.key.trim().length < 16) {
throw new Error('A relay secret of at least 16 characters is required');
}
if (!Array.isArray(options.allowed_origins) || options.allowed_origins.some(origin => {
try {
const url = new URL(origin);
return !['http:', 'https:'].includes(url.protocol) || url.origin !== origin;
} catch { return true; }
})) throw new Error('allowed_origins must contain explicit HTTP(S) origins');
if (options.allow_no_origin !== undefined && typeof options.allow_no_origin !== 'boolean') {
throw new Error('allow_no_origin must be a boolean');
}
const path = options.path ?? '/api/forward_server';
const namespace = options.namespace ?? '/';
if (!/^\/[A-Za-z0-9_/-]+$/.test(path) || path === '/' || path.endsWith('/')) {
throw new Error('path must be an absolute Socket.IO path without a trailing slash');
}
if (!/^\/[A-Za-z0-9_/-]*$/.test(namespace) || (namespace !== '/' && namespace.endsWith('/'))) {
throw new Error('namespace must be an absolute Socket.IO namespace');
}
const port = options.port ?? 38858;
if (!Number.isInteger(port) || port < 0 || port > 65535) throw new Error('Invalid relay port');
if (options.http_server && (options.port !== undefined || options.host !== undefined)) {
throw new Error('Borrowed HTTP server cannot specify a relay host or port');
}
const limits = {...DEFAULT_LIMITS, ...options.limits};
for (const [name, value] of Object.entries(limits)) {
if (!Object.hasOwn(DEFAULT_LIMITS, name) || !Number.isSafeInteger(value) || value < (name === 'clock_skew_ms' ? 0 : 1)
|| value > 2_147_483_647) throw new Error(`Invalid relay limit: ${name}`);
}
if (limits.join_window_ms > limits.max_token_age_ms) throw new Error('Join window exceeds token lifetime');
return {...options, allowed_origins: [...options.allowed_origins], path, namespace, port, limits,
allow_no_origin: options.allow_no_origin ?? true};
}