From 7e7a933b8a9710778c6d3763486de06c301b6924 Mon Sep 17 00:00:00 2001 From: Yeskiy Date: Sat, 25 Mar 2023 01:29:58 +0200 Subject: [PATCH] fix: Add `idResolvers` support. Add export of Integrated Resolvers. Add `PureBrotliCompressionResolver` and `WasmBrotliCompressionResolver` (for different env support) --- package-lock.json | 47 ++++++++++++++ package.json | 3 + src/client.ts | 64 +++++++++++++------ src/compressResolvers/index.ts | 11 ++-- .../pureBrotli.compressionResolver.ts | 29 +++++++++ .../wasmBrotli.compressionResolver.ts | 31 +++++++++ src/functionResolvers/index.ts | 13 ++-- .../weakFunctionPool.functionResolver.ts | 7 +- src/idResolvers/index.ts | 34 ++++++++++ src/idResolvers/pureUuid.idResolver.ts | 20 ++++++ src/idResolvers/uuid.idResolver.ts | 20 ++++++ src/index.ts | 33 ++++++++++ src/modelResolvers/Default.modelResolver.ts | 4 +- src/modelResolvers/index.ts | 8 +-- src/server.ts | 56 +++++++++++----- src/sessionStoreResolver/index.ts | 0 src/types.ts | 15 +++++ src/universalRPC/index.ts | 14 ++++ src/utils/uuid.util.ts | 6 -- 19 files changed, 355 insertions(+), 60 deletions(-) create mode 100644 src/compressResolvers/pureBrotli.compressionResolver.ts create mode 100644 src/compressResolvers/wasmBrotli.compressionResolver.ts create mode 100644 src/idResolvers/index.ts create mode 100644 src/idResolvers/pureUuid.idResolver.ts create mode 100644 src/idResolvers/uuid.idResolver.ts create mode 100644 src/sessionStoreResolver/index.ts delete mode 100644 src/utils/uuid.util.ts diff --git a/package-lock.json b/package-lock.json index 53963cd..712c5fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,8 @@ "version": "2.0.0-alpha.8", "license": "MIT", "dependencies": { + "brotli": "^1.3.3", + "brotli-wasm": "^1.3.1", "buffer": "^6.0.3", "events": "^3.3.0", "pure-uuid": "^1.6.3", @@ -16,6 +18,7 @@ "uuid": "^9.0.0" }, "devDependencies": { + "@types/brotli": "^1.3.1", "@types/uuid": "^9.0.1", "@types/ws": "^8.5.4", "@typescript-eslint/eslint-plugin": "^5.51.0", @@ -337,6 +340,15 @@ "node": ">=14.16" } }, + "node_modules/@types/brotli": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@types/brotli/-/brotli-1.3.1.tgz", + "integrity": "sha512-mGwX0BBQqmpHoX8+b8Oez0X+ZEYnl2gbDL2n0HxYT4imqhTChhj1AAgAKVWNZSuPvXGZXqVoOtBS0071tN6Tkw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/http-cache-semantics": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", @@ -970,6 +982,19 @@ "node": ">=8" } }, + "node_modules/brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "dependencies": { + "base64-js": "^1.1.2" + } + }, + "node_modules/brotli-wasm": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/brotli-wasm/-/brotli-wasm-1.3.1.tgz", + "integrity": "sha512-Vp+v3QXddvy39Ycbmvd3/Y1kUvKhwtnprzeABcKWN4jmyg6W3W5MhGPCfXBMHeSQnizgpV59iWmkSRp7ykOnDQ==" + }, "node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -5993,6 +6018,15 @@ "defer-to-connect": "^2.0.1" } }, + "@types/brotli": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@types/brotli/-/brotli-1.3.1.tgz", + "integrity": "sha512-mGwX0BBQqmpHoX8+b8Oez0X+ZEYnl2gbDL2n0HxYT4imqhTChhj1AAgAKVWNZSuPvXGZXqVoOtBS0071tN6Tkw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/http-cache-semantics": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", @@ -6408,6 +6442,19 @@ "fill-range": "^7.0.1" } }, + "brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "requires": { + "base64-js": "^1.1.2" + } + }, + "brotli-wasm": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/brotli-wasm/-/brotli-wasm-1.3.1.tgz", + "integrity": "sha512-Vp+v3QXddvy39Ycbmvd3/Y1kUvKhwtnprzeABcKWN4jmyg6W3W5MhGPCfXBMHeSQnizgpV59iWmkSRp7ykOnDQ==" + }, "buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", diff --git a/package.json b/package.json index e06e103..25799bf 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,8 @@ "url": "git+https://github.com/yeskiy/prpcow.git" }, "dependencies": { + "brotli": "^1.3.3", + "brotli-wasm": "^1.3.1", "buffer": "^6.0.3", "events": "^3.3.0", "pure-uuid": "^1.6.3", @@ -38,6 +40,7 @@ "ws": "^8.5.0 || ^7.5.0" }, "devDependencies": { + "@types/brotli": "^1.3.1", "@types/uuid": "^9.0.1", "@types/ws": "^8.5.4", "@typescript-eslint/eslint-plugin": "^5.51.0", diff --git a/src/client.ts b/src/client.ts index 3f3bc6f..834a540 100644 --- a/src/client.ts +++ b/src/client.ts @@ -15,6 +15,8 @@ import NoCompressionResolver from "./compressResolvers/noCompression.compression import { ICompressResolver } from "./compressResolvers"; import { IFunctionResolver } from "./functionResolvers"; import { IModelResolver } from "./modelResolvers"; +import { IIdResolver } from "./idResolvers"; +import UuidIdResolver from "./idResolvers/uuid.idResolver"; interface WebsocketProto { prototype: WebSocket; @@ -27,6 +29,7 @@ export type ClientOptions = { FunctionResolver?: IFunctionResolver; ModelResolver?: IModelResolver; CompressResolver?: ICompressResolver; + IdResolver?: IIdResolver; }; logger?: LoggerOptions | boolean; }; @@ -37,6 +40,7 @@ type ClientInnerOptions = ClientOptions & { FunctionResolver: IFunctionResolver; ModelResolver: IModelResolver; CompressResolver: ICompressResolver; + IdResolver: IIdResolver; }; version: string; }; @@ -69,6 +73,7 @@ export class Client { FunctionResolver: WeakFunctionPool, ModelResolver: DefaultResolver, CompressResolver: NoCompressionResolver, + IdResolver: UuidIdResolver, ...finalOptions.universalRPC, }, version: packageJson.version, @@ -122,13 +127,19 @@ export class Client { semver.eq( this.options.version, clientRequest.data.version - ) || - clientRequest.data.functionResolver === - this.options.universalRPC.FunctionResolver.typeName() || - clientRequest.data.modelResolver === - this.options.universalRPC.ModelResolver.typeName() || - clientRequest.data.CompressResolver === - this.options.universalRPC.CompressResolver.typeName() + ) && + this.options.universalRPC.FunctionResolver.isCompatibleWith( + clientRequest.data.functionResolver + ) && + this.options.universalRPC.ModelResolver.isCompatibleWith( + clientRequest.data.modelResolver + ) && + this.options.universalRPC.CompressResolver.isCompatibleWith( + clientRequest.data.compressResolver + ) && + this.options.universalRPC.IdResolver.isCompatibleWith( + clientRequest.data.idResolver + ) ) ) { this.logger.silly( @@ -143,7 +154,10 @@ export class Client { clientRequest.data.modelResolver }`, `Client compressResolver: ${this.options.universalRPC.CompressResolver.typeName()}, Server compressResolver: ${ - clientRequest.data.CompressResolver + clientRequest.data.compressResolver + }`, + `Client idResolver: ${this.options.universalRPC.IdResolver.typeName()}, Server idResolver: ${ + clientRequest.data.idResolver }` ); throw new RuntimeError( @@ -155,30 +169,42 @@ export class Client { ? "" : `Client version: ${this.options.version}, Server version: ${clientRequest.data.version}` } ${ - clientRequest.data - .functionResolver !== - this.options.universalRPC.FunctionResolver.typeName() + !this.options.universalRPC.FunctionResolver.isCompatibleWith( + clientRequest.data + .functionResolver + ) ? "" : `Client functionResolver: ${this.options.universalRPC.FunctionResolver.typeName()}, Server functionResolver: ${ clientRequest.data .functionResolver }` } ${ - clientRequest.data.modelResolver !== - this.options.universalRPC.ModelResolver.typeName() + !this.options.universalRPC.ModelResolver.isCompatibleWith( + clientRequest.data.modelResolver + ) ? "" : `Client modelResolver: ${this.options.universalRPC.ModelResolver.typeName()}, Server modelResolver: ${ clientRequest.data .modelResolver }` } ${ - clientRequest.data - .CompressResolver !== - this.options.universalRPC.CompressResolver.typeName() + !this.options.universalRPC.CompressResolver.isCompatibleWith( + clientRequest.data + .compressResolver + ) ? "" : `Client compressResolver: ${this.options.universalRPC.CompressResolver.typeName()}, Server compressResolver: ${ clientRequest.data - .CompressResolver + .compressResolver + }` + } ${ + !this.options.universalRPC.IdResolver.isCompatibleWith( + clientRequest.data.idResolver + ) + ? "" + : `Client idResolver: ${this.options.universalRPC.IdResolver.typeName()}, Server idResolver: ${ + clientRequest.data + .idResolver }` }`, 400, @@ -191,6 +217,7 @@ export class Client { ); const initPayload = { + key: this.key, version: this.options.version, functionResolver: this.options.universalRPC.FunctionResolver.typeName(), @@ -198,7 +225,8 @@ export class Client { this.options.universalRPC.ModelResolver.typeName(), compressResolver: this.options.universalRPC.CompressResolver.typeName(), - key: this.key, + idResolver: + this.options.universalRPC.IdResolver.typeName(), }; this.logger.debug( diff --git a/src/compressResolvers/index.ts b/src/compressResolvers/index.ts index df0cef0..f4ef0e8 100644 --- a/src/compressResolvers/index.ts +++ b/src/compressResolvers/index.ts @@ -1,8 +1,9 @@ import WebSocket from "ws"; import { BufferLike, ModifiedWebSocket } from "../utils/websocketModifier.util"; import { Logger, LoggerOptions } from "../utils/logger.util"; +import { IResolverStatic, Resolver } from "../types"; -export abstract class CompressResolver { +export abstract class CompressResolver extends Resolver { options: { session: ModifiedWebSocket; logger: LoggerOptions | boolean; @@ -14,6 +15,7 @@ export abstract class CompressResolver { session: ModifiedWebSocket; logger: LoggerOptions | boolean; }) { + super(); this.options = options; if (typeof this.options.logger !== "boolean") { this.logger = new Logger({ @@ -28,20 +30,15 @@ export abstract class CompressResolver { this.logger = new Logger(); } } - - static typeName(): string { - throw new Error("typeName() implementation is required"); - } public abstract compress(messageEvent: object): Promise; public abstract decompress( messageEvent: WebSocket.MessageEvent ): Promise; } -export interface ICompressResolver { +export interface ICompressResolver extends IResolverStatic { new (options: { session: ModifiedWebSocket; logger: LoggerOptions | boolean; }): CompressResolver; - typeName(): string; } diff --git a/src/compressResolvers/pureBrotli.compressionResolver.ts b/src/compressResolvers/pureBrotli.compressionResolver.ts new file mode 100644 index 0000000..1bd585b --- /dev/null +++ b/src/compressResolvers/pureBrotli.compressionResolver.ts @@ -0,0 +1,29 @@ +import { MessageEvent } from "ws"; +import brotli from "brotli"; +import { Buffer } from "buffer"; +import { BufferLike } from "../utils/websocketModifier.util"; +import { CompressResolver } from "./index"; + +export default class PureBrotliCompressionResolver extends CompressResolver { + public static typeName(): string { + return "PureBrotli"; + } + + public static isCompatibleWith(type: string): boolean { + return [this.typeName(), "WasmBrotli"].indexOf(type) !== -1; + } + + // eslint-disable-next-line class-methods-use-this + public async compress(messageEvent: object): Promise { + return brotli.compress(Buffer.from(JSON.stringify(messageEvent)), { + quality: 0, + }); + } + + // eslint-disable-next-line class-methods-use-this + public async decompress(messageEvent: MessageEvent): Promise { + return JSON.parse( + brotli.decompress(messageEvent.data).toString() + ); + } +} diff --git a/src/compressResolvers/wasmBrotli.compressionResolver.ts b/src/compressResolvers/wasmBrotli.compressionResolver.ts new file mode 100644 index 0000000..056ce88 --- /dev/null +++ b/src/compressResolvers/wasmBrotli.compressionResolver.ts @@ -0,0 +1,31 @@ +import WebSocket from "ws"; +import * as brotli from "brotli-wasm"; +import { Buffer } from "buffer"; +import { CompressResolver } from "./index"; +import { BufferLike } from "../utils/websocketModifier.util"; + +export default class WasmBrotliCompressionResolver extends CompressResolver { + public static typeName(): string { + return "WasmBrotli"; + } + + public static isCompatibleWith(type: string): boolean { + return [this.typeName(), "PureBrotli"].indexOf(type) !== -1; + } + + // eslint-disable-next-line class-methods-use-this + public async compress(messageEvent: object): Promise { + return brotli.compress(Buffer.from(JSON.stringify(messageEvent)), { + quality: 0, + }); + } + + // eslint-disable-next-line class-methods-use-this + public async decompress( + messageEvent: WebSocket.MessageEvent + ): Promise { + return JSON.parse( + brotli.decompress(messageEvent.data).toString() + ); + } +} diff --git a/src/functionResolvers/index.ts b/src/functionResolvers/index.ts index 73b8e0e..709831a 100644 --- a/src/functionResolvers/index.ts +++ b/src/functionResolvers/index.ts @@ -2,14 +2,16 @@ import { DataObject } from "../utils/typeAssert.util"; import { ModifiedWebSocket } from "../utils/websocketModifier.util"; import { Logger, LoggerOptions } from "../utils/logger.util"; import { ModelResolver } from "../modelResolvers"; -import { FunctionResolverFunction } from "../types"; +import { FunctionResolverFunction, IResolverStatic, Resolver } from "../types"; +import { IdResolver } from "../idResolvers"; -export abstract class FunctionResolver { +export abstract class FunctionResolver extends Resolver { protected options: { session: ModifiedWebSocket; sendMessage: (message: any) => Promise; deSerializeObject: ModelResolver["deserialize"]; serializeObject: ModelResolver["serialize"]; + uuid: IdResolver["gen"]; logger: LoggerOptions | boolean; }; @@ -20,8 +22,10 @@ export abstract class FunctionResolver { sendMessage: (message: any) => Promise; deSerializeObject: ModelResolver["deserialize"]; serializeObject: ModelResolver["serialize"]; + uuid: IdResolver["gen"]; logger: LoggerOptions | boolean; }) { + super(); this.options = options; if (typeof this.options.logger !== "boolean") { this.logger = new Logger({ @@ -49,14 +53,13 @@ export abstract class FunctionResolver { public abstract close(): void; } -export interface IFunctionResolver { +export interface IFunctionResolver extends IResolverStatic { new (options: { session: ModifiedWebSocket; sendMessage: (message: any) => Promise; deSerializeObject: ModelResolver["deserialize"]; serializeObject: ModelResolver["serialize"]; + uuid: IdResolver["gen"]; logger: LoggerOptions | boolean; }): FunctionResolver; - - typeName(): string; } diff --git a/src/functionResolvers/weakFunctionPool.functionResolver.ts b/src/functionResolvers/weakFunctionPool.functionResolver.ts index ea04fb6..b79c355 100644 --- a/src/functionResolvers/weakFunctionPool.functionResolver.ts +++ b/src/functionResolvers/weakFunctionPool.functionResolver.ts @@ -1,10 +1,10 @@ // eslint-disable-next-line max-classes-per-file import typeAssert, { DataObject } from "../utils/typeAssert.util"; -import { uuidv4 as uuid } from "../utils/uuid.util"; import { FunctionResolver } from "./index"; import { ModifiedWebSocket } from "../utils/websocketModifier.util"; import { ModelResolver } from "../modelResolvers"; import { LoggerOptions } from "../utils/logger.util"; +import { IdResolver } from "../idResolvers"; const constants = { errors: { @@ -74,6 +74,7 @@ export default class WeakFunctionPool extends FunctionResolver { sendMessage: (message: any) => any; deSerializeObject: ModelResolver["deserialize"]; serializeObject: ModelResolver["serialize"]; + uuid: IdResolver["gen"]; logger: LoggerOptions | boolean; }) { super(options); @@ -217,7 +218,7 @@ export default class WeakFunctionPool extends FunctionResolver { } setOurs(executor: Function) { - const ident = uuid(); + const ident = this.options.uuid(); this.oursFunctions[ident] = executor; return ident; } @@ -233,7 +234,7 @@ export default class WeakFunctionPool extends FunctionResolver { id, params ); - const requestId = uuid(); + const requestId = this.options.uuid(); this.theirsFunctionsWaitPool[requestId] = ( response: any ) => { diff --git a/src/idResolvers/index.ts b/src/idResolvers/index.ts new file mode 100644 index 0000000..c7765c3 --- /dev/null +++ b/src/idResolvers/index.ts @@ -0,0 +1,34 @@ +import { ModifiedWebSocket } from "../utils/websocketModifier.util"; +import { Logger, LoggerOptions } from "../utils/logger.util"; +import { IResolverStatic, Resolver } from "../types"; + +export type IdResolverOptions = { + session: ModifiedWebSocket; + logger: LoggerOptions | boolean; +}; + +export abstract class IdResolver extends Resolver { + protected options: IdResolverOptions; + + protected logger: Logger; + + constructor(options: IdResolverOptions) { + super(); + this.options = options; + if (typeof this.options.logger !== "boolean") { + this.logger = new Logger({ + ...this.options.logger, + name: `${Object.getPrototypeOf(this).constructor.typeName()}`, + }); + } else { + this.logger = new Logger(); + } + } + + public abstract gen(...params: any[]): string; + public abstract close(): void; +} + +export interface IIdResolver extends IResolverStatic { + new (options: IdResolverOptions): IdResolver; +} diff --git a/src/idResolvers/pureUuid.idResolver.ts b/src/idResolvers/pureUuid.idResolver.ts new file mode 100644 index 0000000..83ee7e0 --- /dev/null +++ b/src/idResolvers/pureUuid.idResolver.ts @@ -0,0 +1,20 @@ +import UUID from "pure-uuid"; +import { IdResolver } from "./index"; + +export default class PureUuidIdResolver extends IdResolver { + public static typeName(): string { + return "PureUuid"; + } + + public static isCompatibleWith(type: string): boolean { + return [this.typeName(), "Uuid"].indexOf(type) !== -1; + } + + // eslint-disable-next-line class-methods-use-this + close(): void {} + + // eslint-disable-next-line class-methods-use-this + public gen(): string { + return new UUID(4).format(); + } +} diff --git a/src/idResolvers/uuid.idResolver.ts b/src/idResolvers/uuid.idResolver.ts new file mode 100644 index 0000000..d152eb8 --- /dev/null +++ b/src/idResolvers/uuid.idResolver.ts @@ -0,0 +1,20 @@ +import { v4 as uuidv4 } from "uuid"; +import { IdResolver } from "./index"; + +export default class UuidIdResolver extends IdResolver { + public static typeName(): string { + return "Uuid"; + } + + public static isCompatibleWith(type: string): boolean { + return [this.typeName(), "PureUuid"].indexOf(type) !== -1; + } + + // eslint-disable-next-line class-methods-use-this + public gen() { + return uuidv4(); + } + + // eslint-disable-next-line class-methods-use-this + close(): void {} +} diff --git a/src/index.ts b/src/index.ts index 9dbf895..dcfbd60 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,40 @@ +// Integrated Resolvers +import NoCompressionResolver from "./compressResolvers/noCompression.compressionResolver"; +import WeakFunctionPool from "./functionResolvers/weakFunctionPool.functionResolver"; +import DefaultModelResolver from "./modelResolvers/Default.modelResolver"; +import PureBrotliCompressionResolver from "./compressResolvers/pureBrotli.compressionResolver"; +import WasmBrotliCompressionResolver from "./compressResolvers/wasmBrotli.compressionResolver"; +import UuidIdResolver from "./idResolvers/uuid.idResolver"; +import PureUuidIdResolver from "./idResolvers/pureUuid.idResolver"; +// Utils export * from "./utils/logger.util"; +// Resolvers export * from "./functionResolvers"; export * from "./modelResolvers"; export * from "./compressResolvers"; +export * from "./idResolvers"; +// export * from "./sessionStoreResolver"; + +export const compressResolvers = { + NoCompressionResolver, + PureBrotliCompressionResolver, + WasmBrotliCompressionResolver, +}; + +export const functionResolvers = { + WeakFunctionPool, +}; + +export const modelResolvers = { + DefaultModelResolver, +}; + +export const idResolvers = { + UuidIdResolver, + PureUuidIdResolver, +}; + +// Default export export * from "./types"; export * from "./client"; export * from "./server"; diff --git a/src/modelResolvers/Default.modelResolver.ts b/src/modelResolvers/Default.modelResolver.ts index 0e1edeb..253c979 100644 --- a/src/modelResolvers/Default.modelResolver.ts +++ b/src/modelResolvers/Default.modelResolver.ts @@ -217,7 +217,7 @@ export default class DefaultModelResolver extends ModelResolver { return returnValue; } - async deserialize( + deserialize( model: { type?: string; value?: any }, getFunction: FunctionResolver["setTheirs"] ) { @@ -254,7 +254,7 @@ export default class DefaultModelResolver extends ModelResolver { } for (let i = 0; i < Object.keys(model.value).length; i++) { const item = Object.keys(model.value)[i]; - genericCreature[item] = await this.deserialize( + genericCreature[item] = this.deserialize( model.value[item], getFunction ); diff --git a/src/modelResolvers/index.ts b/src/modelResolvers/index.ts index 84fd725..a13efc6 100644 --- a/src/modelResolvers/index.ts +++ b/src/modelResolvers/index.ts @@ -1,8 +1,8 @@ import { ModifiedWebSocket } from "../utils/websocketModifier.util"; import { Logger, LoggerLevels, LoggerOptions } from "../utils/logger.util"; -import { FunctionResolverFunction } from "../types"; +import { FunctionResolverFunction, IResolverStatic, Resolver } from "../types"; -export abstract class ModelResolver { +export abstract class ModelResolver extends Resolver { options: { session: ModifiedWebSocket; logger: LoggerOptions | boolean; @@ -14,6 +14,7 @@ export abstract class ModelResolver { session: ModifiedWebSocket; logger: LoggerOptions | boolean; }) { + super(); this.options = options; if (typeof this.options.logger !== "boolean") { this.logger = new Logger({ @@ -43,7 +44,7 @@ export abstract class ModelResolver { ): any; } -export interface IModelResolver { +export interface IModelResolver extends IResolverStatic { new (options: { session: ModifiedWebSocket; logger: @@ -54,5 +55,4 @@ export interface IModelResolver { } | boolean; }): ModelResolver; - typeName(): string; } diff --git a/src/server.ts b/src/server.ts index 96464bf..7a41d60 100644 --- a/src/server.ts +++ b/src/server.ts @@ -18,6 +18,8 @@ import NoCompressionResolver from "./compressResolvers/noCompression.compression import { IFunctionResolver } from "./functionResolvers"; import { IModelResolver } from "./modelResolvers"; import { ICompressResolver } from "./compressResolvers"; +import { IIdResolver } from "./idResolvers"; +import UuidIdResolver from "./idResolvers/uuid.idResolver"; export type ServerOptions = { ws: WebSocket.ServerOptions; @@ -25,6 +27,7 @@ export type ServerOptions = { FunctionResolver?: IFunctionResolver; ModelResolver?: IModelResolver; CompressResolver?: ICompressResolver; + IdResolver?: IIdResolver; }; logger?: LoggerOptions | boolean; }; @@ -34,6 +37,7 @@ type ServerInnerOptions = ServerOptions & { FunctionResolver: IFunctionResolver; ModelResolver: IModelResolver; CompressResolver: ICompressResolver; + IdResolver: IIdResolver; }; version: string; }; @@ -56,6 +60,7 @@ export class Server extends EventEmitter { FunctionResolver: WeakFunctionPool, ModelResolver: DefaultResolver, CompressResolver: NoCompressionResolver, + IdResolver: UuidIdResolver, ...options.universalRPC, }, version: packageJson.version, @@ -121,13 +126,19 @@ export class Server extends EventEmitter { semver.eq( this.options.version, clientRequest.data.version - ) || - clientRequest.data.functionResolver === - this.options.universalRPC.FunctionResolver.typeName() || - clientRequest.data.modelResolver === - this.options.universalRPC.ModelResolver.typeName() || - clientRequest.data.CompressResolver === - this.options.universalRPC.CompressResolver.typeName() + ) && + this.options.universalRPC.FunctionResolver.isCompatibleWith( + clientRequest.data.functionResolver + ) && + this.options.universalRPC.ModelResolver.isCompatibleWith( + clientRequest.data.modelResolver + ) && + this.options.universalRPC.CompressResolver.isCompatibleWith( + clientRequest.data.compressResolver + ) && + this.options.universalRPC.IdResolver.isCompatibleWith( + clientRequest.data.idResolver + ) ) ) { requestLogger.silly( @@ -142,7 +153,10 @@ export class Server extends EventEmitter { clientRequest.data.modelResolver }`, `Server compressResolver: ${this.options.universalRPC.CompressResolver.typeName()}, Client compressResolver: ${ - clientRequest.data.CompressResolver + clientRequest.data.compressResolver + }`, + `Server idResolver: ${this.options.universalRPC.IdResolver.typeName()}, Client idResolver: ${ + clientRequest.data.idResolver }` ); throw new RuntimeError( @@ -154,28 +168,39 @@ export class Server extends EventEmitter { ? "" : `Server version: ${this.options.version}, Client version: ${clientRequest.data.version}` } ${ - clientRequest.data.functionResolver !== - this.options.universalRPC.FunctionResolver.typeName() + !this.options.universalRPC.FunctionResolver.isCompatibleWith( + clientRequest.data.functionResolver + ) ? "" : `Server functionResolver: ${this.options.universalRPC.FunctionResolver.typeName()}, Client functionResolver: ${ clientRequest.data .functionResolver }` } ${ - clientRequest.data.modelResolver !== - this.options.universalRPC.ModelResolver.typeName() + !this.options.universalRPC.ModelResolver.isCompatibleWith( + clientRequest.data.modelResolver + ) ? "" : `Server modelResolver: ${this.options.universalRPC.ModelResolver.typeName()}, Client modelResolver: ${ clientRequest.data .modelResolver }` } ${ - clientRequest.data.CompressResolver !== - this.options.universalRPC.CompressResolver.typeName() + !this.options.universalRPC.CompressResolver.isCompatibleWith( + clientRequest.data.compressResolver + ) ? "" : `Server compressResolver: ${this.options.universalRPC.CompressResolver.typeName()}, Client compressResolver: ${ clientRequest.data - .CompressResolver + .compressResolver + }` + } ${ + !this.options.universalRPC.IdResolver.isCompatibleWith( + clientRequest.data.idResolver + ) + ? "" + : `Server idResolver: ${this.options.universalRPC.IdResolver.typeName()}, Client idResolver: ${ + clientRequest.data.idResolver }` }`, 400, @@ -261,6 +286,7 @@ export class Server extends EventEmitter { modelResolver: this.options.universalRPC.ModelResolver.typeName(), compressResolver: this.options.universalRPC.CompressResolver.typeName(), + idResolver: this.options.universalRPC.IdResolver.typeName(), }; websocketInstance.send( JSON.stringify({ type: "init", data: initPayload }) diff --git a/src/sessionStoreResolver/index.ts b/src/sessionStoreResolver/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/types.ts b/src/types.ts index 21f8cf9..fd008e5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1 +1,16 @@ export type FunctionResolverFunction = (...args: any[]) => Promise; + +export abstract class Resolver { + static typeName(): string { + throw new Error("typeName() implementation is required"); + } + + static isCompatibleWith(type: string): boolean { + return type === this.typeName(); + } +} + +export interface IResolverStatic { + typeName(): string; + isCompatibleWith(type: string): boolean; +} diff --git a/src/universalRPC/index.ts b/src/universalRPC/index.ts index fb68555..62c248b 100644 --- a/src/universalRPC/index.ts +++ b/src/universalRPC/index.ts @@ -7,12 +7,14 @@ import { ModifiedWebSocket } from "../utils/websocketModifier.util"; import typeAssert, { DataObject } from "../utils/typeAssert.util"; import RuntimeError from "../utils/error.utils"; import badRequestUtil from "../utils/badRequest.util"; +import { IdResolver, IIdResolver } from "../idResolvers"; export default class UniversalRPC extends EventEmitter { private options: { FunctionResolver: IFunctionResolver; ModelResolver: IModelResolver; CompressResolver: ICompressResolver; + IdResolver: IIdResolver; logger: LoggerOptions | boolean; }; @@ -28,6 +30,8 @@ export default class UniversalRPC extends EventEmitter { private readonly compressResolver: CompressResolver; + private idResolver: IdResolver; + private _theirsModel: any; private _oursModel: any; @@ -40,6 +44,7 @@ export default class UniversalRPC extends EventEmitter { FunctionResolver: IFunctionResolver; ModelResolver: IModelResolver; CompressResolver: ICompressResolver; + IdResolver: IIdResolver; logger: LoggerOptions | boolean; } ) { @@ -72,6 +77,14 @@ export default class UniversalRPC extends EventEmitter { }, }); + // create idResolver + this.idResolver = new this.options.IdResolver({ + session: this.session, + logger: { + parentLogger: this.logger, + }, + }); + // create functionResolver this.functionResolver = new this.options.FunctionResolver({ session: this.session, @@ -82,6 +95,7 @@ export default class UniversalRPC extends EventEmitter { serializeObject: this.modelResolver.serialize.bind( this.modelResolver ), + uuid: this.idResolver.gen.bind(this.idResolver), logger: { parentLogger: this.logger, }, diff --git a/src/utils/uuid.util.ts b/src/utils/uuid.util.ts deleted file mode 100644 index ef69ff0..0000000 --- a/src/utils/uuid.util.ts +++ /dev/null @@ -1,6 +0,0 @@ -import UUID from "pure-uuid"; - -// eslint-disable-next-line import/prefer-default-export -export function uuidv4(): string { - return new UUID(4).format(); -}