@hpkv/zustand-multiplayer
Version:
A multiplayer middleware for Zustand using HPKV
168 lines (160 loc) • 5.32 kB
TypeScript
import { StoreMutatorIdentifier, StateCreator } from 'zustand/vanilla';
import { ConnectionStats, ConnectionState } from '@hpkv/websocket-client';
import { StoreApi } from 'zustand';
declare enum LogLevel {
DEBUG = 0,
INFO = 1,
WARN = 2,
ERROR = 3,
NONE = 4
}
interface LogContext {
clientId?: string;
operation?: string;
timestamp: number;
[key: string]: unknown;
}
declare class Logger {
private level;
constructor(level?: LogLevel);
private shouldLog;
private formatMessage;
debug(message: string, context?: Partial<LogContext>): void;
info(message: string, context?: Partial<LogContext>): void;
warn(message: string, context?: Partial<LogContext>): void;
error(message: string, error?: Error, context?: Partial<LogContext>): void;
setLevel(level: LogLevel): void;
getLevel(): LogLevel;
}
/**
* Creates a new logger instance with the specified log level
* @param level The log level for the logger
* @returns A new Logger instance
*/
declare function createLogger(level?: LogLevel): Logger;
interface PerformanceMetrics {
averageSyncTime: number;
}
interface MultiplayerOptions<TState> {
namespace: string;
apiBaseUrl: string;
apiKey?: string;
tokenGenerationUrl?: string;
sync?: Array<keyof TState>;
logLevel?: LogLevel;
rateLimit?: number;
zFactor?: number;
}
interface MultiplayerState {
connectionState: ConnectionState;
hasHydrated: boolean;
performanceMetrics: PerformanceMetrics;
}
type MultiplayerStoreApi<S> = StoreApi<S> & {
multiplayer: {
reHydrate: () => Promise<void>;
clearStorage: () => Promise<void>;
disconnect: () => Promise<void>;
connect: () => Promise<void>;
destroy: () => Promise<void>;
getConnectionStatus: () => ConnectionStats | null;
getMetrics: () => PerformanceMetrics;
};
};
type WithMultiplayer<S> = S & {
multiplayer: MultiplayerState;
};
type MultiplayerStore<S, _U> = S extends {
getState: () => infer T;
} ? MultiplayerStoreApi<T> : never;
declare module 'zustand/vanilla' {
interface StoreMutators<S, A> {
'zustand/multiplayer': MultiplayerStore<S, A>;
}
}
/**
* Main multiplayer middleware type
*/
type Multiplayer = <T, Mps extends [StoreMutatorIdentifier, unknown][] = [], Mcs extends [StoreMutatorIdentifier, unknown][] = []>(initializer: StateCreator<T, [
...Mps,
['zustand/multiplayer', never]
], Mcs, Omit<T, 'multiplayer'>>, options: MultiplayerOptions<Omit<T, 'multiplayer'>>) => StateCreator<T, Mps, [['zustand/multiplayer', never], ...Mcs], WithMultiplayer<T>>;
declare const multiplayer: Multiplayer;
/**
* Generates a cryptographically secure unique client identifier
* @returns A unique client identifier string
*/
declare function generateClientId(): string;
/**
* Normalizes an error to ensure it's an Error instance
* @param error The error to normalize
* @returns A normalized Error instance
*/
declare function normalizeError(error: unknown): Error;
/**
* Creates a delay promise
* @param ms Milliseconds to delay
* @returns Promise that resolves after the delay
*/
declare function createDelay(ms: number): Promise<void>;
/**
* Safely clears a timeout
* @param timeoutId The timeout ID to clear
*/
declare function clearTimeoutSafely(timeoutId: ReturnType<typeof setTimeout> | null): void;
/**
* Escapes special characters in a string for use in a regular expression
* @param string The string to escape
* @returns The escaped string
*/
declare function escapeRegExp(string: string): string;
/**
* Type guard to check if a value is a plain object
* @param value The value to check
* @returns True if the value is a plain object
*/
declare function isPlainObject(value: unknown): value is Record<string, unknown>;
/**
* Request format for token generation endpoint
*/
interface TokenRequest {
/** Store name to generate token for */
namespace: string;
/** Keys and patterns to subscribe to */
subscribedKeysAndPatterns: string[];
}
/**
* Response format from token generation endpoint
*/
interface TokenResponse {
/** The namespace the token is for */
namespace: string;
/** The generated WebSocket token */
token: string;
}
/**
* Utility to help generate WebSocket tokens for HPKV
*/
declare class TokenHelper {
private readonly tokenManager;
/**
* Creates a new TokenHelper instance
*
* @param apiKey HPKV API key
* @param baseUrl HPKV base URL
*/
constructor(apiKey: string, baseUrl: string);
/**
* Generate a token for a store with the given namespace and keys
*/
generateTokenForStore(namespace: string, subscribedKeysAnPatterns: string[]): Promise<string>;
/**
* Process a token request and return a token response
*
* @param requestData The request data object or string
* @returns TokenResponse object with the generated token
*/
processTokenRequest(requestData: unknown): Promise<TokenResponse>;
}
export { LogLevel, Logger, TokenHelper, clearTimeoutSafely, createDelay, createLogger, escapeRegExp, generateClientId, isPlainObject, multiplayer, normalizeError };
export type { LogContext, MultiplayerOptions, MultiplayerState, TokenRequest, TokenResponse, WithMultiplayer };