\ No newline at end of file
+404: This page could not be found
404
This page could not be found.
\ No newline at end of file
diff --git a/404/index.html b/404/index.html
index b514962..8d32a92 100644
--- a/404/index.html
+++ b/404/index.html
@@ -1 +1 @@
-404: This page could not be found
404
This page could not be found.
\ No newline at end of file
+404: This page could not be found
404
This page could not be found.
\ No newline at end of file
diff --git a/_next/static/4SjlsF4YLWxBX6v512Inb/_buildManifest.js b/_next/static/4SjlsF4YLWxBX6v512Inb/_buildManifest.js
deleted file mode 100644
index 28e29b8..0000000
--- a/_next/static/4SjlsF4YLWxBX6v512Inb/_buildManifest.js
+++ /dev/null
@@ -1 +0,0 @@
-self.__BUILD_MANIFEST=function(e,s){return{__rewrites:{afterFiles:[{has:void 0,source:"/api",destination:"/api/index.html"}],beforeFiles:[],fallback:[]},"/":[e,s,"static/chunks/150-94f7ac6572db6a51.js","static/chunks/pages/index-4eedf214cb5cda1f.js"],"/_error":["static/chunks/pages/_error-5a00309fd5f4b49e.js"],"/docs":[e,s,"static/chunks/pages/docs-5c6089a738369669.js"],"/docs/examples/nestjs-websocket":[e,s,"static/chunks/pages/docs/examples/nestjs-websocket-70ead4c1dcdae26c.js"],"/docs/examples/object-oriented-network":[e,s,"static/chunks/pages/docs/examples/object-oriented-network-a21f12d963af3eea.js"],"/docs/examples/remote-function-call":[e,s,"static/chunks/pages/docs/examples/remote-function-call-82d640c8f733aaa8.js"],"/docs/examples/remote-object-call":[e,s,"static/chunks/pages/docs/examples/remote-object-call-1b3ad6a252ed7337.js"],"/docs/features/components":[e,s,"static/chunks/pages/docs/features/components-9cf38702fa77f98a.js"],"/docs/features/websocket":[e,s,"static/chunks/pages/docs/features/websocket-783985e4536efcb9.js"],"/docs/features/worker":[e,s,"static/chunks/pages/docs/features/worker-c05928d0f58e14ac.js"],"/docs/projects/chat":[e,s,"static/chunks/pages/docs/projects/chat-61d7b6654db2685a.js"],"/docs/projects/market":[e,s,"static/chunks/pages/docs/projects/market-20e6a361e3347cd9.js"],"/docs/projects/mutex":[e,s,"static/chunks/pages/docs/projects/mutex-fe23af34585440ff.js"],"/docs/remote-procedure-call":[e,s,"static/chunks/pages/docs/remote-procedure-call-f91d7fae157ddde0.js"],"/docs/setup":[e,s,"static/chunks/pages/docs/setup-066b8635c20cd637.js"],sortedPages:["/","/_app","/_error","/docs","/docs/examples/nestjs-websocket","/docs/examples/object-oriented-network","/docs/examples/remote-function-call","/docs/examples/remote-object-call","/docs/features/components","/docs/features/websocket","/docs/features/worker","/docs/projects/chat","/docs/projects/market","/docs/projects/mutex","/docs/remote-procedure-call","/docs/setup"]}}("static/css/445c4173ffc2bf04.css","static/chunks/718-add7984c140a9b83.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
\ No newline at end of file
diff --git a/_next/static/chunks/nextra-data-en-US.json b/_next/static/chunks/nextra-data-en-US.json
index d796bf1..c9e8918 100644
--- a/_next/static/chunks/nextra-data-en-US.json
+++ b/_next/static/chunks/nextra-data-en-US.json
@@ -1 +1 @@
-{"/docs/projects/chat":{"title":"Chat","data":{"preparing#Preparing":"Preparing the chatting application project documentation.Also, upgrading very old project to be modernized.https://github.com/samchon/tgrid.projects.chat"}},"/docs/projects/market":{"title":"Market","data":{"preparing#Preparing":"Preparing the grid market project documentation.Also, upgrading very old project to be modernized.https://github.com/samchon/tgrid.projects.market"}},"/docs/projects/mutex":{"title":"Mutex","data":{"preparing#Preparing":"Preparing the mutex server project documentation.Also, upgrading very old project to be modernized.https://github.com/samchon/mutex"}},"/docs/setup":{"title":"Setup","data":{"standalone#Standalone":"npm install tgrid\npnpm install tgrid\nyarn add tgrid\nIf you're planning to use TGrid standalone, without NestJS integration, just setup it.","nestjs#NestJS":"Nestia > Guide Documents > SetupOtherwise, you wanna use TGrid with NestJS integration, please refer to the guide documents of nestia.By the way, if you want to setup it right now without reading detailed documents, just run the below commands.\nnpx nestia setup\nnpm install tgrid\nnpx nestia setup --manager pnpm\npnpm install tgrid\nYarn beery is not supported.\nnpx nestia setup --manager yarn\nyarn add tgrid"}},"/":{"title":"Index","data":{"key-features#Key Features":""}},"/docs/examples/object-oriented-network":{"title":"Object Oriented Network","data":{"outline#Outline":"Each remote system is an object.With TGrid, you can easily develop complicated network system, by considering each network system as an object, and interacting with each other through RPC (Remote Procedure Call). TGrid defines this concept as \"Object Oriented Network\".In this chapter, we'll remake the composite calculator system of Remote Object Call chapter again, but replace scientific and statistics calculators to remote system. Therefore, the composite calculator system will be consisted of three remote servers: \"composite server\", \"scientific server\" and \"statistics server\".Let's see how TGrd implements the \"Object Oriented Network\".\nDemonstrationYou can run the example program on Playground Website, or local machine.\ngit clone https://github.com/samchon/tgrid.example.object-oriented-network\nnpm install\nnpm start","client-program#Client Program":"import { Driver, WorkerConnector } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEvent } from \"./interfaces/ICalcEvent\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { ICompositeCalculator } from \"./interfaces/ICompositeCalculator\";\nconst EXTENSION = __filename.endsWith(\".ts\") ? \"ts\" : \"js\";\nexport const workerClientMain = async () => {\n const stack: ICalcEvent[] = [];\n const listener: ICalcEventListener = {\n on: (evt: ICalcEvent) => stack.push(evt),\n };\n const connector: WorkerConnector<\n ICalcConfig,\n ICalcEventListener,\n ICompositeCalculator\n > = new WorkerConnector(\n { precision: 2 }, // header\n listener, // provider for remote server\n \"process\",\n );\n await connector.connect(`${__dirname}/composite.${EXTENSION}`);\n const remote: Driver = connector.getDriver();\n console.log(\n await remote.plus(10, 20), // returns 30\n await remote.multiplies(3, 4), // returns 12\n await remote.divides(5, 3), // returns 1.67\n await remote.scientific.sqrt(2), // returns 1.41\n await remote.statistics.mean(1, 3, 9), // returns 4.33\n );\n await connector.close();\n console.log(stack);\n};\nexport interface ICalcConfig {\n precision: number;\n}\nexport interface ICalcEvent {\n type: string;\n input: number[];\n output: number;\n}\nexport interface ICalcEventListener {\n on(event: ICalcEvent): void;\n}\nexport interface ICompositeCalculator extends ISimpleCalculator {\n plus(x: number, y: number): number;\n minus(x: number, y: number): number;\n multiplies(x: number, y: number): number;\n divides(x: number, y: number): number;\n scientific: IScientificCalculator;\n statistics: IStatisticsCalculator;\n}\nexport interface IScientificCalculator {\n pow(x: number, y: number): number;\n sqrt(x: number): number;\n log(x: number, base: number): number;\n}\nexport interface IStatisticsCalculator {\n mean(...values: number[]): number;\n stdev(...values: number[]): number;\n}\n$ npm start\n30 12 1.67 1.41 4.33\n[\n { type: 'plus', input: [ 10, 20 ], output: 30 },\n { type: 'multiplies', input: [ 3, 4 ], output: 12 },\n { type: 'divides', input: [ 5, 3 ], output: 1.67 },\n { type: 'sqrt', input: [ 2 ], output: 1.41 },\n { type: 'mean', input: [ 1, 3, 9 ], output: 4.33 }\n]","server-programs#Server Programs":"import { Driver, WorkerConnector, WorkerServer } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { IScientificCalculator } from \"./interfaces/IScientificCalculator\";\nimport { IStatisticsCalculator } from \"./interfaces/IStatisticsCalculator\";\nimport { SimpleCalculator } from \"./providers/SimpleCalculator\";\nconst EXTENSION = __filename.endsWith(\".ts\") ? \"ts\" : \"js\";\n/// `CompositeCalculator` has two additional properties\n///\n/// - `scientific` from remote worker server\n/// - `statistics` from remote worker server\nclass CompositeCalculator extends SimpleCalculator {\n public readonly scientific: Driver;\n public readonly statistics: Driver;\n public constructor(props: {\n config: ICalcConfig;\n listener: Driver;\n scientific: Driver;\n statistics: Driver;\n }) {\n super(props.config, props.listener);\n this.scientific = props.scientific;\n this.statistics = props.statistics;\n }\n}\n/// connect to remote worker server\nconst connect = async (\n header: ICalcConfig,\n listener: Driver,\n file: string,\n): Promise> => {\n const connector: WorkerConnector =\n new WorkerConnector(header, listener, \"process\");\n await connector.connect(file);\n return connector.getDriver();\n};\nconst main = async () => {\n const server: WorkerServer<\n ICalcConfig,\n CompositeCalculator,\n ICalcEventListener\n > = new WorkerServer();\n const config: ICalcConfig = await server.getHeader();\n const listener: Driver = server.getDriver();\n // constructor provider combining with remote worker-servers\n const provider: CompositeCalculator = new CompositeCalculator({\n config,\n listener,\n scientific: await connect>(\n config,\n listener,\n `${__dirname}/scientific.${EXTENSION}`,\n ),\n statistics: await connect>(\n config,\n listener,\n `${__dirname}/statistics.${EXTENSION}`,\n ),\n });\n await server.open(provider);\n};\nmain().catch((exp) => {\n console.error(exp);\n process.exit(-1);\n});\nimport { Driver, WorkerServer } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { ScientificCalculator } from \"./providers/ScientificCalculator\";\nconst main = async () => {\n const server: WorkerServer<\n ICalcConfig,\n ScientificCalculator,\n ICalcEventListener\n > = new WorkerServer();\n const header: ICalcConfig = await server.getHeader();\n const listener: Driver = server.getDriver();\n const provider: ScientificCalculator = new ScientificCalculator(\n header,\n listener,\n );\n await server.open(provider);\n};\nmain().catch((exp) => {\n console.error(exp);\n process.exit(-1);\n});\nimport { Driver, WorkerServer } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { StatisticsCalculator } from \"./providers/StatisticsCalculator\";\nconst main = async () => {\n const server: WorkerServer<\n ICalcConfig,\n StatisticsCalculator,\n ICalcEventListener\n > = new WorkerServer();\n const header: ICalcConfig = await server.getHeader();\n const listener: Driver = server.getDriver();\n const provider: StatisticsCalculator = new StatisticsCalculator(\n header,\n listener,\n );\n await server.open(provider);\n};\nmain().catch((exp) => {\n console.error(exp);\n process.exit(-1);\n});\nimport { Driver } from \"tgrid\";\nimport { ICalcConfig } from \"../interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"../interfaces/ICalcEventListener\";\nimport { IScientificCalculator } from \"../interfaces/IScientificCalculator\";\nimport { ISimpleCalculator } from \"../interfaces/ISimpleCalculator\";\nimport { IStatisticsCalculator } from \"../interfaces/IStatisticsCalculator\";\nexport abstract class CalculatorBase {\n public constructor(\n private readonly config: ICalcConfig,\n private readonly listener: Driver,\n ) {}\n protected compute(type: string, input: number[], output: number): number {\n const pow: number = Math.pow(10, this.config.precision);\n output = Math.round(output * pow) / pow;\n this.listener.on({ type, input, output }).catch(() => {});\n return output;\n }\n}\nexport class ScientificCalculator\n extends CalculatorBase\n implements IScientificCalculator\n{\n public pow(x: number, y: number): number {\n return this.compute(\"pow\", [x, y], Math.pow(x, y));\n }\n public sqrt(x: number): number {\n return this.compute(\"sqrt\", [x], Math.sqrt(x));\n }\n public log(x: number, base: number): number {\n return this.compute(\"log\", [x, base], Math.log(x) / Math.log(base));\n }\n}\nexport class StatisticsCalculator\n extends CalculatorBase\n implements IStatisticsCalculator\n{\n public mean(...values: number[]): number {\n const sum: number = values.reduce((x, y) => x + y);\n return this.compute(\"mean\", values, sum / values.length);\n }\n public stdev(...values: number[]): number {\n const mean: number = values.reduce((x, y) => x + y) / values.length;\n const sum: number = values.reduce((x, y) => x + Math.pow(y - mean, 2));\n return this.compute(\"stdev\", values, Math.sqrt(sum / values.length));\n }\n}\nexport interface ICalcConfig {\n precision: number;\n}\nexport interface ICalcEvent {\n type: string;\n input: number[];\n output: number;\n}\nexport interface ICalcEventListener {\n on(event: ICalcEvent): void;\n}\nexport interface ICompositeCalculator extends ISimpleCalculator {\n plus(x: number, y: number): number;\n minus(x: number, y: number): number;\n multiplies(x: number, y: number): number;\n divides(x: number, y: number): number;\n scientific: IScientificCalculator;\n statistics: IStatisticsCalculator;\n}\nexport interface IScientificCalculator {\n pow(x: number, y: number): number;\n sqrt(x: number): number;\n log(x: number, base: number): number;\n}\nexport interface IStatisticsCalculator {\n mean(...values: number[]): number;\n stdev(...values: number[]): number;\n}\nCompose Provider with Driver of another remote system.Looking at the \"Composite Server\", it is providing CompositeCalculator to the \"Client Program\". By the way, the CompositeCalculator is different with before chapter Remote Object Call. Properties scientific and statistics are composed with Driver of another remote system.Therefore, if \"Client Program\" calls Driver.scientific.sqrt(2) function, it will be forwarded to the \"Scientific Server\", and \"Composite Server\" only intermediates the remote function call (network communication) between \"Client Program\" and \"Scientific Server\".This is the \"Object Oriented Network\" of TGrid.","next-chapter#Next Chapter":"At next chapter, we'll learn how to integrate TGrid with NestJS.\nLearn from Examples > NestJS Integration\nimport { TypedRoute, WebSocketRoute } from \"@nestia/core\";\nimport { Controller } from \"@nestjs/common\";\nimport { Driver, WebSocketAcceptor } from \"tgrid\";\nimport { ICalcConfig } from \"./api/interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"./api/interfaces/ICalcEventListener\";\nimport { ICompositeCalculator } from \"./api/interfaces/ICompositeCalculator\";\nimport { IScientificCalculator } from \"./api/interfaces/IScientificCalculator\";\nimport { ISimpleCalculator } from \"./api/interfaces/ISimpleCalculator\";\nimport { IStatisticsCalculator } from \"./api/interfaces/IStatisticsCalculator\";\nimport { CompositeCalculator } from \"./providers/CompositeCalculator\";\nimport { ScientificCalculator } from \"./providers/ScientificCalculator\";\nimport { SimpleCalculator } from \"./providers/SimpleCalculator\";\nimport { StatisticsCalculator } from \"./providers/StatisticsCalculator\";\n@Controller(\"calculate\")\nexport class CalculateController {\n /**\n * Health check API (HTTP GET).\n */\n @TypedRoute.Get(\"health\")\n public health(): string {\n return \"Health check OK\";\n }\n /**\n * Prepare a composite calculator.\n */\n @WebSocketRoute(\"composite\")\n public async composite(\n @WebSocketRoute.Acceptor()\n acceptor: WebSocketAcceptor<\n ICalcConfig,\n ICompositeCalculator,\n ICalcEventListener\n >,\n @WebSocketRoute.Header() header: ICalcConfig,\n @WebSocketRoute.Driver() listener: Driver\n ): Promise {\n const provider: CompositeCalculator = new CompositeCalculator(\n header,\n listener\n );\n await acceptor.accept(provider);\n }\n /**\n * Prepare a simple calculator.\n */\n @WebSocketRoute(\"simple\")\n public async simple(\n @WebSocketRoute.Acceptor()\n acceptor: WebSocketAcceptor<\n ICalcConfig, // header\n ISimpleCalculator, // provider for remote client\n ICalcEventListener // provider from remote client\n >\n ): Promise {\n const header: ICalcConfig = acceptor.header;\n const listener: Driver = acceptor.getDriver();\n const provider: SimpleCalculator = new SimpleCalculator(header, listener);\n await acceptor.accept(provider);\n }\n /**\n * Prepare a scientific calculator.\n */\n @WebSocketRoute(\"scientific\")\n public async scientific(\n @WebSocketRoute.Acceptor()\n acceptor: WebSocketAcceptor<\n ICalcConfig,\n IScientificCalculator,\n ICalcEventListener\n >\n ): Promise {\n const header: ICalcConfig = acceptor.header;\n const listener: Driver = acceptor.getDriver();\n const provider: ScientificCalculator = new ScientificCalculator(\n header,\n listener\n );\n await acceptor.accept(provider);\n }\n /**\n * Prepare a statistics calculator.\n */\n @WebSocketRoute(\"statistics\")\n public async statistics(\n @WebSocketRoute.Acceptor()\n acceptor: WebSocketAcceptor<\n ICalcConfig,\n IStatisticsCalculator,\n ICalcEventListener\n >\n ): Promise {\n const header: ICalcConfig = acceptor.header;\n const listener: Driver = acceptor.getDriver();\n const provider: IStatisticsCalculator = new StatisticsCalculator(\n header,\n listener\n );\n await acceptor.accept(provider);\n }\n}\nimport api from \"./api\";\nimport { ICalcEvent } from \"./api/interfaces/ICalcEvent\";\nimport { ICalcEventListener } from \"./api/interfaces/ICalcEventListener\";\nexport const testCalculateSdk = async () => {\n //----\n // HTTP PROTOCOL\n //---\n // CALL HEALTH CHECK API\n console.log(\n await api.functional.calculate.health({\n host: \"http://127.0.0.1:37000\",\n })\n );\n //----\n // WEBSOCKET PROTOCOL\n //---\n // PROVIDER FOR WEBSOCKET SERVER\n const stack: ICalcEvent[] = [];\n const listener: ICalcEventListener = {\n on: (evt: ICalcEvent) => stack.push(evt),\n };\n // DO CONNECT\n const { connector, driver } = await api.functional.calculate.composite(\n {\n host: \"ws://127.0.0.1:37000\",\n headers: {\n precision: 2,\n },\n },\n listener\n );\n // CALL FUNCTIONS OF REMOTE SERVER\n console.log(\n await driver.plus(10, 20), // returns 30\n await driver.multiplies(3, 4), // returns 12\n await driver.divides(5, 3), // returns 1.67\n await driver.scientific.sqrt(2), // returns 1.41\n await driver.statistics.mean(1, 3, 9) // returns 4.33\n );\n // TERMINATE\n await connector.close();\n console.log(stack);\n};\n/**\n * @packageDocumentation\n * @module api.functional.calculate\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\n//================================================================\nimport type { IConnection, Primitive } from \"@nestia/fetcher\";\nimport { PlainFetcher } from \"@nestia/fetcher/lib/PlainFetcher\";\nimport { WebSocketConnector } from \"tgrid\";\nimport type { Driver } from \"tgrid\";\nimport type { ICalcConfig } from \"../../interfaces/ICalcConfig\";\nimport type { ICalcEventListener } from \"../../interfaces/ICalcEventListener\";\nimport type { ICompositeCalculator } from \"../../interfaces/ICompositeCalculator\";\nimport type { IScientificCalculator } from \"../../interfaces/IScientificCalculator\";\nimport type { ISimpleCalculator } from \"../../interfaces/ISimpleCalculator\";\nimport type { IStatisticsCalculator } from \"../../interfaces/IStatisticsCalculator\";\n/**\n * Health check API (HTTP GET).\n *\n * @controller CalculateController.health\n * @path GET /calculate/health\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function health(connection: IConnection): Promise {\n return PlainFetcher.fetch(connection, {\n ...health.METADATA,\n path: health.path(),\n });\n}\nexport namespace health {\n export type Output = Primitive;\n export const METADATA = {\n method: \"GET\",\n path: \"/calculate/health\",\n request: null,\n response: {\n type: \"application/json\",\n encrypted: false,\n },\n status: null,\n } as const;\n export const path = () => \"/calculate/health\";\n}\n/**\n * Prepare a composite calculator.\n *\n * @controller CalculateController.composite\n * @path /calculate/composite\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function composite(\n connection: IConnection,\n provider: composite.Provider,\n): Promise {\n const connector: WebSocketConnector<\n composite.Header,\n composite.Provider,\n composite.Listener\n > = new WebSocketConnector(connection.headers ?? ({} as any), provider);\n await connector.connect(\n `${connection.host.endsWith(\"/\") ? connection.host.substring(0, connection.host.length - 1) : connection.host}${composite.path()}`,\n );\n const driver: Driver = connector.getDriver();\n return {\n connector,\n driver,\n };\n}\nexport namespace composite {\n export type Output = {\n connector: WebSocketConnector;\n driver: Driver;\n };\n export type Header = ICalcConfig;\n export type Provider = ICalcEventListener;\n export type Listener = ICompositeCalculator;\n export const path = () => \"/calculate/composite\";\n}\n/**\n * Prepare a simple calculator.\n *\n * @controller CalculateController.simple\n * @path /calculate/simple\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function simple(\n connection: IConnection,\n provider: simple.Provider,\n): Promise {\n const connector: WebSocketConnector<\n simple.Header,\n simple.Provider,\n simple.Listener\n > = new WebSocketConnector(connection.headers ?? ({} as any), provider);\n await connector.connect(\n `${connection.host.endsWith(\"/\") ? connection.host.substring(0, connection.host.length - 1) : connection.host}${simple.path()}`,\n );\n const driver: Driver = connector.getDriver();\n return {\n connector,\n driver,\n };\n}\nexport namespace simple {\n export type Output = {\n connector: WebSocketConnector;\n driver: Driver;\n };\n export type Header = ICalcConfig;\n export type Provider = ICalcEventListener;\n export type Listener = ISimpleCalculator;\n export const path = () => \"/calculate/simple\";\n}\n/**\n * Prepare a scientific calculator.\n *\n * @controller CalculateController.scientific\n * @path /calculate/scientific\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function scientific(\n connection: IConnection,\n provider: scientific.Provider,\n): Promise {\n const connector: WebSocketConnector<\n scientific.Header,\n scientific.Provider,\n scientific.Listener\n > = new WebSocketConnector(connection.headers ?? ({} as any), provider);\n await connector.connect(\n `${connection.host.endsWith(\"/\") ? connection.host.substring(0, connection.host.length - 1) : connection.host}${scientific.path()}`,\n );\n const driver: Driver = connector.getDriver();\n return {\n connector,\n driver,\n };\n}\nexport namespace scientific {\n export type Output = {\n connector: WebSocketConnector;\n driver: Driver;\n };\n export type Header = ICalcConfig;\n export type Provider = ICalcEventListener;\n export type Listener = IScientificCalculator;\n export const path = () => \"/calculate/scientific\";\n}\n/**\n * Prepare a statistics calculator.\n *\n * @controller CalculateController.statistics\n * @path /calculate/statistics\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function statistics(\n connection: IConnection,\n provider: statistics.Provider,\n): Promise {\n const connector: WebSocketConnector<\n statistics.Header,\n statistics.Provider,\n statistics.Listener\n > = new WebSocketConnector(connection.headers ?? ({} as any), provider);\n await connector.connect(\n `${connection.host.endsWith(\"/\") ? connection.host.substring(0, connection.host.length - 1) : connection.host}${statistics.path()}`,\n );\n const driver: Driver = connector.getDriver();\n return {\n connector,\n driver,\n };\n}\nexport namespace statistics {\n export type Output = {\n connector: WebSocketConnector;\n driver: Driver;\n };\n export type Header = ICalcConfig;\n export type Provider = ICalcEventListener;\n export type Listener = IStatisticsCalculator;\n export const path = () => \"/calculate/statistics\";\n}"}},"/docs/examples/nestjs-websocket":{"title":"Nestjs Websocket","data":{"outline#Outline":"If you develop websocket application, I recommend integrate TGrid with NestJS / Nestia.It's because you can manage WebSocket API endpoints much effectively and easily by NestJS controller patterns. Also, you can make your server to support both HTTP and WebSocket protocols at the same time. NestJS controllers are compatible with both HTTP and WebSocket operations.Furthermore, you can generate SDK (Software Development Kit) library for your client application through Nestia. With the automatically generated SDK, client developers no more need to write the WebSocket connection and RPC (Remote Procedure Call) codes manually, so that the client development becomes much easier and safer.\nReferences\nNestia > Guide Documents > Setup\nNestia > Guide Documents > WebSocketRoute\nDemonstrationYou can run the example program on Playground Website, or local machine.\ngit clone https://github.com/samchon/tgrid.example.nestjs\nnpm install\nnpm start","server-program#Server Program":"","bootstrap#Bootstrap":"import { WebSocketAdaptor } from \"@nestia/core\";\nimport { INestApplication } from \"@nestjs/common\";\nimport { NestFactory } from \"@nestjs/core\";\nimport { CalculateModule } from \"./calculate.module\";\nexport const bootstrap = async (): Promise => {\n const app: INestApplication = await NestFactory.create(CalculateModule);\n await WebSocketAdaptor.upgrade(app);\n await app.listen(37_000, \"0.0.0.0\");\n return app;\n};\nTo integrate TGrid with NestJS, you have to upgrade the NestJS application like above.Just call the WebSocketAdaptor.upgrade(), then you can utilize TGrid in the NestJS server.","controller#Controller":"import { TypedRoute, WebSocketRoute } from \"@nestia/core\";\nimport { Controller } from \"@nestjs/common\";\nimport { Driver, WebSocketAcceptor } from \"tgrid\";\nimport { ICalcConfig } from \"./api/interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"./api/interfaces/ICalcEventListener\";\nimport { ICompositeCalculator } from \"./api/interfaces/ICompositeCalculator\";\nimport { IScientificCalculator } from \"./api/interfaces/IScientificCalculator\";\nimport { ISimpleCalculator } from \"./api/interfaces/ISimpleCalculator\";\nimport { IStatisticsCalculator } from \"./api/interfaces/IStatisticsCalculator\";\nimport { CompositeCalculator } from \"./providers/CompositeCalculator\";\nimport { ScientificCalculator } from \"./providers/ScientificCalculator\";\nimport { SimpleCalculator } from \"./providers/SimpleCalculator\";\nimport { StatisticsCalculator } from \"./providers/StatisticsCalculator\";\n@Controller(\"calculate\")\nexport class CalculateController {\n /**\n * Health check API (HTTP GET).\n */\n @TypedRoute.Get(\"health\")\n public health(): string {\n return \"Health check OK\";\n }\n /**\n * Prepare a composite calculator.\n */\n @WebSocketRoute(\"composite\")\n public async composite(\n @WebSocketRoute.Acceptor()\n acceptor: WebSocketAcceptor<\n ICalcConfig,\n ICompositeCalculator,\n ICalcEventListener\n >,\n @WebSocketRoute.Header() header: ICalcConfig,\n @WebSocketRoute.Driver() listener: Driver\n ): Promise {\n const provider: CompositeCalculator = new CompositeCalculator(\n header,\n listener\n );\n await acceptor.accept(provider);\n }\n /**\n * Prepare a simple calculator.\n */\n @WebSocketRoute(\"simple\")\n public async simple(\n @WebSocketRoute.Acceptor()\n acceptor: WebSocketAcceptor<\n ICalcConfig, // header\n ISimpleCalculator, // provider for remote client\n ICalcEventListener // provider from remote client\n >\n ): Promise {\n const header: ICalcConfig = acceptor.header;\n const listener: Driver = acceptor.getDriver();\n const provider: SimpleCalculator = new SimpleCalculator(header, listener);\n await acceptor.accept(provider);\n }\n /**\n * Prepare a scientific calculator.\n */\n @WebSocketRoute(\"scientific\")\n public async scientific(\n @WebSocketRoute.Acceptor()\n acceptor: WebSocketAcceptor<\n ICalcConfig,\n IScientificCalculator,\n ICalcEventListener\n >\n ): Promise {\n const header: ICalcConfig = acceptor.header;\n const listener: Driver = acceptor.getDriver();\n const provider: ScientificCalculator = new ScientificCalculator(\n header,\n listener\n );\n await acceptor.accept(provider);\n }\n /**\n * Prepare a statistics calculator.\n */\n @WebSocketRoute(\"statistics\")\n public async statistics(\n @WebSocketRoute.Acceptor()\n acceptor: WebSocketAcceptor<\n ICalcConfig,\n IStatisticsCalculator,\n ICalcEventListener\n >\n ): Promise {\n const header: ICalcConfig = acceptor.header;\n const listener: Driver = acceptor.getDriver();\n const provider: IStatisticsCalculator = new StatisticsCalculator(\n header,\n listener\n );\n await acceptor.accept(provider);\n }\n}\nimport { Module } from \"@nestjs/common\";\nimport { CalculateController } from \"./calculate.controller\";\n@Module({\n controllers: [CalculateController],\n})\nexport class CalculateModule {}\nimport { Driver } from \"tgrid\";\nimport { ICalcConfig } from \"../interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"../interfaces/ICalcEventListener\";\nimport { ICompositeCalculator } from \"../interfaces/ICompositeCalculator\";\nimport { IScientificCalculator } from \"../interfaces/IScientificCalculator\";\nimport { ISimpleCalculator } from \"../interfaces/ISimpleCalculator\";\nimport { IStatisticsCalculator } from \"../interfaces/IStatisticsCalculator\";\nexport abstract class CalculatorBase {\n public constructor(\n private readonly config: ICalcConfig,\n private readonly listener: Driver,\n ) {}\n protected compute(type: string, input: number[], output: number): number {\n const pow: number = Math.pow(10, this.config.precision);\n output = Math.round(output * pow) / pow;\n this.listener.on({ type, input, output }).catch(() => {});\n return output;\n }\n}\nexport class SimpleCalculator\n extends CalculatorBase\n implements ISimpleCalculator\n{\n public plus(x: number, y: number): number {\n return this.compute(\"plus\", [x, y], x + y);\n }\n public minus(x: number, y: number): number {\n return this.compute(\"minus\", [x, y], x - y);\n }\n public multiplies(x: number, y: number): number {\n return this.compute(\"multiplies\", [x, y], x * y);\n }\n public divides(x: number, y: number): number {\n return this.compute(\"divides\", [x, y], x / y);\n }\n}\nexport class ScientificCalculator\n extends CalculatorBase\n implements IScientificCalculator\n{\n public pow(x: number, y: number): number {\n return this.compute(\"pow\", [x, y], Math.pow(x, y));\n }\n public sqrt(x: number): number {\n return this.compute(\"sqrt\", [x], Math.sqrt(x));\n }\n public log(x: number, base: number): number {\n return this.compute(\"log\", [x, base], Math.log(x) / Math.log(base));\n }\n}\nexport class StatisticsCalculator\n extends CalculatorBase\n implements IStatisticsCalculator\n{\n public mean(...values: number[]): number {\n const sum: number = values.reduce((x, y) => x + y);\n return this.compute(\"mean\", values, sum / values.length);\n }\n public stdev(...values: number[]): number {\n const mean: number = values.reduce((x, y) => x + y) / values.length;\n const sum: number = values.reduce((x, y) => x + Math.pow(y - mean, 2));\n return this.compute(\"stdev\", values, Math.sqrt(sum / values.length));\n }\n}\nexport class CompositeCalculator\n extends SimpleCalculator\n implements ICompositeCalculator\n{\n public readonly scientific: ScientificCalculator;\n public readonly statistics: StatisticsCalculator;\n public constructor(\n config: ICalcConfig,\n listener: Driver,\n ) {\n super(config, listener);\n this.scientific = new ScientificCalculator(config, listener);\n this.statistics = new StatisticsCalculator(config, listener);\n }\n}\nexport interface ICalcConfig {\n precision: number;\n}\nexport interface ICalcEvent {\n type: string;\n input: number[];\n output: number;\n}\nexport interface ICalcEventListener {\n on(event: ICalcEvent): void;\n}\nexport interface ICompositeCalculator extends ISimpleCalculator {\n scientific: IScientificCalculator;\n statistics: IStatisticsCalculator;\n}\nexport interface ISimpleCalculator {\n plus(x: number, y: number): number;\n minus(x: number, y: number): number;\n multiplies(x: number, y: number): number;\n divides(x: number, y: number): number;\n}\nexport interface IScientificCalculator {\n pow(x: number, y: number): number;\n sqrt(x: number): number;\n log(x: number, base: number): number;\n}\nexport interface IStatisticsCalculator {\n mean(...values: number[]): number;\n stdev(...values: number[]): number;\n}\nAs you can see from the above code, CalculateController has many API operations, including both HTTP and WebSocket protocols. The CalculatorController.health() is an HTTP Get method operation, and the others are all WebSocket operations.When defining WebSocket operation, attach @WebSocketRoute() decorator to the target controller method with path specification. Also, the controller method must have the @WebSocketRoute.Acceptor() decorated parameter with WebSocketAcceptor type, because you have to determine whether to WebSocketAcceptor.accept() the client's connection or WebSocketAcceptor.reject() it.With such controller patterned WebSocket operation, you can manage WebSocket API endpoints much effectively and easily. Also, you can generate SDK (Software Development Kit) library for your client application through Nestia. Let's see how to generate SDK library, and how it would be looked like in the next section.","software-development-kit#Software Development Kit":"/**\n * @packageDocumentation\n * @module api.functional.calculate\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\n//================================================================\nimport type { IConnection, Primitive } from \"@nestia/fetcher\";\nimport { PlainFetcher } from \"@nestia/fetcher/lib/PlainFetcher\";\nimport { WebSocketConnector } from \"tgrid\";\nimport type { Driver } from \"tgrid\";\nimport type { ICalcConfig } from \"../../interfaces/ICalcConfig\";\nimport type { ICalcEventListener } from \"../../interfaces/ICalcEventListener\";\nimport type { ICompositeCalculator } from \"../../interfaces/ICompositeCalculator\";\nimport type { IScientificCalculator } from \"../../interfaces/IScientificCalculator\";\nimport type { ISimpleCalculator } from \"../../interfaces/ISimpleCalculator\";\nimport type { IStatisticsCalculator } from \"../../interfaces/IStatisticsCalculator\";\n/**\n * Health check API (HTTP GET).\n *\n * @controller CalculateController.health\n * @path GET /calculate/health\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function health(connection: IConnection): Promise {\n return PlainFetcher.fetch(connection, {\n ...health.METADATA,\n path: health.path(),\n });\n}\nexport namespace health {\n export type Output = Primitive;\n export const METADATA = {\n method: \"GET\",\n path: \"/calculate/health\",\n request: null,\n response: {\n type: \"application/json\",\n encrypted: false,\n },\n status: null,\n } as const;\n export const path = () => \"/calculate/health\";\n}\n/**\n * Prepare a composite calculator.\n *\n * @controller CalculateController.composite\n * @path /calculate/composite\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function composite(\n connection: IConnection,\n provider: composite.Provider,\n): Promise {\n const connector: WebSocketConnector<\n composite.Header,\n composite.Provider,\n composite.Listener\n > = new WebSocketConnector(connection.headers ?? ({} as any), provider);\n await connector.connect(\n `${connection.host.endsWith(\"/\") ? connection.host.substring(0, connection.host.length - 1) : connection.host}${composite.path()}`,\n );\n const driver: Driver = connector.getDriver();\n return {\n connector,\n driver,\n };\n}\nexport namespace composite {\n export type Output = {\n connector: WebSocketConnector;\n driver: Driver;\n };\n export type Header = ICalcConfig;\n export type Provider = ICalcEventListener;\n export type Listener = ICompositeCalculator;\n export const path = () => \"/calculate/composite\";\n}\n/**\n * Prepare a simple calculator.\n *\n * @controller CalculateController.simple\n * @path /calculate/simple\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function simple(\n connection: IConnection,\n provider: simple.Provider,\n): Promise {\n const connector: WebSocketConnector<\n simple.Header,\n simple.Provider,\n simple.Listener\n > = new WebSocketConnector(connection.headers ?? ({} as any), provider);\n await connector.connect(\n `${connection.host.endsWith(\"/\") ? connection.host.substring(0, connection.host.length - 1) : connection.host}${simple.path()}`,\n );\n const driver: Driver = connector.getDriver();\n return {\n connector,\n driver,\n };\n}\nexport namespace simple {\n export type Output = {\n connector: WebSocketConnector;\n driver: Driver;\n };\n export type Header = ICalcConfig;\n export type Provider = ICalcEventListener;\n export type Listener = ISimpleCalculator;\n export const path = () => \"/calculate/simple\";\n}\n/**\n * Prepare a scientific calculator.\n *\n * @controller CalculateController.scientific\n * @path /calculate/scientific\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function scientific(\n connection: IConnection,\n provider: scientific.Provider,\n): Promise {\n const connector: WebSocketConnector<\n scientific.Header,\n scientific.Provider,\n scientific.Listener\n > = new WebSocketConnector(connection.headers ?? ({} as any), provider);\n await connector.connect(\n `${connection.host.endsWith(\"/\") ? connection.host.substring(0, connection.host.length - 1) : connection.host}${scientific.path()}`,\n );\n const driver: Driver = connector.getDriver();\n return {\n connector,\n driver,\n };\n}\nexport namespace scientific {\n export type Output = {\n connector: WebSocketConnector;\n driver: Driver;\n };\n export type Header = ICalcConfig;\n export type Provider = ICalcEventListener;\n export type Listener = IScientificCalculator;\n export const path = () => \"/calculate/scientific\";\n}\n/**\n * Prepare a statistics calculator.\n *\n * @controller CalculateController.statistics\n * @path /calculate/statistics\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function statistics(\n connection: IConnection,\n provider: statistics.Provider,\n): Promise {\n const connector: WebSocketConnector<\n statistics.Header,\n statistics.Provider,\n statistics.Listener\n > = new WebSocketConnector(connection.headers ?? ({} as any), provider);\n await connector.connect(\n `${connection.host.endsWith(\"/\") ? connection.host.substring(0, connection.host.length - 1) : connection.host}${statistics.path()}`,\n );\n const driver: Driver = connector.getDriver();\n return {\n connector,\n driver,\n };\n}\nexport namespace statistics {\n export type Output = {\n connector: WebSocketConnector;\n driver: Driver;\n };\n export type Header = ICalcConfig;\n export type Provider = ICalcEventListener;\n export type Listener = IStatisticsCalculator;\n export const path = () => \"/calculate/statistics\";\n}\nexport interface ICalcConfig {\n precision: number;\n}\nexport interface ICalcEvent {\n type: string;\n input: number[];\n output: number;\n}\nexport interface ICalcEventListener {\n on(event: ICalcEvent): void;\n}\nexport interface ICompositeCalculator extends ISimpleCalculator {\n scientific: IScientificCalculator;\n statistics: IStatisticsCalculator;\n}\nexport interface ISimpleCalculator {\n plus(x: number, y: number): number;\n minus(x: number, y: number): number;\n multiplies(x: number, y: number): number;\n divides(x: number, y: number): number;\n}\nexport interface IScientificCalculator {\n pow(x: number, y: number): number;\n sqrt(x: number): number;\n log(x: number, base: number): number;\n}\nexport interface IStatisticsCalculator {\n mean(...values: number[]): number;\n stdev(...values: number[]): number;\n}\nnpx nestia sdk\nWhen you run npx nestia sdk command, SDK (Software Development Kit) library be generated.Above file is one of the SDK library corresponding to the CalculateController class we've seen in the previous NestJS Controller section. Client developers can utilize the automatically generated SDK functions to connect to the WebSocket server, and interact it type safely. Also, HTTP operation is compatible with the WebSocket operation.Let's see how client developer utilizes the SDK library in the next section.","client-program#Client Program":"import api from \"./api\";\nimport { ICalcEvent } from \"./api/interfaces/ICalcEvent\";\nimport { ICalcEventListener } from \"./api/interfaces/ICalcEventListener\";\nexport const testCalculateSdk = async () => {\n //----\n // HTTP PROTOCOL\n //---\n // CALL HEALTH CHECK API\n console.log(\n await api.functional.calculate.health({\n host: \"http://127.0.0.1:37000\",\n })\n );\n //----\n // WEBSOCKET PROTOCOL\n //---\n // PROVIDER FOR WEBSOCKET SERVER\n const stack: ICalcEvent[] = [];\n const listener: ICalcEventListener = {\n on: (evt: ICalcEvent) => stack.push(evt),\n };\n // DO CONNECT\n const { connector, driver } = await api.functional.calculate.composite(\n {\n host: \"ws://127.0.0.1:37000\",\n headers: {\n precision: 2,\n },\n },\n listener\n );\n // CALL FUNCTIONS OF REMOTE SERVER\n console.log(\n await driver.plus(10, 20), // returns 30\n await driver.multiplies(3, 4), // returns 12\n await driver.divides(5, 3), // returns 1.67\n await driver.scientific.sqrt(2), // returns 1.41\n await driver.statistics.mean(1, 3, 9) // returns 4.33\n );\n // TERMINATE\n await connector.close();\n console.log(stack);\n};\n/**\n * @packageDocumentation\n * @module api.functional.calculate\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\n//================================================================\nimport type { IConnection, Primitive } from \"@nestia/fetcher\";\nimport { PlainFetcher } from \"@nestia/fetcher/lib/PlainFetcher\";\nimport { WebSocketConnector } from \"tgrid\";\nimport type { Driver } from \"tgrid\";\nimport type { ICalcConfig } from \"../../interfaces/ICalcConfig\";\nimport type { ICalcEventListener } from \"../../interfaces/ICalcEventListener\";\nimport type { ICompositeCalculator } from \"../../interfaces/ICompositeCalculator\";\nimport type { IScientificCalculator } from \"../../interfaces/IScientificCalculator\";\nimport type { ISimpleCalculator } from \"../../interfaces/ISimpleCalculator\";\nimport type { IStatisticsCalculator } from \"../../interfaces/IStatisticsCalculator\";\n/**\n * Health check API (HTTP GET).\n *\n * @controller CalculateController.health\n * @path GET /calculate/health\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function health(connection: IConnection): Promise {\n return PlainFetcher.fetch(connection, {\n ...health.METADATA,\n path: health.path(),\n });\n}\nexport namespace health {\n export type Output = Primitive;\n export const METADATA = {\n method: \"GET\",\n path: \"/calculate/health\",\n request: null,\n response: {\n type: \"application/json\",\n encrypted: false,\n },\n status: null,\n } as const;\n export const path = () => \"/calculate/health\";\n}\n/**\n * Prepare a composite calculator.\n *\n * @controller CalculateController.composite\n * @path /calculate/composite\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function composite(\n connection: IConnection,\n provider: composite.Provider,\n): Promise {\n const connector: WebSocketConnector<\n composite.Header,\n composite.Provider,\n composite.Listener\n > = new WebSocketConnector(connection.headers ?? ({} as any), provider);\n await connector.connect(\n `${connection.host.endsWith(\"/\") ? connection.host.substring(0, connection.host.length - 1) : connection.host}${composite.path()}`,\n );\n const driver: Driver = connector.getDriver();\n return {\n connector,\n driver,\n };\n}\nexport namespace composite {\n export type Output = {\n connector: WebSocketConnector;\n driver: Driver;\n };\n export type Header = ICalcConfig;\n export type Provider = ICalcEventListener;\n export type Listener = ICompositeCalculator;\n export const path = () => \"/calculate/composite\";\n}\n/**\n * Prepare a simple calculator.\n *\n * @controller CalculateController.simple\n * @path /calculate/simple\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function simple(\n connection: IConnection,\n provider: simple.Provider,\n): Promise {\n const connector: WebSocketConnector<\n simple.Header,\n simple.Provider,\n simple.Listener\n > = new WebSocketConnector(connection.headers ?? ({} as any), provider);\n await connector.connect(\n `${connection.host.endsWith(\"/\") ? connection.host.substring(0, connection.host.length - 1) : connection.host}${simple.path()}`,\n );\n const driver: Driver = connector.getDriver();\n return {\n connector,\n driver,\n };\n}\nexport namespace simple {\n export type Output = {\n connector: WebSocketConnector;\n driver: Driver;\n };\n export type Header = ICalcConfig;\n export type Provider = ICalcEventListener;\n export type Listener = ISimpleCalculator;\n export const path = () => \"/calculate/simple\";\n}\n/**\n * Prepare a scientific calculator.\n *\n * @controller CalculateController.scientific\n * @path /calculate/scientific\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function scientific(\n connection: IConnection,\n provider: scientific.Provider,\n): Promise {\n const connector: WebSocketConnector<\n scientific.Header,\n scientific.Provider,\n scientific.Listener\n > = new WebSocketConnector(connection.headers ?? ({} as any), provider);\n await connector.connect(\n `${connection.host.endsWith(\"/\") ? connection.host.substring(0, connection.host.length - 1) : connection.host}${scientific.path()}`,\n );\n const driver: Driver = connector.getDriver();\n return {\n connector,\n driver,\n };\n}\nexport namespace scientific {\n export type Output = {\n connector: WebSocketConnector;\n driver: Driver;\n };\n export type Header = ICalcConfig;\n export type Provider = ICalcEventListener;\n export type Listener = IScientificCalculator;\n export const path = () => \"/calculate/scientific\";\n}\n/**\n * Prepare a statistics calculator.\n *\n * @controller CalculateController.statistics\n * @path /calculate/statistics\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function statistics(\n connection: IConnection,\n provider: statistics.Provider,\n): Promise {\n const connector: WebSocketConnector<\n statistics.Header,\n statistics.Provider,\n statistics.Listener\n > = new WebSocketConnector(connection.headers ?? ({} as any), provider);\n await connector.connect(\n `${connection.host.endsWith(\"/\") ? connection.host.substring(0, connection.host.length - 1) : connection.host}${statistics.path()}`,\n );\n const driver: Driver = connector.getDriver();\n return {\n connector,\n driver,\n };\n}\nexport namespace statistics {\n export type Output = {\n connector: WebSocketConnector;\n driver: Driver;\n };\n export type Header = ICalcConfig;\n export type Provider = ICalcEventListener;\n export type Listener = IStatisticsCalculator;\n export const path = () => \"/calculate/statistics\";\n}\nexport interface ICalcConfig {\n precision: number;\n}\nexport interface ICalcEvent {\n type: string;\n input: number[];\n output: number;\n}\nexport interface ICalcEventListener {\n on(event: ICalcEvent): void;\n}\nexport interface ICompositeCalculator extends ISimpleCalculator {\n scientific: IScientificCalculator;\n statistics: IStatisticsCalculator;\n}\nexport interface ISimpleCalculator {\n plus(x: number, y: number): number;\n minus(x: number, y: number): number;\n multiplies(x: number, y: number): number;\n divides(x: number, y: number): number;\n}\nexport interface IScientificCalculator {\n pow(x: number, y: number): number;\n sqrt(x: number): number;\n log(x: number, base: number): number;\n}\nexport interface IStatisticsCalculator {\n mean(...values: number[]): number;\n stdev(...values: number[]): number;\n}\n$ npm start\n[Nest] 4328 - 05/15/2024, 3:19:50 AM LOG [NestFactory] Starting Nest application...\n[Nest] 4328 - 05/15/2024, 3:19:50 AM LOG [InstanceLoader] CalculateModule dependencies initialized +5ms\n[Nest] 4328 - 05/15/2024, 3:19:50 AM LOG [RoutesResolver] CalculateController {/calculate}: +5ms\n[Nest] 4328 - 05/15/2024, 3:19:50 AM LOG [NestApplication] Nest application successfully started +2ms\nHealth check OK\n30 12 1.67 1.41 4.33\n[\n { type: 'plus', input: [ 10, 20 ], output: 30 },\n { type: 'multiplies', input: [ 3, 4 ], output: 12 },\n { type: 'divides', input: [ 5, 3 ], output: 1.67 },\n { type: 'sqrt', input: [ 2 ], output: 1.41 },\n { type: 'mean', input: [ 1, 3, 9 ], output: 4.33 }\n]\nDo import the SDK, and enjoy the type-safe and easy-to-use RPC (Remote Procedure Call).Looking at the above code, the client application is calling a function of the automatically generated SDK (Software Development Kit) library, so that connecting to the websocket server, and starting interaction through RPC (Remote Procedure Call) concept with Driver instance.Doesn't the \"SDK based development\" seems much easier and safer than native websocket classes case? This is the reason why I've recommended to combine with the NestJS when using websocket protocol based network system.This is the integration of TGrid with NestJS.","next-chapter#Next Chapter":"We've learned how to utilize TGrid with many examples.By the way, don't you want to know how to utilize TGrid in the real project?In the next chapter, we'll see how TGrid be utilized in the real world.\nLearn from Projects\nChat Application\nGrid Market\nMutex Server"}},"/docs/examples/remote-object-call":{"title":"Remote Object Call","data":{"outline#Outline":"With TGrid, you can call remote system's nested functions as if they are local functions.Such remote procedure calling concept is called as RPC (Remote Procedure Call) in the development world, but it contains not only remote function call, but also contains Object Oriented Network. However, in here chapter, we will focus only on the remote nested function calls.Let's learn how to call remote nested functions with TGrid.\nDemonstrationYou can run the example program on Playground Website, or local machine.\ngit clone https://github.com/samchon/tgrid.example.remote-object-call\nnpm install\nnpm start","client-program#Client Program":"import { Driver, WebSocketConnector } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEvent } from \"./interfaces/ICalcEvent\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { ICompositeCalculator } from \"./interfaces/ICompositeCalculator\";\nexport const webSocketClientMain = async () => {\n const stack: ICalcEvent[] = [];\n const listener: ICalcEventListener = {\n on: (evt: ICalcEvent) => stack.push(evt),\n };\n const connector: WebSocketConnector<\n ICalcConfig,\n ICalcEventListener,\n ICompositeCalculator\n > = new WebSocketConnector(\n { precision: 2 }, // header\n listener, // provider for remote server\n );\n await connector.connect(\"ws://127.0.0.1:37000\");\n const remote: Driver = connector.getDriver();\n console.log(\n await remote.plus(10, 20), // returns 30\n await remote.multiplies(3, 4), // returns 12\n await remote.divides(5, 3), // returns 1.67\n await remote.scientific.sqrt(2), // returns 1.41\n await remote.statistics.mean(1, 3, 9), // returns 4.33\n );\n await connector.close();\n console.log(stack);\n};\nexport interface ICalcConfig {\n precision: number;\n}\nexport interface ICalcEvent {\n type: string;\n input: number[];\n output: number;\n}\nexport interface ICalcEventListener {\n on(event: ICalcEvent): void;\n}\nexport interface ICompositeCalculator extends ISimpleCalculator {\n plus(x: number, y: number): number;\n minus(x: number, y: number): number;\n multiplies(x: number, y: number): number;\n divides(x: number, y: number): number;\n scientific: IScientificCalculator;\n statistics: IStatisticsCalculator;\n}\nexport interface IScientificCalculator {\n pow(x: number, y: number): number;\n sqrt(x: number): number;\n log(x: number, base: number): number;\n}\nexport interface IStatisticsCalculator {\n mean(...values: number[]): number;\n stdev(...values: number[]): number;\n}\n$ npm start\n30 12 1.67 1.41 4.33\n[\n { type: 'plus', input: [ 10, 20 ], output: 30 },\n { type: 'multiplies', input: [ 3, 4 ], output: 12 },\n { type: 'divides', input: [ 5, 3 ], output: 1.67 },\n { type: 'sqrt', input: [ 2 ], output: 1.41 },\n { type: 'mean', input: [ 1, 3, 9 ], output: 4.33 }\n]\nHere is an example websocket client program, calling remote calculator of the websocket server's own.As you can see, the client has constructed a WebSocketConnector instance with the Header and listener objects. The header object represents a Header component which be directly delivered to the remote system when connecting. The listener object is a Provider component provided for the remote websocket server. It means that, the \"Client Program\" configures the header value to precision: 2, and provides ICalcEventListener for the remote \"Server Program\".After that, you can find out the \"Client Program\" is calling the remote calculator's function of \"Server Program\" through Driver typed instance. In the remote function call statements, there is one thing important. It is Driver type supports nested object's funtion calls.This is the \"Remote Object Call\".","server-program#Server Program":"import { Driver, WebSocketServer } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { CompositeCalculator } from \"./providers/CompositeCalculator\";\nexport const webSocketServerMain = async () => {\n const server: WebSocketServer<\n ICalcConfig,\n CompositeCalculator,\n ICalcEventListener\n > = new WebSocketServer();\n await server.open(37_000, async (acceptor) => {\n const config: ICalcConfig = acceptor.header;\n const listener: Driver = acceptor.getDriver();\n await acceptor.accept(new CompositeCalculator(config, listener));\n });\n return server;\n};\nimport { Driver } from \"tgrid\";\nimport { ICalcConfig } from \"../interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"../interfaces/ICalcEventListener\";\nimport { ICompositeCalculator } from \"../interfaces/ICompositeCalculator\";\nimport { IScientificCalculator } from \"../interfaces/IScientificCalculator\";\nimport { ISimpleCalculator } from \"../interfaces/ISimpleCalculator\";\nimport { IStatisticsCalculator } from \"../interfaces/IStatisticsCalculator\";\nexport abstract class CalculatorBase {\n public constructor(\n private readonly config: ICalcConfig,\n private readonly listener: Driver,\n ) {}\n protected compute(type: string, input: number[], output: number): number {\n const pow: number = Math.pow(10, this.config.precision);\n output = Math.round(output * pow) / pow;\n this.listener.on({ type, input, output }).catch(() => {});\n return output;\n }\n}\nexport class ScientificCalculator\n extends CalculatorBase\n implements IScientificCalculator\n{\n public pow(x: number, y: number): number {\n return this.compute(\"pow\", [x, y], Math.pow(x, y));\n }\n public sqrt(x: number): number {\n return this.compute(\"sqrt\", [x], Math.sqrt(x));\n }\n public log(x: number, base: number): number {\n return this.compute(\"log\", [x, base], Math.log(x) / Math.log(base));\n }\n}\nexport class StatisticsCalculator\n extends CalculatorBase\n implements IStatisticsCalculator\n{\n public mean(...values: number[]): number {\n const sum: number = values.reduce((x, y) => x + y);\n return this.compute(\"mean\", values, sum / values.length);\n }\n public stdev(...values: number[]): number {\n const mean: number = values.reduce((x, y) => x + y) / values.length;\n const sum: number = values.reduce((x, y) => x + Math.pow(y - mean, 2));\n return this.compute(\"stdev\", values, Math.sqrt(sum / values.length));\n }\n}\nexport class CompositeCalculator\n extends CalculatorBase\n implements ICompositeCalculator\n{\n public readonly scientific: ScientificCalculator;\n public readonly statistics: StatisticsCalculator;\n public constructor(\n config: ICalcConfig,\n listener: Driver,\n ) {\n super(config, listener);\n this.scientific = new ScientificCalculator(config, listener);\n this.statistics = new StatisticsCalculator(config, listener);\n }\n public plus(x: number, y: number): number {\n return this.compute(\"plus\", [x, y], x + y);\n }\n public minus(x: number, y: number): number {\n return this.compute(\"minus\", [x, y], x - y);\n }\n public multiplies(x: number, y: number): number {\n return this.compute(\"multiplies\", [x, y], x * y);\n }\n public divides(x: number, y: number): number {\n return this.compute(\"divides\", [x, y], x / y);\n }\n}\nexport interface ICalcConfig {\n precision: number;\n}\nexport interface ICalcEvent {\n type: string;\n input: number[];\n output: number;\n}\nexport interface ICalcEventListener {\n on(event: ICalcEvent): void;\n}\nexport interface ICompositeCalculator extends ISimpleCalculator {\n plus(x: number, y: number): number;\n minus(x: number, y: number): number;\n multiplies(x: number, y: number): number;\n divides(x: number, y: number): number;\n scientific: IScientificCalculator;\n statistics: IStatisticsCalculator;\n}\nexport interface IScientificCalculator {\n pow(x: number, y: number): number;\n sqrt(x: number): number;\n log(x: number, base: number): number;\n}\nexport interface IStatisticsCalculator {\n mean(...values: number[]): number;\n stdev(...values: number[]): number;\n}\nThe \"Server Program\" is providing CompositeCalculator class to the websocket client.By the way, when composing the CompositeCalculator class instance as a Provider, you can find out that it is delivering the Header and Driver typed instances to the construction parameter. By getting the Header value from the \"Client Program\", \"Server Program\" configures precision level of the calculator.Also, if you click the second Providers tab, you can find out that calculators are reporting their calculator operations to the \"Client Program\" through the Driver object. Therefore, whenever the calculator function be called, \"Server Program\" calls remote function of the \"Client Program\" for event reporting.Such two-way remote functions providing and header for initialization, this is the \"Remote Object Call\".","next-chapter#Next Chapter":"Until this chapter, we've learned only about simple structructured network systems. Complexity has only come to the Provider level, and the network systems were always monotonous. Only single type of server and client were existed.By the way, TGrid has said that it is useful for developing complicated network system like grid computing in the README and index page of guide documents. At the next chapter, we will learn about the complicated network system.\nLearn from Examples > Object Oriented Network.\nimport { Driver, WorkerConnector, WorkerServer } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { IScientificCalculator } from \"./interfaces/IScientificCalculator\";\nimport { IStatisticsCalculator } from \"./interfaces/IStatisticsCalculator\";\nimport { SimpleCalculator } from \"./providers/SimpleCalculator\";\nconst EXTENSION = __filename.endsWith(\".ts\") ? \"ts\" : \"js\";\n/// `CompositeCalculator` has two additional properties\n///\n/// - `scientific` from remote worker server\n/// - `statistics` from remote worker server\nclass CompositeCalculator extends SimpleCalculator {\n public readonly scientific: Driver;\n public readonly statistics: Driver;\n public constructor(props: {\n config: ICalcConfig;\n listener: Driver;\n scientific: Driver;\n statistics: Driver;\n }) {\n super(props.config, props.listener);\n this.scientific = props.scientific;\n this.statistics = props.statistics;\n }\n}\n/// connect to remote worker server\nconst connect = async (\n header: ICalcConfig,\n listener: Driver,\n file: string,\n): Promise> => {\n const connector: WorkerConnector =\n new WorkerConnector(header, listener, \"process\");\n await connector.connect(file);\n return connector.getDriver();\n};\nconst main = async () => {\n const server: WorkerServer<\n ICalcConfig,\n CompositeCalculator,\n ICalcEventListener\n > = new WorkerServer();\n const config: ICalcConfig = await server.getHeader();\n const listener: Driver = server.getDriver();\n // constructor provider combining with remote worker-servers\n const provider: CompositeCalculator = new CompositeCalculator({\n config,\n listener,\n scientific: await connect>(\n config,\n listener,\n `${__dirname}/scientific.${EXTENSION}`,\n ),\n statistics: await connect>(\n config,\n listener,\n `${__dirname}/statistics.${EXTENSION}`,\n ),\n });\n await server.open(provider);\n};\nmain().catch((exp) => {\n console.error(exp);\n process.exit(-1);\n});\nimport { Driver, WorkerConnector } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEvent } from \"./interfaces/ICalcEvent\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { ICompositeCalculator } from \"./interfaces/ICompositeCalculator\";\nconst EXTENSION = __filename.endsWith(\".ts\") ? \"ts\" : \"js\";\nexport const workerClientMain = async () => {\n const stack: ICalcEvent[] = [];\n const listener: ICalcEventListener = {\n on: (evt: ICalcEvent) => stack.push(evt),\n };\n const connector: WorkerConnector<\n ICalcConfig,\n ICalcEventListener,\n ICompositeCalculator\n > = new WorkerConnector(\n { precision: 2 }, // header\n listener, // provider for remote server\n \"process\",\n );\n await connector.connect(`${__dirname}/composite.${EXTENSION}`);\n const remote: Driver = connector.getDriver();\n console.log(\n await remote.plus(10, 20), // returns 30\n await remote.multiplies(3, 4), // returns 12\n await remote.divides(5, 3), // returns 1.67\n await remote.scientific.sqrt(2), // returns 1.41\n await remote.statistics.mean(1, 3, 9), // returns 4.33\n );\n await connector.close();\n console.log(stack);\n};"}},"/docs/examples/remote-function-call":{"title":"Remote Function Call","data":{"outline#Outline":"With TGrid, you can call remote system's functions as if they are local functions.Such remote procedure calling concept is called as RPC (Remote Procedure Call) in the development world, but it contains not only remote funtion call, but also contains Remote Object Call and Object Oriented Network. However, in here chapter, we will focus only on the remote function calls.Let's learn how to call remote functions with TGrid.\nDemonstrationYou can run the example program on Playground Website, or local machine.\ngit clone https://github.com/samchon/tgrid.example.remote-function-call\nnpm install\nnpm start","client-program#Client Program":"import { Driver, WebSocketConnector } from \"tgrid\";\nimport { ICalculator } from \"./ICalculator\";\nexport const webSocketClientMain = async () => {\n const connector: WebSocketConnector =\n new WebSocketConnector(\n null, // header\n null, // provider for remote server\n );\n await connector.connect(\"ws://127.0.0.1:37000/composite\");\n const remote: Driver = connector.getDriver();\n console.log(\n await remote.plus(10, 20), // returns 30\n await remote.minus(7, 3), // returns 4\n await remote.multiply(3, 4), // returns 12\n await remote.divide(5, 2), // returns 2.5\n );\n await connector.close();\n};\ninterface ICalculator {\n plus(a: number, b: number): number\n minus(a: number, b: number): number\n multiply(a: number, b: number): number\n divide(a: number, b: number): number\n}\n$ npm start\n30 4 12 2.5\nHere is an example websocket client program, calling remote calculator of the websocket server's own.As you can see, the client program has written the remote function call statements on the remote instance of Driver type. Also, the Driver typed instance has been composed by the WebSocketConnector.getDriver() method. It's because Driver is a proxy instance hooking the function call expressions, so that delivers them to the remote system, and receives the return value from the remote system.This is the secret of how TGrid has implemented the RPC (Remote Procedure Call). Just call functions of remote Provider to the Driver typed instance with await symbol, as if the Provider instance was your own. Then, TGrid will perform the proper network communications for RPC instead of you.","server-program#Server Program":"import { WebSocketServer } from \"tgrid\";\nimport { Calculator } from \"./Calculator\";\nexport const webSocketServerMain = async () => {\n const server: WebSocketServer<\n null, // header\n Calculator, // provider for remote client\n null // provider from remote client\n > = new WebSocketServer();\n await server.open(37_000, async (acceptor) => {\n const provider: Calculator = new Calculator();\n await acceptor.accept(provider);\n });\n return server;\n};\nclass Calculator {\n public plus(x: number, y: number): number {\n return x + y;\n }\n public minus(x: number, y: number): number {\n return x - y;\n }\n public multiply(x: number, y: number): number {\n return x * y;\n }\n public divide(x: number, y: number): number {\n return x / y;\n }\n}\nimport { Driver, WebSocketConnector } from \"tgrid\";\nimport { ICalculator } from \"./ICalculator\";\nexport const webSocketClientMain = async () => {\n const connector: WebSocketConnector =\n new WebSocketConnector(\n null, // header\n null, // provider for remote server\n );\n await connector.connect(\"ws://127.0.0.1:37000/composite\");\n const remote: Driver = connector.getDriver();\n console.log(\n await remote.plus(10, 20), // returns 30\n await remote.minus(7, 3), // returns 4\n await remote.multiply(3, 4), // returns 12\n await remote.divide(5, 2), // returns 2.5\n );\n await connector.close();\n};\ninterface ICalculator {\n plus(a: number, b: number): number\n minus(a: number, b: number): number\n multiply(a: number, b: number): number\n divide(a: number, b: number): number\n}\nHere is the websocket server program providing Calculator class to the client.Above \"Client Program\" is calling remote functions to the calculator through the remote instance of Driver typed. To make the client program works properly, \"Server Program\" must provide the actual instance implemented the ICalculator type. This \"Server Program\" is serving it providing the Calculator class instance in such reason.By the way, the provided Calculator does not have any asynchronous method, but \"Client Program\" is calling the remote functions with await symbol. It's because remote function calls are actually asynchrounous operations perform by the network communication, and Driver is a type changing every function's return type to be asynchronous (Promise) in such reason.This is the RPC (Remote Procedure Call) of TGrid.","next-chapter#Next Chapter":"In this chapter, we've learned only about one-way remote function calls. By the way, most of real-time network systems need two-way communication. Also, we have not utilized Header, the value directly delivered after the connection, at all.\nLearn from Examples > Remote Object Call chapter.\nimport { Driver, WebSocketConnector } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEvent } from \"./interfaces/ICalcEvent\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { ICompositeCalculator } from \"./interfaces/ICompositeCalculator\";\nexport const webSocketClientMain = async () => {\n const stack: ICalcEvent[] = [];\n const provider: ICalcEventListener = {\n on: (evt: ICalcEvent) => stack.push(evt),\n };\n const connector: WebSocketConnector<\n ICalcConfig,\n ICalcEventListener,\n ICompositeCalculator\n > = new WebSocketConnector(\n { precision: 2 }, // header\n provider, // provider for remote server\n );\n await connector.connect(\"ws://127.0.0.1:37000/composite\");\n const remote: Driver = connector.getDriver();\n console.log(\n await driver.plus(10, 20), // returns 30\n await driver.multiplies(3, 4), // returns 12\n await driver.divides(5, 3), // returns 1.67\n await driver.scientific.sqrt(2), // returns 1.41\n await driver.statistics.mean(1, 3, 9), // returns 4.33\n );\n await connector.close();\n console.log(stack);\n};"}},"/docs/features/components":{"title":"Components","data":{"outline#Outline":"This chapter describes key components of the TGrid only in the conceptual level.If you're not familar with theoretical stories, it's okay to skip to the next chapter Features > WebSocket Protocol.Even you want to see the example codes, step to the Learn from Examples > Remote Funtion Call chapter.Otherwise, let's study about the key components of the TGrid.\nCommunicator: network communication with remote system\nHeader: header value directly delivered after the connection\nProvider: object provided for remote system\nDriver: proxy instance for calling functions of the remote system's Provider","communicator#Communicator":"Communicates with a remote system.Communicator is a class taking full responsibility to network communication with remote system. You can register a Provider, an object would be provided to the remote system, to the Communicator. Also, Driver, which can access to the remote system's Provider, is created by this Communicator.For reference, actual Communicator is the top-level abstract class, and all the classes responsible for network communication in TGrid are inheriting from this Communicator class. Here is the list of every communicator classes in TGrid.\nProtocol\tClient\tServer\tWeb Socket\tWebSocketConnector\tWebSocketAcceptor\tDedicated Worker\tWorkerConnector\tWorkerServer\tShared Worker\tSharedWorkerConnector\tSharedWorkerAcceptor","header#Header":"import { Driver, WebSocketConnector } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEvent } from \"./interfaces/ICalcEvent\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { ICompositeCalculator } from \"./interfaces/ICompositeCalculator\";\nexport const webSocketClientMain = async () => {\n const stack: ICalcEvent[] = [];\n const listener: ICalcEventListener = {\n on: (evt: ICalcEvent) => stack.push(evt),\n };\n const connector: WebSocketConnector<\n ICalcConfig,\n ICalcEventListener,\n ICompositeCalculator\n > = new WebSocketConnector(\n { precision: 2 }, // header\n listener, // provider for remote server\n );\n await connector.connect(\"ws://127.0.0.1:37000/composite\");\n const remote: Driver = connector.getDriver();\n console.log(\n await driver.plus(10, 20), // returns 30\n await driver.multiplies(3, 4), // returns 12\n await driver.divides(5, 3), // returns 1.67\n await driver.scientific.sqrt(2), // returns 1.41\n await driver.statistics.mean(1, 3, 9), // returns 4.33\n );\n await connector.close();\n console.log(stack);\n};\nimport { Driver, WebSocketServer } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { CompositeCalculator } from \"./providers/CompositeCalculator\";\nimport { ScientificCalculator } from \"./providers/ScientificCalculator\";\nimport { SimpleCalculator } from \"./providers/SimpleCalculator\";\nimport { StatisticsCalculator } from \"./providers/StatisticsCalculator\";\nexport const webSocketServerMain = async () => {\n const server: WebSocketServer<\n ICalcConfig,\n | CompositeCalculator\n | SimpleCalculator\n | StatisticsCalculator\n | ScientificCalculator,\n ICalcEventListener\n > = new WebSocketServer();\n await server.open(37_000, async (acceptor) => {\n // LIST UP PROPERTIES\n const config: ICalcConfig = acceptor.header;\n const listener: Driver = acceptor.getDriver();\n // ACCEPT OR REJECT\n if (acceptor.path === \"/composite\")\n await acceptor.accept(new CompositeCalculator(config, listener));\n else if (acceptor.path === \"/simple\")\n await acceptor.accept(new SimpleCalculator(config, listener));\n else if (acceptor.path === \"/statistics\")\n await acceptor.accept(new StatisticsCalculator(config, listener));\n else if (acceptor.path === \"/scientific\")\n await acceptor.accept(new ScientificCalculator(config, listener));\n else await acceptor.reject(1002, `WebSocket API endpoint not found.`);\n });\n return server;\n};\nimport { Driver, WorkerServer } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { CompositeCalculator } from \"./providers/CompositeCalculator\";\nconst main = async () => {\n const server: WorkerServer<\n ICalcConfig,\n CompositeCalculator,\n ICalcEventListener\n > = new WorkerServer();\n const header: ICalcConfig = await server.getHeader();\n const listener: Driver = server.getDriver();\n const provider: CompositeCalculator = new CompositeCalculator(\n header,\n listener,\n );\n await server.open(provider);\n};\nmain().catch((exp) => {\n console.error(exp);\n process.exit(-1);\n});\nHeader value delivered after the connection.Header is a value, delivered from client to server directly, after the connection.Server can get the Header value through acceptor's header property.\nWebSocketAcceptor.header\nWorkerServer.getHeader()\nSharedWorkerSever.header","provider#Provider":"import { Driver, WebSocketServer } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { CompositeCalculator } from \"./providers/CompositeCalculator\";\nimport { ScientificCalculator } from \"./providers/ScientificCalculator\";\nimport { SimpleCalculator } from \"./providers/SimpleCalculator\";\nimport { StatisticsCalculator } from \"./providers/StatisticsCalculator\";\nexport const webSocketServerMain = async () => {\n const server: WebSocketServer<\n ICalcConfig,\n | CompositeCalculator\n | SimpleCalculator\n | StatisticsCalculator\n | ScientificCalculator,\n ICalcEventListener\n > = new WebSocketServer();\n await server.open(37_000, async (acceptor) => {\n // LIST UP PROPERTIES\n const config: ICalcConfig = acceptor.header;\n const listener: Driver = acceptor.getDriver();\n // ACCEPT OR REJECT\n if (acceptor.path === \"/composite\")\n await acceptor.accept(new CompositeCalculator(config, listener));\n else if (acceptor.path === \"/simple\")\n await acceptor.accept(new SimpleCalculator(config, listener));\n else if (acceptor.path === \"/statistics\")\n await acceptor.accept(new StatisticsCalculator(config, listener));\n else if (acceptor.path === \"/scientific\")\n await acceptor.accept(new ScientificCalculator(config, listener));\n else await acceptor.reject(1002, `WebSocket API endpoint not found.`);\n });\n return server;\n};\nObject provided for remote system.Provider is an object provided for the remote system.The remote system can call the Provider's functions through Driver.","driver#Driver":"import { Driver, WebSocketConnector } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEvent } from \"./interfaces/ICalcEvent\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { ICompositeCalculator } from \"./interfaces/ICompositeCalculator\";\nexport const webSocketClientMain = async () => {\n const stack: ICalcEvent[] = [];\n const listener: ICalcEventListener = {\n on: (evt: ICalcEvent) => stack.push(evt),\n };\n const connector: WebSocketConnector<\n ICalcConfig,\n ICalcEventListener,\n ICompositeCalculator\n > = new WebSocketConnector(\n { precision: 2 }, // header\n listener, // provider for remote server\n );\n await connector.connect(\"ws://127.0.0.1:37000/composite\");\n const remote: Driver = connector.getDriver();\n console.log(\n await driver.plus(10, 20), // returns 30\n await driver.multiplies(3, 4), // returns 12\n await driver.divides(5, 3), // returns 1.67\n await driver.scientific.sqrt(2), // returns 1.41\n await driver.statistics.mean(1, 3, 9), // returns 4.33\n );\n await connector.close();\n console.log(stack);\n};\n$ npm start\n30 12 1.67 1.41 4.33\n[\n { type: 'plus', input: [ 10, 20 ], output: 30 },\n { type: 'multiplies', input: [ 3, 4 ], output: 12 },\n { type: 'divides', input: [ 5, 3 ], output: 1.67 },\n { type: 'sqrt', input: [ 2 ], output: 1.41 },\n { type: 'mean', input: [ 1, 3, 9 ], output: 4.33 }\n]\nDriver of RPC (Remote Procedure Call).Driver is a proxy instance designed to call functions of the remote system. It has a generic argument Remote which means the type of remote system's Provider, and you can remotely call the functions of the Provider asynchronously through the Drive instance.When you call some function of remote Provider by the Driver instance, it hooks the function call expression, and delivers the function name and arguments (parameter values) to the remote system through the Communicator. If the remote system suceeded to reply the result of the function call, Communicator resolves the promise of the function call expression with the result, so that makes Driver working.Otherwise exception is thrown in the remote Provider function, Communicator deliveries the exception instance instead to the remote system, so that actual exception being thrown from the Driver instance.\nDemonstrationYou can run it on Playground Website, or local machine.\ngit clone https://github.com/samchon/tgrid.example.websocket\nnpm install\nnpm start"}},"/docs/features/websocket":{"title":"Websocket","data":{"outline#Outline":"TGrid supports WebSocket protocol.With TGrid, you can easily develop WebSocket system under the RPC (Remote Procedure Call) concept.By the way, when you're developing WebSocket server natively only with TGrid, you have to construct and open the WebSocket server through WebSocketServer class. Also, you have to access to the WebSocket server with manual WebSocketConnector composition.Instead, if you develop the WebSocket server with NestJS, client can easily interact with the WebSocket server by SDK (Software Development Kit) library generated by Nestia. Also, you can make both http and websocket operations to the NestJS controllers, so that makes the server compatible on both protocols.Therefore, when you develop WebSocket server, I recommend to use NestJS with TGrid for the best development experience.","native-classes#Native Classes":"","websocketserver#WebSocketServer":"import { Driver, WebSocketServer } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { CompositeCalculator } from \"./providers/CompositeCalculator\";\nimport { ScientificCalculator } from \"./providers/ScientificCalculator\";\nimport { SimpleCalculator } from \"./providers/SimpleCalculator\";\nimport { StatisticsCalculator } from \"./providers/StatisticsCalculator\";\nexport const webSocketServerMain = async () => {\n const server: WebSocketServer<\n ICalcConfig,\n | CompositeCalculator\n | SimpleCalculator\n | StatisticsCalculator\n | ScientificCalculator,\n ICalcEventListener\n > = new WebSocketServer();\n await server.open(37_000, async (acceptor) => {\n // LIST UP PROPERTIES\n const config: ICalcConfig = acceptor.header;\n const listener: Driver = acceptor.getDriver();\n // ACCEPT OR REJECT\n if (acceptor.path === \"/composite\")\n await acceptor.accept(new CompositeCalculator(config, listener));\n else if (acceptor.path === \"/simple\")\n await acceptor.accept(new SimpleCalculator(config, listener));\n else if (acceptor.path === \"/statistics\")\n await acceptor.accept(new StatisticsCalculator(config, listener));\n else if (acceptor.path === \"/scientific\")\n await acceptor.accept(new ScientificCalculator(config, listener));\n else await acceptor.reject(1002, `WebSocket API endpoint not found.`);\n });\n return server;\n};\nimport { Driver } from \"tgrid\";\nimport { ICalcConfig } from \"../interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"../interfaces/ICalcEventListener\";\nimport { ICompositeCalculator } from \"../interfaces/ICompositeCalculator\";\nimport { IScientificCalculator } from \"../interfaces/IScientificCalculator\";\nimport { ISimpleCalculator } from \"../interfaces/ISimpleCalculator\";\nimport { IStatisticsCalculator } from \"../interfaces/IStatisticsCalculator\";\nexport abstract class CalculatorBase {\n public constructor(\n private readonly config: ICalcConfig,\n private readonly listener: Driver,\n ) {}\n protected compute(type: string, input: number[], output: number): number {\n const pow: number = Math.pow(10, this.config.precision);\n output = Math.round(output * pow) / pow;\n this.listener.on({ type, input, output }).catch(() => {});\n return output;\n }\n}\nexport class SimpleCalculator\n extends CalculatorBase\n implements ISimpleCalculator\n{\n public plus(x: number, y: number): number {\n return this.compute(\"plus\", [x, y], x + y);\n }\n public minus(x: number, y: number): number {\n return this.compute(\"minus\", [x, y], x - y);\n }\n public multiplies(x: number, y: number): number {\n return this.compute(\"multiplies\", [x, y], x * y);\n }\n public divides(x: number, y: number): number {\n return this.compute(\"divides\", [x, y], x / y);\n }\n}\nexport class ScientificCalculator\n extends CalculatorBase\n implements IScientificCalculator\n{\n public pow(x: number, y: number): number {\n return this.compute(\"pow\", [x, y], Math.pow(x, y));\n }\n public sqrt(x: number): number {\n return this.compute(\"sqrt\", [x], Math.sqrt(x));\n }\n public log(x: number, base: number): number {\n return this.compute(\"log\", [x, base], Math.log(x) / Math.log(base));\n }\n}\nexport class StatisticsCalculator\n extends CalculatorBase\n implements IStatisticsCalculator\n{\n public mean(...values: number[]): number {\n const sum: number = values.reduce((x, y) => x + y);\n return this.compute(\"mean\", values, sum / values.length);\n }\n public stdev(...values: number[]): number {\n const mean: number = values.reduce((x, y) => x + y) / values.length;\n const sum: number = values.reduce((x, y) => x + Math.pow(y - mean, 2));\n return this.compute(\"stdev\", values, Math.sqrt(sum / values.length));\n }\n}\nexport class CompositeCalculator\n extends SimpleCalculator\n implements ICompositeCalculator\n{\n public readonly scientific: ScientificCalculator;\n public readonly statistics: StatisticsCalculator;\n public constructor(\n config: ICalcConfig,\n listener: Driver,\n ) {\n super(config, listener);\n this.scientific = new ScientificCalculator(config, listener);\n this.statistics = new StatisticsCalculator(config, listener);\n }\n}\nexport interface ICalcConfig {\n precision: number;\n}\nexport interface ICalcEvent {\n type: string;\n input: number[];\n output: number;\n}\nexport interface ICalcEventListener {\n on(event: ICalcEvent): void;\n}\nexport interface ICompositeCalculator extends ISimpleCalculator {\n scientific: IScientificCalculator;\n statistics: IStatisticsCalculator;\n}\nexport interface ISimpleCalculator {\n plus(x: number, y: number): number;\n minus(x: number, y: number): number;\n multiplies(x: number, y: number): number;\n divides(x: number, y: number): number;\n}\nexport interface IScientificCalculator {\n pow(x: number, y: number): number;\n sqrt(x: number): number;\n log(x: number, base: number): number;\n}\nexport interface IStatisticsCalculator {\n mean(...values: number[]): number;\n stdev(...values: number[]): number;\n}\nAvailable only in the NodeJS.\nWebSocketServer is a class which can open an websocket server. Clients connecting to the WebSocketServer would communicate with this websocket server through WebSocketAcceptor objects with RPC (Remote Procedure Call) concept.To open the websocket server, call the WebSocketServer.open() method with target port number, and your custom callback function which would be called whenever a WebSocketAcceptor has been newly created by a new client's connection.Also, when declaring WebSocketServer type, you have to specify three generic arguments; Header, Provider and Remote. Those generic arguments would be propagated to the WebSocketAcceptor, so that WebSocketAcceptor would have the same generic arguments, too.For reference, the first Header type repersents an initial data from the remote client after the connection. I recommend utilize it as an activation tool for security enhancement. The second generic argument Provider represents a provider from server to client, and the other Remote means a provider from the remote client to server.\nAbove example case:\nHeader: ICalcConfig type\nProvider: Server is providing one of below to the client\nSimpleCalculator\nStatisticsCalculator\nScientificCalculator\nRemote: Client is providing ICalcEventListener to the server","websocketacceptor#WebSocketAcceptor":"import { Driver, WebSocketAcceptor, WebSocketServer } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { CompositeCalculator } from \"./providers/CompositeCalculator\";\nimport { ScientificCalculator } from \"./providers/ScientificCalculator\";\nimport { SimpleCalculator } from \"./providers/SimpleCalculator\";\nimport { StatisticsCalculator } from \"./providers/StatisticsCalculator\";\nexport const webSocketServerMain = async () => {\n const server: WebSocketServer<\n ICalcConfig,\n | CompositeCalculator\n | SimpleCalculator\n | StatisticsCalculator\n | ScientificCalculator,\n ICalcEventListener\n > = new WebSocketServer();\n await server.open(\n 37_000,\n async (\n acceptor: WebSocketAcceptor<\n ICalcConfig,\n | CompositeCalculator\n | SimpleCalculator\n | StatisticsCalculator\n | ScientificCalculator,\n ICalcEventListener\n >,\n ) => {\n // LIST UP PROPERTIES\n const config: ICalcConfig = acceptor.header;\n const listener: Driver = acceptor.getDriver();\n // ACCEPT OR REJECT\n if (acceptor.path === \"/composite\")\n await acceptor.accept(new CompositeCalculator(config, listener));\n else if (acceptor.path === \"/simple\")\n await acceptor.accept(new SimpleCalculator(config, listener));\n else if (acceptor.path === \"/statistics\")\n await acceptor.accept(new StatisticsCalculator(config, listener));\n else if (acceptor.path === \"/scientific\")\n await acceptor.accept(new ScientificCalculator(config, listener));\n else await acceptor.reject(1002, `WebSocket API endpoint not found.`);\n },\n );\n return server;\n};\nimport { Driver } from \"tgrid\";\nimport { ICalcConfig } from \"../interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"../interfaces/ICalcEventListener\";\nimport { ICompositeCalculator } from \"../interfaces/ICompositeCalculator\";\nimport { IScientificCalculator } from \"../interfaces/IScientificCalculator\";\nimport { ISimpleCalculator } from \"../interfaces/ISimpleCalculator\";\nimport { IStatisticsCalculator } from \"../interfaces/IStatisticsCalculator\";\nexport abstract class CalculatorBase {\n public constructor(\n private readonly config: ICalcConfig,\n private readonly listener: Driver,\n ) {}\n protected compute(type: string, input: number[], output: number): number {\n const pow: number = Math.pow(10, this.config.precision);\n output = Math.round(output * pow) / pow;\n this.listener.on({ type, input, output }).catch(() => {});\n return output;\n }\n}\nexport class SimpleCalculator\n extends CalculatorBase\n implements ISimpleCalculator\n{\n public plus(x: number, y: number): number {\n return this.compute(\"plus\", [x, y], x + y);\n }\n public minus(x: number, y: number): number {\n return this.compute(\"minus\", [x, y], x - y);\n }\n public multiplies(x: number, y: number): number {\n return this.compute(\"multiplies\", [x, y], x * y);\n }\n public divides(x: number, y: number): number {\n return this.compute(\"divides\", [x, y], x / y);\n }\n}\nexport class ScientificCalculator\n extends CalculatorBase\n implements IScientificCalculator\n{\n public pow(x: number, y: number): number {\n return this.compute(\"pow\", [x, y], Math.pow(x, y));\n }\n public sqrt(x: number): number {\n return this.compute(\"sqrt\", [x], Math.sqrt(x));\n }\n public log(x: number, base: number): number {\n return this.compute(\"log\", [x, base], Math.log(x) / Math.log(base));\n }\n}\nexport class StatisticsCalculator\n extends CalculatorBase\n implements IStatisticsCalculator\n{\n public mean(...values: number[]): number {\n const sum: number = values.reduce((x, y) => x + y);\n return this.compute(\"mean\", values, sum / values.length);\n }\n public stdev(...values: number[]): number {\n const mean: number = values.reduce((x, y) => x + y) / values.length;\n const sum: number = values.reduce((x, y) => x + Math.pow(y - mean, 2));\n return this.compute(\"stdev\", values, Math.sqrt(sum / values.length));\n }\n}\nexport class CompositeCalculator\n extends SimpleCalculator\n implements ICompositeCalculator\n{\n public readonly scientific: ScientificCalculator;\n public readonly statistics: StatisticsCalculator;\n public constructor(\n config: ICalcConfig,\n listener: Driver,\n ) {\n super(config, listener);\n this.scientific = new ScientificCalculator(config, listener);\n this.statistics = new StatisticsCalculator(config, listener);\n }\n}\nexport interface ICalcConfig {\n precision: number;\n}\nexport interface ICalcEvent {\n type: string;\n input: number[];\n output: number;\n}\nexport interface ICalcEventListener {\n on(event: ICalcEvent): void;\n}\nexport interface ICompositeCalculator extends ISimpleCalculator {\n scientific: IScientificCalculator;\n statistics: IStatisticsCalculator;\n}\nexport interface ISimpleCalculator {\n plus(x: number, y: number): number;\n minus(x: number, y: number): number;\n multiplies(x: number, y: number): number;\n divides(x: number, y: number): number;\n}\nexport interface IScientificCalculator {\n pow(x: number, y: number): number;\n sqrt(x: number): number;\n log(x: number, base: number): number;\n}\nexport interface IStatisticsCalculator {\n mean(...values: number[]): number;\n stdev(...values: number[]): number;\n}\nAvailable only in the NodeJS.\nThe WebSocketAcceptor is a communicator class interacting with the remote websocket client through (RPC)(Remote Procedure Call) concept, created by the WebSocketServer class whenever a remote client connects to the websocket server.When the closure function being called by the connection of a remote client, you can determine whether to accept the client's connection or not, reading the WebSocketAcceptor.header or WebSocketAcceptor.path properties. If you've decided to accept the connection, call the WebSocketAcceptor.accept() method with Provider instance. Otherwise, reject it through the WebSocketAcceptor.reject() method.After accepting the connection, don't forget to closing the connection after your business logic has been completed to clean up the resources. Otherwise the closing must be performed by the remote client, you can wait the remote client's closing signal by the WebSocketAcceptor.join() method.","websocketconnector#WebSocketConnector":"import { Driver, WebSocketConnector } from \"tgrid\";\nimport { ICalcConfig } from \"../interfaces/ICalcConfig\";\nimport { ICalcEvent } from \"../interfaces/ICalcEvent\";\nimport { ICalcEventListener } from \"../interfaces/ICalcEventListener\";\nimport { ICompositeCalculator } from \"../interfaces/ICompositeCalculator\";\nexport const webSocketClientMain = async () => {\n const stack: ICalcEvent[] = [];\n const listener: ICalcEventListener = {\n on: (evt: ICalcEvent) => stack.push(evt),\n };\n const connector: WebSocketConnector<\n ICalcConfig,\n ICalcEventListener,\n ICompositeCalculator\n > = new WebSocketConnector(\n { precision: 2 }, // header\n listener, // provider for remote server\n );\n await connector.connect(\"ws://127.0.0.1:37000/composite\");\n const remote: Driver = connector.getDriver();\n console.log(\n await driver.plus(10, 20), // returns 30\n await driver.multiplies(3, 4), // returns 12\n await driver.divides(5, 3), // returns 1.67\n await driver.scientific.sqrt(2), // returns 1.41\n await driver.statistics.mean(1, 3, 9), // returns 4.33\n );\n await connector.close();\n console.log(stack);\n};\nexport interface ICalcConfig {\n precision: number;\n}\nexport interface ICalcEvent {\n type: string;\n input: number[];\n output: number;\n}\nexport interface ICalcEventListener {\n on(event: ICalcEvent): void;\n}\nexport interface ICompositeCalculator extends ISimpleCalculator {\n scientific: IScientificCalculator;\n statistics: IStatisticsCalculator;\n}\nexport interface ISimpleCalculator {\n plus(x: number, y: number): number;\n minus(x: number, y: number): number;\n multiplies(x: number, y: number): number;\n divides(x: number, y: number): number;\n}\nexport interface IScientificCalculator {\n pow(x: number, y: number): number;\n sqrt(x: number): number;\n log(x: number, base: number): number;\n}\nexport interface IStatisticsCalculator {\n mean(...values: number[]): number;\n stdev(...values: number[]): number;\n}\n$ npm start\n30 12 1.67 1.41 4.33\n[\n { type: 'plus', input: [ 10, 20 ], output: 30 },\n { type: 'multiplies', input: [ 3, 4 ], output: 12 },\n { type: 'divides', input: [ 5, 3 ], output: 1.67 },\n { type: 'sqrt', input: [ 2 ], output: 1.41 },\n { type: 'mean', input: [ 1, 3, 9 ], output: 4.33 }\n]\nAvailable in both Browser/NodeJS.\nWeb Socket Connector.The WebSocketConnector is a communicator class which connects to a websocket server and interacts with it through RPC (Remote Procedure Call) concept.You can connect to the websocket server using WebSocketConnector.connect() method. The interaction would be started if the server accepts your connection by calling the WebSocketAcceptor.accept() method. If the remote server rejects your connection through WebSocketAcceptor.reject() method, the exception would be thrown.After the connection, don't forget to closing the connection, if your business logics have been completed, to clean up the resources. Otherwise, the closing must be performed by the remote websocket server, you can wait the remote server's closing signal through the WebSocketConnector.join() method.Also, when declaring this WebSocketConnector type, you've to define three generic arguments; Header, Provider and Remote. Those generic arguments must be same with the ones defined in the target WebSocketServer and WebSocketAcceptor classes (Provider and Remote must be reversed).For reference, the first Header type repersents an initial data from the remote client after the connection. I recommend utilize it as an activation tool for security enhancement. The second generic argument Provider represents a provider from client to server, and the other Remote means a provider from the remote server to client.\nAbove example case:\nHeader: ICalcConfig type\nProvider: Client is providing ICalcEventListener to the server\nRemote: Server is providing ISimpleCalculator to the client\nDemonstrationYou can run it on Playground Website, or local machine.\ngit clone https://github.com/samchon/tgrid.example.websocket\nnpm install\nnpm start","nestjs-integration#NestJS Integration":"If you develop websocket application, I recommend integrate TGrid with NestJS.It's because you can manage WebSocket API endpoints much effectively and easily by NestJS controller patterns. Also, you can make your server to support both HTTP and WebSocket protocols at the same time. NestJS controllers are compatible with both HTTP and WebSocket operations.In the client side, you also can take advantages of automatically generated SDK (Software Development Kit) library for the client developers. With the SDK, client developers no more need to write the WebSocket connection and RPC (Remote Procedure Call) codes manually, so that the client development becomes much easier and safer.","bootstrap#Bootstrap":"import { WebSocketAdaptor } from \"@nestia/core\";\nimport { INestApplication } from \"@nestjs/common\";\nimport { NestFactory } from \"@nestjs/core\";\nimport { CalculateModule } from \"./calculate.module\";\nexport const bootstrap = async (): Promise => {\n const app: INestApplication = await NestFactory.create(CalculateModule);\n await WebSocketAdaptor.upgrade(app);\n await app.listen(37_000, \"0.0.0.0\");\n return app;\n};\nTo utilize TGrid in the NestJS, upgrade the NestJS application like above.Just call the WebSocketAdaptor.upgrade() method, then you can utilize TGrid in the NestJS server.\nAbout detailed setup or more detailed informations, please refer below docs:\nNestia > Guide Documents > Setup\nNestia > Guide Documents > WebSocketRoute","nestjs-controller#NestJS Controller":"import { TypedRoute, WebSocketRoute } from \"@nestia/core\";\nimport { Controller } from \"@nestjs/common\";\nimport { Driver, WebSocketAcceptor } from \"tgrid\";\nimport { ICalcConfig } from \"./api/interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"./api/interfaces/ICalcEventListener\";\nimport { ICompositeCalculator } from \"./api/interfaces/ICompositeCalculator\";\nimport { IScientificCalculator } from \"./api/interfaces/IScientificCalculator\";\nimport { ISimpleCalculator } from \"./api/interfaces/ISimpleCalculator\";\nimport { IStatisticsCalculator } from \"./api/interfaces/IStatisticsCalculator\";\nimport { CompositeCalculator } from \"./providers/CompositeCalculator\";\nimport { ScientificCalculator } from \"./providers/ScientificCalculator\";\nimport { SimpleCalculator } from \"./providers/SimpleCalculator\";\nimport { StatisticsCalculator } from \"./providers/StatisticsCalculator\";\n@Controller(\"calculate\")\nexport class CalculateController {\n /**\n * Health check API (HTTP GET).\n */\n @TypedRoute.Get(\"health\")\n public health(): string {\n return \"Health check OK\";\n }\n /**\n * Prepare a composite calculator.\n */\n @WebSocketRoute(\"composite\")\n public async composite(\n @WebSocketRoute.Acceptor()\n acceptor: WebSocketAcceptor<\n ICalcConfig,\n ICompositeCalculator,\n ICalcEventListener\n >,\n @WebSocketRoute.Header() header: ICalcConfig,\n @WebSocketRoute.Driver() listener: Driver\n ): Promise {\n const provider: CompositeCalculator = new CompositeCalculator(\n header,\n listener\n );\n await acceptor.accept(provider);\n }\n /**\n * Prepare a simple calculator.\n */\n @WebSocketRoute(\"simple\")\n public async simple(\n @WebSocketRoute.Acceptor()\n acceptor: WebSocketAcceptor<\n ICalcConfig, // header\n ISimpleCalculator, // provider for remote client\n ICalcEventListener // provider from remote client\n >\n ): Promise {\n const header: ICalcConfig = acceptor.header;\n const listener: Driver = acceptor.getDriver();\n const provider: SimpleCalculator = new SimpleCalculator(header, listener);\n await acceptor.accept(provider);\n }\n /**\n * Prepare a scientific calculator.\n */\n @WebSocketRoute(\"scientific\")\n public async scientific(\n @WebSocketRoute.Acceptor()\n acceptor: WebSocketAcceptor<\n ICalcConfig,\n IScientificCalculator,\n ICalcEventListener\n >\n ): Promise {\n const header: ICalcConfig = acceptor.header;\n const listener: Driver = acceptor.getDriver();\n const provider: ScientificCalculator = new ScientificCalculator(\n header,\n listener\n );\n await acceptor.accept(provider);\n }\n /**\n * Prepare a statistics calculator.\n */\n @WebSocketRoute(\"statistics\")\n public async statistics(\n @WebSocketRoute.Acceptor()\n acceptor: WebSocketAcceptor<\n ICalcConfig,\n IStatisticsCalculator,\n ICalcEventListener\n >\n ): Promise {\n const header: ICalcConfig = acceptor.header;\n const listener: Driver = acceptor.getDriver();\n const provider: IStatisticsCalculator = new StatisticsCalculator(\n header,\n listener\n );\n await acceptor.accept(provider);\n }\n}\nimport { Module } from \"@nestjs/common\";\nimport { CalculateController } from \"./calculate.controller\";\n@Module({\n controllers: [CalculateController],\n})\nexport class CalculateModule {}\nimport { Driver } from \"tgrid\";\nimport { ICalcConfig } from \"../interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"../interfaces/ICalcEventListener\";\nimport { ICompositeCalculator } from \"../interfaces/ICompositeCalculator\";\nimport { IScientificCalculator } from \"../interfaces/IScientificCalculator\";\nimport { ISimpleCalculator } from \"../interfaces/ISimpleCalculator\";\nimport { IStatisticsCalculator } from \"../interfaces/IStatisticsCalculator\";\nexport abstract class CalculatorBase {\n public constructor(\n private readonly config: ICalcConfig,\n private readonly listener: Driver,\n ) {}\n protected compute(type: string, input: number[], output: number): number {\n const pow: number = Math.pow(10, this.config.precision);\n output = Math.round(output * pow) / pow;\n this.listener.on({ type, input, output }).catch(() => {});\n return output;\n }\n}\nexport class SimpleCalculator\n extends CalculatorBase\n implements ISimpleCalculator\n{\n public plus(x: number, y: number): number {\n return this.compute(\"plus\", [x, y], x + y);\n }\n public minus(x: number, y: number): number {\n return this.compute(\"minus\", [x, y], x - y);\n }\n public multiplies(x: number, y: number): number {\n return this.compute(\"multiplies\", [x, y], x * y);\n }\n public divides(x: number, y: number): number {\n return this.compute(\"divides\", [x, y], x / y);\n }\n}\nexport class ScientificCalculator\n extends CalculatorBase\n implements IScientificCalculator\n{\n public pow(x: number, y: number): number {\n return this.compute(\"pow\", [x, y], Math.pow(x, y));\n }\n public sqrt(x: number): number {\n return this.compute(\"sqrt\", [x], Math.sqrt(x));\n }\n public log(x: number, base: number): number {\n return this.compute(\"log\", [x, base], Math.log(x) / Math.log(base));\n }\n}\nexport class StatisticsCalculator\n extends CalculatorBase\n implements IStatisticsCalculator\n{\n public mean(...values: number[]): number {\n const sum: number = values.reduce((x, y) => x + y);\n return this.compute(\"mean\", values, sum / values.length);\n }\n public stdev(...values: number[]): number {\n const mean: number = values.reduce((x, y) => x + y) / values.length;\n const sum: number = values.reduce((x, y) => x + Math.pow(y - mean, 2));\n return this.compute(\"stdev\", values, Math.sqrt(sum / values.length));\n }\n}\nexport class CompositeCalculator\n extends SimpleCalculator\n implements ICompositeCalculator\n{\n public readonly scientific: ScientificCalculator;\n public readonly statistics: StatisticsCalculator;\n public constructor(\n config: ICalcConfig,\n listener: Driver,\n ) {\n super(config, listener);\n this.scientific = new ScientificCalculator(config, listener);\n this.statistics = new StatisticsCalculator(config, listener);\n }\n}\nexport interface ICalcConfig {\n precision: number;\n}\nexport interface ICalcEvent {\n type: string;\n input: number[];\n output: number;\n}\nexport interface ICalcEventListener {\n on(event: ICalcEvent): void;\n}\nexport interface ICompositeCalculator extends ISimpleCalculator {\n scientific: IScientificCalculator;\n statistics: IStatisticsCalculator;\n}\nexport interface ISimpleCalculator {\n plus(x: number, y: number): number;\n minus(x: number, y: number): number;\n multiplies(x: number, y: number): number;\n divides(x: number, y: number): number;\n}\nexport interface IScientificCalculator {\n pow(x: number, y: number): number;\n sqrt(x: number): number;\n log(x: number, base: number): number;\n}\nexport interface IStatisticsCalculator {\n mean(...values: number[]): number;\n stdev(...values: number[]): number;\n}\nFrom now on, you can define WebSocket API operations like above.Just import and attach the @WebSocketRoute() decorator function to the target controller methods.Note that, don't forget to define @WebSocketRoute.Acceptor() decorated parameter with WebSocketAcceptor type. It's because the websocket server must determine whether to WebSocketAcceptor.accept() the client's connection or WebSocketAcceptor.reject() it.Also, when declaring the WebSocketAcceptor type, ou have to specify three generic arguments; Header, Provider and Remote. Those generic arguments would be propagated to the automatically generated Software Development Kit for the client, so that the client developers will utilize the same generic types what you've defined (Provider and Remote must be reversed).For reference, the first Header type repersents an initial data from the remote client after the connection. I recommend utilize it as an activation tool for security enhancement. The second generic argument Provider represents a provider from server to client, and the other Remote means a provider from the remote client to server.\nYou can find more detailed informations about @WebSocketRoute():\nNestia > Guide Documents > WebSocketRoute]","software-development-kit#Software Development Kit":"/**\n * @packageDocumentation\n * @module api.functional.calculate\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\n//================================================================\nimport type { IConnection, Primitive } from \"@nestia/fetcher\";\nimport { PlainFetcher } from \"@nestia/fetcher/lib/PlainFetcher\";\nimport { WebSocketConnector } from \"tgrid\";\nimport type { Driver } from \"tgrid\";\nimport type { ICalcConfig } from \"../../interfaces/ICalcConfig\";\nimport type { ICalcEventListener } from \"../../interfaces/ICalcEventListener\";\nimport type { ICompositeCalculator } from \"../../interfaces/ICompositeCalculator\";\nimport type { IScientificCalculator } from \"../../interfaces/IScientificCalculator\";\nimport type { ISimpleCalculator } from \"../../interfaces/ISimpleCalculator\";\nimport type { IStatisticsCalculator } from \"../../interfaces/IStatisticsCalculator\";\n/**\n * Health check API (HTTP GET).\n *\n * @controller CalculateController.health\n * @path GET /calculate/health\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function health(connection: IConnection): Promise {\n return PlainFetcher.fetch(connection, {\n ...health.METADATA,\n path: health.path(),\n });\n}\nexport namespace health {\n export type Output = Primitive;\n export const METADATA = {\n method: \"GET\",\n path: \"/calculate/health\",\n request: null,\n response: {\n type: \"application/json\",\n encrypted: false,\n },\n status: null,\n } as const;\n export const path = () => \"/calculate/health\";\n}\n/**\n * Prepare a composite calculator.\n *\n * @controller CalculateController.composite\n * @path /calculate/composite\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function composite(\n connection: IConnection,\n provider: composite.Provider,\n): Promise {\n const connector: WebSocketConnector<\n composite.Header,\n composite.Provider,\n composite.Listener\n > = new WebSocketConnector(connection.headers ?? ({} as any), provider);\n await connector.connect(\n `${connection.host.endsWith(\"/\") ? connection.host.substring(0, connection.host.length - 1) : connection.host}${composite.path()}`,\n );\n const driver: Driver = connector.getDriver();\n return {\n connector,\n driver,\n };\n}\nexport namespace composite {\n export type Output = {\n connector: WebSocketConnector;\n driver: Driver;\n };\n export type Header = ICalcConfig;\n export type Provider = ICalcEventListener;\n export type Listener = ICompositeCalculator;\n export const path = () => \"/calculate/composite\";\n}\n/**\n * Prepare a simple calculator.\n *\n * @controller CalculateController.simple\n * @path /calculate/simple\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function simple(\n connection: IConnection,\n provider: simple.Provider,\n): Promise {\n const connector: WebSocketConnector<\n simple.Header,\n simple.Provider,\n simple.Listener\n > = new WebSocketConnector(connection.headers ?? ({} as any), provider);\n await connector.connect(\n `${connection.host.endsWith(\"/\") ? connection.host.substring(0, connection.host.length - 1) : connection.host}${simple.path()}`,\n );\n const driver: Driver = connector.getDriver();\n return {\n connector,\n driver,\n };\n}\nexport namespace simple {\n export type Output = {\n connector: WebSocketConnector;\n driver: Driver;\n };\n export type Header = ICalcConfig;\n export type Provider = ICalcEventListener;\n export type Listener = ISimpleCalculator;\n export const path = () => \"/calculate/simple\";\n}\n/**\n * Prepare a scientific calculator.\n *\n * @controller CalculateController.scientific\n * @path /calculate/scientific\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function scientific(\n connection: IConnection,\n provider: scientific.Provider,\n): Promise {\n const connector: WebSocketConnector<\n scientific.Header,\n scientific.Provider,\n scientific.Listener\n > = new WebSocketConnector(connection.headers ?? ({} as any), provider);\n await connector.connect(\n `${connection.host.endsWith(\"/\") ? connection.host.substring(0, connection.host.length - 1) : connection.host}${scientific.path()}`,\n );\n const driver: Driver = connector.getDriver();\n return {\n connector,\n driver,\n };\n}\nexport namespace scientific {\n export type Output = {\n connector: WebSocketConnector;\n driver: Driver;\n };\n export type Header = ICalcConfig;\n export type Provider = ICalcEventListener;\n export type Listener = IScientificCalculator;\n export const path = () => \"/calculate/scientific\";\n}\n/**\n * Prepare a statistics calculator.\n *\n * @controller CalculateController.statistics\n * @path /calculate/statistics\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function statistics(\n connection: IConnection,\n provider: statistics.Provider,\n): Promise {\n const connector: WebSocketConnector<\n statistics.Header,\n statistics.Provider,\n statistics.Listener\n > = new WebSocketConnector(connection.headers ?? ({} as any), provider);\n await connector.connect(\n `${connection.host.endsWith(\"/\") ? connection.host.substring(0, connection.host.length - 1) : connection.host}${statistics.path()}`,\n );\n const driver: Driver = connector.getDriver();\n return {\n connector,\n driver,\n };\n}\nexport namespace statistics {\n export type Output = {\n connector: WebSocketConnector;\n driver: Driver;\n };\n export type Header = ICalcConfig;\n export type Provider = ICalcEventListener;\n export type Listener = IStatisticsCalculator;\n export const path = () => \"/calculate/statistics\";\n}\nexport interface ICalcConfig {\n precision: number;\n}\nexport interface ICalcEvent {\n type: string;\n input: number[];\n output: number;\n}\nexport interface ICalcEventListener {\n on(event: ICalcEvent): void;\n}\nexport interface ICompositeCalculator extends ISimpleCalculator {\n scientific: IScientificCalculator;\n statistics: IStatisticsCalculator;\n}\nexport interface ISimpleCalculator {\n plus(x: number, y: number): number;\n minus(x: number, y: number): number;\n multiplies(x: number, y: number): number;\n divides(x: number, y: number): number;\n}\nexport interface IScientificCalculator {\n pow(x: number, y: number): number;\n sqrt(x: number): number;\n log(x: number, base: number): number;\n}\nexport interface IStatisticsCalculator {\n mean(...values: number[]): number;\n stdev(...values: number[]): number;\n}\nnpx nestia sdk\nWhen you run npx nestia sdk command, SDK (Software Development Kit) library be generated.Above file is one of the SDK library corresponding to the CalculateController class we've seen in the previous NestJS Controller section. Client developers can utilize the automatically generated SDK functions to connect to the WebSocket server, and interact it type safely. Also, HTTP operation is compatible with the WebSocket operation.Let's see how client developer utilizes the SDK library in the next section.\nYou can find more detailed informations about SDK generator:\nNestia > Guide Documents > S/W Development Kit","client-application#Client Application":"import api from \"./api\";\nimport { ICalcEvent } from \"./api/interfaces/ICalcEvent\";\nimport { ICalcEventListener } from \"./api/interfaces/ICalcEventListener\";\nexport const testCalculateSdk = async () => {\n //----\n // HTTP PROTOCOL\n //---\n // CALL HEALTH CHECK API\n console.log(\n await api.functional.calculate.health({\n host: \"http://127.0.0.1:37000\",\n })\n );\n //----\n // WEBSOCKET PROTOCOL\n //---\n // PROVIDER FOR WEBSOCKET SERVER\n const stack: ICalcEvent[] = [];\n const listener: ICalcEventListener = {\n on: (evt: ICalcEvent) => stack.push(evt),\n };\n // DO CONNECT\n const { connector, driver } = await api.functional.calculate.composite(\n {\n host: \"ws://127.0.0.1:37000\",\n headers: {\n precision: 2,\n },\n },\n listener\n );\n // CALL FUNCTIONS OF REMOTE SERVER\n console.log(\n await driver.plus(10, 20), // returns 30\n await driver.multiplies(3, 4), // returns 12\n await driver.divides(5, 3), // returns 1.67\n await driver.scientific.sqrt(2), // returns 1.41\n await driver.statistics.mean(1, 3, 9) // returns 4.33\n );\n // TERMINATE\n await connector.close();\n console.log(stack);\n};\n/**\n * @packageDocumentation\n * @module api.functional.calculate\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\n//================================================================\nimport type { IConnection, Primitive } from \"@nestia/fetcher\";\nimport { PlainFetcher } from \"@nestia/fetcher/lib/PlainFetcher\";\nimport { WebSocketConnector } from \"tgrid\";\nimport type { Driver } from \"tgrid\";\nimport type { ICalcConfig } from \"../../interfaces/ICalcConfig\";\nimport type { ICalcEventListener } from \"../../interfaces/ICalcEventListener\";\nimport type { ICompositeCalculator } from \"../../interfaces/ICompositeCalculator\";\nimport type { IScientificCalculator } from \"../../interfaces/IScientificCalculator\";\nimport type { ISimpleCalculator } from \"../../interfaces/ISimpleCalculator\";\nimport type { IStatisticsCalculator } from \"../../interfaces/IStatisticsCalculator\";\n/**\n * Health check API (HTTP GET).\n *\n * @controller CalculateController.health\n * @path GET /calculate/health\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function health(connection: IConnection): Promise {\n return PlainFetcher.fetch(connection, {\n ...health.METADATA,\n path: health.path(),\n });\n}\nexport namespace health {\n export type Output = Primitive;\n export const METADATA = {\n method: \"GET\",\n path: \"/calculate/health\",\n request: null,\n response: {\n type: \"application/json\",\n encrypted: false,\n },\n status: null,\n } as const;\n export const path = () => \"/calculate/health\";\n}\n/**\n * Prepare a composite calculator.\n *\n * @controller CalculateController.composite\n * @path /calculate/composite\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function composite(\n connection: IConnection,\n provider: composite.Provider,\n): Promise {\n const connector: WebSocketConnector<\n composite.Header,\n composite.Provider,\n composite.Listener\n > = new WebSocketConnector(connection.headers ?? ({} as any), provider);\n await connector.connect(\n `${connection.host.endsWith(\"/\") ? connection.host.substring(0, connection.host.length - 1) : connection.host}${composite.path()}`,\n );\n const driver: Driver = connector.getDriver();\n return {\n connector,\n driver,\n };\n}\nexport namespace composite {\n export type Output = {\n connector: WebSocketConnector;\n driver: Driver;\n };\n export type Header = ICalcConfig;\n export type Provider = ICalcEventListener;\n export type Listener = ICompositeCalculator;\n export const path = () => \"/calculate/composite\";\n}\n/**\n * Prepare a simple calculator.\n *\n * @controller CalculateController.simple\n * @path /calculate/simple\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function simple(\n connection: IConnection,\n provider: simple.Provider,\n): Promise {\n const connector: WebSocketConnector<\n simple.Header,\n simple.Provider,\n simple.Listener\n > = new WebSocketConnector(connection.headers ?? ({} as any), provider);\n await connector.connect(\n `${connection.host.endsWith(\"/\") ? connection.host.substring(0, connection.host.length - 1) : connection.host}${simple.path()}`,\n );\n const driver: Driver = connector.getDriver();\n return {\n connector,\n driver,\n };\n}\nexport namespace simple {\n export type Output = {\n connector: WebSocketConnector;\n driver: Driver;\n };\n export type Header = ICalcConfig;\n export type Provider = ICalcEventListener;\n export type Listener = ISimpleCalculator;\n export const path = () => \"/calculate/simple\";\n}\n/**\n * Prepare a scientific calculator.\n *\n * @controller CalculateController.scientific\n * @path /calculate/scientific\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function scientific(\n connection: IConnection,\n provider: scientific.Provider,\n): Promise {\n const connector: WebSocketConnector<\n scientific.Header,\n scientific.Provider,\n scientific.Listener\n > = new WebSocketConnector(connection.headers ?? ({} as any), provider);\n await connector.connect(\n `${connection.host.endsWith(\"/\") ? connection.host.substring(0, connection.host.length - 1) : connection.host}${scientific.path()}`,\n );\n const driver: Driver = connector.getDriver();\n return {\n connector,\n driver,\n };\n}\nexport namespace scientific {\n export type Output = {\n connector: WebSocketConnector;\n driver: Driver;\n };\n export type Header = ICalcConfig;\n export type Provider = ICalcEventListener;\n export type Listener = IScientificCalculator;\n export const path = () => \"/calculate/scientific\";\n}\n/**\n * Prepare a statistics calculator.\n *\n * @controller CalculateController.statistics\n * @path /calculate/statistics\n * @nestia Generated by Nestia - https://github.com/samchon/nestia\n */\nexport async function statistics(\n connection: IConnection,\n provider: statistics.Provider,\n): Promise {\n const connector: WebSocketConnector<\n statistics.Header,\n statistics.Provider,\n statistics.Listener\n > = new WebSocketConnector(connection.headers ?? ({} as any), provider);\n await connector.connect(\n `${connection.host.endsWith(\"/\") ? connection.host.substring(0, connection.host.length - 1) : connection.host}${statistics.path()}`,\n );\n const driver: Driver = connector.getDriver();\n return {\n connector,\n driver,\n };\n}\nexport namespace statistics {\n export type Output = {\n connector: WebSocketConnector;\n driver: Driver;\n };\n export type Header = ICalcConfig;\n export type Provider = ICalcEventListener;\n export type Listener = IStatisticsCalculator;\n export const path = () => \"/calculate/statistics\";\n}\nexport interface ICalcConfig {\n precision: number;\n}\nexport interface ICalcEvent {\n type: string;\n input: number[];\n output: number;\n}\nexport interface ICalcEventListener {\n on(event: ICalcEvent): void;\n}\nexport interface ICompositeCalculator extends ISimpleCalculator {\n scientific: IScientificCalculator;\n statistics: IStatisticsCalculator;\n}\nexport interface ISimpleCalculator {\n plus(x: number, y: number): number;\n minus(x: number, y: number): number;\n multiplies(x: number, y: number): number;\n divides(x: number, y: number): number;\n}\nexport interface IScientificCalculator {\n pow(x: number, y: number): number;\n sqrt(x: number): number;\n log(x: number, base: number): number;\n}\nexport interface IStatisticsCalculator {\n mean(...values: number[]): number;\n stdev(...values: number[]): number;\n}\nexport interface ICalcConfig {\n precision: number;\n}\nexport interface ICalcEvent {\n type: string;\n input: number[];\n output: number;\n}\nexport interface ICalcEventListener {\n on(event: ICalcEvent): void;\n}\nexport interface ICompositeCalculator extends ISimpleCalculator {\n scientific: IScientificCalculator;\n statistics: IStatisticsCalculator;\n}\nexport interface ISimpleCalculator {\n plus(x: number, y: number): number;\n minus(x: number, y: number): number;\n multiplies(x: number, y: number): number;\n divides(x: number, y: number): number;\n}\nexport interface IScientificCalculator {\n pow(x: number, y: number): number;\n sqrt(x: number): number;\n log(x: number, base: number): number;\n}\nexport interface IStatisticsCalculator {\n mean(...values: number[]): number;\n stdev(...values: number[]): number;\n}\n$ npm start\n[Nest] 4328 - 05/15/2024, 3:19:50 AM LOG [NestFactory] Starting Nest application...\n[Nest] 4328 - 05/15/2024, 3:19:50 AM LOG [InstanceLoader] CalculateModule dependencies initialized +5ms\n[Nest] 4328 - 05/15/2024, 3:19:50 AM LOG [RoutesResolver] CalculateController {/calculate}: +5ms\n[Nest] 4328 - 05/15/2024, 3:19:50 AM LOG [NestApplication] Nest application successfully started +2ms\nHealth check OK\n30 12 1.67 1.41 4.33\n[\n { type: 'plus', input: [ 10, 20 ], output: 30 },\n { type: 'multiplies', input: [ 3, 4 ], output: 12 },\n { type: 'divides', input: [ 5, 3 ], output: 1.67 },\n { type: 'sqrt', input: [ 2 ], output: 1.41 },\n { type: 'mean', input: [ 1, 3, 9 ], output: 4.33 }\n]\nDo import the SDK, and enjoy the type-safe and easy-to-use RPC.Looking at the above code, the client application is calling a function of the automatically generated SDK (Software Development Kit) library, so that connecting to the websocket server, and starting interaction through RPC (Remote Procedure Call) concept with Driver instance.Doesn't the \"SDK based development\" seems much easier and safer than the previous Natives Classes > WebSocketConnector case? This is the reason why I've recommended to combine with the NestJS when using websocket protocol based network system.For reference, return type of SDK function is a pair WebSocketConnector and Driver instances, but it would be actually returned only when the websocket server accepts your connection. Otherwise, the websocket server rejects your connection, an exception would be thrown.Also, don't forget to closing the connection, if your business logics have been completed, to clean up the resources. Otherwise, the closing must be performed by the remote websocket server, you can wait the remote server's closing signal through the WebSocketConnector.join() method.\nDemonstrationYou can run it on Playground Website, or local machine.\ngit clone https://github.com/samchon/tgrid.example.nestjs\nnpm install\nnpm start"}},"/docs/features/worker":{"title":"Worker","data":{"outline#Outline":"TGrid supports Worker/SharedWorker protocols.With TGrid, you can easily develop Worker programs under the RPC (Remote Procedure Call) concept.TGrid considers Worker as a 1: 1 dedicated server, and SharedWorker as a 1: N multi-client acceptable server running on the local. Therefore, the interfaces of Worker and SharedWorker in the TGrid are similar with WebSocket components. In such reason, if you're developing a complicate WebSocket system, you can simulate it in the local environment by using Worker/SharedWorker components.","worker#Worker":"Available in both Browser/NodeJS.\nYou can utilize RPC (Remote Procedure Call) even in the Worker.","workerconnector#WorkerConnector":"import { Driver, WorkerConnector } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEvent } from \"./interfaces/ICalcEvent\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { ICompositeCalculator } from \"./interfaces/ICompositeCalculator\";\nconst EXTENSION = __filename.endsWith(\".ts\") ? \"ts\" : \"js\";\nexport const workerClientMain = async () => {\n const stack: ICalcEvent[] = [];\n const listener: ICalcEventListener = {\n on: (evt: ICalcEvent) => stack.push(evt),\n };\n const connector: WorkerConnector<\n ICalcConfig,\n ICalcEventListener,\n ICompositeCalculator\n > = new WorkerConnector(\n { precision: 2 }, // header\n listener, // provider for remote server\n \"process\",\n );\n await connector.connect(`${__dirname}/server.${EXTENSION}`);\n const remote: Driver = connector.getDriver();\n console.log(\n await remote.plus(10, 20), // returns 30\n await remote.multiplies(3, 4), // returns 12\n await remote.divides(5, 3), // returns 1.67\n await remote.scientific.sqrt(2), // returns 1.41\n await remote.statistics.mean(1, 3, 9), // returns 4.33\n );\n await connector.close();\n console.log(stack);\n};\nexport interface ICalcConfig {\n precision: number;\n}\nexport interface ICalcEvent {\n type: string;\n input: number[];\n output: number;\n}\nexport interface ICalcEventListener {\n on(event: ICalcEvent): void;\n}\nexport interface ICompositeCalculator extends ISimpleCalculator {\n scientific: IScientificCalculator;\n statistics: IStatisticsCalculator;\n}\nexport interface ISimpleCalculator {\n plus(x: number, y: number): number;\n minus(x: number, y: number): number;\n multiplies(x: number, y: number): number;\n divides(x: number, y: number): number;\n}\nexport interface IScientificCalculator {\n pow(x: number, y: number): number;\n sqrt(x: number): number;\n log(x: number, base: number): number;\n}\nexport interface IStatisticsCalculator {\n mean(...values: number[]): number;\n stdev(...values: number[]): number;\n}\n$ npm start\n30 12 1.67 1.41 4.33\n[\n { type: 'plus', input: [ 10, 20 ], output: 30 },\n { type: 'multiplies', input: [ 3, 4 ], output: 12 },\n { type: 'divides', input: [ 5, 3 ], output: 1.67 },\n { type: 'sqrt', input: [ 2 ], output: 1.41 },\n { type: 'mean', input: [ 1, 3, 9 ], output: 4.33 }\n]\nWorker Connector.The WorkerConnetor is a communicator class, which creates an Worker instance, and interacts with it through RPC (Remote Procedure Call). In other words, WorkerConnector considers the Worker instance as a remote server accepting only one client; WorkerServer.You can create the Worker instance and communicate with it by WorkerConnector.connect() or WorkerConnector.compile() method. The WorkerConnector.connect() method just opens an existing JS (or TS) file, and the WorkerConnector.compile() method writes a temporary JS (TS) file and connects to it. Anyway, the Worker instanced program must open the WorkerServer.By the way, don't forget closing the worker to clean up the resources. If the closing be performed by WorkerServer, you can wait the worker server closing through the WorkerConnector.wait() method.Also, when declaring this WorkerConnector type, you've to define three generic arguments; Header, Provider and Remote. Those generic arguments must be same with the ones defined in the target WorkerServer class (Provider and Remote must be reversed).For reference, the first Header type repersents an initial data from the remote client after the connection. I recommend utilize it as an activation tool for security enhancement. The second generic argument Provider represents a provider from client to server, and the other Remote means a provider from the remote server to client.\nAbove example case:\nHeader: ICalcConfig type\nProvider: Client is providing ICalcEventListener to the server\nRemote: Server is providing ISimpleCalculator to the client\nDemonstrationYou can run it on Playground Website, or local machine.\ngit clone https://github.com/samchon/tgrid.example.worker\nnpm install\nnpm start","workerserver#WorkerServer":"import { Driver, WorkerServer } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { CompositeCalculator } from \"./providers/CompositeCalculator\";\nconst main = async () => {\n const server: WorkerServer<\n ICalcConfig,\n CompositeCalculator,\n ICalcEventListener\n > = new WorkerServer();\n const header: ICalcConfig = await server.getHeader();\n const listener: Driver = server.getDriver();\n const provider: CompositeCalculator = new CompositeCalculator(\n header,\n listener,\n );\n await server.open(provider);\n};\nmain().catch((exp) => {\n console.error(exp);\n process.exit(-1);\n});\nimport { Driver } from \"tgrid\";\nimport { ICalcConfig } from \"../interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"../interfaces/ICalcEventListener\";\nimport { ICompositeCalculator } from \"../interfaces/ICompositeCalculator\";\nimport { IScientificCalculator } from \"../interfaces/IScientificCalculator\";\nimport { ISimpleCalculator } from \"../interfaces/ISimpleCalculator\";\nimport { IStatisticsCalculator } from \"../interfaces/IStatisticsCalculator\";\nexport abstract class CalculatorBase {\n public constructor(\n private readonly config: ICalcConfig,\n private readonly listener: Driver,\n ) {}\n protected compute(type: string, input: number[], output: number): number {\n const pow: number = Math.pow(10, this.config.precision);\n output = Math.round(output * pow) / pow;\n this.listener.on({ type, input, output }).catch(() => {});\n return output;\n }\n}\nexport class SimpleCalculator\n extends CalculatorBase\n implements ISimpleCalculator\n{\n public plus(x: number, y: number): number {\n return this.compute(\"plus\", [x, y], x + y);\n }\n public minus(x: number, y: number): number {\n return this.compute(\"minus\", [x, y], x - y);\n }\n public multiplies(x: number, y: number): number {\n return this.compute(\"multiplies\", [x, y], x * y);\n }\n public divides(x: number, y: number): number {\n return this.compute(\"divides\", [x, y], x / y);\n }\n}\nexport class ScientificCalculator\n extends CalculatorBase\n implements IScientificCalculator\n{\n public pow(x: number, y: number): number {\n return this.compute(\"pow\", [x, y], Math.pow(x, y));\n }\n public sqrt(x: number): number {\n return this.compute(\"sqrt\", [x], Math.sqrt(x));\n }\n public log(x: number, base: number): number {\n return this.compute(\"log\", [x, base], Math.log(x) / Math.log(base));\n }\n}\nexport class StatisticsCalculator\n extends CalculatorBase\n implements IStatisticsCalculator\n{\n public mean(...values: number[]): number {\n const sum: number = values.reduce((x, y) => x + y);\n return this.compute(\"mean\", values, sum / values.length);\n }\n public stdev(...values: number[]): number {\n const mean: number = values.reduce((x, y) => x + y) / values.length;\n const sum: number = values.reduce((x, y) => x + Math.pow(y - mean, 2));\n return this.compute(\"stdev\", values, Math.sqrt(sum / values.length));\n }\n}\nexport class CompositeCalculator\n extends SimpleCalculator\n implements ICompositeCalculator\n{\n public readonly scientific: ScientificCalculator;\n public readonly statistics: StatisticsCalculator;\n public constructor(\n config: ICalcConfig,\n listener: Driver,\n ) {\n super(config, listener);\n this.scientific = new ScientificCalculator(config, listener);\n this.statistics = new StatisticsCalculator(config, listener);\n }\n}\nexport interface ICalcConfig {\n precision: number;\n}\nexport interface ICalcEvent {\n type: string;\n input: number[];\n output: number;\n}\nexport interface ICalcEventListener {\n on(event: ICalcEvent): void;\n}\nexport interface ICompositeCalculator extends ISimpleCalculator {\n scientific: IScientificCalculator;\n statistics: IStatisticsCalculator;\n}\nexport interface ISimpleCalculator {\n plus(x: number, y: number): number;\n minus(x: number, y: number): number;\n multiplies(x: number, y: number): number;\n divides(x: number, y: number): number;\n}\nexport interface IScientificCalculator {\n pow(x: number, y: number): number;\n sqrt(x: number): number;\n log(x: number, base: number): number;\n}\nexport interface IStatisticsCalculator {\n mean(...values: number[]): number;\n stdev(...values: number[]): number;\n}\nWorker Server.The WorkerServer is a class representing a Worker server which communicate with client (WorkerConnector), through the RPC (Remote Procedure Call).Unlike other servers, WorkerServer can accept only one client (WorkerConnector), because the Worker is dependent on its parent instance (web page, node or parent worker). Thus, WorkerServer does not have any acceptor and communicates with client (its parent) directly.To start communication with the client, call the WorkerServer.open() method with Provider instance. After your business, don't forget closing this Worker instance. If the termination is performed by the WorkerConnector, you can wait the closing signal through the WorkerServer.join() method.Also, when declaring this WorkerServer type, you've to define three generic arguments; Header, Provider and Remote. Those generic arguments must be same with the ones defined in the target WorkerConnector class (Provider and Remote must be reversed).For reference, the first Header type repersents an initial data from the client after the connection. I recommend utilize it as an activation tool for security enhancement. The second generic argument Provider represents a provider from server to client, and the other Remote means a provider from the client to server.\nAbove example case:\nHeader: ICalcConfig type\nProvider: Server is providing CompositeCalculator to the client\nRemote: Client is providing ICalcEventListener to the server","shared-worker#Shared Worker":"Available only in the Web Browser.\nIn the Web Browser, you also can perform RPC (Remote Procedure Call) in the SharedWorker.Also, as SharedWorker can accept multiple clients, TGrid considers it as a local server running on the web browser, and its interfaces are similar with WebSocket components.","sharedworkerserver#SharedWorkerServer":"import { Driver, SharedWorkerServer } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { CompositeCalculator } from \"./providers/CompositeCalculator\";\nconst main = async () => {\n let pool: number = 0;\n const server: SharedWorkerServer<\n ICalcConfig,\n CompositeCalculator,\n ICalcEventListener\n > = new SharedWorkerServer();\n await server.open(async (acceptor) => {\n // LIST UP PROPERTIES\n const config: ICalcConfig = acceptor.header;\n const listener: Driver = acceptor.getDriver();\n // ACCEPT OR REJECT THE CONNECTION\n if (pool >= 8) {\n await acceptor.reject(\"Too much connections.\");\n } else {\n await acceptor.accept(new CompositeCalculator(config, listener));\n ++pool;\n await acceptor.join();\n --pool;\n }\n });\n};\nmain().catch(console.error);\nimport { Driver } from \"tgrid\";\nimport { ICalcConfig } from \"../interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"../interfaces/ICalcEventListener\";\nimport { ICompositeCalculator } from \"../interfaces/ICompositeCalculator\";\nimport { IScientificCalculator } from \"../interfaces/IScientificCalculator\";\nimport { ISimpleCalculator } from \"../interfaces/ISimpleCalculator\";\nimport { IStatisticsCalculator } from \"../interfaces/IStatisticsCalculator\";\nexport abstract class CalculatorBase {\n public constructor(\n private readonly config: ICalcConfig,\n private readonly listener: Driver,\n ) {}\n protected compute(type: string, input: number[], output: number): number {\n const pow: number = Math.pow(10, this.config.precision);\n output = Math.round(output * pow) / pow;\n this.listener.on({ type, input, output }).catch(() => {});\n return output;\n }\n}\nexport class SimpleCalculator\n extends CalculatorBase\n implements ISimpleCalculator\n{\n public plus(x: number, y: number): number {\n return this.compute(\"plus\", [x, y], x + y);\n }\n public minus(x: number, y: number): number {\n return this.compute(\"minus\", [x, y], x - y);\n }\n public multiplies(x: number, y: number): number {\n return this.compute(\"multiplies\", [x, y], x * y);\n }\n public divides(x: number, y: number): number {\n return this.compute(\"divides\", [x, y], x / y);\n }\n}\nexport class ScientificCalculator\n extends CalculatorBase\n implements IScientificCalculator\n{\n public pow(x: number, y: number): number {\n return this.compute(\"pow\", [x, y], Math.pow(x, y));\n }\n public sqrt(x: number): number {\n return this.compute(\"sqrt\", [x], Math.sqrt(x));\n }\n public log(x: number, base: number): number {\n return this.compute(\"log\", [x, base], Math.log(x) / Math.log(base));\n }\n}\nexport class StatisticsCalculator\n extends CalculatorBase\n implements IStatisticsCalculator\n{\n public mean(...values: number[]): number {\n const sum: number = values.reduce((x, y) => x + y);\n return this.compute(\"mean\", values, sum / values.length);\n }\n public stdev(...values: number[]): number {\n const mean: number = values.reduce((x, y) => x + y) / values.length;\n const sum: number = values.reduce((x, y) => x + Math.pow(y - mean, 2));\n return this.compute(\"stdev\", values, Math.sqrt(sum / values.length));\n }\n}\nexport class CompositeCalculator\n extends SimpleCalculator\n implements ICompositeCalculator\n{\n public readonly scientific: ScientificCalculator;\n public readonly statistics: StatisticsCalculator;\n public constructor(\n config: ICalcConfig,\n listener: Driver,\n ) {\n super(config, listener);\n this.scientific = new ScientificCalculator(config, listener);\n this.statistics = new StatisticsCalculator(config, listener);\n }\n}\nexport interface ICalcConfig {\n precision: number;\n}\nexport interface ICalcEvent {\n type: string;\n input: number[];\n output: number;\n}\nexport interface ICalcEventListener {\n on(event: ICalcEvent): void;\n}\nexport interface ICompositeCalculator extends ISimpleCalculator {\n scientific: IScientificCalculator;\n statistics: IStatisticsCalculator;\n}\nexport interface ISimpleCalculator {\n plus(x: number, y: number): number;\n minus(x: number, y: number): number;\n multiplies(x: number, y: number): number;\n divides(x: number, y: number): number;\n}\nexport interface IScientificCalculator {\n pow(x: number, y: number): number;\n sqrt(x: number): number;\n log(x: number, base: number): number;\n}\nexport interface IStatisticsCalculator {\n mean(...values: number[]): number;\n stdev(...values: number[]): number;\n}\nShared Worker Server.The SharedWorkerServer is a class representing a server in SharedWorker environment. Clients connecting to the SharedWorkerServer would communicate with this server through SharedWorkerAcceptor instaces using RPC (Remote Procedure Call) concept.To open the server, call the SharedWorkerServer.open() method with your callback function which would be called whenever a SharedWorkerAcceptor has been newly created by a new client's connection.Also, when declaring this SharedWorkerServer type, you have to define three generic arguments; Header, Provider and Remote. Those generic arguments would be propagated to the SharedWorkerAcceptor, so that SharedWorkerAcceptor would have the same generic arguments, too.For reference, the first Header type repersents an initial data from the remote client after the connection. I recommend utilize it as an activation tool for security enhancement. The second generic argument Provider represents a provider from server to client, and the other Remote means a provider from the remote client to server.\nAbove example case:\nHeader: ICalcConfig type\nProvider: Server is providing CompositeCalculator to the client\nRemote: Client is providing ICalcEventListener to the server","sharedworkeracceptor#SharedWorkerAcceptor":"import { Driver, SharedWorkerAcceptor, SharedWorkerServer } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { CompositeCalculator } from \"./providers/CompositeCalculator\";\nconst main = async () => {\n let pool: number = 0;\n const server: SharedWorkerServer<\n ICalcConfig,\n CompositeCalculator,\n ICalcEventListener\n > = new SharedWorkerServer();\n await server.open(\n async (\n acceptor: SharedWorkerAcceptor<\n ICalcConfig,\n CompositeCalculator,\n ICalcEventListener\n >,\n ) => {\n // LIST UP PROPERTIES\n const config: ICalcConfig = acceptor.header;\n const listener: Driver = acceptor.getDriver();\n // ACCEPT OR REJECT THE CONNECTION\n if (pool >= 8) {\n await acceptor.reject(\"Too much connections.\");\n } else {\n await acceptor.accept(new CompositeCalculator(config, listener));\n ++pool;\n await acceptor.join();\n --pool;\n }\n },\n );\n};\nmain().catch(console.error);\nexport interface ICalcConfig {\n precision: number;\n}\nexport interface ICalcEvent {\n type: string;\n input: number[];\n output: number;\n}\nexport interface ICalcEventListener {\n on(event: ICalcEvent): void;\n}\nexport interface ICompositeCalculator extends ISimpleCalculator {\n scientific: IScientificCalculator;\n statistics: IStatisticsCalculator;\n}\nexport interface ISimpleCalculator {\n plus(x: number, y: number): number;\n minus(x: number, y: number): number;\n multiplies(x: number, y: number): number;\n divides(x: number, y: number): number;\n}\nexport interface IScientificCalculator {\n pow(x: number, y: number): number;\n sqrt(x: number): number;\n log(x: number, base: number): number;\n}\nexport interface IStatisticsCalculator {\n mean(...values: number[]): number;\n stdev(...values: number[]): number;\n}\nShared Worker Acceptor.The SharedWorkerAcceptor is a communicator class interacting with the SharedWorkerConnector through RFC (Remote Function Call), created by the SharedWorkerServer class whenever a client connects to the SharedWorker instance.When a remote client connects to the SharedWorkerServer, so that a new SharedworkerAcceptor instance being created, you can determine whether to accept the client's connection or not, reading the SharedWorkerAcceptor.header property. If you've decided to accept the connection, call the SharedWorkerAcceptor.accept() method with Provider instance. Otherwise, reject it thorugh the SharedWorkerAcceptor.reject() method.After accepting the connection, don't forget to closing the connection after your business has been completed to clean up the resources. Otherwise the closing must be performed by the remote client, you can wait the remote client's closing signal by the SharedWorkerAcceptor.join() method.Also, when declaring this SharedworkerAcceptor type, you have to define three generic arguments; Header, Provider and Remote. Those generic arguments must be same with the ones defined in the SharedWorkerServer class.For reference, the first Header type repersents an initial data from the remote client after the connection. I recommend utilize it as an activation tool for security enhancement. The second generic argument Provider represents a provider from server to client, and the other Remote means a provider from the remote client to server.","sharedworkerconnector#SharedWorkerConnector":"import { Driver, SharedWorkerConnector } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEvent } from \"./interfaces/ICalcEvent\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { ICompositeCalculator } from \"./interfaces/ICompositeCalculator\";\nconst main = async () => {\n const stack: ICalcEvent[] = [];\n const listener: ICalcEventListener = {\n on: (evt: ICalcEvent) => stack.push(evt),\n };\n const connector: SharedWorkerConnector<\n ICalcConfig,\n ICalcEventListener,\n ICompositeCalculator\n > = new SharedWorkerConnector(\n { precision: 2 }, // header\n listener, // provider for remote server\n );\n await connector.connect(`./server.js`);\n const remote: Driver = connector.getDriver();\n console.log(\n await remote.plus(10, 20), // returns 30\n await remote.multiplies(3, 4), // returns 12\n await remote.divides(5, 3), // returns 1.67\n await remote.scientific.sqrt(2), // returns 1.41\n await remote.statistics.mean(1, 3, 9), // returns 4.33\n );\n await connector.close();\n for (const evt of stack) console.log(JSON.stringify(evt));\n};\nmain().catch(console.error);\nexport interface ICalcConfig {\n precision: number;\n}\nexport interface ICalcEvent {\n type: string;\n input: number[];\n output: number;\n}\nexport interface ICalcEventListener {\n on(event: ICalcEvent): void;\n}\nexport interface ICompositeCalculator extends ISimpleCalculator {\n scientific: IScientificCalculator;\n statistics: IStatisticsCalculator;\n}\nexport interface ISimpleCalculator {\n plus(x: number, y: number): number;\n minus(x: number, y: number): number;\n multiplies(x: number, y: number): number;\n divides(x: number, y: number): number;\n}\nexport interface IScientificCalculator {\n pow(x: number, y: number): number;\n sqrt(x: number): number;\n log(x: number, base: number): number;\n}\nexport interface IStatisticsCalculator {\n mean(...values: number[]): number;\n stdev(...values: number[]): number;\n}\n30 12 1.67 1.41 4.33\n{\"type\":\"plus\",\"input\":[10,20],\"output\":30}\n{\"type\":\"multiplies\",\"input\":[3,4],\"output\":12}\n{\"type\":\"divides\",\"input\":[5,3],\"output\":1.67}\n{\"type\":\"sqrt\",\"input\":[2],\"output\":1.41}\n{\"type\":\"mean\",\"input\":[1,3,9],\"output\":4.33}\nShared Worker Connector.The SharedWorkerConnector is a communicator class which connects to an SharedWorker instance, and interacts with it through RFC (Remote Function Call) concept.You can connect to the SharedWorkerServer using SharedWorkerConnector.connect() method. The interaction would be started if the server accepts your connection by calling the SharedWorkerAcceptor.accept() method. If the remote server rejects your connection through SharedWorkerAcceptor.reject() method, the exception would be thrown.After the connection, don't forget to closing the connection, if your business logics have been completed, to clean up the resources. Otherwise, the closing must be performed by the remote shared worker server, you can wait the remote server's closing signal through the SharedWorkerConnector.join() method.Also, when declaring this SharedWorkerConnector type, you've to define three generic arguments; Header, Provider and Remote. Those generic arguments must be same with the ones defined in the target SharedWorkerServer and SharedWorkerAcceptor classes (Provider and Remote must be reversed).For reference, the first Header type repersents an initial data from the remote client after the connection. I recommend utilize it as an activation tool for security enhancement. The second generic argument Provider represents a provider from client to server, and the other Remote means a provider from the remote server to client.\nAbove example case:\nHeader: ICalcConfig type\nProvider: Client is providing ICalcEventListener to the server\nRemote: Server is providing ISimpleCalculator to the client\nDemonstrationYou can run it on your local machine.\ngit clone https://github.com/samchon/tgrid.example.shared-worker\nnpm install\nnpm run build\nnpm start"}},"/docs":{"title":"Index","data":{"":"TypeScript Grid Computing Framework.TypeScript RPC (Remote Procedure Call) framework for WebSocket and Worker protocols.\nWebSocket\nWorker\nSharedWorker\nNestJS\nAlso, easy to develop complicated network system like grid computing.\nimport { Driver, WebSocketConnector } from \"tgrid\";\nimport { ICalculator } from \"./ICalculator\";\nexport const webSocketClientMain = async () => {\n // CONNECT TO WEBSOCKET SERVER\n const connector: WebSocketConnector =\n new WebSocketConnector(\n null, // header\n null, // provider for remote server\n );\n await connector.connect(\"ws://127.0.0.1:37000/composite\");\n // CALL REMOTE FUNCTIONS\n const remote: Driver = connector.getDriver();\n console.log(\n await remote.plus(10, 20), // returns 30\n await remote.minus(7, 3), // returns 4\n await remote.multiply(3, 4), // returns 12\n await remote.divide(5, 2), // returns 2.5\n );\n await connector.close();\n};\ninterface ICalculator {\n plus(a: number, b: number): number\n minus(a: number, b: number): number\n multiply(a: number, b: number): number\n divide(a: number, b: number): number\n}\n$ npm start\n30 12 1.67 1.41 4.33"}},"/docs/remote-procedure-call":{"title":"Remote Procedure Call","data":{"outline#Outline":"With TGrid, you can call remote procedures of provided by remote system.If remote system provides a function, TGrid lets you call it as if it was a local function own. If remote system provides some functions that are capsuled in hierarchical structured objects, you still can call them as if they were your own. This is the concept of RPC (Remote Procedure Call) what TGrid is saying.By the way, there are many other RPC (Remote Procedure Call) frameworks or libraries in the world. However, TGrid is different from them. RPC of TGrid does not mean only calling and getting returned value from the remote system's procedure, but also ensuring type safety. With the type safety, you can actually feel like that the remote procedure is your own.","demonstration#Demonstration":"import { Driver, WebSocketConnector } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEvent } from \"./interfaces/ICalcEvent\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { ICompositeCalculator } from \"./interfaces/ICompositeCalculator\";\nexport const webSocketClientMain = async () => {\n const stack: ICalcEvent[] = [];\n const listener: ICalcEventListener = {\n on: (evt: ICalcEvent) => stack.push(evt),\n };\n const connector: WebSocketConnector<\n ICalcConfig,\n ICalcEventListener,\n ICompositeCalculator\n > = new WebSocketConnector(\n { precision: 2 }, // header\n listener, // provider for remote server\n );\n await connector.connect(\"ws://127.0.0.1:37000/composite\");\n const remote: Driver = connector.getDriver();\n console.log(\n await driver.plus(10, 20), // returns 30\n await driver.multiplies(3, 4), // returns 12\n await driver.divides(5, 3), // returns 1.67\n await driver.scientific.sqrt(2), // returns 1.41\n await driver.statistics.mean(1, 3, 9), // returns 4.33\n );\n await connector.close();\n console.log(stack);\n};\nimport { Driver, WebSocketServer } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { CompositeCalculator } from \"./providers/CompositeCalculator\";\nimport { ScientificCalculator } from \"./providers/ScientificCalculator\";\nimport { SimpleCalculator } from \"./providers/SimpleCalculator\";\nimport { StatisticsCalculator } from \"./providers/StatisticsCalculator\";\nexport const webSocketServerMain = async () => {\n const server: WebSocketServer<\n ICalcConfig,\n | CompositeCalculator\n | SimpleCalculator\n | StatisticsCalculator\n | ScientificCalculator,\n ICalcEventListener\n > = new WebSocketServer();\n await server.open(37_000, async (acceptor) => {\n // LIST UP PROPERTIES\n const config: ICalcConfig = acceptor.header;\n const listener: Driver = acceptor.getDriver();\n // ACCEPT OR REJECT\n if (acceptor.path === \"/composite\")\n await acceptor.accept(new CompositeCalculator(config, listener));\n else if (acceptor.path === \"/simple\")\n await acceptor.accept(new SimpleCalculator(config, listener));\n else if (acceptor.path === \"/statistics\")\n await acceptor.accept(new StatisticsCalculator(config, listener));\n else if (acceptor.path === \"/scientific\")\n await acceptor.accept(new ScientificCalculator(config, listener));\n else await acceptor.reject(1002, `WebSocket API endpoint not found.`);\n });\n return server;\n};\nexport interface ICalcConfig {\n precision: number;\n}\nexport interface ICalcEvent {\n type: string;\n input: number[];\n output: number;\n}\nexport interface ICalcEventListener {\n on(event: ICalcEvent): void;\n}\nexport interface ICompositeCalculator extends ISimpleCalculator {\n scientific: IScientificCalculator;\n statistics: IStatisticsCalculator;\n}\nexport interface ISimpleCalculator {\n plus(x: number, y: number): number;\n minus(x: number, y: number): number;\n multiplies(x: number, y: number): number;\n divides(x: number, y: number): number;\n}\nexport interface IScientificCalculator {\n pow(x: number, y: number): number;\n sqrt(x: number): number;\n log(x: number, base: number): number;\n}\nexport interface IStatisticsCalculator {\n mean(...values: number[]): number;\n stdev(...values: number[]): number;\n}\nimport { Driver } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { ICompositeCalculator } from \"./interfaces/ICompositeCalculator\";\nimport { IScientificCalculator } from \"./interfaces/IScientificCalculator\";\nimport { ISimpleCalculator } from \"./interfaces/ISimpleCalculator\";\nimport { IStatisticsCalculator } from \"./interfaces/IStatisticsCalculator\";\nexport abstract class CalculatorBase {\n public constructor(\n private readonly config: ICalcConfig,\n private readonly listener: Driver,\n ) {}\n protected compute(type: string, input: number[], output: number): number {\n const pow: number = Math.pow(10, this.config.precision);\n output = Math.round(output * pow) / pow;\n this.listener.on({ type, input, output }).catch(() => {});\n return output;\n }\n}\nexport class SimpleCalculator\n extends CalculatorBase\n implements ISimpleCalculator\n{\n public plus(x: number, y: number): number {\n return this.compute(\"plus\", [x, y], x + y);\n }\n public minus(x: number, y: number): number {\n return this.compute(\"minus\", [x, y], x - y);\n }\n public multiplies(x: number, y: number): number {\n return this.compute(\"multiplies\", [x, y], x * y);\n }\n public divides(x: number, y: number): number {\n return this.compute(\"divides\", [x, y], x / y);\n }\n}\nexport class ScientificCalculator\n extends CalculatorBase\n implements IScientificCalculator\n{\n public pow(x: number, y: number): number {\n return this.compute(\"pow\", [x, y], Math.pow(x, y));\n }\n public sqrt(x: number): number {\n return this.compute(\"sqrt\", [x], Math.sqrt(x));\n }\n public log(x: number, base: number): number {\n return this.compute(\"log\", [x, base], Math.log(x) / Math.log(base));\n }\n}\nexport class StatisticsCalculator\n extends CalculatorBase\n implements IStatisticsCalculator\n{\n public mean(...values: number[]): number {\n const sum: number = values.reduce((x, y) => x + y);\n return this.compute(\"mean\", values, sum / values.length);\n }\n public stdev(...values: number[]): number {\n const mean: number = values.reduce((x, y) => x + y) / values.length;\n const sum: number = values.reduce((x, y) => x + Math.pow(y - mean, 2));\n return this.compute(\"stdev\", values, Math.sqrt(sum / values.length));\n }\n}\nexport class CompositeCalculator\n extends SimpleCalculator\n implements ICompositeCalculator\n{\n public readonly scientific: ScientificCalculator;\n public readonly statistics: StatisticsCalculator;\n public constructor(\n config: ICalcConfig,\n listener: Driver,\n ) {\n super(config, listener);\n this.scientific = new ScientificCalculator(config, listener);\n this.statistics = new StatisticsCalculator(config, listener);\n }\n}\nexport interface ICalcConfig {\n precision: number;\n}\nexport interface ICalcEvent {\n type: string;\n input: number[];\n output: number;\n}\nexport interface ICalcEventListener {\n on(event: ICalcEvent): void;\n}\nexport interface ICompositeCalculator extends ISimpleCalculator {\n scientific: IScientificCalculator;\n statistics: IStatisticsCalculator;\n}\nexport interface ISimpleCalculator {\n plus(x: number, y: number): number;\n minus(x: number, y: number): number;\n multiplies(x: number, y: number): number;\n divides(x: number, y: number): number;\n}\nexport interface IScientificCalculator {\n pow(x: number, y: number): number;\n sqrt(x: number): number;\n log(x: number, base: number): number;\n}\nexport interface IStatisticsCalculator {\n mean(...values: number[]): number;\n stdev(...values: number[]): number;\n}\n$ npm start\n30 12 1.67 1.41 4.33\n[\n { type: 'plus', input: [ 10, 20 ], output: 30 },\n { type: 'multiplies', input: [ 3, 4 ], output: 12 },\n { type: 'divides', input: [ 5, 3 ], output: 1.67 },\n { type: 'sqrt', input: [ 2 ], output: 1.41 },\n { type: 'mean', input: [ 1, 3, 9 ], output: 4.33 }\n]\nHere is the one of example programs that demonstrating the RPC (Remote Procedure Call) of TGrid.At first, looking at the \"Client Program\" tab, you can find out that the \"Client Program\" is calling the \"Server Program\"'s functions as if they were its own, through the Driver typed instance with await symbols.At next, change the tab to \"Server Program\", then you can find out that the \"Server Program\" is serving CompositeCalculator class to the \"Client Program\". Calling the functions of CompositeCalculator in the \"Server Program\" from the \"Client Program\" through the Driver typed instance, this is the RPC (Remote Procedure Call) of TGrid.\nDemonstrationYou can run it on Playground Website, or local machine.\ngit clone https://github.com/samchon/tgrid.example.websocket\nnpm install\nnpm start","rpc-driver#RPC Driver":"import { Driver, WebSocketConnector } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEvent } from \"./interfaces/ICalcEvent\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { ICompositeCalculator } from \"./interfaces/ICompositeCalculator\";\nexport const webSocketClientMain = async () => {\n const stack: ICalcEvent[] = [];\n const listener: ICalcEventListener = {\n on: (evt: ICalcEvent) => stack.push(evt),\n };\n const connector: WebSocketConnector<\n ICalcConfig,\n ICalcEventListener,\n ICompositeCalculator\n > = new WebSocketConnector(\n { precision: 2 }, // header\n listener, // provider for remote server\n );\n await connector.connect(\"ws://127.0.0.1:37000/composite\");\n const remote: Driver = connector.getDriver();\n console.log(\n await driver.plus(10, 20), // returns 30\n await driver.multiplies(3, 4), // returns 12\n await driver.divides(5, 3), // returns 1.67\n await driver.scientific.sqrt(2), // returns 1.41\n await driver.statistics.mean(1, 3, 9), // returns 4.33\n );\n await connector.close();\n console.log(stack);\n};\nimport { Driver, WebSocketServer } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { CompositeCalculator } from \"./providers/CompositeCalculator\";\nimport { ScientificCalculator } from \"./providers/ScientificCalculator\";\nimport { SimpleCalculator } from \"./providers/SimpleCalculator\";\nimport { StatisticsCalculator } from \"./providers/StatisticsCalculator\";\nexport const webSocketServerMain = async () => {\n const server: WebSocketServer<\n ICalcConfig,\n | CompositeCalculator\n | SimpleCalculator\n | StatisticsCalculator\n | ScientificCalculator,\n ICalcEventListener\n > = new WebSocketServer();\n await server.open(37_000, async (acceptor) => {\n // LIST UP PROPERTIES\n const config: ICalcConfig = acceptor.header;\n const listener: Driver = acceptor.getDriver();\n // ACCEPT OR REJECT\n if (acceptor.path === \"/composite\")\n await acceptor.accept(new CompositeCalculator(config, listener));\n else if (acceptor.path === \"/simple\")\n await acceptor.accept(new SimpleCalculator(config, listener));\n else if (acceptor.path === \"/statistics\")\n await acceptor.accept(new StatisticsCalculator(config, listener));\n else if (acceptor.path === \"/scientific\")\n await acceptor.accept(new ScientificCalculator(config, listener));\n else await acceptor.reject(1002, `WebSocket API endpoint not found.`);\n });\n return server;\n};\nexport interface ICalcConfig {\n precision: number;\n}\nexport interface ICalcEvent {\n type: string;\n input: number[];\n output: number;\n}\nexport interface ICalcEventListener {\n on(event: ICalcEvent): void;\n}\nexport interface ICompositeCalculator extends ISimpleCalculator {\n scientific: IScientificCalculator;\n statistics: IStatisticsCalculator;\n}\nexport interface ISimpleCalculator {\n plus(x: number, y: number): number;\n minus(x: number, y: number): number;\n multiplies(x: number, y: number): number;\n divides(x: number, y: number): number;\n}\nexport interface IScientificCalculator {\n pow(x: number, y: number): number;\n sqrt(x: number): number;\n log(x: number, base: number): number;\n}\nexport interface IStatisticsCalculator {\n mean(...values: number[]): number;\n stdev(...values: number[]): number;\n}\nimport { Driver } from \"tgrid\";\nimport { ICalcConfig } from \"../interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"../interfaces/ICalcEventListener\";\nimport { ICompositeCalculator } from \"../interfaces/ICompositeCalculator\";\nimport { IScientificCalculator } from \"../interfaces/IScientificCalculator\";\nimport { ISimpleCalculator } from \"../interfaces/ISimpleCalculator\";\nimport { IStatisticsCalculator } from \"../interfaces/IStatisticsCalculator\";\nexport abstract class CalculatorBase {\n public constructor(\n private readonly config: ICalcConfig,\n private readonly listener: Driver,\n ) {}\n protected compute(type: string, input: number[], output: number): number {\n const pow: number = Math.pow(10, this.config.precision);\n output = Math.round(output * pow) / pow;\n this.listener.on({ type, input, output }).catch(() => {});\n return output;\n }\n}\nexport class SimpleCalculator\n extends CalculatorBase\n implements ISimpleCalculator\n{\n public plus(x: number, y: number): number {\n return this.compute(\"plus\", [x, y], x + y);\n }\n public minus(x: number, y: number): number {\n return this.compute(\"minus\", [x, y], x - y);\n }\n public multiplies(x: number, y: number): number {\n return this.compute(\"multiplies\", [x, y], x * y);\n }\n public divides(x: number, y: number): number {\n return this.compute(\"divides\", [x, y], x / y);\n }\n}\nexport class ScientificCalculator\n extends CalculatorBase\n implements IScientificCalculator\n{\n public pow(x: number, y: number): number {\n return this.compute(\"pow\", [x, y], Math.pow(x, y));\n }\n public sqrt(x: number): number {\n return this.compute(\"sqrt\", [x], Math.sqrt(x));\n }\n public log(x: number, base: number): number {\n return this.compute(\"log\", [x, base], Math.log(x) / Math.log(base));\n }\n}\nexport class StatisticsCalculator\n extends CalculatorBase\n implements IStatisticsCalculator\n{\n public mean(...values: number[]): number {\n const sum: number = values.reduce((x, y) => x + y);\n return this.compute(\"mean\", values, sum / values.length);\n }\n public stdev(...values: number[]): number {\n const mean: number = values.reduce((x, y) => x + y) / values.length;\n const sum: number = values.reduce((x, y) => x + Math.pow(y - mean, 2));\n return this.compute(\"stdev\", values, Math.sqrt(sum / values.length));\n }\n}\nexport class CompositeCalculator\n extends SimpleCalculator\n implements ICompositeCalculator\n{\n public readonly scientific: ScientificCalculator;\n public readonly statistics: StatisticsCalculator;\n public constructor(\n config: ICalcConfig,\n listener: Driver,\n ) {\n super(config, listener);\n this.scientific = new ScientificCalculator(config, listener);\n this.statistics = new StatisticsCalculator(config, listener);\n }\n}\nexport interface ICalcConfig {\n precision: number;\n}\nexport interface ICalcEvent {\n type: string;\n input: number[];\n output: number;\n}\nexport interface ICalcEventListener {\n on(event: ICalcEvent): void;\n}\nexport interface ICompositeCalculator extends ISimpleCalculator {\n scientific: IScientificCalculator;\n statistics: IStatisticsCalculator;\n}\nexport interface ISimpleCalculator {\n plus(x: number, y: number): number;\n minus(x: number, y: number): number;\n multiplies(x: number, y: number): number;\n divides(x: number, y: number): number;\n}\nexport interface IScientificCalculator {\n pow(x: number, y: number): number;\n sqrt(x: number): number;\n log(x: number, base: number): number;\n}\nexport interface IStatisticsCalculator {\n mean(...values: number[]): number;\n stdev(...values: number[]): number;\n}\nLooking at the above ICompositeCalculator type from the \"Interfaces\" tab, none of the functions are actually asynchrounous. However, the \"Client Program\" is attaching await symbols. It's because every return types of ICompositeCalculator have changed to Promise types through the Driver type.As the Driver typed instance is not a \"Client Program\"'s own, but the \"Server Program\"'s own (CompositeCalculator), the function call must be passed through the asynchronous network communication. In such reason, the remote function calling cannot be synchronous, but asynchronous, and Driver type is casting them.\nTipDescription of Driver type in the Features > Components chatper.Driver is a proxy instance designed to call functions of the remote system. It has a generic argument Remote which means the type of remote system's Provider, and you can remotely call the functions of the Provider asynchronously through the Drive instance.When you call some function of remote Provider by the Driver instance, it hooks the function call expression, and delivers the function name and arguments (parameter values) to the remote system through the Communicator. If the remote system suceeded to reply the result of the function call, Communicator resolves the promise of the function call expression with the result, so that makes Driver working.Otherwise exception is thrown in the remote Provider function, Communicator deliveries the exception instance instead to the remote system, so that actual exception being thrown from the Driver instance.","restrictions#Restrictions":"TGrid has implemented the RPC (Remote Procedure Call) by communicating JSON message. Therefore, if parameters or return values of the remote provided functions are not compatible JSON, you can't use them.For example, JSON does not support bigint type of JavaScript. Therefore, if some of the remote provided functions are using bigint type on their parameters or return value, it would throw an exception.Also, as JSON does not contain class transformation spec, every parameters and return values must be primitive types. If you try to deliver the class instance as a parameter or return value, it would be downgraded to primitive instance in the remote system."}}}
\ No newline at end of file
+{"/docs/projects/chat":{"title":"Chat","data":{"preparing#Preparing":"Preparing the chatting application project documentation.Also, upgrading very old project to be modernized.https://github.com/samchon/tgrid.projects.chat"}},"/docs/projects/market":{"title":"Market","data":{"preparing#Preparing":"Preparing the grid market project documentation.Also, upgrading very old project to be modernized.https://github.com/samchon/tgrid.projects.market"}},"/docs/projects/mutex":{"title":"Mutex","data":{"preparing#Preparing":"Preparing the mutex server project documentation.Also, upgrading very old project to be modernized.https://github.com/samchon/mutex"}},"/docs/setup":{"title":"Setup","data":{"standalone#Standalone":"npm install tgrid\npnpm install tgrid\nyarn add tgrid\nIf you're planning to use TGrid standalone, without NestJS integration, just setup it.","nestjs#NestJS":"Nestia > Guide Documents > SetupOtherwise, you wanna use TGrid with NestJS integration, please refer to the guide documents of nestia.By the way, if you want to setup it right now without reading detailed documents, just run the below commands.\nnpx nestia setup\nnpm install tgrid\nnpx nestia setup --manager pnpm\npnpm install tgrid\nYarn beery is not supported.\nnpx nestia setup --manager yarn\nyarn add tgrid"}},"/":{"title":"Index","data":{"key-features#Key Features":""}},"/docs/examples/object-oriented-network":{"title":"Object Oriented Network","data":{"outline#Outline":"Each remote system is an object.With TGrid, you can easily develop complicated network system, by considering each network system as an object, and interacting with each other through RPC (Remote Procedure Call). TGrid defines this concept as \"Object Oriented Network\".In this chapter, we'll remake the composite calculator system of Remote Object Call chapter again, but replace scientific and statistics calculators to remote system. Therefore, the composite calculator system will be consisted of three remote servers: \"composite server\", \"scientific server\" and \"statistics server\".Let's see how TGrd implements the \"Object Oriented Network\".\nDemonstrationYou can run the example program on Playground Website, or local machine.\ngit clone https://github.com/samchon/tgrid.example.object-oriented-network\nnpm install\nnpm start","client-program#Client Program":"import { Driver, WorkerConnector } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEvent } from \"./interfaces/ICalcEvent\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { ICompositeCalculator } from \"./interfaces/ICompositeCalculator\";\nconst EXTENSION = __filename.endsWith(\".ts\") ? \"ts\" : \"js\";\nexport const workerClientMain = async () => {\n const stack: ICalcEvent[] = [];\n const listener: ICalcEventListener = {\n on: (evt: ICalcEvent) => stack.push(evt),\n };\n const connector: WorkerConnector<\n ICalcConfig,\n ICalcEventListener,\n ICompositeCalculator\n > = new WorkerConnector(\n { precision: 2 }, // header\n listener, // provider for remote server\n \"process\",\n );\n await connector.connect(`${__dirname}/composite.${EXTENSION}`);\n const remote: Driver = connector.getDriver();\n console.log(\n await remote.plus(10, 20), // returns 30\n await remote.multiplies(3, 4), // returns 12\n await remote.divides(5, 3), // returns 1.67\n await remote.scientific.sqrt(2), // returns 1.41\n await remote.statistics.mean(1, 3, 9), // returns 4.33\n );\n await connector.close();\n console.log(stack);\n};\nexport interface ICalcConfig {\n precision: number;\n}\nexport interface ICalcEvent {\n type: string;\n input: number[];\n output: number;\n}\nexport interface ICalcEventListener {\n on(event: ICalcEvent): void;\n}\nexport interface ICompositeCalculator extends ISimpleCalculator {\n plus(x: number, y: number): number;\n minus(x: number, y: number): number;\n multiplies(x: number, y: number): number;\n divides(x: number, y: number): number;\n scientific: IScientificCalculator;\n statistics: IStatisticsCalculator;\n}\nexport interface IScientificCalculator {\n pow(x: number, y: number): number;\n sqrt(x: number): number;\n log(x: number, base: number): number;\n}\nexport interface IStatisticsCalculator {\n mean(...values: number[]): number;\n stdev(...values: number[]): number;\n}\n$ npm start\n30 12 1.67 1.41 4.33\n[\n { type: 'plus', input: [ 10, 20 ], output: 30 },\n { type: 'multiplies', input: [ 3, 4 ], output: 12 },\n { type: 'divides', input: [ 5, 3 ], output: 1.67 },\n { type: 'sqrt', input: [ 2 ], output: 1.41 },\n { type: 'mean', input: [ 1, 3, 9 ], output: 4.33 }\n]","server-programs#Server Programs":"import { Driver, WorkerConnector, WorkerServer } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { IScientificCalculator } from \"./interfaces/IScientificCalculator\";\nimport { IStatisticsCalculator } from \"./interfaces/IStatisticsCalculator\";\nimport { SimpleCalculator } from \"./providers/SimpleCalculator\";\nconst EXTENSION = __filename.endsWith(\".ts\") ? \"ts\" : \"js\";\n/// `CompositeCalculator` has two additional properties\n///\n/// - `scientific` from remote worker server\n/// - `statistics` from remote worker server\nclass CompositeCalculator extends SimpleCalculator {\n public readonly scientific: Driver;\n public readonly statistics: Driver;\n public constructor(props: {\n config: ICalcConfig;\n listener: Driver;\n scientific: Driver;\n statistics: Driver;\n }) {\n super(props.config, props.listener);\n this.scientific = props.scientific;\n this.statistics = props.statistics;\n }\n}\n/// connect to remote worker server\nconst connect = async (\n header: ICalcConfig,\n listener: Driver,\n file: string,\n): Promise> => {\n const connector: WorkerConnector =\n new WorkerConnector(header, listener, \"process\");\n await connector.connect(file);\n return connector.getDriver();\n};\nconst main = async () => {\n const server: WorkerServer<\n ICalcConfig,\n CompositeCalculator,\n ICalcEventListener\n > = new WorkerServer();\n const config: ICalcConfig = await server.getHeader();\n const listener: Driver = server.getDriver();\n // constructor provider combining with remote worker-servers\n const provider: CompositeCalculator = new CompositeCalculator({\n config,\n listener,\n scientific: await connect>(\n config,\n listener,\n `${__dirname}/scientific.${EXTENSION}`,\n ),\n statistics: await connect>(\n config,\n listener,\n `${__dirname}/statistics.${EXTENSION}`,\n ),\n });\n await server.open(provider);\n};\nmain().catch((exp) => {\n console.error(exp);\n process.exit(-1);\n});\nimport { Driver, WorkerServer } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { ScientificCalculator } from \"./providers/ScientificCalculator\";\nconst main = async () => {\n const server: WorkerServer<\n ICalcConfig,\n ScientificCalculator,\n ICalcEventListener\n > = new WorkerServer();\n const header: ICalcConfig = await server.getHeader();\n const listener: Driver = server.getDriver();\n const provider: ScientificCalculator = new ScientificCalculator(\n header,\n listener,\n );\n await server.open(provider);\n};\nmain().catch((exp) => {\n console.error(exp);\n process.exit(-1);\n});\nimport { Driver, WorkerServer } from \"tgrid\";\nimport { ICalcConfig } from \"./interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"./interfaces/ICalcEventListener\";\nimport { StatisticsCalculator } from \"./providers/StatisticsCalculator\";\nconst main = async () => {\n const server: WorkerServer<\n ICalcConfig,\n StatisticsCalculator,\n ICalcEventListener\n > = new WorkerServer();\n const header: ICalcConfig = await server.getHeader();\n const listener: Driver = server.getDriver();\n const provider: StatisticsCalculator = new StatisticsCalculator(\n header,\n listener,\n );\n await server.open(provider);\n};\nmain().catch((exp) => {\n console.error(exp);\n process.exit(-1);\n});\nimport { Driver } from \"tgrid\";\nimport { ICalcConfig } from \"../interfaces/ICalcConfig\";\nimport { ICalcEventListener } from \"../interfaces/ICalcEventListener\";\nimport { IScientificCalculator } from \"../interfaces/IScientificCalculator\";\nimport { ISimpleCalculator } from \"../interfaces/ISimpleCalculator\";\nimport { IStatisticsCalculator } from \"../interfaces/IStatisticsCalculator\";\nexport abstract class CalculatorBase {\n public constructor(\n private readonly config: ICalcConfig,\n private readonly listener: Driver,\n ) {}\n protected compute(type: string, input: number[], output: number): number {\n const pow: number = Math.pow(10, this.config.precision);\n output = Math.round(output * pow) / pow;\n this.listener.on({ type, input, output }).catch(() => {});\n return output;\n }\n}\nexport class ScientificCalculator\n extends CalculatorBase\n implements IScientificCalculator\n{\n public pow(x: number, y: number): number {\n return this.compute(\"pow\", [x, y], Math.pow(x, y));\n }\n public sqrt(x: number): number {\n return this.compute(\"sqrt\", [x], Math.sqrt(x));\n }\n public log(x: number, base: number): number {\n return this.compute(\"log\", [x, base], Math.log(x) / Math.log(base));\n }\n}\nexport class StatisticsCalculator\n extends CalculatorBase\n implements IStatisticsCalculator\n{\n public mean(...values: number[]): number {\n const sum: number = values.reduce((x, y) => x + y);\n return this.compute(\"mean\", values, sum / values.length);\n }\n public stdev(...values: number[]): number {\n const mean: number = values.reduce((x, y) => x + y) / values.length;\n const sum: number = values.reduce((x, y) => x + Math.pow(y - mean, 2));\n return this.compute(\"stdev\", values, Math.sqrt(sum / values.length));\n }\n}\nexport interface ICalcConfig {\n precision: number;\n}\nexport interface ICalcEvent {\n type: string;\n input: number[];\n output: number;\n}\nexport interface ICalcEventListener {\n on(event: ICalcEvent): void;\n}\nexport interface ICompositeCalculator extends ISimpleCalculator {\n plus(x: number, y: number): number;\n minus(x: number, y: number): number;\n multiplies(x: number, y: number): number;\n divides(x: number, y: number): number;\n scientific: IScientificCalculator;\n statistics: IStatisticsCalculator;\n}\nexport interface IScientificCalculator {\n pow(x: number, y: number): number;\n sqrt(x: number): number;\n log(x: number, base: number): number;\n}\nexport interface IStatisticsCalculator {\n mean(...values: number[]): number;\n stdev(...values: number[]): number;\n}\nCompose Provider with Driver of another remote system.Looking at the \"Composite Server\", it is providing CompositeCalculator to the \"Client Program\". By the way, the CompositeCalculator is different with before chapter Remote Object Call. Properties scientific and statistics are composed with Driver of another remote system.Therefore, if \"Client Program\" calls Driver